diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 14f10e2b1c416..f410a9d280347 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -1,10 +1,11 @@ use std::sync::Arc; use rustc_ast::{self as ast, *}; +use rustc_attr_ir::target::Target; use rustc_errors::StashKey; use rustc_hir::def::{DefKind, PerNS, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, GenericArg}; +use rustc_hir::{self as hir, GenericArg, HirId}; use rustc_middle::middle::resolve::PartialRes; use rustc_middle::ty; use rustc_session::diagnostics::add_feature_diagnostics; @@ -263,6 +264,10 @@ impl<'hir> LoweringContext<'_, 'hir> { bound_modifier_allowed_features: Option>, ) -> hir::PathSegment<'hir> { debug!("path_span: {:?}, lower_path_segment(segment: {:?})", path_span, segment); + + let res = self.expect_full_res(segment.id); + let hir_id = self.lower_node_id(segment.id); + let (mut generic_args, infer_args) = if let Some(generic_args) = segment.args.as_deref() { match generic_args { GenericArgs::AngleBracketed(data) => { @@ -315,6 +320,7 @@ impl<'hir> LoweringContext<'_, 'hir> { data, itctx, bound_modifier_allowed_features, + hir_id, ), GenericArgsMode::Err => { // Suggest replacing parentheses with angle brackets `Trait(params...)` to `Trait` @@ -398,13 +404,10 @@ impl<'hir> LoweringContext<'_, 'hir> { ); } - let res = self.expect_full_res(segment.id); - let hir_id = self.lower_node_id(segment.id); debug!( "lower_path_segment: ident={:?} original-id={:?} new-id={:?}", segment.ident, segment.id, hir_id, ); - hir::PathSegment { ident: self.lower_ident(segment.ident), hir_id, @@ -508,6 +511,7 @@ impl<'hir> LoweringContext<'_, 'hir> { data: &ParenthesizedArgs, itctx: ImplTraitContext, bound_modifier_allowed_features: Option>, + segment_hir_id: HirId, ) -> (GenericArgsCtor<'hir>, bool) { // Switch to `PassThrough` mode for anonymous lifetimes; this // means that we permit things like `&Ref`, where `Ref` has @@ -515,6 +519,13 @@ impl<'hir> LoweringContext<'_, 'hir> { // compatibility, even in contexts like an impl header where // we generally don't permit such things (see #51008). let ParenthesizedArgs { span, inputs, inputs_span, output } = data; + + // FIXME uses the segments HIR id which is not correct, it should + // be the parameters HIR id instead. This is currently not lowered. + for input in inputs { + self.lower_attrs(segment_hir_id, &input.attrs, input.span, Target::Param); + } + let inputs = self.arena.alloc_from_iter(inputs.iter().map(|param| { self.lower_ty(¶m.ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitParam)) })); diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index d33c42132ecea..bd869a72f91dd 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -399,7 +399,7 @@ impl<'a> AstValidator<'a> { self.check_decl_num_args(fn_decl); let c_variadic_span = self.check_decl_cvariadic_pos(fn_decl); self.check_decl_splatting(fn_decl, c_variadic_span, splat_semantic); - self.check_decl_attrs(fn_decl); + self.check_decl_attrs(&fn_decl.inputs); self.check_decl_self_param(&fn_decl.inputs, self_semantic); self.check_decl_const_param(&fn_decl.inputs); } @@ -518,10 +518,9 @@ impl<'a> AstValidator<'a> { } } - fn check_decl_attrs(&self, fn_decl: &FnDecl) { + fn check_decl_attrs(&self, fn_inputs: &[Param]) { use SyntheticAttr::*; - fn_decl - .inputs + fn_inputs .iter() .flat_map(|i| i.attrs.as_ref()) .filter(|attr| match &attr.kind { @@ -2230,6 +2229,7 @@ impl Visitor<'_> for AstValidator<'_> { fn visit_path_segment(&mut self, seg: &PathSegment) -> Self::Result { if let Some(Parenthesized(args)) = &seg.args { self.check_decl_self_param(&args.inputs, SelfSemantic::No); + self.check_decl_attrs(&args.inputs); Self::check_decl_no_pat(&args.inputs, |span, _, _| { self.dcx().emit_err(diagnostics::PatternParenthesizedArgList { span }); }); diff --git a/compiler/rustc_attr_ir/src/data_structures.rs b/compiler/rustc_attr_ir/src/data_structures.rs index 6ae4eb1d4b388..4a88cd6190c96 100644 --- a/compiler/rustc_attr_ir/src/data_structures.rs +++ b/compiler/rustc_attr_ir/src/data_structures.rs @@ -859,6 +859,8 @@ pub enum AttributeKind { Deprecated { deprecation: Deprecation, span: Span, + /// `true` if this an inner attribute (ie `#![]`). + is_inner: bool, }, /// Represents `#[diagnostic::do_not_recommend]`. diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index b68042c39670c..084a20b74347d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -210,6 +210,7 @@ impl SingleAttributeParser for DeprecatedParser { Some(AttributeKind::Deprecated { deprecation: Deprecation { since, note, suggestion }, span: cx.attr_span, + is_inner: matches!(cx.attr_style, rustc_ast::AttrStyle::Inner), }) } } diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 648f68588e04b..5d4011b659335 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -161,9 +161,7 @@ rm -r tests/run-make-cargo/panic-immediate-abort-codegen # same rm -r tests/run-make-cargo/panic-strategies # same rm -r tests/run-make/missing-unstable-trait-bound # This disables support for unstable features, but running cg_clif needs some unstable features rm -r tests/run-make/const-trait-stable-toolchain # same -rm -r tests/run-make/print-request-help-stable-unstable # same rm -r tests/run-make/issue-149402-suggest-unresolve # same -rm -r tests/run-make/const-destruct-stable-toolchain # same rm -r tests/run-make/unstable-target-feature # same rm -r tests/run-make/incr-add-rust-src-component rm tests/ui/errors/remap-path-prefix-sysroot.rs # different sysroot source path diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 06a7f28a7d830..ce3741b582827 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -22,8 +22,8 @@ use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_middle::dep_graph::WorkProduct; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mono::{MonoItem, MonoItemData, Visibility}; -use rustc_session::Session; use rustc_session::config::{OptLevel, OutputFilenames, OutputType}; +use rustc_session::{BorrowedIncrCompSession, Session}; use rustc_span::Symbol; use crate::base::CodegenedFunction; @@ -341,6 +341,7 @@ impl WriteBackendMethods for AotDriver { fn run_thin_lto( _cgcx: &CodegenContext, _prof: &SelfProfilerRef, + _incr_comp_session: Option<&BorrowedIncrCompSession>, _dcx: rustc_errors::DiagCtxtHandle<'_>, _exported_symbols_for_lto: &[String], _each_linked_rlib_for_lto: &[PathBuf], diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 0e44966d9b85d..414bff06866a8 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -92,7 +92,9 @@ use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; use rustc_middle::ty::TyCtxt; use rustc_session::config::{OptLevel, OutputFilenames}; -use rustc_session::{CodegenBackendInit, EarlySession, IncrCompSession, Session}; +use rustc_session::{ + BorrowedIncrCompSession, CodegenBackendInit, EarlySession, IncrCompSession, Session, +}; use rustc_span::{Symbol, sym}; use rustc_target::spec::{RelocModel, TargetTuple}; use tempfile::TempDir; @@ -427,6 +429,7 @@ impl WriteBackendMethods for GccCodegenBackend { fn run_thin_lto( _cgcx: &CodegenContext, _prof: &SelfProfilerRef, + _incr_comp_session: Option<&BorrowedIncrCompSession>, _dcx: DiagCtxtHandle<'_>, // FIXME(bjorn3): Limit LTO exports to these symbols _exported_symbols_for_lto: &[String], diff --git a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs index 7aa1920ccf81d..0bc6363d075b5 100644 --- a/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs +++ b/compiler/rustc_codegen_llvm/src/back/llvm_backend.rs @@ -23,7 +23,9 @@ use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; use rustc_middle::ty::TyCtxt; use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; -use rustc_session::{CodegenBackendInit, EarlySession, IncrCompSession, Session}; +use rustc_session::{ + BorrowedIncrCompSession, CodegenBackendInit, EarlySession, IncrCompSession, Session, +}; use rustc_span::{Symbol, sym}; use rustc_target::spec::{RelocModel, TlsModel}; @@ -122,6 +124,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { fn run_thin_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, + incr_comp_session: Option<&BorrowedIncrCompSession>, dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], @@ -130,6 +133,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { back::lto::run_thin( cgcx, prof, + incr_comp_session, dcx, exported_symbols_for_lto, each_linked_rlib_for_lto, @@ -381,21 +385,35 @@ impl CodegenBackend for LlvmCodegenBackend { }); } - (compiled_modules, work_products) - } + if sess.codegen_units().as_usize() == 1 && sess.opts.unstable_opts.time_llvm_passes { + let timings = + llvm::build_string(|s| unsafe { llvm::LLVMRustPrintPassTimings(s) }).unwrap(); + print!("{timings}"); + } - fn print_pass_timings(&self) { - let timings = llvm::build_string(|s| unsafe { llvm::LLVMRustPrintPassTimings(s) }).unwrap(); - print!("{timings}"); - } + if sess.print_llvm_stats() { + let stats = + llvm::build_string(|s| unsafe { llvm::LLVMRustPrintStatistics(s) }).unwrap(); + print!("{stats}"); + } - fn print_statistics(&self) { - let stats = llvm::build_string(|s| unsafe { llvm::LLVMRustPrintStatistics(s) }).unwrap(); - print!("{stats}"); - } + if let Some(out_path) = sess.print_llvm_stats_json() { + let llvm_stats_json = + llvm::build_string(|s| unsafe { llvm::LLVMRustPrintStatisticsJSON(s) }).unwrap(); + + if !llvm_stats_json.is_empty() { + if let Err(e) = std::fs::write(&out_path, llvm_stats_json) { + sess.dcx().err(format!("failed to write stats to {out_path}: {e}")); + } + } else { + sess.dcx().warn(format!( + "requested to print LLVM statistics to JSON file {out_path}, but the codegen backend \ + did not provide any statistics", + )); + } + } - fn print_statistics_json(&self) -> String { - llvm::build_string(|s| unsafe { llvm::LLVMRustPrintStatisticsJSON(s) }).unwrap() + (compiled_modules, work_products) } fn link( diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 13cbd021c8262..c99eb45438353 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -19,7 +19,7 @@ use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_middle::dep_graph::WorkProduct; -use rustc_session::config; +use rustc_session::{BorrowedIncrCompSession, config}; use rustc_span::bug; use rustc_structures::SanitizerSet; use tracing::{debug, info}; @@ -186,6 +186,7 @@ pub(crate) fn run_fat( pub(crate) fn run_thin( cgcx: &CodegenContext, prof: &SelfProfilerRef, + incr_comp_session: Option<&BorrowedIncrCompSession>, dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], @@ -201,7 +202,7 @@ pub(crate) fn run_thin( is deferred to the linker" ); } - thin_lto(cgcx, prof, dcx, modules, upstream_modules, &symbols_below_threshold) + thin_lto(prof, incr_comp_session, dcx, modules, upstream_modules, &symbols_below_threshold) } fn fat_lto( @@ -368,8 +369,8 @@ fn fat_lto( /// all of the `LtoModuleCodegen` units returned below and destroyed once /// they all go out of scope. fn thin_lto( - cgcx: &CodegenContext, prof: &SelfProfilerRef, + incr_comp_session: Option<&BorrowedIncrCompSession>, dcx: DiagCtxtHandle<'_>, modules: Vec>, serialized_modules: Vec<(SerializedModule, CString)>, @@ -460,13 +461,12 @@ fn thin_lto( info!("thin LTO data created"); - let new_key_map_path = cgcx - .new_incr_comp_session_dir - .as_ref() - .map(|dir| dir.join(THIN_LTO_KEYS_INCR_COMP_FILE_NAME)); + let new_key_map_path = incr_comp_session.as_ref().map(|incr_comp_session| { + incr_comp_session.new_session_directory.join(THIN_LTO_KEYS_INCR_COMP_FILE_NAME) + }); - let prev_key_map = if let Some(ref old_incr_comp_session_dir) = - cgcx.old_incr_comp_session_dir + let prev_key_map = if let Some(ref old_incr_comp_session_dir) = incr_comp_session + .and_then(|incr_comp_session| incr_comp_session.old_session_directory.as_deref()) { let old_path = old_incr_comp_session_dir.join(THIN_LTO_KEYS_INCR_COMP_FILE_NAME); @@ -478,7 +478,7 @@ fn thin_lto( assert!(green_modules.is_empty()); None }; - let curr_key_map = if cgcx.new_incr_comp_session_dir.is_some() { + let curr_key_map = if incr_comp_session.is_some() { ThinLTOKeysMap::from_thin_lto_modules(&data, &thin_modules, &module_names) } else { assert!(green_modules.is_empty()); @@ -503,8 +503,7 @@ fn thin_lto( if let (Some(prev_key_map), true) = (prev_key_map.as_ref(), green_modules.contains_key(module_name)) { - assert!(cgcx.old_incr_comp_session_dir.is_some()); - assert!(cgcx.new_incr_comp_session_dir.is_some()); + assert!(incr_comp_session.unwrap().old_session_directory.is_some()); // If a module exists in both the current and the previous session, // and has the same LTO cache key in both sessions, then we can re-use it diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 60a29e5c73b16..87e0612bc102d 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -6,6 +6,7 @@ use std::{assert_matches, iter, ptr}; use libc::{c_longlong, c_uint}; use rustc_abi::{Align, Endian, Layout, NumScalableVectors, Size}; +use rustc_ast::{IntTy, UintTy}; use rustc_codegen_ssa::debuginfo::type_names::{VTableNameKind, cpp_like_debuginfo}; use rustc_codegen_ssa::traits::*; use rustc_hir::def::{CtorKind, DefKind}; @@ -794,9 +795,50 @@ fn build_basic_type_di_node<'ll, 'tcx>( _ => bug!("debuginfo::build_basic_type_di_node - `t` is invalid type"), }; - let ty_di_node = create_basic_type(cx, name, cx.size_of(t), encoding); + // Around LLDB 21 or 22, LLDB started using prebuilt Clang type nodes for primitives rather than + // reading the full DWARF data. These nodes are based on the size and signedness of the + // primitive, and ignore pretty much all other fields. This causes 2 issues: + // 1. type names get normalized to C-style names which isn't ideal. The normalization occurs + // even in generic args. + // 2. if you have a generic type `S`, and 2 instantiations are `S` + // and `S` (on a 64-bit target), the use of prebuilt nodes causes a name collision. + // Whichever instantiation is interacted with first will work fine, all others will break a + // bunch of stuff in the LLDB pipeline. To avoid this, we explicitly treat `usize` and `isize` + // as typedefs of their equivalent types. For more info see: llvm/llvm-project#196812 + + let mut needs_typedef = cpp_like_debuginfo; + + let ty_di_node = match t.kind() { + ty::Uint(UintTy::Usize) => { + needs_typedef = true; + + let equivalent = match t.primitive_size(cx.tcx).bits() { + 8 => cx.tcx.types.u8, + 16 => cx.tcx.types.u16, + 32 => cx.tcx.types.u32, + 64 => cx.tcx.types.u64, + 128 => cx.tcx.types.u128, + x => bug!("Unexpected signed int size {x}"), + }; + type_di_node(cx, equivalent) + } + ty::Int(IntTy::Isize) => { + needs_typedef = true; + + let equivalent = match t.primitive_size(cx.tcx).bits() { + 8 => cx.tcx.types.i8, + 16 => cx.tcx.types.i16, + 32 => cx.tcx.types.i32, + 64 => cx.tcx.types.i64, + 128 => cx.tcx.types.i128, + x => bug!("Unexpected signed int size: {x}"), + }; + type_di_node(cx, equivalent) + } + _ => create_basic_type(cx, name, cx.size_of(t), encoding), + }; - if !cpp_like_debuginfo { + if !needs_typedef { return DINodeCreationResult::new(ty_di_node, false); } diff --git a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs index 431783552bcd0..792ab9873378a 100644 --- a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs +++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs @@ -111,7 +111,7 @@ impl<'tcx> AssertModuleSource<'tcx> { if !self.check_config(cfg) { debug!("check_attr: config does not match, ignoring attr"); - return; + continue; } let user_path = module.as_str(); diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 18968f0f89960..0d7d85320d6fd 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -25,7 +25,7 @@ use rustc_middle::ty::TyCtxt; use rustc_session::config::{ self, Lto, OptLevel, OutFileName, OutputFilenames, OutputType, Passes, SwitchWithOptPath, }; -use rustc_session::{IncrCompSession, Session}; +use rustc_session::{BorrowedIncrCompSession, IncrCompSession, Session}; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, FileName, InnerSpan, Span, SyntaxContext, bug}; use rustc_structures::CrateType; @@ -349,12 +349,6 @@ pub struct CodegenContext { /// Directory into which should the LLVM optimization remarks be written. /// If `None`, they will be written to stderr. pub remark_dir: Option, - /// The previous incremental compilation session directory, or None if we - /// are not compiling incrementally or there is no previous session. - pub old_incr_comp_session_dir: Option, - /// The incremental compilation session directory, or None if we are not - /// compiling incrementally - pub new_incr_comp_session_dir: Option, /// `Some(limit)` if the codegen should be run in parallel. /// /// Depends on [`WriteBackendMethods::supports_parallel()`] and `--jobs-backend`. @@ -364,6 +358,7 @@ pub struct CodegenContext { fn generate_thin_lto_work( cgcx: &CodegenContext, prof: &SelfProfilerRef, + incr_comp_session: Option<&BorrowedIncrCompSession>, dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], @@ -374,6 +369,7 @@ fn generate_thin_lto_work( let (lto_modules, copy_jobs) = B::run_thin_lto( cgcx, prof, + incr_comp_session, dcx, exported_symbols_for_lto, each_linked_rlib_for_lto, @@ -820,6 +816,7 @@ pub(crate) fn compute_per_cgu_lto_type( fn execute_optimize_work_item( cgcx: &CodegenContext, prof: &SelfProfilerRef, + new_incr_comp_session_dir: Option<&Path>, shared_emitter: SharedEmitter, mut module: ModuleCodegen, ) -> WorkItemResult { @@ -839,7 +836,7 @@ fn execute_optimize_work_item( // save our module to disk first. let bitcode = if cgcx.module_config.emit_pre_lto_bc { let filename = pre_lto_bitcode_filename(&module.name); - cgcx.new_incr_comp_session_dir.as_ref().map(|path| path.join(&filename)) + new_incr_comp_session_dir.map(|path| path.join(&filename)) } else { None }; @@ -877,6 +874,7 @@ fn execute_optimize_work_item( fn execute_copy_from_cache_work_item( cgcx: &CodegenContext, prof: &SelfProfilerRef, + old_incr_comp_session_dir: &Path, shared_emitter: SharedEmitter, module: CachedModuleCodegen, ) -> CompiledModule { @@ -886,10 +884,8 @@ fn execute_copy_from_cache_work_item( let dcx = DiagCtxt::new(Box::new(shared_emitter)); let dcx = dcx.handle(); - let incr_comp_session_dir = cgcx.old_incr_comp_session_dir.as_ref().unwrap(); - let load_from_incr_comp_dir = |output_path: PathBuf, saved_path: &str| { - let source_file_in_incr_comp_dir = incr_comp_session_dir.join(saved_path); + let source_file_in_incr_comp_dir = old_incr_comp_session_dir.join(saved_path); debug!( "copying preexisting module `{}` from {:?} to {}", module.name, @@ -989,6 +985,7 @@ fn do_fat_lto( fn do_thin_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, + incr_comp_session: Option, shared_emitter: SharedEmitter, tm_factory: TargetMachineFactoryFn, exported_symbols_for_lto: &[String], @@ -1032,6 +1029,7 @@ fn do_thin_lto( for (i, (work, cost)) in generate_thin_lto_work::( cgcx, prof, + incr_comp_session.as_ref(), dcx, &exported_symbols_for_lto, &each_linked_rlib_for_lto, @@ -1081,6 +1079,12 @@ fn do_thin_lto( spawn_thin_lto_work( &cgcx, prof, + incr_comp_session + .as_ref() + .and_then(|incr_comp_session| { + incr_comp_session.old_session_directory.as_deref() + }) + .map(ToOwned::to_owned), shared_emitter.clone(), Arc::clone(&tm_factory), coordinator_send.clone(), @@ -1214,6 +1218,8 @@ fn start_executing_work( ) -> thread::JoinHandle, ()>> { let sess = tcx.sess; let prof = sess.prof.clone(); + let incr_comp_session = + tcx.incr_comp_session.map(|incr_comp_session| incr_comp_session.borrow()); // Compute the set of symbols we need to retain when doing thin local LTO (if we need to) let exported_symbols_for_lto = @@ -1266,15 +1272,6 @@ fn start_executing_work( time_trace: sess.opts.unstable_opts.llvm_time_trace, remark: sess.opts.cg.remark.clone(), remark_dir, - old_incr_comp_session_dir: tcx - .incr_comp_session - .as_ref() - .and_then(|incr_comp_session| incr_comp_session.old_session_directory.as_deref()) - .map(ToOwned::to_owned), - new_incr_comp_session_dir: tcx - .incr_comp_session - .as_ref() - .map(|incr_comp_session| (&*incr_comp_session.new_session_directory).to_owned()), output_filenames: Arc::clone(tcx.output_filenames(())), module_config: regular_config, opt_level, @@ -1514,6 +1511,7 @@ fn start_executing_work( spawn_work( &cgcx, &prof, + incr_comp_session.as_ref(), shared_emitter.clone(), coordinator_send.clone(), &mut llvm_start_time, @@ -1540,6 +1538,7 @@ fn start_executing_work( spawn_work( &cgcx, &prof, + incr_comp_session.as_ref(), shared_emitter.clone(), coordinator_send.clone(), &mut llvm_start_time, @@ -1584,6 +1583,7 @@ fn start_executing_work( spawn_work( &cgcx, &prof, + incr_comp_session.as_ref(), shared_emitter.clone(), coordinator_send.clone(), &mut llvm_start_time, @@ -1752,6 +1752,7 @@ fn start_executing_work( compiled_modules.extend(do_thin_lto::( &cgcx, &prof, + incr_comp_session, shared_emitter.clone(), tm_factory, &exported_symbols_for_lto, @@ -1849,6 +1850,7 @@ pub(crate) struct WorkerFatalError; fn spawn_work<'a, B: WriteBackendMethods>( cgcx: &CodegenContext, prof: &'a SelfProfilerRef, + incr_comp_session: Option<&BorrowedIncrCompSession>, shared_emitter: SharedEmitter, coordinator_send: Sender>, llvm_start_time: &mut Option>, @@ -1859,6 +1861,11 @@ fn spawn_work<'a, B: WriteBackendMethods>( *llvm_start_time = Some(prof.verbose_generic_activity("LLVM_passes")); } + let old_incr_comp_session_dir = incr_comp_session + .and_then(|incr_comp_session| incr_comp_session.old_session_directory.clone()); + let new_incr_comp_session_dir = + incr_comp_session.map(|incr_comp_session| incr_comp_session.new_session_directory.clone()); + let cgcx = cgcx.clone(); let prof = prof.clone(); @@ -1867,10 +1874,22 @@ fn spawn_work<'a, B: WriteBackendMethods>( let _profiler = if cgcx.time_trace { B::thread_profiler() } else { Box::new(()) }; let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work { - WorkItem::Optimize(m) => execute_optimize_work_item(&cgcx, &prof, shared_emitter, m), - WorkItem::CopyPostLtoArtifacts(m) => WorkItemResult::Finished( - execute_copy_from_cache_work_item(&cgcx, &prof, shared_emitter, m), + WorkItem::Optimize(m) => execute_optimize_work_item( + &cgcx, + &prof, + new_incr_comp_session_dir.as_deref(), + shared_emitter, + m, ), + WorkItem::CopyPostLtoArtifacts(m) => { + WorkItemResult::Finished(execute_copy_from_cache_work_item( + &cgcx, + &prof, + old_incr_comp_session_dir.as_deref().unwrap(), + shared_emitter, + m, + )) + } })); let msg = match result { @@ -1897,6 +1916,7 @@ fn spawn_work<'a, B: WriteBackendMethods>( fn spawn_thin_lto_work( cgcx: &CodegenContext, prof: &SelfProfilerRef, + old_incr_comp_session_dir: Option, shared_emitter: SharedEmitter, tm_factory: TargetMachineFactoryFn, coordinator_send: Sender, @@ -1911,9 +1931,13 @@ fn spawn_thin_lto_work( let _profiler = if cgcx.time_trace { B::thread_profiler() } else { Box::new(()) }; let result = std::panic::catch_unwind(AssertUnwindSafe(|| match work { - ThinLtoWorkItem::CopyPostLtoArtifacts(m) => { - execute_copy_from_cache_work_item(&cgcx, &prof, shared_emitter, m) - } + ThinLtoWorkItem::CopyPostLtoArtifacts(m) => execute_copy_from_cache_work_item( + &cgcx, + &prof, + old_incr_comp_session_dir.as_deref().unwrap(), + shared_emitter, + m, + ), ThinLtoWorkItem::ThinLto(m) => { let _timer = prof.generic_activity_with_arg("codegen_module_perform_lto", m.name()); B::optimize_and_codegen_thin(&cgcx, &prof, &shared_emitter, tm_factory, m) @@ -2113,15 +2137,25 @@ impl OngoingCodegen { let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); // Catch fatal errors to ensure shared_emitter_main.check() can emit the actual diagnostics - let compiled_modules = catch_fatal_errors(|| match maybe_lto_modules { + let compilation_output = catch_fatal_errors(|| match maybe_lto_modules { MaybeLtoModules::NoLto(compiled_modules) => { drop(shared_emitter); - compiled_modules + + let work_products = copy_all_cgu_workproducts_to_incr_comp_cache_dir( + sess, + incr_comp_session, + &compiled_modules, + ); + + (compiled_modules, work_products) } MaybeLtoModules::FatLto { cgcx, needs_fat_lto } => { let tm_factory = self.backend.target_machine_factory(sess, cgcx.opt_level); - CompiledModules { + // FIXME cache local pre-LTO bitcode + let work_products = WorkProductMap::default(); + + let compiled_modules = CompiledModules { modules: vec![do_fat_lto( sess, &cgcx, @@ -2132,15 +2166,18 @@ impl OngoingCodegen { needs_fat_lto, )], allocator_module: None, - } + }; + + (compiled_modules, work_products) } MaybeLtoModules::ThinLto { cgcx, needs_thin_lto } => { let tm_factory = self.backend.target_machine_factory(sess, cgcx.opt_level); - CompiledModules { + let compiled_modules = CompiledModules { modules: do_thin_lto::( &cgcx, &sess.prof, + incr_comp_session.map(|incr_comp_session| incr_comp_session.borrow()), shared_emitter, tm_factory, &crate_info.exported_symbols_for_lto, @@ -2149,7 +2186,17 @@ impl OngoingCodegen { sess.opts.recommended_stack_size, ), allocator_module: None, - } + }; + + // FIXME include pre-LTO bitcode in workproduct tracking + // FIXME add separate incr comp session for post-LTO outputs to use during link step + let work_products = copy_all_cgu_workproducts_to_incr_comp_cache_dir( + sess, + incr_comp_session, + &compiled_modules, + ); + + (compiled_modules, work_products) } }); @@ -2157,19 +2204,14 @@ impl OngoingCodegen { sess.dcx().abort_if_errors(); - let mut compiled_modules = - compiled_modules.expect("fatal error emitted but not sent to SharedEmitter"); + let (mut compiled_modules, work_products) = + compilation_output.expect("fatal error emitted but not sent to SharedEmitter"); // Regardless of what order these modules completed in, report them to // the backend in the same order every time to ensure that we're handing // out deterministic results. compiled_modules.modules.sort_by(|a, b| a.name.cmp(&b.name)); - let work_products = copy_all_cgu_workproducts_to_incr_comp_cache_dir( - sess, - incr_comp_session, - &compiled_modules, - ); produce_final_output_artifacts(sess, &compiled_modules, &self.output_filenames); (compiled_modules, work_products) diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index e06ac36fd758a..bf5d67c213573 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -120,14 +120,6 @@ pub trait CodegenBackend { crate_info: &CrateInfo, ) -> (CompiledModules, WorkProductMap); - fn print_pass_timings(&self) {} - - fn print_statistics(&self) {} - - fn print_statistics_json(&self) -> String { - String::new() - } - /// This is called on the returned [`CompiledModules`] from [`join_codegen`](Self::join_codegen). fn link( &self, diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index bb63e189d20d3..4c0df97a41985 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -5,7 +5,7 @@ use std::path::PathBuf; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::DiagCtxtHandle; use rustc_middle::dep_graph::WorkProduct; -use rustc_session::{Session, config}; +use rustc_session::{BorrowedIncrCompSession, Session, config}; use crate::back::lto::ThinModule; use crate::back::write::{ @@ -50,6 +50,7 @@ pub trait WriteBackendMethods: Clone + 'static { fn run_thin_lto( cgcx: &CodegenContext, prof: &SelfProfilerRef, + incr_comp_session: Option<&BorrowedIncrCompSession>, dcx: DiagCtxtHandle<'_>, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 3e673a6c1aa45..b878affae386a 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -1305,7 +1305,7 @@ pub fn handle_help(matches: &getopts::Matches, args: &[String]) -> bool { let chelp_pos = opt_help_pos("C"); let print_help = || { // Only show unstable options in --help if we accept unstable options. - let unstable_enabled = nightly_options::is_unstable_enabled(&matches); + let unstable_enabled = nightly_options::unstable_options_enabled(&matches); let nightly_build = nightly_options::match_is_nightly_build(&matches); usage(matches.opt_present("verbose"), unstable_enabled, nightly_build); }; diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 4505bcf394ace..e67f25105ed26 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -100,7 +100,6 @@ pub(crate) enum MatcherLoc { Token { token: Token, }, - Delimited, Sequence { op: KleeneOp, num_metavar_decls: usize, @@ -132,7 +131,6 @@ impl MatcherLoc { pub(super) fn span(&self) -> Option { match self { MatcherLoc::Token { token } => Some(token.span), - MatcherLoc::Delimited => None, MatcherLoc::Sequence { .. } => None, MatcherLoc::SequenceKleeneOpNoSep { .. } => None, MatcherLoc::SequenceSep { .. } => None, @@ -155,7 +153,6 @@ impl Display for MatcherLoc { MatcherLoc::Eof => f.write_str("end of macro"), // These are not printed in the diagnostic - MatcherLoc::Delimited => f.write_str("delimiter"), MatcherLoc::Sequence { .. } => f.write_str("sequence start"), MatcherLoc::SequenceKleeneOpNoSep { .. } => f.write_str("sequence end"), MatcherLoc::SequenceKleeneOpAfterSep { .. } => f.write_str("sequence end"), @@ -173,13 +170,17 @@ pub(super) fn compute_locs(matcher: &[TokenTree]) -> Vec { for tt in tts { match tt { TokenTree::Token(token) => { - locs.push(MatcherLoc::Token { token: *token }); + // We ignore doc comments. This is a bug, but #95267 showed that existing + // programs rely on this behaviour, and changing it would require some care and + // a transition period. + if !matches!(token, Token { kind: DocComment(..), .. }) { + locs.push(MatcherLoc::Token { token: *token }); + } } TokenTree::Delimited(span, _, delimited) => { let open_token = Token::new(delimited.delim.as_open_token_kind(), span.open); let close_token = Token::new(delimited.delim.as_close_token_kind(), span.close); - locs.push(MatcherLoc::Delimited); locs.push(MatcherLoc::Token { token: open_token }); inner(&delimited.tts, locs, next_metavar, seq_depth); locs.push(MatcherLoc::Token { token: close_token }); @@ -508,28 +509,16 @@ impl TtParser { match matcher_loc { MatcherLoc::Token { token: t } => { - // If it's a doc comment, we just ignore it and move on to the next tt in the - // matcher. This is a bug, but #95267 showed that existing programs rely on this - // behaviour, and changing it would require some care and a transition period. - // // If the token matches, we can just advance the parser. // // Otherwise, this match has failed, there is nothing to do, and hopefully another // mp in `cur_mps` will match. - if matches!(t, Token { kind: DocComment(..), .. }) { - mp.idx += 1; - self.cur_mps.push(mp); - } else if token_name_eq(t, token) { + if token_name_eq(t, token) { track.matched_one(parser, mp.idx); mp.idx += 1; self.next_mps.push(mp); } } - MatcherLoc::Delimited => { - // Entering the delimiter is trivial. - mp.idx += 1; - self.cur_mps.push(mp); - } &MatcherLoc::Sequence { op, num_metavar_decls, diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 12f308fc546c3..1ded75caaa48d 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -245,7 +245,8 @@ impl MacroRulesMacroExpander { } match try_match_macro_derive(psess, body, rules, &mut NoopTracker) { - Ok((rule_index, rule, named_matches)) => { + Ok((rule_index, named_matches)) => { + let rule = &rules[rule_index]; let MacroRule::Derive { rhs, .. } = rule else { panic!("try_match_macro_derive returned non-derive rule"); }; @@ -450,7 +451,8 @@ fn expand_macro<'cx, 'a: 'cx>( let try_success_result = try_match_macro(psess, &arg, rules, &mut NoopTracker); match try_success_result { - Ok((rule_index, rule, named_matches)) => { + Ok((rule_index, named_matches)) => { + let rule = &rules[rule_index]; let MacroRule::Func { lhs, rhs, .. } = rule else { panic!("try_match_macro returned non-func rule"); }; @@ -539,7 +541,8 @@ fn expand_macro_attr( // Track nothing for the best performance. match try_match_macro_attr(psess, &args, &body, rules, &mut NoopTracker) { - Ok((i, rule, named_matches)) => { + Ok((i, named_matches)) => { + let rule = &rules[i]; let MacroRule::Attr { rhs, unsafe_rule, .. } = rule else { panic!("try_macro_match_attr returned non-attr rule"); }; @@ -609,7 +612,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>( arg: &TokenStream, rules: &'matcher [MacroRule], track: &mut T, -) -> Result<(usize, &'matcher MacroRule, NamedMatches), CanRetry> { +) -> Result<(usize, NamedMatches), CanRetry> { // We create a base parser that can be used for the "black box" parts. // Every iteration needs a fresh copy of that parser. However, the parser // is not mutated on many of the iterations, particularly when dealing with @@ -653,7 +656,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>( // Merge the gated spans from parsing the matcher with the preexisting ones. psess.gated_spans.merge(gated_spans_snapshot); - return Ok((i, rule, named_matches)); + return Ok((i, named_matches)); } Failure => { trace!("Failed to match arm, trying the next one"); @@ -689,7 +692,7 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>( attr_body: &TokenStream, rules: &'matcher [MacroRule], track: &mut T, -) -> Result<(usize, &'matcher MacroRule, NamedMatches), CanRetry> { +) -> Result<(usize, NamedMatches), CanRetry> { // This uses the same strategy as `try_match_macro` let args_parser = parser_from_cx(psess, attr_args.clone(), T::recovery()); let body_parser = parser_from_cx(psess, attr_body.clone(), T::recovery()); @@ -722,7 +725,7 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>( psess.gated_spans.merge(gated_spans_snapshot); #[allow(rustc::potential_query_instability)] named_matches.extend(body_named_matches); - return Ok((i, rule, named_matches)); + return Ok((i, named_matches)); } Failure => { mem::swap(&mut gated_spans_snapshot, &mut psess.gated_spans.spans.borrow_mut()) @@ -744,7 +747,7 @@ pub(super) fn try_match_macro_derive<'matcher, T: Tracker<'matcher>>( body: &TokenStream, rules: &'matcher [MacroRule], track: &mut T, -) -> Result<(usize, &'matcher MacroRule, NamedMatches), CanRetry> { +) -> Result<(usize, NamedMatches), CanRetry> { // This uses the same strategy as `try_match_macro` let body_parser = parser_from_cx(psess, body.clone(), T::recovery()); let mut tt_parser = TtParser::new(); @@ -760,7 +763,7 @@ pub(super) fn try_match_macro_derive<'matcher, T: Tracker<'matcher>>( match result { Success(named_matches) => { psess.gated_spans.merge(gated_spans_snapshot); - return Ok((i, rule, named_matches)); + return Ok((i, named_matches)); } Failure => { mem::swap(&mut gated_spans_snapshot, &mut psess.gated_spans.spans.borrow_mut()) diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 83bf4f47a8ac9..d0d0f9283f98d 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -34,17 +34,38 @@ pub struct Feature { issue: Option>, } -#[derive(Clone, Copy, Debug, Hash)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub enum UnstableFeatures { - /// Disallow use of unstable features, as on beta/stable channels. + /// Disallow use of unstable features and unstable compiler flags. + /// + /// Used by default by beta/stable channels. Disallow, - /// Allow use of unstable features, as on nightly. + /// Allow use of unstable features and unstable compiler flags. + /// + /// Used by default by the nightly channel. Allow, + /// Allow use of unstable features and unstable compiler flags, even if it is not the default + /// behavior. + /// + /// Can be used to make a beta/stable channel act like a nightly channel. + /// /// Errors are bypassed for bootstrapping. This is required any time /// during the build that feature-related lints are set to warn or above /// because the build turns on warnings-as-errors and uses lots of unstable /// features. As a result, this is always required for building Rust itself. Cheat, + /// Allow the use of selected *unstable compiler flags*, but disallow the use of + /// *unstable language features*. + /// + /// **This mode is designed only for internal testing of rustc.** + /// + /// Specifically, we need it when we want to test how rustc behaves on the stable channel, + /// but at the same time we need to be able to pass it some unstable `-Z` compiler flags + /// required for the testing infrastructure itself to work. + AllowSelectedUnstableCompilerFlagsOnly { + /// Unstable compiler flags that we are allowed to use, without their `-Z` prefix. + allowed_flags: Vec, + }, } impl UnstableFeatures { @@ -70,8 +91,15 @@ impl UnstableFeatures { let is_unstable_crate = |var: &str| krate.is_some_and(|name| var.split(',').any(|new_krate| new_krate == name)); + let allow_unstable_flags_prefix = "-allow-unstable-compiler-flags="; if let Ok(val) = env_var_rustc_bootstrap.as_deref() { match val { + // Act as stable, but still accept unstable compiler flags required by compiletest + val if let Some(flags) = val.strip_prefix(allow_unstable_flags_prefix) => { + return UnstableFeatures::AllowSelectedUnstableCompilerFlagsOnly { + allowed_flags: flags.split(",").map(|s| s.trim().to_string()).collect(), + }; + } val if val == "1" || is_unstable_crate(val) => return UnstableFeatures::Cheat, // Hypnotize ourselves so that we think we are a stable compiler and thus don't // allow any unstable features. @@ -83,10 +111,26 @@ impl UnstableFeatures { if disable_unstable_features { UnstableFeatures::Disallow } else { UnstableFeatures::Allow } } + /// Are we allowed to use the given unstable `flag`? + /// If it is a `-Z`, it should be passed without the `-Z` prefix. + pub fn allow_unstable_compiler_flag(&self, flag: &str) -> bool { + match self { + UnstableFeatures::Allow | UnstableFeatures::Cheat => true, + UnstableFeatures::AllowSelectedUnstableCompilerFlagsOnly { allowed_flags } + if allowed_flags.iter().any(|allowed_flag| allowed_flag == flag) => + { + true + } + UnstableFeatures::Disallow + | UnstableFeatures::AllowSelectedUnstableCompilerFlagsOnly { .. } => false, + } + } + pub fn is_nightly_build(&self) -> bool { match *self { UnstableFeatures::Allow | UnstableFeatures::Cheat => true, - UnstableFeatures::Disallow => false, + UnstableFeatures::Disallow + | UnstableFeatures::AllowSelectedUnstableCompilerFlagsOnly { .. } => false, } } } diff --git a/compiler/rustc_feature/src/tests.rs b/compiler/rustc_feature/src/tests.rs index a5d589171d105..32e7cc5ddc9fd 100644 --- a/compiler/rustc_feature/src/tests.rs +++ b/compiler/rustc_feature/src/tests.rs @@ -34,4 +34,16 @@ fn rustc_bootstrap_parsing() { // Does not support specifying any crate. assert!(is_force_stable(Some("x"))); assert!(is_force_stable(Some("x,y,z"))); + + // `RUSTC_BOOTSTRAP=-allow-unstable-compiler-flags` simulates the stable toolchain, only + // allowing the specified unstable compiler flags to be passed. + std::assert_eq!( + UnstableFeatures::from_environment_value( + None, + Ok("-allow-unstable-compiler-flags=foo,bar".to_string()) + ), + UnstableFeatures::AllowSelectedUnstableCompilerFlagsOnly { + allowed_flags: vec!["foo".to_string(), "bar".to_string()] + } + ); } diff --git a/compiler/rustc_hir_id/src/definitions.rs b/compiler/rustc_hir_id/src/definitions.rs index 557b87bedff85..52b64a2c7fcfc 100644 --- a/compiler/rustc_hir_id/src/definitions.rs +++ b/compiler/rustc_hir_id/src/definitions.rs @@ -18,6 +18,7 @@ pub use rustc_span::def_id::DefPathHash; use rustc_span::def_id::{ CRATE_DEF_INDEX, CrateNum, DefIndex, LOCAL_CRATE, LocalDefId, LocalDefIdMap, StableCrateId, }; +use rustc_span::hygiene::{HygieneRemapper, LocalExpnIdRemapper, SyntaxContextRemapper}; use rustc_span::{SpanEncoder, Symbol, kw, sym}; use tracing::{debug, instrument}; @@ -313,6 +314,9 @@ impl Definitions { let def_index = DefIndex::from_usize(self.def_id_to_key.len()); self.first_non_det_index.replace(def_index); + + SyntaxContextRemapper::with(|mut r| r.commit_end_of_determinism()); + LocalExpnIdRemapper::with(|mut r| r.commit_end_of_determinism()); } pub fn first_non_det_index(&self) -> DefIndex { diff --git a/compiler/rustc_hir_id/src/lib.rs b/compiler/rustc_hir_id/src/lib.rs index 0e01600f9cb3d..7cb43e9a8ccf9 100644 --- a/compiler/rustc_hir_id/src/lib.rs +++ b/compiler/rustc_hir_id/src/lib.rs @@ -16,6 +16,7 @@ use rustc_data_structures::stable_hash::{ StableHash, StableHashCtxt, StableHasher, StableOrd, ToStableHashKey, }; use rustc_macros::{Decodable, Encodable, StableHash}; +use rustc_serialize::ContextOrd; use rustc_span::def_id::{CRATE_DEF_ID, DefId, DefIndex, DefPathHash, LocalDefId, LocalModId}; #[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable)] @@ -176,6 +177,12 @@ rustc_index::newtype_index! { pub struct ItemLocalId {} } +impl ContextOrd for ItemLocalId { + fn stable_cmp_with_context(_context: E) -> impl FnMut(&Self, &Self) -> std::cmp::Ordering { + ::cmp + } +} + impl ItemLocalId { /// Signal local id which should never be used. pub const INVALID: ItemLocalId = ItemLocalId::MAX; diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 3f896584eaef2..038bddaa021d2 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -268,7 +268,7 @@ pub(crate) fn prepare_session_directory( None }; - IncrCompSession { old_session_directory, new_session_directory } + IncrCompSession::new(old_session_directory, new_session_directory) } /// This function finalizes and thus 'publishes' the session directory by diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 759297bc69592..94cc1484586c8 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -67,30 +67,6 @@ impl Linker { } }); - if sess.codegen_units().as_usize() == 1 && sess.opts.unstable_opts.time_llvm_passes { - codegen_backend.print_pass_timings() - } - - if sess.print_llvm_stats() { - codegen_backend.print_statistics() - } - - if let Some(out_path) = sess.print_llvm_stats_json() { - let llvm_stats_json = codegen_backend.print_statistics_json(); - - if !llvm_stats_json.is_empty() { - if let Err(e) = std::fs::write(&out_path, llvm_stats_json) { - sess.dcx().err(format!("failed to write stats to {}: {}", out_path, e)); - } - } else { - sess.dcx().warn(format!( - "requested to print LLVM statistics to JSON file {}, but the codegen backend \ - did not provide any statistics", - out_path, - )); - } - } - sess.timings.end_section(sess.dcx(), TimingSection::Codegen); if sess.opts.incremental.is_some() diff --git a/compiler/rustc_lint/src/diagnostics.rs b/compiler/rustc_lint/src/diagnostics.rs index 5167737ac001f..6273414e9ccda 100644 --- a/compiler/rustc_lint/src/diagnostics.rs +++ b/compiler/rustc_lint/src/diagnostics.rs @@ -2299,19 +2299,21 @@ pub(crate) enum InvalidNanComparisons { #[derive(Subdiagnostic)] pub(crate) enum InvalidNanComparisonsSuggestion { #[multipart_suggestion( - "use `f32::is_nan()` or `f64::is_nan()` instead", + "use the `is_nan` method instead", style = "verbose", applicability = "machine-applicable" )] - Spanful { - #[suggestion_part(code = "!")] - neg: Option, - #[suggestion_part(code = ".is_nan()")] - float: Span, + Ufcs { + prefix: String, + #[suggestion_part(code = "{prefix}")] + prefix_span: Span, + suffix: &'static str, + #[suggestion_part(code = "{suffix}")] + close_span: Span, #[suggestion_part(code = "")] - nan_plus_binop: Span, + remove_nan: Span, }, - #[help("use `f32::is_nan()` or `f64::is_nan()` instead")] + #[help("use the `is_nan` method instead")] Spanless, } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 87b79f12838ae..08c0e8d6f2557 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -2,6 +2,7 @@ use std::iter; use rustc_abi::{BackendRepr, TagEncoding, Variants, WrappingRange}; use rustc_ast as ast; +use rustc_ast::util::parser::ExprPrecedence; use rustc_attr_ir::find_attr; use rustc_attr_ir::lang_items::LangItem; use rustc_hir as hir; @@ -112,8 +113,8 @@ declare_lint! { } declare_lint! { - /// The `invalid_nan_comparisons` lint checks comparison with `f32::NAN` or `f64::NAN` - /// as one of the operand. + /// The `invalid_nan_comparisons` lint checks comparisons with the `NAN` constants + /// of `f16`, `f32`, `f64`, and `f128`. /// /// ### Example /// @@ -126,8 +127,8 @@ declare_lint! { /// /// ### Explanation /// - /// NaN does not compare meaningfully to anything – not - /// even itself – so those comparisons are always false. + /// NaN compares unequal to every floating-point value, including itself. + /// Use the `is_nan` method to check whether a value is NaN. INVALID_NAN_COMPARISONS, Warn, "detects invalid floating point NaN comparisons" @@ -228,59 +229,124 @@ fn lint_nan<'tcx>( l: &'tcx hir::Expr<'tcx>, r: &'tcx hir::Expr<'tcx>, ) { - fn is_nan(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - let expr = expr.peel_blocks().peel_borrows(); - match expr.kind { - ExprKind::Path(qpath) => { - let Some(def_id) = cx.typeck_results().qpath_res(&qpath, expr.hir_id).opt_def_id() - else { - return false; - }; + fn nan_ty(cx: &LateContext<'_>, mut expr: &hir::Expr<'_>) -> Option { + loop { + expr = expr.peel_blocks(); + match expr.kind { + ExprKind::AddrOf(.., inner) => expr = inner, + _ => break, + } + } + let ExprKind::Path(qpath) = expr.kind else { return None }; + let def_id = cx.typeck_results().qpath_res(&qpath, expr.hir_id).opt_def_id()?; + Some(match cx.tcx.get_diagnostic_name(def_id)? { + sym::f16_nan => ty::FloatTy::F16, + sym::f32_nan => ty::FloatTy::F32, + sym::f64_nan => ty::FloatTy::F64, + sym::f128_nan => ty::FloatTy::F128, + _ => return None, + }) + } - matches!( - cx.tcx.get_diagnostic_name(def_id), - Some(sym::f16_nan | sym::f32_nan | sym::f64_nan | sym::f128_nan) - ) + fn nan_method_path( + cx: &LateContext<'_>, + mut nan: &hir::Expr<'_>, + float_ty: ty::FloatTy, + cmp_span: Span, + ) -> Option { + loop { + match nan.kind { + ExprKind::AddrOf(.., inner) => nan = inner, + ExprKind::Block(block, _) if block.stmts.is_empty() => nan = block.expr?, + _ => break, } - _ => false, + } + let ExprKind::Path(hir::QPath::TypeRelative(ty, _)) = nan.kind else { + return None; + }; + if !ty.span.from_expansion() && cmp_span.contains(ty.span) { + let ty = cx.sess().source_map().span_to_snippet(ty.span).ok()?; + Some(format!("<{ty}>::is_nan")) + } else if matches!(float_ty, ty::FloatTy::F32 | ty::FloatTy::F64) { + let krate = std_or_core(cx)?; + Some(format!("::{krate}::primitive::{}::is_nan", float_ty.name_str())) + } else { + None } } - fn eq_ne( - e: &hir::Expr<'_>, - l: &hir::Expr<'_>, - r: &hir::Expr<'_>, - f: impl FnOnce(Span, Span) -> InvalidNanComparisonsSuggestion, - ) -> InvalidNanComparisons { - let suggestion = if let Some(l_span) = l.span.find_ancestor_inside(e.span) - && let Some(r_span) = r.span.find_ancestor_inside(e.span) - { - f(l_span, r_span) + fn eq_ne_suggestion<'a>( + cx: &LateContext<'a>, + cmp: &'a hir::Expr<'a>, + value: &'a hir::Expr<'a>, + nan: &'a hir::Expr<'a>, + float_ty: ty::FloatTy, + is_ne: bool, + ) -> InvalidNanComparisonsSuggestion { + if cmp.span.from_expansion() { + return InvalidNanComparisonsSuggestion::Spanless; + } + let (Some(value_span), Some(nan_span)) = ( + value.span.find_ancestor_inside_same_ctxt(cmp.span), + nan.span.find_ancestor_inside_same_ctxt(cmp.span), + ) else { + return InvalidNanComparisonsSuggestion::Spanless; + }; + if value_span.overlaps(nan_span) { + return InvalidNanComparisonsSuggestion::Spanless; + } + let remove_nan = if nan_span.lo() < value_span.lo() { + nan_span.until(value_span) } else { - InvalidNanComparisonsSuggestion::Spanless + value_span.shrink_to_hi().to(nan_span) }; - InvalidNanComparisons::EqNe { suggestion } + let Some(mut ty) = cx.typeck_results().expr_ty_opt(value) else { + return InvalidNanComparisonsSuggestion::Spanless; + }; + let mut deref_count = 0; + while let ty::Ref(_, inner, _) = *ty.kind() { + deref_count += 1; + ty = inner; + } + let neg = if is_ne { "!" } else { "" }; + let Some(method_path) = nan_method_path(cx, nan, float_ty, cmp.span) else { + return InvalidNanComparisonsSuggestion::Spanless; + }; + let (open, suffix) = if deref_count != 0 && cx.precedence(value) < ExprPrecedence::Prefix { + ("(", "))") + } else { + ("", ")") + }; + InvalidNanComparisonsSuggestion::Ufcs { + prefix: format!("{neg}{method_path}({}{open}", "*".repeat(deref_count)), + prefix_span: value_span.shrink_to_lo(), + suffix, + close_span: value_span.shrink_to_hi(), + remove_nan, + } } + let Some((value, nan, float_ty)) = nan_ty(cx, l) + .map(|float_ty| (r, l, float_ty)) + .or_else(|| nan_ty(cx, r).map(|float_ty| (l, r, float_ty))) + else { + return; + }; + let typeck_results = cx.typeck_results(); + // Custom comparison implementations need not follow primitive float NaN semantics. + if ![l, r].into_iter().all(|operand| { + typeck_results + .expr_ty_opt(operand) + .is_some_and(|ty| matches!(*ty.peel_refs().kind(), ty::Float(fty) if fty == float_ty)) + }) { + return; + } let lint = match binop { - hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => { - eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful { - nan_plus_binop: l_span.until(r_span), - float: r_span.shrink_to_hi(), - neg: (binop == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()), - }) - } - hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => { - eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful { - nan_plus_binop: l_span.shrink_to_hi().to(r_span), - float: l_span.shrink_to_hi(), - neg: (binop == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()), - }) - } - hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge - if is_nan(cx, l) || is_nan(cx, r) => - { + hir::BinOpKind::Eq | hir::BinOpKind::Ne => InvalidNanComparisons::EqNe { + suggestion: eq_ne_suggestion(cx, e, value, nan, float_ty, binop == hir::BinOpKind::Ne), + }, + hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge => { InvalidNanComparisons::LtLeGtGe } _ => return, diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 674eff4a51f09..dc8a117704ca4 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -36,10 +36,13 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_session::config::mitigation_coverage::DeniedPartialMitigation; use rustc_session::config::{OptLevel, OutputType, TargetModifier}; use rustc_span::def_id::CRATE_MOD_ID; -use rustc_span::hygiene::HygieneEncodeContext; +use rustc_span::hygiene::{ + HygieneEncodeContext, HygieneRemapper, HygieneRemappingResults, LocalExpnIdRemapper, + SyntaxContextRemapper, +}; use rustc_span::{ - ByteSymbol, ExternalSource, FileName, SourceFile, SpanData, SpanEncoder, StableSourceFileId, - Symbol, SyntaxContext, bug, span_bug, sym, + ByteSymbol, ExternalSource, FileName, LocalExpnId, SourceFile, SpanData, SpanEncoder, + StableSourceFileId, Symbol, SyntaxContext, bug, span_bug, sym, }; use rustc_structures::CrateType; use tracing::{debug, instrument, trace}; @@ -80,6 +83,8 @@ pub(super) struct EncodeContext<'a, 'tcx> { // Used for both `Symbol`s and `ByteSymbol`s. symbol_index_table: FxHashMap, local_defs_remapping: LocalDefsRemapping, + syntax_context_remapping: HygieneRemappingResults, + local_expn_id_remapping: HygieneRemappingResults, } /// If the current crate is a proc-macro, returns early with `LazyArray::default()`. @@ -102,6 +107,8 @@ macro_rules! encoder_methods { } impl<'a, 'tcx> Encoder for EncodeContext<'a, 'tcx> { + const DETERMINISTIC_HASH_MAP_ENCODE: bool = true; + encoder_methods! { emit_usize(usize); emit_u128(u128); @@ -150,6 +157,10 @@ impl<'a, 'tcx> Encodable> for ExpnIndex { } impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { + fn def_path_hasher(&self) -> impl Fn(DefId) -> DefPathHash { + self.tcx.prelocked_def_path_hasher() + } + fn encode_crate_num(&mut self, crate_num: CrateNum) { if crate_num != LOCAL_CRATE && self.is_proc_macro { panic!("Attempted to encode non-local CrateNum {crate_num:?} for proc-macro crate"); @@ -173,8 +184,8 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { } fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) { - let idx = self.hygiene_ctxt.borrow_mut().get_syntax_ctxt_encoding_index(syntax_context); - idx.encode(self); + self.hygiene_ctxt.borrow_mut().enqueue_syntax_context_for_encoding(syntax_context); + self.emit_u32(self.syntax_context_remapping.map_id(syntax_context).as_u32()); } fn encode_expn_id(&mut self, expn_id: ExpnId) { @@ -186,7 +197,13 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { self.hygiene_ctxt.borrow_mut().schedule_expn_data_for_encoding(expn_id); } expn_id.krate.encode(self); - expn_id.local_id.encode(self); + + let expn_index = match expn_id.as_local() { + Some(local_expn) => self.local_expn_id_remapping.map_id(local_expn).as_raw(), + None => expn_id.local_id, + }; + + expn_index.encode(self); } fn encode_span(&mut self, span: Span) { @@ -666,6 +683,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // while `with_encode_metadata_header` is used in some other places too. self.local_defs_remapping = LocalDefsRemapping::new(tcx); + (self.syntax_context_remapping, self.local_expn_id_remapping) = tcx + .with_stable_hashing_context(|mut ctx| { + ( + SyntaxContextRemapper::with(|r| r.remapping_results(&mut ctx)), + LocalExpnIdRemapper::with(|r| r.remapping_results(&mut ctx)), + ) + }); + let mut stats: Vec<(&'static str, usize)> = Vec::with_capacity(32); macro_rules! stat { @@ -2061,15 +2086,16 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { HygieneEncodeContext::encode( &Rc::clone(&self.hygiene_ctxt), &mut (&mut *self, &mut syntax_contexts, &mut expn_data_table, &mut expn_hash_table), - |(this, syntax_contexts, _, _), index, ctxt_data| { - syntax_contexts.set_some(index, this.lazy(ctxt_data)); + |(this, syntax_contexts, _, _), ctxt, ctxt_data| { + let context = this.syntax_context_remapping.map_id(ctxt); + syntax_contexts.set_some(context.as_u32(), this.lazy(ctxt_data)); }, - |(this, _, expn_data_table, expn_hash_table), index, expn_data, hash| { - if let Some(index) = index.as_local() { - expn_data_table - .set_some(index.as_raw(), this.lazy(expn_data.expect("local expn"))); + |(this, _, expn_data_table, expn_hash_table), expn_id, expn_data, hash| { + if let Some(expn_id) = expn_id.as_local() { + let expn_index = this.local_expn_id_remapping.map_id(expn_id).as_raw(); - expn_hash_table.set_some(index.as_raw(), this.lazy(hash)); + expn_data_table.set_some(expn_index, this.lazy(expn_data.expect("local expn"))); + expn_hash_table.set_some(expn_index, this.lazy(hash)); } }, ); @@ -2756,6 +2782,8 @@ fn with_encode_metadata_header( hygiene_ctxt: Default::default(), symbol_index_table: Default::default(), local_defs_remapping: Default::default(), + syntax_context_remapping: Default::default(), + local_expn_id_remapping: Default::default(), }; // Encode the rustc version string in a predictable location. diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs index dcb5775f20595..f93b80cc6c95c 100644 --- a/compiler/rustc_middle/src/dep_graph/graph.rs +++ b/compiler/rustc_middle/src/dep_graph/graph.rs @@ -861,12 +861,6 @@ impl DepGraph { self.data.as_ref().and_then(|data| data.previous_work_products.get(v).cloned()) } - /// Access the map of work-products created during the cached run. Only - /// used during saving of the dep-graph. - pub fn previous_work_products(&self) -> &WorkProductMap { - &self.data.as_ref().unwrap().previous_work_products - } - pub fn debug_was_loaded_from_disk(&self, dep_node: DepNode) -> bool { self.data.as_ref().unwrap().debug_loaded_from_disk.lock().contains(&dep_node) } diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 7383ad23273be..14794b5cb73c7 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -14,7 +14,7 @@ use rustc_hir::definitions::DefPathHash; use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable}; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixedSize, MemDecoder}; -use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; +use rustc_serialize::{ContextOrd, Decodable, Decoder, Encodable, Encoder}; use rustc_session::Session; use rustc_span::hygiene::{ ExpnId, HygieneDecodeContext, HygieneEncodeContext, SyntaxContext, SyntaxContextKey, @@ -116,6 +116,9 @@ struct Footer { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Encodable, Decodable)] struct SourceFileIndex(u32); +// this is never used by the on disk cache, since it is not required to be deterministic +impl ContextOrd for SourceFileIndex {} + #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Encodable, Decodable)] struct AbsoluteBytePos(u64); @@ -283,10 +286,10 @@ impl OnDiskCache { HygieneEncodeContext::encode( &Rc::clone(&encoder.hygiene_context), &mut encoder, - |encoder, index, ctxt_data| { + |encoder, ctxt, ctxt_data| { let pos = AbsoluteBytePos::new(encoder.position()); encoder.encode_tagged(TAG_SYNTAX_CONTEXT, ctxt_data); - syntax_contexts.insert(index, pos); + syntax_contexts.insert(ctxt.as_u32(), pos); }, |encoder, expn_id, data, hash| { if expn_id.krate == LOCAL_CRATE { @@ -838,8 +841,8 @@ impl<'tcx> CacheEncoder<'tcx> { impl<'tcx> SpanEncoder for CacheEncoder<'tcx> { fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) { - let idx = self.hygiene_context.borrow_mut().get_syntax_ctxt_encoding_index(syntax_context); - idx.encode(self); + self.hygiene_context.borrow_mut().enqueue_syntax_context_for_encoding(syntax_context); + self.emit_u32(syntax_context.as_u32()); } fn encode_expn_id(&mut self, expn_id: ExpnId) { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 913f56eba36f5..20a2cc4dadaa7 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1144,6 +1144,22 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Useful when one needs to access a lot of `DefPathHash`-es in quick succession. Stable + /// sorting a collection for example. + /// + /// Read locks the structures containing the `DefPathHash` mappings, allowing us to skip lots of + /// locking and releasing using `[Self::def_path_hash]` would do + pub fn prelocked_def_path_hasher( + self, + ) -> impl Fn(DefId) -> rustc_hir::definitions::DefPathHash { + let definitions = self.definitions_untracked(); + let cstore = self.cstore_untracked(); + move |def_id| match def_id.as_local() { + Some(def_id) => definitions.def_path_hash(def_id), + None => cstore.def_path_hash(def_id), + } + } + #[inline] pub fn crate_types(self) -> &'tcx [CrateType] { &self.crate_types diff --git a/compiler/rustc_monomorphize/src/offload/manifest.rs b/compiler/rustc_monomorphize/src/offload/manifest.rs index 8fdd8e10ab419..fdaa3f6b017fd 100644 --- a/compiler/rustc_monomorphize/src/offload/manifest.rs +++ b/compiler/rustc_monomorphize/src/offload/manifest.rs @@ -8,7 +8,7 @@ use std::fs; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lock; -use rustc_hir::def_id::{DefId, DefIndex, LOCAL_CRATE, StableCrateId}; +use rustc_hir::def_id::{DefId, DefIndex, DefPathHash, LOCAL_CRATE, StableCrateId}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mono::MonoItem; use rustc_middle::ty::codec::{TyDecoder, TyEncoder}; @@ -44,6 +44,8 @@ impl<'a, 'tcx> OffloadManifestEncoder<'a, 'tcx> { } impl<'a, 'tcx> Encoder for OffloadManifestEncoder<'a, 'tcx> { + const DETERMINISTIC_HASH_MAP_ENCODE: bool = true; + fn emit_usize(&mut self, v: usize) { self.encoder.emit_usize(v); } @@ -86,6 +88,10 @@ impl<'a, 'tcx> Encoder for OffloadManifestEncoder<'a, 'tcx> { } impl<'a, 'tcx> SpanEncoder for OffloadManifestEncoder<'a, 'tcx> { + fn def_path_hasher(&self) -> impl Fn(DefId) -> DefPathHash { + self.tcx.prelocked_def_path_hasher() + } + fn encode_span(&mut self, _span: Span) { // Spans are not needed in the manifest, encode a dummy span. self.emit_usize(0); diff --git a/compiler/rustc_parse/src/diagnostics.rs b/compiler/rustc_parse/src/diagnostics.rs index a942d6b060dab..6331eb6d499e2 100644 --- a/compiler/rustc_parse/src/diagnostics.rs +++ b/compiler/rustc_parse/src/diagnostics.rs @@ -2077,14 +2077,6 @@ pub(crate) struct PathFoundCVariadicParams { pub span: Span, } -#[derive(Diagnostic)] -#[diag("`Trait(...)` syntax does not support attributes in parameters")] -pub(crate) struct PathFoundAttributeInParams { - #[primary_span] - #[suggestion("remove the attributes", applicability = "machine-applicable", code = "")] - pub span: Span, -} - #[derive(Diagnostic)] #[diag("path separator must be a double colon")] pub(crate) struct PathSingleColon { diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 8dfe95a849449..738b207ad76ce 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -15,8 +15,8 @@ use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{Parser, Restrictions, TokenType}; use crate::ast::{PatKind, TyKind}; use crate::diagnostics::{ - self, ConstGenericWithoutBraces, ConstGenericWithoutBracesSugg, PathFoundAttributeInParams, - PathFoundCVariadicParams, PathSingleColon, PathTripleColon, + self, ConstGenericWithoutBraces, ConstGenericWithoutBracesSugg, PathFoundCVariadicParams, + PathSingleColon, PathTripleColon, }; use crate::exp; use crate::parser::{CommaRecoveryMode, Expr, FnContext, FnParseMode, RecoverColon, RecoverComma}; @@ -433,7 +433,9 @@ impl<'a> Parser<'a> { dcx.emit_err(PathFoundCVariadicParams { span: param.pat.span }); } if !param.attrs.is_empty() { - dcx.emit_err(PathFoundAttributeInParams { span: param.attrs[0].span }); + self.psess + .gated_spans + .gate(sym::named_fn_trait_parameters, param.attrs[0].span); } Ok(param) }); diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 162914c95c0e5..451c3a39dd480 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -101,7 +101,7 @@ fn annotation_kind(tcx: TyCtxt<'_>, def_id: LocalDefId) -> AnnotationKind { fn lookup_deprecation_entry(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { let depr = find_attr!(tcx, def_id, - Deprecated { deprecation, span: _ } => *deprecation + Deprecated { deprecation, .. } => *deprecation ); let Some(depr) = depr else { diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index 364ae2a10a1e7..6f1076619ca80 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -14,7 +14,7 @@ #[cfg(test)] extern crate self as rustc_serialize; -pub use self::serialize::{Decodable, Decoder, Encodable, Encoder}; +pub use self::serialize::{ContextOrd, Decodable, Decoder, Encodable, Encoder}; mod serialize; diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs index 32a301a28ae8e..6426dc37e99a0 100644 --- a/compiler/rustc_serialize/src/serialize.rs +++ b/compiler/rustc_serialize/src/serialize.rs @@ -85,6 +85,67 @@ pub trait Encoder { } fn emit_raw_bytes(&mut self, s: &[u8]); + + /// Controls whether `HashMap`-s are stable sorted by their key before encoding. Some encoders + /// don't need determinism, improving performance. + const DETERMINISTIC_HASH_MAP_ENCODE: bool = false; + + fn encode_hash_map(&mut self, map: &HashMap) + where + Self: Sized, + K: Encodable + for<'a> ContextOrd<&'a Self>, + V: Encodable, + S: BuildHasher, + { + self.emit_usize(map.len()); + if Self::DETERMINISTIC_HASH_MAP_ENCODE { + // we are sorting right after + #[expect(rustc::potential_query_instability)] + let mut values: Vec<_> = map.iter().collect(); + let mut cmp = >::stable_cmp_with_context(self); + values.sort_by(|(k1, _), (k2, _)| cmp(*k1, *k2)); + drop(cmp); + + for (key, val) in values { + key.encode(self); + val.encode(self); + } + } else { + // this is explicitly allowed when Self::DETERMINISTIC_HASH_MAP_ENCODE is false + #[expect(rustc::potential_query_instability)] + for (key, val) in map.iter() { + key.encode(self); + val.encode(self); + } + } + } + + fn encode_hash_set(&mut self, map: &HashSet) + where + Self: Sized, + K: Encodable + for<'a> ContextOrd<&'a Self>, + S: BuildHasher, + { + self.emit_usize(map.len()); + if Self::DETERMINISTIC_HASH_MAP_ENCODE { + // we are sorting right after + #[expect(rustc::potential_query_instability)] + let mut values: Vec<_> = map.iter().collect(); + let mut cmp = >::stable_cmp_with_context(self); + values.sort_by(|k1, k2| cmp(*k1, *k2)); + drop(cmp); + + for key in values { + key.encode(self); + } + } else { + // this is explicitly allowed when Self::DETERMINISTIC_HASH_MAP_ENCODE is false + #[expect(rustc::potential_query_instability)] + for key in map.iter() { + key.encode(self); + } + } + } } // Note: all the methods in this trait are infallible, which may be surprising. @@ -608,16 +669,13 @@ where impl Encodable for HashMap where - K: Encodable + Eq, + E: Sized, + K: Encodable + Eq + for<'a> ContextOrd<&'a E>, V: Encodable, S: BuildHasher, { fn encode(&self, e: &mut E) { - e.emit_usize(self.len()); - for (key, val) in self { - key.encode(e); - val.encode(e); - } + e.encode_hash_map(self) } } @@ -635,14 +693,11 @@ where impl Encodable for HashSet where - T: Encodable + Eq, + T: Encodable + for<'a> ContextOrd<&'a E>, S: BuildHasher, { - fn encode(&self, s: &mut E) { - s.emit_usize(self.len()); - for e in self { - e.encode(s); - } + fn encode(&self, e: &mut E) { + e.encode_hash_set(self) } } @@ -763,3 +818,26 @@ impl Decodable for Hash128 { Self::new(u128::from_le_bytes(d.read_raw_bytes(16).try_into().unwrap())) } } + +pub trait ContextOrd { + /// The returned comparator must be stable. + /// E. g. compares `DefPathHash` instead of `DefIndex` + fn stable_cmp_with_context(_context: Ctx) -> impl FnMut(&Self, &Self) -> std::cmp::Ordering { + unimplemented!(); + #[allow(unused)] + |_i, _i2| unreachable!() + } +} + +impl ContextOrd for String { + fn stable_cmp_with_context(_context: Ctx) -> impl FnMut(&Self, &Self) -> std::cmp::Ordering { + ::cmp + } +} + +// FIXME remove this. This is only here because CacheEncoder, which does not need to be +// deterministic, encodes a HashMap. If a real implementation is ever needed, that use should +// be switched to a new type instead of u32 +impl ContextOrd for u32 {} + +impl ContextOrd for i64 {} diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index fd148dccf097c..5a96c0524c093 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -513,7 +513,7 @@ impl LinkerFeaturesCli { /// When *not* using `-Z unstable-options` on the CLI, ensure only stable linker features are /// used, for the given `TargetTuple`. Returns `Ok` if no unstable variants are used. - /// The caller should ensure that e.g. `nightly_options::is_unstable_enabled()` + /// The caller should ensure that e.g. [`nightly_options::unstable_options_enabled`] /// returns false. pub(crate) fn check_unstable_variants(&self, target_tuple: &TargetTuple) -> Result<(), String> { // `-C linker-features=-lld` is only stable on x64 linux. @@ -2411,7 +2411,7 @@ pub fn parse_crate_edition( } }; - if !edition.is_stable() && !nightly_options::is_unstable_enabled(matches) { + if !edition.is_stable() && !nightly_options::unstable_options_enabled(matches) { let msg = if !is_nightly { format!( "the crate requires edition {edition}, but the latest edition supported by this \ @@ -2429,10 +2429,10 @@ pub fn parse_crate_edition( fn check_error_format_stability( early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions, - is_nightly_build: bool, + unstable_flags_enabled: bool, format: ErrorOutputType, ) { - if unstable_opts.unstable_options || is_nightly_build { + if unstable_opts.unstable_options || unstable_flags_enabled { return; } let format = match format { @@ -2792,7 +2792,7 @@ pub fn build_session_options( check_error_format_stability( early_dcx, &unstable_opts, - unstable_features.is_nightly_build(), + unstable_features.allow_unstable_compiler_flag("error-format"), error_format, ); @@ -2888,7 +2888,7 @@ pub fn build_session_options( } } - let unstable_options_enabled = nightly_options::is_unstable_enabled(matches); + let unstable_options_enabled = nightly_options::unstable_options_enabled(matches); if !unstable_options_enabled && cg.force_frame_pointers == FramePointer::NonLeaf { early_dcx.early_fatal( "`-Cforce-frame-pointers=non-leaf` or `always` also requires `-Zunstable-options` \ @@ -2896,7 +2896,7 @@ pub fn build_session_options( ) } - if !nightly_options::is_unstable_enabled(matches) && !unstable_opts.offload.is_empty() { + if !unstable_options_enabled && !unstable_opts.offload.is_empty() { early_dcx.early_fatal( "`-Zoffload=Enable` also requires `-Zunstable-options` and a nightly compiler", ) @@ -2974,7 +2974,7 @@ pub fn build_session_options( } // Parse any `-l` flags, which link to native libraries. - let libs = parse_native_libs(early_dcx, &unstable_opts, unstable_features, matches); + let libs = parse_native_libs(early_dcx, &unstable_opts, &unstable_features, matches); let test = matches.opt_present("test"); @@ -3203,17 +3203,17 @@ pub mod nightly_options { use super::{OptionStability, RustcOptGroup}; use crate::EarlyDiagCtxt; - pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool { - match_is_nightly_build(matches) + /// Are we allowed to use -Z compiler flags *and* -Zunstable-options was passed? + pub fn unstable_options_enabled(matches: &getopts::Matches) -> bool { + let unstable_features = + UnstableFeatures::from_environment(matches.opt_str("crate-name").as_deref()); + unstable_features.allow_unstable_compiler_flag("unstable-options") && matches.opt_strs("Z").iter().any(|x| *x == "unstable-options") } pub fn match_is_nightly_build(matches: &getopts::Matches) -> bool { - is_nightly_build(matches.opt_str("crate-name").as_deref()) - } - - fn is_nightly_build(krate: Option<&str>) -> bool { - UnstableFeatures::from_environment(krate).is_nightly_build() + let krate = matches.opt_str("crate-name"); + UnstableFeatures::from_environment(krate.as_deref()).is_nightly_build() } pub fn check_nightly_options( @@ -3222,7 +3222,8 @@ pub mod nightly_options { flags: &[RustcOptGroup], ) { let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options"); - let really_allows_unstable_options = match_is_nightly_build(matches); + let unstable_features = + UnstableFeatures::from_environment(matches.opt_str("crate-name").as_deref()); let mut nightly_options_on_stable = 0; for opt in flags.iter() { @@ -3239,7 +3240,38 @@ pub mod nightly_options { opt.name )); } - if really_allows_unstable_options { + + // Check if the matched values are enabled + // If we're dealing with -Z, then check all the matched values (i.e. -Zfoo, -Zbar, etc.) + // If we're dealing with other flags, check their name directly + if opt.name == "Z" { + // For `-Zui-testing -Zfoo=bar -Zfoo=baz`, this returns + // ["ui-testing", "foo=bar", "foo=baz"]. + // Check that all of them are allowed + let values = matches.opt_strs(opt.name); + let disallowed_flags = values + .iter() + .map(|flag| flag.split_once("=").map(|(k, _)| k).unwrap_or(flag)) + .filter(|flag| !unstable_features.allow_unstable_compiler_flag(flag)) + .collect::>(); + if disallowed_flags.is_empty() { + continue; + } + + // Check if we're using the "allow specified unstable flags only" mode. + // In this case, if the user allowed only foo and bar, but then -Zbaz was also + // passed, rustc would only print that `-Z` is not allowed, which doesn't tell the + // user the actual -Z flag value that was disallowed. So we try to provide better + // diagnostics here (solely for the use-case of debugging compiletest itself). + if matches!( + unstable_features, + UnstableFeatures::AllowSelectedUnstableCompilerFlagsOnly { .. } + ) { + for flag in disallowed_flags { + let _ = early_dcx.early_err(format!("the -Z{flag} option was not allowed through `-allow-unstable-compiler-flags`")); + } + } + } else if unstable_features.allow_unstable_compiler_flag(opt.name) { continue; } diff --git a/compiler/rustc_session/src/config/native_libs.rs b/compiler/rustc_session/src/config/native_libs.rs index 0733bd3ca4b78..1f159f8071323 100644 --- a/compiler/rustc_session/src/config/native_libs.rs +++ b/compiler/rustc_session/src/config/native_libs.rs @@ -18,7 +18,7 @@ mod tests; pub(crate) fn parse_native_libs( early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions, - unstable_features: UnstableFeatures, + unstable_features: &UnstableFeatures, matches: &getopts::Matches, ) -> Vec { let cx = ParseNativeLibCx { diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 5e96e48cf7333..ad866eb5b28a7 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1872,6 +1872,45 @@ pub struct IncrCompSession { /// The directory to which cached data for the current session can be /// written to. pub new_session_directory: flock::LockedDir, + borrows: Arc<()>, +} + +impl IncrCompSession { + pub fn new( + old_session_directory: Option, + new_session_directory: flock::LockedDir, + ) -> Self { + IncrCompSession { old_session_directory, new_session_directory, borrows: Arc::new(()) } + } + + pub fn borrow(&self) -> BorrowedIncrCompSession { + BorrowedIncrCompSession { + old_session_directory: self.old_session_directory.as_deref().map(ToOwned::to_owned), + new_session_directory: (&*self.new_session_directory).to_owned(), + _borrows: Arc::clone(&self.borrows), + } + } +} + +impl Drop for IncrCompSession { + fn drop(&mut self) { + // Check that there are no worker threads remaining that use the incr + // comp session before we unlock the old and new session dir. If there + // does exist a worker thread, there is not much we can do, but at + // least we will unconditionally complain rather than the worker thread + // sometimes crashing depending on what other rustc instances run. + assert!( + Arc::strong_count(&self.borrows) == 1, + "Incr comp session dropped while there are still references", + ); + } +} + +/// A runtime tracked borrow of the incr comp session. Can be sent to worker threads. +pub struct BorrowedIncrCompSession { + pub old_session_directory: Option, + pub new_session_directory: PathBuf, + _borrows: Arc<()>, } /// A wrapper around an [`DiagCtxt`] that is used for early error emissions. diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index e0143923fb5d5..7a51bc4af828a 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -10,7 +10,7 @@ use rustc_data_structures::unhash::Unhasher; use rustc_hashes::Hash64; use rustc_index::Idx; use rustc_macros::{BlobDecodable, Decodable, Encodable, StableHash}; -use rustc_serialize::{Decodable, Encodable}; +use rustc_serialize::{ContextOrd, Decodable, Encodable}; use crate::{BlobDecoder, SpanEncoder, Symbol}; @@ -23,6 +23,13 @@ rustc_index::newtype_index! { pub struct CrateNum {} } +impl ContextOrd<&E> for CrateNum { + fn stable_cmp_with_context(context: &E) -> impl FnMut(&Self, &Self) -> std::cmp::Ordering { + let hasher = context.def_path_hasher(); + move |id1, id2| hasher(id1.as_def_id()).cmp(&hasher(id2.as_def_id())) + } +} + /// Item definitions in the currently-compiled crate would have the `CrateNum` /// `LOCAL_CRATE` in their `DefId`. pub const LOCAL_CRATE: CrateNum = CrateNum::ZERO; @@ -410,6 +417,13 @@ impl Decodable for LocalDefId { } } +impl ContextOrd<&E> for LocalDefId { + fn stable_cmp_with_context(context: &E) -> impl FnMut(&Self, &Self) -> std::cmp::Ordering { + let hasher = context.def_path_hasher(); + move |id1, id2| hasher(id1.to_def_id()).cmp(&hasher(id2.to_def_id())) + } +} + rustc_data_structures::define_id_collections!( LocalDefIdMap, LocalDefIdSet, @@ -417,6 +431,13 @@ rustc_data_structures::define_id_collections!( LocalDefId ); +impl ContextOrd<&E> for DefId { + fn stable_cmp_with_context(context: &E) -> impl FnMut(&Self, &Self) -> std::cmp::Ordering { + let hasher = context.def_path_hasher(); + move |id1, id2| hasher(*id1).cmp(&hasher(*id2)) + } +} + impl StableHash for DefId { #[inline] fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 6489b3cc6d54b..4745a26ade953 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -25,7 +25,7 @@ // trigger runtime aborts. (Fortunately these are obvious and easy to fix.) use std::cell::RefCell; -use std::hash::Hash; +use std::hash::{Hash, Hasher}; use std::sync::Arc; use std::{fmt, iter, mem}; @@ -39,7 +39,7 @@ use rustc_data_structures::unhash::UnhashMap; use rustc_hashes::Hash64; use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable, StableHash}; -use rustc_serialize::{Decodable, Decoder, Encodable}; +use rustc_serialize::{ContextOrd, Decodable, Decoder, Encodable}; use tracing::{debug, trace}; use crate::def_id::{CRATE_DEF_ID, CrateNum, DefId, LOCAL_CRATE, ModId, StableCrateId}; @@ -51,9 +51,23 @@ use crate::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, with_session_globals}; /// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks". /// /// See for more explanation. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Default, Clone, Copy, PartialEq, Eq, Hash)] pub struct SyntaxContext(u32); +impl From for SyntaxContext { + #[inline(always)] + fn from(value: u32) -> SyntaxContext { + SyntaxContext(value) + } +} + +impl Into for SyntaxContext { + #[inline(always)] + fn into(self) -> u32 { + self.0 + } +} + // To ensure correctness of incremental compilation, // `SyntaxContext` must not implement `Ord` or `PartialOrd`. // See https://github.com/rust-lang/rust/issues/90317. @@ -123,6 +137,12 @@ rustc_index::newtype_index! { pub struct LocalExpnId {} } +impl Default for LocalExpnId { + fn default() -> LocalExpnId { + LocalExpnId::ROOT + } +} + // To ensure correctness of incremental compilation, // `LocalExpnId` must not implement `Ord` or `PartialOrd`. // See https://github.com/rust-lang/rust/issues/90317. @@ -133,6 +153,12 @@ impl !PartialOrd for LocalExpnId {} #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encodable, Decodable, StableHash)] pub struct ExpnHash(Fingerprint); +impl ContextOrd for ExpnHash { + fn stable_cmp_with_context(_context: E) -> impl FnMut(&Self, &Self) -> std::cmp::Ordering { + |h1, h2| h1.0.cmp(&h2.0) + } +} + impl ExpnHash { /// Returns the [StableCrateId] identifying the crate this [ExpnHash] /// originates from. @@ -341,6 +367,141 @@ impl ExpnId { } } +#[derive(Default)] +pub struct HygieneRemappingResults { + pub remapping: FxHashMap, + pub first_non_det_index: T, +} + +impl + Copy> HygieneRemappingResults { + #[inline] + pub fn map_id(&self, idx: T) -> T { + if idx.into() < self.first_non_det_index.into() { + idx + } else { + self.remapping.get(&idx).copied().unwrap_or(idx) + } + } +} + +/// Includes common logic for remapping both expansion ids and syntax contexts. +/// FIXME: this solution does not take into account syntax contexts and expansion ids +/// that are allocated during metadata encoding, if we will have non-determinism there +/// this remapping will not help as it is constructed before metadata encoding. +pub trait HygieneRemapper { + fn stable_hash(&self, t: T, hcx: &mut impl StableHashCtxt, hasher: &mut StableHasher); + fn len(&self) -> usize; + fn first_non_det_index_mut(&mut self) -> &mut Option; + fn first_non_det_index(&self) -> u32; + + fn commit_end_of_determinism(&mut self) + where + T: From, + { + let len = self.len() as u32; + + let prev = self.first_non_det_index_mut().replace(T::from(len)); + assert!(prev.is_none(), "this function can be called only once") + } + + fn remapping_results(&self, hcx: &mut impl StableHashCtxt) -> HygieneRemappingResults + where + T: From + Hash + Eq + Copy, + { + let start = self.first_non_det_index(); + let range = start..self.len() as u32; + + let mut indices_to_remap = range.map(|raw_id| T::from(raw_id)).collect::>(); + indices_to_remap.sort_by_cached_key(|&key| { + let hasher = &mut StableHasher::new(); + self.stable_hash(key, hcx, hasher); + + hasher.finish() + }); + + let mut remapping = FxHashMap::::default(); + + for (idx, key) in indices_to_remap.into_iter().enumerate() { + let from = key; + let to = T::from(start + idx as u32); + + if from != to { + remapping.insert(from, to); + } + } + + HygieneRemappingResults { remapping, first_non_det_index: T::from(start) } + } +} + +pub struct SyntaxContextRemapper<'a>(&'a mut HygieneData); +impl SyntaxContextRemapper<'_> { + pub fn with(f: impl FnOnce(SyntaxContextRemapper<'_>) -> T) -> T { + HygieneData::with(|data| f(SyntaxContextRemapper(data))) + } +} + +pub struct LocalExpnIdRemapper<'a>(&'a mut HygieneData); +impl LocalExpnIdRemapper<'_> { + pub fn with(f: impl FnOnce(LocalExpnIdRemapper<'_>) -> T) -> T { + HygieneData::with(|data| f(LocalExpnIdRemapper(data))) + } +} + +impl HygieneRemapper for SyntaxContextRemapper<'_> { + #[inline] + fn len(&self) -> usize { + self.0.syntax_context_data.len() + } + + #[inline] + fn first_non_det_index_mut(&mut self) -> &mut Option { + &mut self.0.first_non_det_syntax_context + } + + #[inline] + fn first_non_det_index(&self) -> u32 { + self.0.first_non_det_syntax_context.map(|s| s.as_u32()).expect("must be set at this point") + } + + #[inline] + fn stable_hash( + &self, + ctxt: SyntaxContext, + hcx: &mut impl StableHashCtxt, + hasher: &mut StableHasher, + ) { + ctxt.stable_hash_internal(Some(self.0), hcx, hasher); + } +} + +impl HygieneRemapper for LocalExpnIdRemapper<'_> { + #[inline] + fn len(&self) -> usize { + self.0.local_expn_data.len() + } + + #[inline] + fn first_non_det_index_mut(&mut self) -> &mut Option { + &mut self.0.first_non_det_local_expn_id + } + + #[inline] + fn first_non_det_index(&self) -> u32 { + self.0.first_non_det_local_expn_id.map(|l| l.as_u32()).expect("must be set at this point") + } + + #[inline] + fn stable_hash( + &self, + local_expn_id: LocalExpnId, + hcx: &mut impl StableHashCtxt, + hasher: &mut StableHasher, + ) { + local_expn_id.to_expn_id().stable_hash_internal(Some(self.0), hcx, hasher); + } +} + #[derive(Debug)] pub(crate) struct HygieneData { /// Each expansion should have an associated expansion data, but sometimes there's a delay @@ -354,6 +515,8 @@ pub(crate) struct HygieneData { foreign_expn_hashes: FxHashMap, expn_hash_to_expn_id: UnhashMap, syntax_context_data: Vec, + first_non_det_syntax_context: Option, + first_non_det_local_expn_id: Option, syntax_context_map: FxHashMap, /// Maps the `local_hash` of an `ExpnData` to the next disambiguator value. /// This is used by `update_disambiguator` to keep track of which `ExpnData`s @@ -386,6 +549,8 @@ impl HygieneData { syntax_context_data: vec![root_ctxt_data], syntax_context_map: iter::once((root_ctxt_data.key(), SyntaxContext(0))).collect(), expn_data_disambiguators: UnhashMap::default(), + first_non_det_syntax_context: None, + first_non_det_local_expn_id: None, } } @@ -708,7 +873,7 @@ impl SyntaxContext { } #[inline] - pub(crate) const fn as_u32(self) -> u32 { + pub const fn as_u32(self) -> u32 { self.0 } @@ -1284,43 +1449,10 @@ impl DesugaringKind { pub struct HygieneEncodeContext { /// All `SyntaxContexts` for which we have written `SyntaxContextData` into crate metadata. serialized_ctxts: FxHashSet, - /// The `SyntaxContexts` that we have serialized (e.g. as a result of encoding `Spans`) - /// in the most recent 'round' of serializing. Serializing `SyntaxContextData` - /// may cause us to serialize more `SyntaxContext`s, so serialize in a loop - /// until we reach a fixed point. - latest_ctxts: Vec<(u32 /* Encoding index */, SyntaxContext)>, + latest_ctxts: Vec, serialized_expns: FxHashSet, latest_expns: Vec, - - /// Maps every `SyntaxContext` into its encoding index. - /// Earlier the `ctxt.0` was used when writing metadata, however, - /// this results into non-deterministic metadata (see #129094). - /// The non-determinism is encountered when decoding syntax contexts - /// in `decode_syntax_context` function below. The syntax contexts from - /// other crate metadata can be decoded in different order, which results - /// into different ids assigned to decoded syntax contexts. - /// First invocation: - /// (ALLOC - syntax context id, ORIG - original id of decoded syntax context: - /// `raw_id` in `decode_syntax_context`) - /// ALLOC: #3, ORIG: 1 - /// ALLOC: #9, ORIG: 18769 - /// ALLOC: #10, ORIG: 25868 - /// ALLOC: #11, ORIG: 18822 - /// ALLOC: #12, ORIG: 23092 - /// - /// Second invocation: - /// ALLOC: #3, ORIG: 1 - /// ALLOC: #9, ORIG: 25868 - /// ALLOC: #10, ORIG: 18769 - /// ALLOC: #11, ORIG: 18822 - /// ALLOC: #12, ORIG: 23092 - /// - /// We see that `18769` and `25868` assigned different syntax context ids, - /// however, the order of encoding is deterministic, so we can remap allocated - /// syntax context ids into encoding indices and use them, thus outputting - /// same metadata. - encoding_indices: FxHashMap, } impl Default for HygieneEncodeContext { @@ -1330,20 +1462,11 @@ impl Default for HygieneEncodeContext { latest_ctxts: Default::default(), serialized_expns: Default::default(), latest_expns: Default::default(), - // Zero is taken by root syntax context. - encoding_indices: FxHashMap::from_iter(iter::once((SyntaxContext::root(), 0))), } } } impl HygieneEncodeContext { - #[inline] - fn get_encoding_index(&mut self, ctxt: SyntaxContext) -> u32 { - let map = &mut self.encoding_indices; - let len = map.len(); - *map.entry(ctxt).or_insert(len as u32) - } - /// Record the fact that we need to serialize the corresponding `ExpnData`. #[inline] pub fn schedule_expn_data_for_encoding(&mut self, expn: ExpnId) { @@ -1355,7 +1478,7 @@ impl HygieneEncodeContext { pub fn encode( h_ctxt: &RefCell, encoder: &mut T, - mut encode_ctxt: impl FnMut(&mut T, u32, &SyntaxContextKey), + mut encode_ctxt: impl FnMut(&mut T, SyntaxContext, &SyntaxContextKey), mut encode_expn: impl FnMut(&mut T, ExpnId, Option<&ExpnData>, ExpnHash), ) { // When we serialize a `SyntaxContextData`, we may end up serializing @@ -1376,9 +1499,9 @@ impl HygieneEncodeContext { #[allow(rustc::potential_query_instability)] let latest_contexts = { mem::take(&mut h_ctxt.borrow_mut().latest_ctxts) }.into_iter(); - for (idx, ctxt) in latest_contexts { + for ctxt in latest_contexts { let key = HygieneData::with(|data| data.syntax_context_data[ctxt.0 as usize].key()); - encode_ctxt(encoder, idx, &key); + encode_ctxt(encoder, ctxt, &key); } // Same as above, but for expansions instead of syntax contexts. @@ -1402,18 +1525,10 @@ impl HygieneEncodeContext { } #[inline] - pub fn get_syntax_ctxt_encoding_index(&mut self, ctxt: SyntaxContext) -> u32 { - let index = self.get_encoding_index(ctxt); + pub fn enqueue_syntax_context_for_encoding(&mut self, ctxt: SyntaxContext) { if self.serialized_ctxts.insert(ctxt) { - // If we created new encoding index then it is greater - // than any previous index, so this vector is in ascending order. - // We can't push existing, possibly out-of-order, index - // as we check if we already saw this syntax context above. - // This property is important for deterministic output (see #129094). - self.latest_ctxts.push((index, ctxt)); + self.latest_ctxts.push(ctxt); } - - index } } @@ -1579,8 +1694,13 @@ fn update_disambiguator(expn_data: &mut ExpnData, mut hcx: impl StableHashCtxt) ExpnHash::new(LOCAL_CRATE.as_def_id().to_stable_hash_key(&mut hcx).stable_crate_id(), expn_hash) } -impl StableHash for SyntaxContext { - fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { +impl SyntaxContext { + fn stable_hash_internal( + self, + data: Option<&HygieneData>, + hcx: &mut impl StableHashCtxt, + hasher: &mut StableHasher, + ) { const TAG_EXPANSION: u8 = 0; const TAG_NO_EXPANSION: u8 = 1; @@ -1591,32 +1711,64 @@ impl StableHash for SyntaxContext { // This duplicates `ExpnId::stable_hash`, but reads the outer mark and // expansion hash in a single `HygieneData::with` call to avoid extra locking. hcx.assert_default_stable_hash_controls("ExpnId"); - let (hash, transparency) = HygieneData::with(|data| { - let (expn_id, transparency) = data.outer_mark(*self); + + let get_hashing_data = |data: &HygieneData| { + let (expn_id, transparency) = data.outer_mark(self); (data.expn_hash(expn_id).0, transparency) - }); + }; + + let (hash, transparency) = match data { + Some(data) => get_hashing_data(data), + None => HygieneData::with(|data| get_hashing_data(data)), + }; + hash.stable_hash(hcx, hasher); transparency.stable_hash(hcx, hasher); } } } -impl StableHash for ExpnId { +impl StableHash for SyntaxContext { + #[inline] fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { + self.stable_hash_internal(None, hcx, hasher); + } +} + +impl ExpnId { + fn stable_hash_internal( + self, + data: Option<&HygieneData>, + hcx: &mut impl StableHashCtxt, + hasher: &mut StableHasher, + ) { hcx.assert_default_stable_hash_controls("ExpnId"); - let hash = if *self == ExpnId::root() { + let hash = if self == ExpnId::root() { // Avoid fetching TLS storage for a trivial often-used value. Fingerprint::ZERO } else { - self.expn_hash().0 + let get_hashing_data = |data: &HygieneData| data.expn_hash(self).0; + + match data { + Some(data) => get_hashing_data(data), + None => HygieneData::with(|data| get_hashing_data(data)), + } }; hash.stable_hash(hcx, hasher); } } +impl StableHash for ExpnId { + #[inline] + fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { + self.stable_hash_internal(None, hcx, hasher) + } +} + impl StableHash for LocalExpnId { + #[inline] fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { - self.to_expn_id().stable_hash(hcx, hasher); + self.to_expn_id().stable_hash_internal(None, hcx, hasher); } } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index f69efebf5c950..a0806126177cd 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -46,6 +46,7 @@ pub mod source_map; use source_map::{SourceMap, SourceMapInputs}; pub use self::caching_source_map_view::CachingSourceMapView; +use crate::def_id::DefPathHash; use crate::fatal_error::FatalError; pub mod edition; @@ -1344,6 +1345,16 @@ rustc_index::newtype_index! { /// This trait is used to allow encoder specific encodings of certain types. /// It is similar to rustc_type_ir's TyEncoder. pub trait SpanEncoder: Encoder { + /// Only needed if the encoder wants deterministic encoding for `HashMap` and `HashSet` with + /// `CrateNum`, `DefId` or `LocalDefId` keys. Can be unimplemented otherwise + fn def_path_hasher(&self) -> impl Fn(DefId) -> DefPathHash { + panic!( + "Deterministic encoding of HashMap requires overriding `SpanEncoder::def_path_hasher`" + ); + #[allow(unreachable_code)] + |_def_id| unimplemented!() + } + fn encode_span(&mut self, span: Span); fn encode_symbol(&mut self, sym: Symbol); fn encode_byte_symbol(&mut self, byte_sym: ByteSymbol); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4ab0a6d988b84..65bea89e1c713 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -11,6 +11,7 @@ use rustc_data_structures::hash_table::{Entry, HashTable}; use rustc_data_structures::stable_hash::{StableCompare, StableHash, StableHashCtxt, StableHasher}; use rustc_data_structures::sync::Lock; use rustc_macros::{Decodable, Encodable, StableHash, symbols}; +use rustc_serialize::ContextOrd; use crate::edit_distance::find_best_match_for_name; use crate::{DUMMY_SP, Edition, Span, with_session_globals}; @@ -2735,6 +2736,12 @@ impl fmt::Display for MacroRulesNormalizedIdent { #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Symbol(SymbolIndex); +impl ContextOrd for Symbol { + fn stable_cmp_with_context(_context: E) -> impl FnMut(&Self, &Self) -> std::cmp::Ordering { + StableCompare::stable_cmp + } +} + // Used within both `Symbol` and `ByteSymbol`. rustc_index::newtype_index! { #[orderable] diff --git a/compiler/rustc_structures/src/crate_type.rs b/compiler/rustc_structures/src/crate_type.rs index 505370a524281..58f192bc1d952 100644 --- a/compiler/rustc_structures/src/crate_type.rs +++ b/compiler/rustc_structures/src/crate_type.rs @@ -1,3 +1,4 @@ +use rustc_serialize::ContextOrd; #[cfg(feature = "nightly")] use { rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash}, @@ -25,6 +26,13 @@ pub enum CrateType { // Unstable; feature(export_stable) Sdylib, } + +impl ContextOrd for CrateType { + fn stable_cmp_with_context(_context: Ctx) -> impl FnMut(&Self, &Self) -> std::cmp::Ordering { + ::cmp + } +} + #[cfg(feature = "nightly")] impl CrateType { /// Pairs of each `#[crate_type] = "..."` value and the crate type it resolves to diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index d356a62200cfd..20a3f56ef5e48 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -845,39 +845,39 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; let mut suggs = vec![]; - let lt_name = self.suggest_name_region(generic_param_scope, sub, &mut suggs); - - if let Some((sp, suggestion_type)) = type_param_sugg_span - && suggestion_scope == type_scope - { - match suggestion_type { - LifetimeSuggestion::NeedsPlus(open_paren_sp) => { - let suggestion = format!(" + {lt_name}"); - if let Some(open_paren_sp) = open_paren_sp { - suggs.push((open_paren_sp, "(".to_string())); - suggs.push((sp, format!("){suggestion}"))); - } else { - suggs.push((sp, suggestion)); + if let Some(lt_name) = self.suggest_name_region(generic_param_scope, sub, &mut suggs) { + if let Some((sp, suggestion_type)) = type_param_sugg_span + && suggestion_scope == type_scope + { + match suggestion_type { + LifetimeSuggestion::NeedsPlus(open_paren_sp) => { + let suggestion = format!(" + {lt_name}"); + if let Some(open_paren_sp) = open_paren_sp { + suggs.push((open_paren_sp, "(".to_string())); + suggs.push((sp, format!("){suggestion}"))); + } else { + suggs.push((sp, suggestion)); + } } + LifetimeSuggestion::NeedsColon => suggs.push((sp, format!(": {lt_name}"))), + LifetimeSuggestion::HasColon => suggs.push((sp, format!(" {lt_name}"))), } - LifetimeSuggestion::NeedsColon => suggs.push((sp, format!(": {lt_name}"))), - LifetimeSuggestion::HasColon => suggs.push((sp, format!(" {lt_name}"))), + } else if let GenericKind::Alias(ref p) = bound_kind + && let ty::Projection { def_id } = p.kind(self.tcx) + && let DefKind::AssocTy = self.tcx.def_kind(def_id) + && let Some(ty::ImplTraitInTraitData::Trait { .. }) = + self.tcx.opt_rpitit_info(def_id) + { + // The lifetime found in the `impl` is longer than the one on the RPITIT. + // Do not suggest `::{opaque}: 'static`. + } else if let Some(generics) = self.tcx.hir_get_generics(suggestion_scope) { + let pred = format!("{bound_kind}: {lt_name}"); + let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred); + suggs.push((generics.tail_span_for_predicate_suggestion(), suggestion)) + } else { + let consider = format!("{msg} `{bound_kind}: {sub}`..."); + err.help(consider); } - } else if let GenericKind::Alias(ref p) = bound_kind - && let ty::Projection { def_id } = p.kind - && let DefKind::AssocTy = self.tcx.def_kind(def_id) - && let Some(ty::ImplTraitInTraitData::Trait { .. }) = - self.tcx.opt_rpitit_info(def_id) - { - // The lifetime found in the `impl` is longer than the one on the RPITIT. - // Do not suggest `::{opaque}: 'static`. - } else if let Some(generics) = self.tcx.hir_get_generics(suggestion_scope) { - let pred = format!("{bound_kind}: {lt_name}"); - let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred); - suggs.push((generics.tail_span_for_predicate_suggestion(), suggestion)) - } else { - let consider = format!("{msg} `{bound_kind}: {sub}`..."); - err.help(consider); } if !suggs.is_empty() { @@ -933,7 +933,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { generic_param_scope: LocalDefId, lifetime: Region<'tcx>, add_lt_suggs: &mut Vec<(Span, String)>, - ) -> String { + ) -> Option { struct LifetimeReplaceVisitor<'a> { needle: hir::LifetimeKind, new_lt: &'a str, @@ -953,7 +953,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Some(info) if !lifetime.is_named(self.tcx) => { (info.region_def_id.expect_local(), info.scope) } - _ => return lifetime.get_name_or_anon(self.tcx).to_string(), + _ => return Some(lifetime.get_name(self.tcx)?.to_string()), }; let new_lt = { @@ -999,7 +999,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>"))); add_lt_suggs.push(sugg); - new_lt + Some(new_lt) } fn report_sub_sup_conflict( diff --git a/compiler/rustc_ty_utils/src/layout/invariant.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index 562a3ef387d9a..a244c597eef3d 100644 --- a/compiler/rustc_ty_utils/src/layout/invariant.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -310,16 +310,10 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou } // Ensure that for niche encoded tags the discriminant coincides with the variant index. - let val = layout.ty.discriminant_for_variant(tcx, idx).unwrap().val; - if val != u128::from(idx.as_u32()) { - let adt_def = layout.ty.ty_adt_def().unwrap(); - cx.tcx().dcx().span_delayed_bug( - cx.tcx().def_span(adt_def.did()), - format!( - "variant {idx:?} has discriminant {val:?} in niche-encoded type" - ), - ); - } + assert_eq!( + layout.ty.discriminant_for_variant(tcx, idx).unwrap().val, + u128::from(idx.as_u32()), + ); } } for variant in variants.iter() { diff --git a/library/core/src/iter/adapters/skip.rs b/library/core/src/iter/adapters/skip.rs index f7a6beb2b8c63..600e897b13bbd 100644 --- a/library/core/src/iter/adapters/skip.rs +++ b/library/core/src/iter/adapters/skip.rs @@ -135,6 +135,14 @@ where #[inline] #[rustc_inherit_overflow_checks] fn advance_by(&mut self, mut n: usize) -> Result<(), NonZero> { + if self.n > 0 && n == 0 { + // If we try to process our skips now, we might witness the end of the inner iterator, + // but there's no valid `Err(k)` that we could return. This is especially a problem for + // unfused iterators that might end up called again by `Chain`, for example, so it's + // better that we do nothing at all. + return Ok(()); + } + let skip_inner = self.n; let skip_and_advance = skip_inner.saturating_add(n); diff --git a/library/core/src/num/imp/int_bits.rs b/library/core/src/num/imp/int_bits.rs index 2bc95e66e7ef6..dbb1a54fc59f6 100644 --- a/library/core/src/num/imp/int_bits.rs +++ b/library/core/src/num/imp/int_bits.rs @@ -115,7 +115,7 @@ macro_rules! uint_impl { // [0; n], [X; a], [0; n], [0; b] // q // [0; n], [0; a + n], [Y; b] // x ^= q // [0; n + n], [X; a], [0; b] // q >> n - // [0; n], [0; n], [X; a], [Y; b] // x ^= q << n + // [0; n], [0; n], [X; a], [Y; b] // x ^= q >> n // ``` // Only zeros are shifted out, satisfying the assumption // for the next group. @@ -123,9 +123,18 @@ macro_rules! uint_impl { // In effect, the upper run of data is swapped with the // group of `n` zeros below it. let q = x & masks[stage]; - x ^= q; - x ^= q >> n; + if cfg!(all(target_arch = "aarch64", target_feature = "neon")) && $U::BITS <= 64 + { + // `q >> n` has no set bits in common with `x ^ q`, so addition + // is equivalent to XOR here. For widths up to 64 bits this + // allows AArch64 to use `usra`. For `u128`, this add may introduce + // a slower cross-limb carry chain. + x = (x & !masks[stage]).wrapping_add(q >> n); + } else { + x ^= q; + x ^= q >> n; + } stage += 1; } x diff --git a/library/core/src/ops/bit.rs b/library/core/src/ops/bit.rs index 7c008531c1f5a..5b80b4edc20de 100644 --- a/library/core/src/ops/bit.rs +++ b/library/core/src/ops/bit.rs @@ -1,4 +1,5 @@ -/// The unary logical negation operator `!`. +/// The unary negation operator `!`. It performs either logical or bitwise negation, +/// depending on the type. /// /// # Examples /// @@ -37,15 +38,23 @@ pub const trait Not { #[stable(feature = "rust1", since = "1.0.0")] type Output; - /// Performs the unary `!` operation. + /// Performs the unary `!` operation using logical or bitwise negation. /// /// # Examples /// /// ``` + /// // Logical negation changes between true and false: /// assert_eq!(!true, false); /// assert_eq!(!false, true); + /// + /// // Bitwise negation changes between 0 and 1 in the binary + /// // representation of integers: /// assert_eq!(!1u8, 254); /// assert_eq!(!0u8, 255); + /// + /// // The same examples, represented in binary: + /// assert_eq!(!0b0000_0001u8, 0b1111_1110); + /// assert_eq!(!0b0000_0000u8, 0b1111_1111); /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 6deba9ce9752e..35af6488895a1 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2523,7 +2523,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_split_once)] /// let s = [1, 2, 3, 2, 4]; /// assert_eq!(s.split_once(|&x| x == 2), Some(( /// &[1][..], @@ -2531,7 +2530,7 @@ impl [T] { /// ))); /// assert_eq!(s.split_once(|&x| x == 0), None); /// ``` - #[unstable(feature = "slice_split_once", issue = "112811")] + #[stable(feature = "slice_split_once", since = "CURRENT_RUSTC_VERSION")] #[inline] pub fn split_once(&self, pred: F) -> Option<(&[T], &[T])> where @@ -2552,7 +2551,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_split_once)] /// let s = [1, 2, 3, 2, 4]; /// assert_eq!(s.rsplit_once(|&x| x == 2), Some(( /// &[1, 2, 3][..], @@ -2560,7 +2558,7 @@ impl [T] { /// ))); /// assert_eq!(s.rsplit_once(|&x| x == 0), None); /// ``` - #[unstable(feature = "slice_split_once", issue = "112811")] + #[stable(feature = "slice_split_once", since = "CURRENT_RUSTC_VERSION")] #[inline] pub fn rsplit_once(&self, pred: F) -> Option<(&[T], &[T])> where diff --git a/library/coretests/tests/iter/adapters/skip.rs b/library/coretests/tests/iter/adapters/skip.rs index 8d5d06ad9fb3a..cfd00ac609715 100644 --- a/library/coretests/tests/iter/adapters/skip.rs +++ b/library/coretests/tests/iter/adapters/skip.rs @@ -86,6 +86,31 @@ fn test_skip_advance_by() { assert_eq!((0..0).skip(1).advance_back_by(0), Ok(())); } +#[test] +fn test_skip_unfused_advance_by() { + /// A legal non-fused iterator: `None` once, then `Some(1)`, `Some(2)`, ... + struct Gap(u32); + + impl Iterator for Gap { + type Item = u32; + fn next(&mut self) -> Option { + self.0 += 1; + if self.0 == 1 { None } else { Some(self.0 - 1) } + } + } + + assert_eq!(Gap(0).skip(1).next(), None); + + let mut iter = Gap(0).skip(1); + assert_eq!(iter.advance_by(0), Ok(())); + // `Ok` means we didn't see the (unfused) end yet... + assert_eq!(iter.next(), None); + + // Chain also depends on this behavior internally + assert_eq!(Gap(0).skip(1).chain(empty()).next(), None); + assert_eq!(Gap(0).skip(1).chain(empty()).nth(0), None); +} + #[test] fn test_iterator_skip_count() { let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 9054f9331f7bf..8f2fd10b9e475 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -107,7 +107,6 @@ #![feature(slice_internals)] #![feature(slice_partition_dedup)] #![feature(slice_shift)] -#![feature(slice_split_once)] #![feature(sliceindex_wrappers)] #![feature(splat)] #![feature(split_array)] diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 65e32c16cbdb7..bf61ab79964c4 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2795,6 +2795,20 @@ impl AsInner for Permissions { } } +impl ReadDir { + /// Returns a handle for the directory this `ReadDir` is reading. + /// + /// Whenever possible, the new handle will be created in a race-free manner from the underlying + /// handle used by `ReadDir`. However, this may fall back to opening a new handle based on the + /// path, which is subject to race conditions if the file system has changed since the `ReadDir` + /// was created. + // FIXME: before stabilization, ensure that we have a race-free implementation on Windows! + #[unstable(feature = "dirfd", issue = "120426")] + pub fn dir(&self) -> io::Result { + self.0.dir().map(|d| Dir { inner: d }) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Iterator for ReadDir { type Item = io::Result; diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 28d79b9695504..8af3fa2b3b391 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -3341,6 +3341,19 @@ fn test_dir_metadata() { assert!(metadata.is_symlink()); } +#[test] +fn test_read_dir_dir() { + let tmpdir = tmpdir(); + check!(fs::write(tmpdir.path().join("file.txt"), b"hello")); + + let read_dir = check!(fs::read_dir(tmpdir.path())); + let dir = check!(read_dir.dir()); + let mut f = check!(dir.open_file("file.txt")); + let mut data = vec![]; + check!(f.read_to_end(&mut data)); + assert_eq!(data, b"hello"); +} + fn root_test_dir(what: &str) -> PathBuf { crate::env::current_dir().unwrap().ancestors().last().unwrap().join(what) } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 0d0d9e0759d82..85f7235903273 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -381,7 +381,6 @@ #![feature(slice_internals)] #![feature(slice_ptr_get)] #![feature(slice_range)] -#![feature(slice_split_once)] #![feature(std_internals)] #![feature(str_internals)] #![feature(sync_unsafe_cell)] @@ -711,6 +710,8 @@ pub mod arch { pub use std_detect::is_arm_feature_detected; #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] pub use std_detect::is_loongarch_feature_detected; + #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] + pub use std_detect::is_powerpc_feature_detected; #[stable(feature = "riscv_ratified", since = "1.78.0")] pub use std_detect::is_riscv_feature_detected; #[stable(feature = "stdarch_s390x_feature_detection", since = "1.93.0")] @@ -719,8 +720,6 @@ pub mod arch { pub use std_detect::is_x86_feature_detected; #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] pub use std_detect::{is_mips_feature_detected, is_mips64_feature_detected}; - #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] - pub use std_detect::{is_powerpc_feature_detected, is_powerpc64_feature_detected}; } // This was stabilized in the crate root so we have to keep it there. diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 6c6f9b552c926..7b62116dff404 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -90,6 +90,27 @@ impl BorrowedFd<'_> { pub const unsafe fn borrow_raw(fd: RawFd) -> Self { Self { fd: ValidRawFd::new(fd).expect("fd != -1"), _phantom: PhantomData } } + + /// Converts a `BorrowedFd` into a reference to a file-like type. + /// + #[cfg_attr(unix, doc = "```")] + #[cfg_attr(not(unix), doc = "```ignore (needs unix)")] + /// # #![feature(fd_view)] + /// # use std::fs::File; + /// # use std::io; + /// # use std::os::fd::AsFd; + /// let meta = io::stdout().as_fd().to_view::().metadata().unwrap(); + /// if meta.is_file() { + /// println!("stdout is a regular file"); + /// } + /// ``` + #[unstable(feature = "fd_view", issue = "160402")] + pub fn to_view(&self) -> &T + where + Self: AsRef, + { + self.as_ref() + } } impl OwnedFd { @@ -435,6 +456,58 @@ impl From for crate::net::UdpSocket { } } +/// Implements `AsRef<$t>` conversions from `BorrowedFd` to the specified types. +/// +/// # Safety +/// +/// It must be sound to transmute `&BorrowedFd` to `&$t`. +/// +/// This typically implies that: +/// +/// - $t must be a wrapper with `BorrowedFd`, `OwnedFd` or `RawFd` being the single non-ZST-field. +/// Nominally it also requires repr(transparent), but std can cheat because we can stay in +/// sync with rustc's layout implementation details. +/// - `&$t` must not have any methods that would allow closing the file descriptor. +/// - `&$t` impls must tolerate instances being created from any kind of file-descriptor without +/// going through some dedicated constructor function, akin to `From` being a noop +/// conversion. +pub(crate) macro unsafe_impl_fd_asrefs { + ($($t:ty),*$(,)?) => {$( + #[unstable(feature = "fd_view", issue = "160402")] + #[unstable_feature_bound(fd_view)] + impl AsRef<$t> for BorrowedFd<'_> { + #[inline] + fn as_ref(&self) -> &$t { + use core::mem; + + // These are neither necessary nor sufficient, but generally + // a type that implements them is more likely to have the required behavior. + fn type_check + Into + AsFd>() {} + + // sanity check in case layouts change + const { + assert!(mem::size_of::() == mem::size_of::<$t>()); + assert!(mem::size_of::() == mem::size_of::>()); + } + type_check::<$t>(); + // SAFETY: See macro-level requirements. + unsafe { mem::transmute::<&BorrowedFd<'_>, &$t>(self) } + } + } + )*} +} + +#[cfg(not(target_os = "trusty"))] +unsafe_impl_fd_asrefs!( + crate::fs::File, + crate::net::TcpStream, + crate::net::TcpListener, + crate::net::UdpSocket, +); + +#[cfg(unix)] +unsafe_impl_fd_asrefs!(io::PipeWriter, io::PipeReader); + #[stable(feature = "asfd_ptrs", since = "1.64.0")] /// This impl allows implementing traits that require `AsFd` on Arc. /// ``` diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 548dca3592063..309f80f7923b2 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -20,7 +20,9 @@ use super::{SocketAncillary, recv_vectored_with_ancillary_from, send_vectored_wi #[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "cygwin"))] use crate::io::{IoSlice, IoSliceMut}; use crate::net::Shutdown; -use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::os::unix::io::{ + AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd, unsafe_impl_fd_asrefs, +}; use crate::path::Path; use crate::sys::net::Socket; use crate::sys::{AsInner, FromInner, IntoInner, cvt}; @@ -1015,6 +1017,11 @@ impl AsFd for UnixDatagram { } } +// SAFETY: It's a tower of 1-field wrappers around OwnedFd. +// No methods to close the socket, no methods with safety preconditions, +// other than the fd being valid. +unsafe_impl_fd_asrefs!(UnixDatagram); + #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedFd { /// Takes ownership of a [`UnixDatagram`]'s socket file descriptor. diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index 2e47a4b23a1cb..713ad2944a8cb 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -1,5 +1,7 @@ use super::{SocketAddr, UnixStream, sockaddr_un}; -use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::os::unix::io::{ + AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd, unsafe_impl_fd_asrefs, +}; use crate::path::Path; use crate::sys::net::Socket; use crate::sys::{AsInner, FromInner, IntoInner, cvt}; @@ -355,6 +357,11 @@ impl From for UnixListener { } } +// SAFETY: It's a tower of 1-field wrappers around OwnedFd. +// No methods to close the socket, no methods with safety preconditions, +// other than the fd being valid. +unsafe_impl_fd_asrefs!(UnixListener); + #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedFd { /// Takes ownership of a [`UnixListener`]'s socket file descriptor. diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index dba49d0406bf1..00c3e75592ee2 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -40,7 +40,9 @@ use super::{UCred, peer_cred}; use crate::fmt; use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::Shutdown; -use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::os::unix::io::{ + AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd, unsafe_impl_fd_asrefs, +}; use crate::path::Path; use crate::sys::net::Socket; use crate::sys::{AsInner, FromInner, cvt}; @@ -745,6 +747,11 @@ impl AsFd for UnixStream { } } +// SAFETY: It's a tower of 1-field wrappers around OwnedFd. +// No methods to close the socket, no methods with safety preconditions, +// other than the fd being valid. +unsafe_impl_fd_asrefs!(UnixStream); + #[stable(feature = "io_safety", since = "1.63.0")] impl From for OwnedFd { /// Takes ownership of a [`UnixStream`]'s socket file descriptor. diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index 01bd36155b110..ec20baf2bf2ce 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -151,6 +151,27 @@ impl BorrowedHandle<'_> { pub const unsafe fn borrow_raw(handle: RawHandle) -> Self { Self { handle, _phantom: PhantomData } } + + /// Converts a `BorrowedHandle` into a reference to a file-like type. + /// + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] + /// # #![feature(fd_view)] + /// # use std::fs::File; + /// # use std::io; + /// # use std::os::windows::io::AsHandle; + /// let meta = io::stdout().as_handle().to_view::().metadata().unwrap(); + /// if meta.is_file() { + /// println!("stdout is a regular file"); + /// } + /// ``` + #[unstable(feature = "fd_view", issue = "160402")] + pub fn to_view(&self) -> &T + where + Self: AsRef, + { + self.as_ref() + } } #[stable(feature = "io_safety", since = "1.63.0")] @@ -680,6 +701,49 @@ impl AsHandle for io::PipeWriter { } } +/// Implements `AsRef<$t>` conversions from `BorrowedHandle` to the specified types. +/// +/// # Safety +/// +/// It must be sound to transmute `&BorrowedHandle` to `&$t`. +/// +/// This typically implies that: +/// +/// - $t must be a wrapper with `BorrowedHandle`, `OwnedHandle` or `RawHandle` being the single non-ZST-field. +/// Nominally it also requires repr(transparent), but std can cheat because we can stay in +/// sync with rustc's layout implementation details. +/// - `&$t` must not have any methods that would allow closing the file descriptor. +/// - `&$t` impls must tolerate instances being created from any kind of file-descriptor without +/// going through some dedicated constructor function, akin to `From` being a noop +/// conversion. +pub(crate) macro unsafe_impl_handle_asrefs { + ($($t:ty),*$(,)?) => {$( + #[unstable(feature = "fd_view", issue = "160402")] + #[unstable_feature_bound(fd_view)] + impl AsRef<$t> for BorrowedHandle<'_> { + #[inline] + fn as_ref(&self) -> &$t { + use core::mem; + + // These are neither necessary nor sufficient, but generally + // a type that implements them is more likely to have the required behavior. + fn type_check + Into + AsHandle>() {} + + // sanity check in case layouts change + const { + assert!(mem::size_of::() == mem::size_of::<$t>()); + assert!(mem::size_of::() == mem::size_of::>()); + } + type_check::<$t>(); + // SAFETY: See macro-level requirements. + unsafe { mem::transmute::<&BorrowedHandle<'_>, &$t>(self) } + } + } + )*} +} + +unsafe_impl_handle_asrefs!(crate::fs::File, io::PipeWriter, io::PipeReader,); + #[stable(feature = "anonymous_pipe", since = "1.87.0")] impl From for OwnedHandle { fn from(pipe: io::PipeWriter) -> Self { diff --git a/library/std/src/sys/fs/hermit.rs b/library/std/src/sys/fs/hermit.rs index 650f7e884c41d..32ed0bd76214f 100644 --- a/library/std/src/sys/fs/hermit.rs +++ b/library/std/src/sys/fs/hermit.rs @@ -221,6 +221,14 @@ impl FileType { } } +impl ReadDir { + pub fn dir(&self) -> io::Result { + let mut options = OpenOptions::new(); + options.read(true); + Dir::open(&self.inner.root, &options) + } +} + impl fmt::Debug for ReadDir { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. diff --git a/library/std/src/sys/fs/motor.rs b/library/std/src/sys/fs/motor.rs index 06d7356b2a519..269ece5666494 100644 --- a/library/std/src/sys/fs/motor.rs +++ b/library/std/src/sys/fs/motor.rs @@ -410,6 +410,14 @@ pub fn readdir(path: &Path) -> io::Result { }) } +impl ReadDir { + pub fn dir(&self) -> io::Result { + let mut options = OpenOptions::new(); + options.read(true); + Dir::open(Path::new(&self.path), &options) + } +} + impl Iterator for ReadDir { type Item = io::Result; diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs index 849a1657b761d..c43a4794e620e 100644 --- a/library/std/src/sys/fs/solid.rs +++ b/library/std/src/sys/fs/solid.rs @@ -159,6 +159,14 @@ pub fn readdir(p: &Path) -> io::Result { } } +impl ReadDir { + pub fn dir(&self) -> io::Result { + let mut options = OpenOptions::new(); + options.read(true); + Dir::open(&self.inner.root, &options) + } +} + impl fmt::Debug for ReadDir { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index ef902931533d3..945eaeaff1d7d 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -147,6 +147,14 @@ impl FileType { } } +impl ReadDir { + pub fn dir(&self) -> io::Result { + let mut options = OpenOptions::new(); + options.read(true); + Dir::open(&self.0.path(), &options) + } +} + impl fmt::Debug for ReadDir { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut b = f.debug_struct("ReadDir"); diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index ab65863df95d2..f66fdeabb8a83 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -796,6 +796,39 @@ impl fmt::Debug for FilePermissions { } } +impl ReadDir { + pub fn dir(&self) -> io::Result { + let mut options = OpenOptions::new(); + options.read(true); + + cfg_select! { + // Some targets don't have `dirfd`. Open `Dir` based on path. + any( + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nto", + target_os = "qnx", + target_os = "vxworks", + target_os = "l4re", + ) => Dir::open(&self.inner.root, &options), + // Use `dirfd` where possible. + _ => { + let fd = unsafe { libc::dirfd(self.inner.dirp.0) }; + // Make this FD into a directory handle. We don't actually drop it, + // so having an `OwnedFd` is fine. + let dir_handle = + mem::ManuallyDrop::new(dir::Dir(unsafe { OwnedFd::from_raw_fd(fd) })); + // We don't want to expose `fd` or even the underlying file description + // as the directory stream has state attached to it. So we open a completely + // new file description based on this one. + dir_handle.open_dir(Path::new("."), &options) + } + } + } +} + impl fmt::Debug for ReadDir { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. diff --git a/library/std/src/sys/fs/unix/dir.rs b/library/std/src/sys/fs/unix/dir.rs index dd01db58c600d..2ac5695e3d72a 100644 --- a/library/std/src/sys/fs/unix/dir.rs +++ b/library/std/src/sys/fs/unix/dir.rs @@ -60,7 +60,7 @@ impl Dir { pub fn self_metadata(&self) -> io::Result { // Reuse the implementation for files, which should work for all FDs. let fd = self.0.as_raw_fd(); - let f = core::mem::ManuallyDrop::new(File( + let f = mem::ManuallyDrop::new(File( // SAFETY: we borrowed `self` so the FD will not be closed while this function runs. unsafe { FileDesc::from_raw_fd(fd) }, )); diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs index 1421dad97272f..78b4a1a698a33 100644 --- a/library/std/src/sys/fs/unsupported.rs +++ b/library/std/src/sys/fs/unsupported.rs @@ -157,6 +157,12 @@ impl fmt::Debug for FileType { } } +impl ReadDir { + pub fn dir(&self) -> io::Result { + self.0 + } +} + impl fmt::Debug for ReadDir { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0 diff --git a/library/std/src/sys/fs/vexos.rs b/library/std/src/sys/fs/vexos.rs index 6529159eb49fc..5dbcdfef7c80d 100644 --- a/library/std/src/sys/fs/vexos.rs +++ b/library/std/src/sys/fs/vexos.rs @@ -127,6 +127,12 @@ impl FileType { } } +impl ReadDir { + pub fn dir(&self) -> io::Result { + self.0 + } +} + impl fmt::Debug for ReadDir { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0 diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index 548b2d4b6f00f..c7dbc815b3434 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -113,6 +113,15 @@ impl fmt::Debug for c::FILETIME { #[derive(Debug)] pub struct DirBuilder; +impl ReadDir { + pub fn dir(&self) -> io::Result { + let mut options = OpenOptions::new(); + options.read(true); + // FIXME: use race-free handle-based approach instead. + Dir::open(&self.root, &options) + } +} + impl fmt::Debug for ReadDir { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. diff --git a/library/std/tests/run-time-detect.rs b/library/std/tests/run-time-detect.rs index 5506fd469732d..d202a2e1bb800 100644 --- a/library/std/tests/run-time-detect.rs +++ b/library/std/tests/run-time-detect.rs @@ -9,7 +9,10 @@ feature(stdarch_aarch64_feature_detection) )] #![cfg_attr( - all(target_arch = "powerpc", target_os = "linux"), + all( + any(target_arch = "powerpc", target_arch = "powerpc64"), + any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"), + ), feature(stdarch_powerpc_feature_detection) )] #![cfg_attr( @@ -111,7 +114,10 @@ fn aarch64_linux() { } #[test] -#[cfg(all(target_arch = "powerpc", target_os = "linux"))] +#[cfg(all( + any(target_arch = "powerpc", target_arch = "powerpc64"), + any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"), +))] fn powerpc_linux() { use std::arch::is_powerpc_feature_detected; // tidy-alphabetical-start @@ -121,17 +127,6 @@ fn powerpc_linux() { // tidy-alphabetical-end } -#[test] -#[cfg(all(target_arch = "powerpc64", target_os = "linux"))] -fn powerpc64_linux() { - use std::arch::is_powerpc64_feature_detected; - // tidy-alphabetical-start - println!("altivec: {}", is_powerpc64_feature_detected!("altivec")); - println!("power8: {}", is_powerpc64_feature_detected!("power8")); - println!("vsx: {}", is_powerpc64_feature_detected!("vsx")); - // tidy-alphabetical-end -} - #[test] #[cfg(all(target_arch = "s390x", target_os = "linux"))] fn s390x_linux() { diff --git a/library/std_detect/src/detect/arch/mod.rs b/library/std_detect/src/detect/arch/mod.rs index 2ca4cbd1e3fa0..d3ef2b65c87aa 100644 --- a/library/std_detect/src/detect/arch/mod.rs +++ b/library/std_detect/src/detect/arch/mod.rs @@ -12,8 +12,6 @@ mod riscv; #[macro_use] mod powerpc; #[macro_use] -mod powerpc64; -#[macro_use] mod mips; #[macro_use] mod mips64; @@ -39,14 +37,10 @@ cfg_select! { #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] pub use riscv::*; } - target_arch = "powerpc" => { + any(target_arch = "powerpc", target_arch = "powerpc64") => { #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] pub use powerpc::*; } - target_arch = "powerpc64" => { - #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] - pub use powerpc64::*; - } target_arch = "mips" => { #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] pub use mips::*; diff --git a/library/std_detect/src/detect/arch/powerpc.rs b/library/std_detect/src/detect/arch/powerpc.rs index be2db0b81c2fc..02167e5935bdf 100644 --- a/library/std_detect/src/detect/arch/powerpc.rs +++ b/library/std_detect/src/detect/arch/powerpc.rs @@ -2,7 +2,7 @@ features! { @TARGET: powerpc; - @CFG: target_arch = "powerpc"; + @CFG: any(target_arch = "powerpc", target_arch = "powerpc64"); @MACRO_NAME: is_powerpc_feature_detected; @MACRO_ATTRS: /// Check for the presence of a CPU feature at runtime. diff --git a/library/std_detect/src/detect/arch/powerpc64.rs b/library/std_detect/src/detect/arch/powerpc64.rs deleted file mode 100644 index 98e8d5f32b75c..0000000000000 --- a/library/std_detect/src/detect/arch/powerpc64.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Run-time feature detection on PowerPC64. - -features! { - @TARGET: powerpc64; - @CFG: target_arch = "powerpc64"; - @MACRO_NAME: is_powerpc64_feature_detected; - @MACRO_ATTRS: - /// Check for the presence of a CPU feature at runtime. - /// - /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) - /// the macro expands to `true`. - #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] altivec: "altivec"; - /// Altivec - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] vsx: "vsx"; - /// VSX - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8: "power8"; - without cfg check: true; - /// Power8 - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_altivec: "power8-altivec"; - /// Power8 altivec - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_vector: "power8-vector"; - /// Power8 vector - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_crypto: "power8-crypto"; - /// Power8 crypto - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9: "power9"; - without cfg check: true; - /// Power9 - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9_altivec: "power9-altivec"; - /// Power9 altivec - @FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9_vector: "power9-vector"; - /// Power9 vector -} diff --git a/library/std_detect/src/detect/os/freebsd/powerpc.rs b/library/std_detect/src/detect/os/freebsd/powerpc.rs index d03af68cd0815..e41c37f1a5f73 100644 --- a/library/std_detect/src/detect/os/freebsd/powerpc.rs +++ b/library/std_detect/src/detect/os/freebsd/powerpc.rs @@ -1,8 +1,16 @@ //! Run-time feature detection for PowerPC on FreeBSD. +//! +//! Constants for both 32- and 64-bit come from +//! . use super::auxvec; use crate::detect::{Feature, cache}; +const PPC_FEATURE_HAS_ALTIVEC: usize = 0x10000000; +const PPC_FEATURE_HAS_VSX: usize = 0x00000080; +/// ISA 2.07, typically power8 +const PPC_FEATURE2_ARCH_2_07: usize = 0x80000000; + pub(crate) fn detect_features() -> cache::Initializer { let mut value = cache::Initializer::default(); let enable_feature = |value: &mut cache::Initializer, f, enable| { @@ -12,9 +20,9 @@ pub(crate) fn detect_features() -> cache::Initializer { }; if let Ok(auxv) = auxvec::auxv() { - enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0); - enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0); - enable_feature(&mut value, Feature::power8, auxv.hwcap2 & 0x80000000 != 0); + enable_feature(&mut value, Feature::altivec, auxv.hwcap & PPC_FEATURE_HAS_ALTIVEC != 0); + enable_feature(&mut value, Feature::vsx, auxv.hwcap & PPC_FEATURE_HAS_VSX != 0); + enable_feature(&mut value, Feature::power8, auxv.hwcap2 & PPC_FEATURE2_ARCH_2_07 != 0); return value; } value diff --git a/library/std_detect/src/detect/os/linux/powerpc.rs b/library/std_detect/src/detect/os/linux/powerpc.rs index 6a4f7e715d932..73d5f3d1f7347 100644 --- a/library/std_detect/src/detect/os/linux/powerpc.rs +++ b/library/std_detect/src/detect/os/linux/powerpc.rs @@ -1,8 +1,18 @@ //! Run-time feature detection for PowerPC on Linux. +//! +//! Constants for both 32- and 64-bit come from +//! . use super::auxvec; use crate::detect::{Feature, cache}; +const PPC_FEATURE_HAS_ALTIVEC: usize = 0x10000000; +const PPC_FEATURE_HAS_VSX: usize = 0x00000080; +/// ISA 2.07, typically power8 +const PPC_FEATURE2_ARCH_2_07: usize = 0x80000000; +/// ISA 3.00, typically power9 +const PPC_FEATURE2_ARCH_3_00: usize = 0x00800000; + /// Try to read the features from the auxiliary vector. pub(crate) fn detect_features() -> cache::Initializer { let mut value = cache::Initializer::default(); @@ -12,20 +22,20 @@ pub(crate) fn detect_features() -> cache::Initializer { } }; - // The values are part of the platform-specific [asm/cputable.h][cputable] - // - // [cputable]: https://github.com/torvalds/linux/blob/master/arch/powerpc/include/uapi/asm/cputable.h if let Ok(auxv) = auxvec::auxv() { // note: the PowerPC values are the mask to do the test (instead of the // index of the bit to test like in ARM and Aarch64) - enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0); - enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0); - let power8_features = auxv.hwcap2 & 0x80000000 != 0; + enable_feature(&mut value, Feature::altivec, auxv.hwcap & PPC_FEATURE_HAS_ALTIVEC != 0); + enable_feature(&mut value, Feature::vsx, auxv.hwcap & PPC_FEATURE_HAS_VSX != 0); + // FIXME(powerpc_target_feature): this code assumes the ISA hwcaps imply vsx/altivec, but + // this is not a correct assumption. See rust-lang/compiler-team#1046 for + // a possible resolution. + let power8_features = auxv.hwcap2 & PPC_FEATURE2_ARCH_2_07 != 0; enable_feature(&mut value, Feature::power8, power8_features); enable_feature(&mut value, Feature::power8_altivec, power8_features); enable_feature(&mut value, Feature::power8_crypto, power8_features); enable_feature(&mut value, Feature::power8_vector, power8_features); - let power9_features = auxv.hwcap2 & 0x00800000 != 0; + let power9_features = auxv.hwcap2 & PPC_FEATURE2_ARCH_3_00 != 0; enable_feature(&mut value, Feature::power9, power9_features); enable_feature(&mut value, Feature::power9_altivec, power9_features); enable_feature(&mut value, Feature::power9_vector, power9_features); diff --git a/library/std_detect/src/detect/os/openbsd/powerpc.rs b/library/std_detect/src/detect/os/openbsd/powerpc.rs index dd98ab2a3f76e..7a684cd273c64 100644 --- a/library/std_detect/src/detect/os/openbsd/powerpc.rs +++ b/library/std_detect/src/detect/os/openbsd/powerpc.rs @@ -1,8 +1,19 @@ //! Run-time feature detection for PowerPC on OpenBSD. +//! +//! Constants for 32-bit are located at +//! , and for 64-bit +//! . use super::auxvec; use crate::detect::{Feature, cache}; +/* Note that most constants are the same for 32- and 64-bit, but not all are */ + +const PPC_FEATURE_HAS_ALTIVEC: usize = 0x10000000; +const PPC_FEATURE_HAS_VSX: usize = 0x00000080; +/// ISA 2.07, typically power8 +const PPC_FEATURE2_ARCH_2_07: usize = 0x80000000; + pub(crate) fn detect_features() -> cache::Initializer { let mut value = cache::Initializer::default(); let enable_feature = |value: &mut cache::Initializer, f, enable| { @@ -12,9 +23,9 @@ pub(crate) fn detect_features() -> cache::Initializer { }; if let Ok(auxv) = auxvec::auxv() { - enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0); - enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0); - enable_feature(&mut value, Feature::power8, auxv.hwcap2 & 0x80000000 != 0); + enable_feature(&mut value, Feature::altivec, auxv.hwcap & PPC_FEATURE_HAS_ALTIVEC != 0); + enable_feature(&mut value, Feature::vsx, auxv.hwcap & PPC_FEATURE_HAS_VSX != 0); + enable_feature(&mut value, Feature::power8, auxv.hwcap2 & PPC_FEATURE2_ARCH_2_07 != 0); return value; } value diff --git a/library/std_detect/src/lib.rs b/library/std_detect/src/lib.rs index f9d79df670a0f..d5805875ad9d6 100644 --- a/library/std_detect/src/lib.rs +++ b/library/std_detect/src/lib.rs @@ -10,7 +10,6 @@ //! * `mips`: [`is_mips_feature_detected`] //! * `mips64`: [`is_mips64_feature_detected`] //! * `powerpc`: [`is_powerpc_feature_detected`] -//! * `powerpc64`: [`is_powerpc64_feature_detected`] //! * `loongarch`: [`is_loongarch_feature_detected`] //! * `s390x`: [`is_s390x_feature_detected`] diff --git a/library/std_detect/tests/cpu-detection.rs b/library/std_detect/tests/cpu-detection.rs index a381d84cfbbc3..9e714679284b0 100644 --- a/library/std_detect/tests/cpu-detection.rs +++ b/library/std_detect/tests/cpu-detection.rs @@ -317,24 +317,16 @@ fn riscv_linux() { println!("p: {}", is_riscv_feature_detected!("p")); } -#[test] -#[cfg(all(target_arch = "powerpc", target_os = "linux"))] -fn powerpc_linux() { - println!("altivec: {}", is_powerpc_feature_detected!("altivec")); - println!("vsx: {}", is_powerpc_feature_detected!("vsx")); - println!("power8: {}", is_powerpc_feature_detected!("power8")); -} - #[test] #[cfg(all( - target_arch = "powerpc64", + any(target_arch = "powerpc", target_arch = "powerpc64"), any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"), ))] -fn powerpc64_linux_or_bsd() { - println!("altivec: {}", is_powerpc64_feature_detected!("altivec")); - println!("vsx: {}", is_powerpc64_feature_detected!("vsx")); - println!("power8: {}", is_powerpc64_feature_detected!("power8")); - println!("power9: {}", is_powerpc64_feature_detected!("power9")); +fn powerpc_linux_or_bsd() { + println!("altivec: {}", is_powerpc_feature_detected!("altivec")); + println!("vsx: {}", is_powerpc_feature_detected!("vsx")); + println!("power8: {}", is_powerpc_feature_detected!("power8")); + println!("power9: {}", is_powerpc_feature_detected!("power9")); } #[test] diff --git a/library/std_detect/tests/macro_trailing_commas.rs b/library/std_detect/tests/macro_trailing_commas.rs index a60b34acb872f..f1d2973b961e8 100644 --- a/library/std_detect/tests/macro_trailing_commas.rs +++ b/library/std_detect/tests/macro_trailing_commas.rs @@ -75,19 +75,12 @@ fn loongarch() { } #[test] -#[cfg(target_arch = "powerpc")] +#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] fn powerpc() { let _ = is_powerpc_feature_detected!("altivec"); let _ = is_powerpc_feature_detected!("altivec",); } -#[test] -#[cfg(target_arch = "powerpc64")] -fn powerpc64() { - let _ = is_powerpc64_feature_detected!("altivec"); - let _ = is_powerpc64_feature_detected!("altivec",); -} - #[test] #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] fn riscv() { diff --git a/library/stdarch/crates/simd-test-macro/src/lib.rs b/library/stdarch/crates/simd-test-macro/src/lib.rs index 9219540a1065f..51dd8aaf46fe7 100644 --- a/library/stdarch/crates/simd-test-macro/src/lib.rs +++ b/library/stdarch/crates/simd-test-macro/src/lib.rs @@ -67,8 +67,7 @@ pub fn simd_test( "arm" | "armv7" | "thumbv7neon" => "is_arm_feature_detected", "aarch64" | "arm64ec" | "aarch64_be" => "is_aarch64_feature_detected", maybe_riscv if maybe_riscv.starts_with("riscv") => "is_riscv_feature_detected", - "powerpc" | "powerpcle" => "is_powerpc_feature_detected", - "powerpc64" | "powerpc64le" => "is_powerpc64_feature_detected", + "powerpc" | "powerpcle" | "powerpc64" | "powerpc64le" => "is_powerpc_feature_detected", "loongarch32" | "loongarch64" => "is_loongarch_feature_detected", "s390x" => "is_s390x_feature_detected", "mips64" | "mips64el" => "is_mips64_feature_detected", diff --git a/src/doc/rustc-dev-guide/src/tests/misc.md b/src/doc/rustc-dev-guide/src/tests/misc.md index e8671257a4144..4f496860f2d3e 100644 --- a/src/doc/rustc-dev-guide/src/tests/misc.md +++ b/src/doc/rustc-dev-guide/src/tests/misc.md @@ -11,23 +11,29 @@ This is a bootstrap/compiler implementation detail, but it can also be useful fo even if it's actually a nightly `rustc`. This is useful because some behaviors of the compiler (e.g. diagnostics) can differ depending on whether the compiler is nightly or not. - -Note that setting `RUSTC_BOOTSTRAP` in ui tests (e.g. via `//@ rustc-env`) is pointless since ui -tests inherit the bootstrap env var from bootstrap (so it's set to `1` by default), -and since compiletest itself passes `-Z` flags (so `-1` breaks the test). -To test stable-only behavior, you need to write a `run-make` test instead. - -For `run-make`/`run-make-cargo` tests, `//@ rustc-env` is not supported. -You can do something like the following for individual `rustc` invocations. - -```rust,ignore -use run_make_support::rustc; - -fn main() { - rustc() - // Pretend that I am very stable - .env("RUSTC_BOOTSTRAP", "-1") - //... - .run(); -} -``` +- `RUSTC_BOOTSTRAP=-allow-unstable-compiler-flags=` will force a given `rustc` to pretend it + is a stable compiler, even if it's actually a nightly `rustc`. + However, the compiler will still accept the passed unstable compiler flags `` (the allowed flag + should be separated by commas). + For example, if you use `RUSTC_BOOTSTRAP=-allow-unstable-compiler-flags=foo,bar`, it will be possible to + pass `-Zfoo` and `-Zbar` to the compiler. + This is useful for internal testing of the compiler, where we need to pass + unstable flags required by compiletest, but we also want to test the compiler's + behavior on the stable channel. + +### UI tests + +Note that setting `RUSTC_BOOTSTRAP=-1` in ui tests (e.g. via `//@ rustc-env`) is pointless since ui +tests inherit the bootstrap env var from bootstrap (so it's set to `1` by default), and since +compiletest itself passes `-Z` flags (so `-1` breaks the test). + +To test stable-only behavior, use the `act-as-stable` directive, which will set +`RUSTC_BOOTSTRAP=-allow-unstable-compiler-flags` with the flags required by compiletest for you. With this +directive, the compiler will not allow using unstable language features or unstable flags that are not set by +compiletest. + +### Run-make tests + +For `run-make`/`run-make-cargo` tests, `//@ act-as-stable` is not supported. +You can use the `run_make_support::stable_bare_rustc` function to create an instance of the compiler +that acts as the stable channel (this internally uses `RUSTC_BOOTSTRAP=-1`). diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 7604973739f08..597c648c1106a 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -452,7 +452,7 @@ impl Options { match ( output_format_s.as_ref().map(|_| output_format), show_coverage, - nightly_options::is_unstable_enabled(matches), + nightly_options::unstable_options_enabled(matches), ) { (None | Some(OutputFormat::CoverageJson), true, _) => {} (_, true, _) => { @@ -944,7 +944,7 @@ impl Options { output_format, json_unused_externs, scrape_examples_options, - unstable_features, + unstable_features: unstable_features.clone(), doctest_build_args, target_modifiers: collected_options.target_modifiers, }; diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index bc6bfc27b7ba0..6af9c48881e34 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -173,7 +173,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions lint_cap: Some(options.lint_cap.unwrap_or(lint::Forbid)), cg: options.codegen_options.clone(), externs: options.externs.clone(), - unstable_features: options.unstable_features, + unstable_features: options.unstable_features.clone(), actually_rustdoc: true, edition: options.edition, target_triple: options.target.clone(), diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index ea131574b17e3..4caaca9c776b2 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1152,8 +1152,11 @@ impl LinkCollector<'_, '_> { // Also resolve links in the note text of `#[deprecated]`. for attr in &item.attrs.other_attrs { - let Attribute::Parsed(AttributeKind::Deprecated { span: depr_span, deprecation }) = - attr + let Attribute::Parsed(AttributeKind::Deprecated { + span: depr_span, + deprecation, + is_inner, + }) = attr else { continue; }; @@ -1185,7 +1188,12 @@ impl LinkCollector<'_, '_> { } else { item.item_id.expect_def_id() }; - try_insert_links(item_id, &DocStrings { outer: note.to_owned(), inner: String::new() }); + let attr = if *is_inner { + DocStrings { outer: String::new(), inner: note.to_owned() } + } else { + DocStrings { outer: note.to_owned(), inner: String::new() } + }; + try_insert_links(item_id, &attr); } } diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 1037b2c6375b7..4ef2b5fd46d65 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -222,6 +222,7 @@ pub(crate) struct TestProps { } mod directives { + pub(crate) const ACT_AS_STABLE: &str = "act-as-stable"; pub(crate) const ERROR_PATTERN: &str = "error-pattern"; pub(crate) const REGEX_ERROR_PATTERN: &str = "regex-error-pattern"; pub(crate) const COMPILE_FLAGS: &str = "compile-flags"; diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index 520a5468e45f0..7af285775380c 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -9,6 +9,7 @@ pub(crate) static KNOWN_DIRECTIVE_NAMES_SET: LazyLock> = /// a best-effort approximation for diagnostics. Add new directives to this list when needed. pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ // tidy-alphabetical-start + "act-as-stable", "add-minicore", "assembly-output", "aux-bin", diff --git a/src/tools/compiletest/src/directives/handlers.rs b/src/tools/compiletest/src/directives/handlers.rs index 4e86dcb308f0d..0e80d6d865f22 100644 --- a/src/tools/compiletest/src/directives/handlers.rs +++ b/src/tools/compiletest/src/directives/handlers.rs @@ -1,12 +1,13 @@ use std::collections::HashMap; use std::sync::{Arc, LazyLock}; -use crate::common::{Config, PassFailMode, TestMode}; +use crate::common::{Config, PassFailMode, TestMode, TestSuite}; use crate::directives::{ DirectiveLine, NormalizeKind, NormalizeRule, TestProps, parse_and_update_aux, parse_edition_range, split_flags, }; use crate::errors::ErrorKind; +use crate::runtest::COMPILETEST_UI_SUITE_UNSTABLE_FLAGS; pub(crate) static DIRECTIVE_HANDLERS_MAP: LazyLock> = LazyLock::new(make_directive_handlers_map); @@ -64,6 +65,23 @@ fn make_directive_handlers_map() -> HashMap<&'static str, Handler> { // these handlers, e.g. by getting rid of now-redundant name checks. let handlers: Vec = vec![ + handler(ACT_AS_STABLE, |config, ln, props| { + if ln.name == ACT_AS_STABLE { + if ![TestSuite::Ui, TestSuite::UiFullDeps].contains(&config.suite) { + panic!( + "The {ACT_AS_STABLE} directive is only usable in the ui and ui-fulldeps test suites" + ); + } + // Simulate the stable toolchain, but allow unstable -Z flags passed by compiletest + props.rustc_env.push(( + "RUSTC_BOOTSTRAP".to_string(), + format!( + "-allow-unstable-compiler-flags={}", + COMPILETEST_UI_SUITE_UNSTABLE_FLAGS.join(",") + ), + )); + } + }), handler(ERROR_PATTERN, |config, ln, props| { config.push_name_value_directive(ln, ERROR_PATTERN, &mut props.error_patterns, |r| r); }), diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index b7453adc31331..ddea906f500a2 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -260,6 +260,28 @@ enum CompilerKind { Rustdoc, } +/// Unstable compiler flags that compiletest passes to UI tests on its own, i.e. without specific +/// tests opting into them explicitly. +/// This should include both flags that are always passed unconditionally (e.g. `-Zui-testing`) and +/// flags that are enabled across the whole test suite when compiletest is configured in a +/// certain way (e.g. a specific `-Zcodegen-backend` being used for all tests). +/// +/// Note: try to keep this up-to-date with [`TestCx::make_compile_args`]. +pub(crate) const COMPILETEST_UI_SUITE_UNSTABLE_FLAGS: &[&str] = &[ + "simulate-remapped-rust-src-base", + "translate-remapped-path-to-local-path", + "ignore-directory-in-diagnostics-source-blocks", + "deduplicate-diagnostics", + "write-long-types-to-disk", + "codegen-backend", + "next-solver", + "polonius", + "wasm-proc-macros", + "threads", + "ui-testing", + "unstable-options", +]; + impl<'test> TestCx<'test> { /// Code executed for each revision in turn (or, if there are no /// revisions, exactly once, with revision == None). @@ -1661,6 +1683,9 @@ impl<'test> TestCx<'test> { compiler.arg("-Zunstable-options").arg("--disable-minification"); } + // Note: when adding new unstable compile flags here, also update + // `COMPILETEST_UI_SUITE_UNSTABLE_FLAGS` + // Hide libstd sources from ui tests to make sure we generate the stderr // output that users will see. // Without this, we may be producing good diagnostics in-tree but users diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index bee60af61f0fa..4622114dcacd8 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -1367,14 +1367,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match result { Ok(read_dir) => { - // Also open the same directory as a directory handle, so we have - // an underlying FD. - // FIXME(https://github.com/rust-lang/miri/issues/5326): This is racy! We can't even - // verify whether there was a race. We just trust that the directory did not change - // in between above and here. One day, the standard library will support converting - // between `Dir` and `ReadDir` (one of the two directions would suffice for our - // needs), then we'll use that. - let Ok(dir) = fs::Dir::open(&name) else { + // Create an underlying FD as well, in case someone calls `dirfd` later. + // (We could do this lazily but that does not seem worth it.) + #[cfg(not(bootstrap))] + let dir = read_dir.dir(); + #[cfg(bootstrap)] + let dir = fs::Dir::open(&name); + let Ok(dir) = dir else { throw_unsup_format!( "cannot `opendir` this directory: failed to create directory handle" ); diff --git a/src/tools/miri/tests/pass-dep/libc/fs.rs b/src/tools/miri/tests/pass-dep/libc/fs.rs index 90053d36fd1b1..a5a60de75f6b0 100644 --- a/src/tools/miri/tests/pass-dep/libc/fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/fs.rs @@ -1659,6 +1659,8 @@ fn test_dirfd() { assert!(!dir.is_null()); let dirfd = unsafe { libc::dirfd(dir) }; + let dirfd2 = unsafe { libc::dirfd(dir) }; + assert_eq!(dirfd, dirfd2); // make sure we always return the same one let mut stat = MaybeUninit::::uninit(); errno_check(unsafe { libc::fstat(dirfd, stat.as_mut_ptr()) }); diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 683b0f5605b0e..cd95ed21e5dc6 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -501,22 +501,24 @@ fn test_directory() { let dir_1 = dir_path.join("test_dir_1"); create_dir(&dir_1).unwrap(); // Test that read_dir metadata calls succeed + let mut dir_iter = read_dir(&dir_path).unwrap(); assert_eq!( BTreeMap::from([ (OsString::from("test_file_1"), true), (OsString::from("test_file_2"), true), (OsString::from("test_dir_1"), false) ]), - read_dir(&dir_path) - .unwrap() + (&mut dir_iter) .map(|e| { let e = e.unwrap(); (e.file_name(), e.metadata().unwrap().is_file()) }) .collect::>() ); + // We can access the directory handle from this iterator. + let dir_handle = dir_iter.dir().unwrap(); + drop(dir_handle.open_file("test_file_1").unwrap()); // Deleting the directory should fail, since it is not empty. - // Solaris/Illumos `rmdir` call set errno to EEXIST if directory contains // other entries than `.` and `..`. // https://docs.oracle.com/cd/E86824_01/html/E54765/rmdir-2.html diff --git a/src/tools/rustfmt/Subtree sync procedure.md b/src/tools/rustfmt/Subtree sync procedure.md index cedca7b40f5da..01f581a6fb570 100644 --- a/src/tools/rustfmt/Subtree sync procedure.md +++ b/src/tools/rustfmt/Subtree sync procedure.md @@ -19,6 +19,11 @@ sync process (both pulls and pushes). You can install it with `cargo install --g 4. Run `rustc-josh-sync pull` 5. Confirm the creation of a PR directly by `rustc-josh-sync`, or create a PR to `rust-lang/rustfmt` repository +> [!CAUTION] +> +> For now, do not use the `--allow-noop` option of `rustc-josh-sync`. See +> . + The `rustfmt` maintainers will run Diff Check against the PR to catch any unexpected formatting changes. - Maintainers should trigger Diff-Check for the combinations of Edition {2021, 2024} x Style @@ -32,7 +37,7 @@ Where applicable, we may need to update the CHANGELOG entries with merged PRs (b repository and also in the `rust-lang/rust` `rustfmt` subtree that was included in the subtree-push merge), and then bump rustfmt version number. -## Subtree push direction: syncing changes from `rustfmt` to rust-lang/rust` +## Subtree push direction: syncing changes from `rustfmt` to `rust-lang/rust` 1. Be in a `rustfmt` checkout 2. Checkout the latest `main` branch from upstream diff --git a/tests/codegen-llvm/debug-vtable.rs b/tests/codegen-llvm/debug-vtable.rs index 8a7b1cc3c4bcc..031153832629c 100644 --- a/tests/codegen-llvm/debug-vtable.rs +++ b/tests/codegen-llvm/debug-vtable.rs @@ -19,7 +19,7 @@ // CHECK: @vtable.3 = private constant <{ // CHECK: @vtable.4 = private constant <{ -// NONMSVC: ![[USIZE:[0-9]+]] = !DIBasicType(name: "usize" +// NONMSVC: ![[USIZE:[0-9]+]] = !DIDerivedType(tag: DW_TAG_typedef, name: "usize" // MSVC: ![[USIZE:[0-9]+]] = !DIDerivedType(tag: DW_TAG_typedef, name: "usize" // NONMSVC: ![[PTR:[0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, name: "*const ()" // MSVC: ![[PTR:[0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, name: "ptr_const$ >" diff --git a/tests/ui/enum-discriminant/invalid-niche-discriminant.rs b/tests/crashes/invalid-niche-discriminant.rs similarity index 96% rename from tests/ui/enum-discriminant/invalid-niche-discriminant.rs rename to tests/crashes/invalid-niche-discriminant.rs index f70f7d1736bae..b5a355d5b9a41 100644 --- a/tests/ui/enum-discriminant/invalid-niche-discriminant.rs +++ b/tests/crashes/invalid-niche-discriminant.rs @@ -1,3 +1,4 @@ +//@ known-bug: #144501 //@ needs-rustc-debug-assertions //@ revisions: normal with_delayed //@ [with_delayed] compile-flags: -Z eagerly-emit-delayed-bugs diff --git a/tests/debuginfo/basic-types/lldb_input/non_windows.json b/tests/debuginfo/basic-types/lldb_input/non_windows.json index af225246e6109..67c45674981a2 100644 --- a/tests/debuginfo/basic-types/lldb_input/non_windows.json +++ b/tests/debuginfo/basic-types/lldb_input/non_windows.json @@ -12,7 +12,7 @@ "value": false }, "i": { - "type": "long", + "type": "isize", "pretty_print": "-1", "value": -1 }, @@ -43,7 +43,7 @@ "value": -64 }, "u": { - "type": "unsigned long", + "type": "usize", "pretty_print": "1", "value": 1 }, diff --git a/tests/debuginfo/basic-types/lldb_input/windows_gnu.json b/tests/debuginfo/basic-types/lldb_input/windows_gnu.json index 80c35ffcbea60..06b21d7cb0b35 100644 --- a/tests/debuginfo/basic-types/lldb_input/windows_gnu.json +++ b/tests/debuginfo/basic-types/lldb_input/windows_gnu.json @@ -1,7 +1,7 @@ { "bless_metadata": { "python_version": "3.11.9 (tags/v3.11.9:de54cf5, Apr 2 2024, 10:12:12) [MSC v.1938 64 bit (AMD64)]", - "debugger_version": "lldb version 22.1.2 (https://github.com/llvm/llvm-project revision 1ab49a973e210e97d61e5db6557180dcb92c3e98)\n clang revision 1ab49a973e210e97d61e5db6557180dcb92c3e98\n llvm revision 1ab49a973e210e97d61e5db6557180dcb92c3e98", + "debugger_version": "lldb version 22.1.8 (https://github.com/llvm/llvm-project revision ca7933e47d3a3451d81e72ac174dcb5aa28b59d1)\n clang revision ca7933e47d3a3451d81e72ac174dcb5aa28b59d1\n llvm revision ca7933e47d3a3451d81e72ac174dcb5aa28b59d1", "feature_flags": "LLDBFeature.StaticFields|TypeRecognizers|Float128" }, "breakpoints": [ @@ -12,7 +12,7 @@ "value": false }, "i": { - "type": "long long", + "type": "isize", "pretty_print": "-1", "value": -1 }, @@ -43,7 +43,7 @@ "value": -64 }, "u": { - "type": "unsigned long long", + "type": "usize", "pretty_print": "1", "value": 1 }, diff --git a/tests/debuginfo/pretty-std/lldb_input/non_windows.json b/tests/debuginfo/pretty-std/lldb_input/non_windows.json index 737eb5004b2bf..f76ba1ded06a1 100644 --- a/tests/debuginfo/pretty-std/lldb_input/non_windows.json +++ b/tests/debuginfo/pretty-std/lldb_input/non_windows.json @@ -162,7 +162,7 @@ }, { "name": "length", - "type": "unsigned long", + "type": "usize", "offset": 8 } ] @@ -178,7 +178,7 @@ }, { "name": "length", - "type": "unsigned long", + "type": "usize", "offset": 8 } ] @@ -273,7 +273,7 @@ }, { "name": "len", - "type": "unsigned long", + "type": "usize", "offset": 16 } ], @@ -293,7 +293,7 @@ }, { "name": "len", - "type": "unsigned long", + "type": "usize", "offset": 16 } ], @@ -322,7 +322,7 @@ "fields": [ { "name": "__0", - "type": "unsigned long", + "type": "usize", "offset": 0 } ] @@ -560,6 +560,11 @@ "size": 2, "type_class": 4, "basic_type": 12 + }, + "usize": { + "size": 8, + "type_class": 32768, + "basic_type": 16 } } } diff --git a/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json b/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json index 7f7772bd4caa1..56bfb83deef32 100644 --- a/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json +++ b/tests/debuginfo/pretty-std/lldb_input/windows_gnu.json @@ -173,7 +173,7 @@ }, { "name": "length", - "type": "unsigned long long", + "type": "usize", "offset": 8 } ] @@ -284,7 +284,7 @@ }, { "name": "len", - "type": "unsigned long long", + "type": "usize", "offset": 16 } ], diff --git a/tests/debuginfo/rc_arc.rs b/tests/debuginfo/rc_arc.rs index adeb96dba1b80..e1af1eba02193 100644 --- a/tests/debuginfo/rc_arc.rs +++ b/tests/debuginfo/rc_arc.rs @@ -21,6 +21,11 @@ //@ lldb-check:[...] strong=11, weak=2 { value = 111 } //@ lldb-command:v arc //@ lldb-check:[...] strong=21, weak=2 { data = 222 } +//@ lldb-command:continue +//@ lldb-command:v rc_u64 +//@ lldb-check:[...] rc_u64 = strong=1, weak=1 { value = 10 } +//@ lldb-command:v rc_usize +//@ lldb-check:[...] rc_usize = strong=1, weak=1 { value = 20 } // === CDB TESTS ================================================================================== @@ -132,6 +137,11 @@ fn main() { let slice_arc_weak = Arc::downgrade(&slice_arc); zzz(); // #break + + let rc_u64 = Rc::new(10u64); + let rc_usize = Rc::new(20usize); + + zzz(); // #break } fn inc_ref_count(rc: &T, count: usize) { diff --git a/tests/incremental/lto.rs b/tests/incremental/lto.rs index ddeddf95f8aee..c82b040c238cb 100644 --- a/tests/incremental/lto.rs +++ b/tests/incremental/lto.rs @@ -1,11 +1,20 @@ //@ no-prefer-dynamic //@ revisions:rpass1 rpass2 -//@ compile-flags: -C lto +//@ compile-flags: -Z query-dep-graph -C lto=fat //@ ignore-backends: gcc +#![feature(rustc_attrs)] +#![rustc_partition_codegened(module = "lto", cfg = "rpass1")] +#![rustc_partition_codegened(module = "lto-x", cfg = "rpass1")] +#![rustc_partition_codegened(module = "lto-y", cfg = "rpass1")] +#![rustc_partition_codegened(module = "lto", cfg = "rpass2")] // FIXME should be reused +#![rustc_partition_codegened(module = "lto-x", cfg = "rpass2")] +#![rustc_partition_codegened(module = "lto-y", cfg = "rpass2")] // FIXME should be reused + mod x { pub struct X { - x: u32, y: u32, + x: u32, + y: u32, } #[cfg(rpass1)] diff --git a/tests/pretty/delegation/inherit-attributes.pp b/tests/pretty/delegation/inherit-attributes.pp index 01aa7bd2fa5eb..575e309c392bb 100644 --- a/tests/pretty/delegation/inherit-attributes.pp +++ b/tests/pretty/delegation/inherit-attributes.pp @@ -23,11 +23,13 @@ fn foo_no_reason(x: usize) -> usize { x } #[attr = Cold] - #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}] + #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}, + is_inner: false}] fn bar(x: usize) -> usize { x } } -#[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}] +#[attr = Deprecated {deprecation: Deprecation {since: Unspecified}, +is_inner: false}] #[attr = MustUse {reason: "foo: some reason"}] #[attr = Inline(Hint)] fn foo1(arg0: _) -> _ { to_reuse::foo(self + 1) } @@ -36,7 +38,8 @@ #[attr = Inline(Hint)] fn foo_no_reason(arg0: _) -> _ { to_reuse::foo_no_reason(self + 1) } -#[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}] +#[attr = Deprecated {deprecation: Deprecation {since: Unspecified}, +is_inner: false}] #[attr = MustUse {reason: "some reason"}] #[attr = Inline(Hint)] fn foo2(arg0: _) -> _ { to_reuse::foo(self + 1) } diff --git a/tests/pretty/delegation/inline-attribute.pp b/tests/pretty/delegation/inline-attribute.pp index bf55d76ad3fb8..dfe1097035674 100644 --- a/tests/pretty/delegation/inline-attribute.pp +++ b/tests/pretty/delegation/inline-attribute.pp @@ -46,7 +46,8 @@ // Check that #[inline(hint)] is added when other attributes present in inner reuse #[attr = Cold] #[attr = MustUse] - #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}] + #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}, + is_inner: false}] #[attr = Inline(Hint)] fn foo1(arg0: _) -> _ { to_reuse::foo(self / 2) } @@ -62,7 +63,8 @@ #[attr = Cold] #[attr = MustUse] #[attr = Inline(Never)] - #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}] + #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}, + is_inner: false}] fn foo4(arg0: _) -> _ { to_reuse::foo(self / 2) } Trait::foo(self) } @@ -70,7 +72,8 @@ // Check that #[inline(hint)] is added when there are other attributes present in trait reuse #[attr = Cold] #[attr = MustUse] - #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}] + #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}, + is_inner: false}] #[attr = Inline(Hint)] fn foo1(self: _) -> _ { Trait::foo1(self.0) } @@ -86,7 +89,8 @@ #[attr = Cold] #[attr = MustUse] #[attr = Inline(Never)] - #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}] + #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}, + is_inner: false}] fn foo4(self: _) -> _ { Trait::foo4(self.0) } } diff --git a/tests/run-make/const-destruct-stable-toolchain/const-drop-stable.stderr b/tests/run-make/const-destruct-stable-toolchain/const-drop-stable.stderr deleted file mode 100644 index dc37d840658fd..0000000000000 --- a/tests/run-make/const-destruct-stable-toolchain/const-drop-stable.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0493]: destructor of `T` cannot be evaluated at compile-time - --> const-drop.rs:1:24 - | -1 | const fn const_drop(_: T) {} - | ^ - value is dropped here - | | - | the destructor for this type cannot be evaluated in constant functions - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0493`. diff --git a/tests/run-make/const-destruct-stable-toolchain/const-drop.rs b/tests/run-make/const-destruct-stable-toolchain/const-drop.rs deleted file mode 100644 index 6479498281fe5..0000000000000 --- a/tests/run-make/const-destruct-stable-toolchain/const-drop.rs +++ /dev/null @@ -1,3 +0,0 @@ -const fn const_drop(_: T) {} - -fn main() {} diff --git a/tests/run-make/const-destruct-stable-toolchain/rmake.rs b/tests/run-make/const-destruct-stable-toolchain/rmake.rs deleted file mode 100644 index 47d66cf267ab9..0000000000000 --- a/tests/run-make/const-destruct-stable-toolchain/rmake.rs +++ /dev/null @@ -1,28 +0,0 @@ -//@ needs-target-std -//@ ignore-backends: gcc - -// Test that the suggestion to constrain a type parameter that is dropped in a const -// function with a `[const] Destruct` bound is only offered on nightly, since the bound -// requires an unstable feature. - -use run_make_support::{diff, rustc, stable_bare_rustc}; - -fn main() { - let out = stable_bare_rustc() - .input("const-drop.rs") - .edition("2015") - .run_fail() - .assert_stderr_not_contains("consider restricting type parameter `T`") - .stderr_utf8(); - diff().expected_file("const-drop-stable.stderr").actual_text("(rustc)", &out).run(); - let out = rustc() - .input("const-drop.rs") - .edition("2015") - .ui_testing() - .run_fail() - .assert_stderr_contains( - "consider restricting type parameter `T` with unstable trait `Destruct`", - ) - .stderr_utf8(); - diff().expected_file("const-drop-nightly.stderr").actual_text("(rustc)", &out).run(); -} diff --git a/tests/run-make/parallel-reproducible-build/generics-of-issue-163878.rs b/tests/run-make/parallel-reproducible-build/generics-of-issue-163878.rs new file mode 100644 index 0000000000000..815218b58b20b --- /dev/null +++ b/tests/run-make/parallel-reproducible-build/generics-of-issue-163878.rs @@ -0,0 +1,10 @@ +struct A {} + +impl A { + fn f1(&self, b1: &()) -> impl Iterator + '_ { + std::iter::empty() + } + fn f2(&self, b2: &()) -> impl Iterator + '_ { + std::iter::empty() + } +} diff --git a/tests/run-make/parallel-reproducible-build/rmake.rs b/tests/run-make/parallel-reproducible-build/rmake.rs index 01de08e0551f3..24c441aa9bd32 100644 --- a/tests/run-make/parallel-reproducible-build/rmake.rs +++ b/tests/run-make/parallel-reproducible-build/rmake.rs @@ -47,6 +47,16 @@ fn main() { "-Clink-dead-code=true", ], ), + ( + "generics-of-issue-163878", + &[ + "-Zthreads=2", + "--edition=2024", + "-Zremap-cwd-prefix=reproducible_dir", + "-Ccodegen-units=1", + "--crate-type=lib", + ], + ), ]; for (file, args) in TESTS { diff --git a/tests/run-make/print-request-help-stable-unstable/help-diff.diff b/tests/run-make/print-request-help-stable-unstable/help-diff.diff deleted file mode 100644 index 8a9c3d0cf46c1..0000000000000 --- a/tests/run-make/print-request-help-stable-unstable/help-diff.diff +++ /dev/null @@ -1,7 +0,0 @@ -@@ -1,5 +1,5 @@ - error: unknown print request: `xxx` - | -- = help: valid print requests are: `calling-conventions`, `cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `tls-models` -+ = help: valid print requests are: `all-target-specs-json`, `backend-has-mnemonic`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models`, `wasm-proc-macro-tuple` - = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information - diff --git a/tests/run-make/print-request-help-stable-unstable/rmake.rs b/tests/run-make/print-request-help-stable-unstable/rmake.rs deleted file mode 100644 index 49df84b353cd4..0000000000000 --- a/tests/run-make/print-request-help-stable-unstable/rmake.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Check that unstable print requests are omitted from help if compiler is in stable channel. -//! -//! Issue: - -//@ ignore-backends: gcc - -use run_make_support::{diff, rustc, similar, stable_bare_rustc}; - -fn main() { - let stable_invalid_print_request_help = - stable_bare_rustc().edition("2015").print("xxx").run_fail().stderr_utf8(); - assert!(!stable_invalid_print_request_help.contains("all-target-specs-json")); - diff() - .expected_file("stable-invalid-print-request-help.err") - .actual_text("stable_invalid_print_request_help", &stable_invalid_print_request_help) - .run(); - - let unstable_invalid_print_request_help = - rustc().edition("2015").print("xxx").run_fail().stderr_utf8(); - assert!(unstable_invalid_print_request_help.contains("all-target-specs-json")); - diff() - .expected_file("unstable-invalid-print-request-help.err") - .actual_text("unstable_invalid_print_request_help", &unstable_invalid_print_request_help) - .run(); - - let help_diff = similar::TextDiff::from_lines( - &stable_invalid_print_request_help, - &unstable_invalid_print_request_help, - ) - .unified_diff() - .to_string(); - diff().expected_file("help-diff.diff").actual_text("help_diff", help_diff).run(); -} diff --git a/tests/rustdoc-ui/intra-doc/link-in-deprecation-note.rs b/tests/rustdoc-ui/intra-doc/link-in-deprecation-note.rs new file mode 100644 index 0000000000000..4546c257bee37 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/link-in-deprecation-note.rs @@ -0,0 +1,12 @@ +// Because inner and outer attributes don't have the same scope for a module, we need +// to also apply the logic to the other module attributes which can contain intra-doc +// links. +// Regression test for . + +#![deny(rustdoc::broken_intra_doc_links)] + +pub mod a { + //! + #![deprecated = "[](TypeAlias::x)"] + //~^^ ERROR broken_intra_doc_links +} diff --git a/tests/rustdoc-ui/intra-doc/link-in-deprecation-note.stderr b/tests/rustdoc-ui/intra-doc/link-in-deprecation-note.stderr new file mode 100644 index 0000000000000..9b7df6f02c533 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/link-in-deprecation-note.stderr @@ -0,0 +1,20 @@ +error: unresolved link to `TypeAlias::x` + --> $DIR/link-in-deprecation-note.rs:9:5 + | +LL | / //! +LL | | #![deprecated = "[](TypeAlias::x)"] + | |______________________________________^ + | + = note: the link appears in this line: + + [](TypeAlias::x) + ^^^^^^^^^^^^ + = note: no item named `TypeAlias` in scope +note: the lint level is defined here + --> $DIR/link-in-deprecation-note.rs:6:9 + | +LL | #![deny(rustdoc::broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/bootstrap/rustc_bootstrap.force_stable_disallow_additional_flags.stderr b/tests/ui/bootstrap/rustc_bootstrap.force_stable_disallow_additional_flags.stderr new file mode 100644 index 0000000000000..13d619cfc388f --- /dev/null +++ b/tests/ui/bootstrap/rustc_bootstrap.force_stable_disallow_additional_flags.stderr @@ -0,0 +1,10 @@ +error: the -Zmir-opt-level option was not allowed through `-allow-unstable-compiler-flags` + +error: the option `Z` is only accepted on the nightly compiler + +error: 1 nightly option was parsed + | + = help: consider switching to a nightly toolchain: `rustup default nightly` + = note: selecting a toolchain with `+toolchain` arguments require a rustup proxy; see + = note: for more information about Rust's stability policy, see + diff --git a/tests/ui/bootstrap/rustc_bootstrap.force_stable_disallow_unstable_features.stderr b/tests/ui/bootstrap/rustc_bootstrap.force_stable_disallow_unstable_features.stderr new file mode 100644 index 0000000000000..0a0772ff7453e --- /dev/null +++ b/tests/ui/bootstrap/rustc_bootstrap.force_stable_disallow_unstable_features.stderr @@ -0,0 +1,9 @@ +error[E0554]: `#![feature]` may not be used on the nightly release channel + --> $DIR/rustc_bootstrap.rs:56:1 + | +LL | #![feature(rustc_attrs)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0554`. diff --git a/tests/ui/bootstrap/rustc_bootstrap.rs b/tests/ui/bootstrap/rustc_bootstrap.rs index fb72bba95a158..a9d77214add14 100644 --- a/tests/ui/bootstrap/rustc_bootstrap.rs +++ b/tests/ui/bootstrap/rustc_bootstrap.rs @@ -6,11 +6,14 @@ //! - `1`: cheat, allow usage of unstable features even if rustc thinks it is a stable compiler. //! - `x,y,z`: comma-delimited list of crates. //! - `-1`: force rustc to think it is a stable compiler. +//! - `-allow-unstable-compiler-flags=`: force rustc to think it is a stable compiler, +//! while still allowing the specified -Z flags. // ignore-tidy-linelength -//@ revisions: default_nightly cheat cheat_single_crate cheat_multi_crate force_stable invalid_zero invalid_junk +//@ revisions: default_nightly cheat cheat_single_crate cheat_multi_crate force_stable force_stable_disallow_additional_flags force_stable_disallow_unstable_features invalid_zero invalid_junk //@ only-nightly +//@ normalize-stderr: "dev release channel" -> "nightly release channel" //@[default_nightly] unset-rustc-env:RUSTC_BOOTSTRAP //@[default_nightly] check-pass @@ -39,10 +42,18 @@ //@[force_stable] rustc-env:RUSTC_BOOTSTRAP=-1 //@[force_stable] compile-flags: -Z unstable-options -#![crate_type = "lib"] +// Here rustc shouldn't complain about the allowed compiletest -Z flags being passed, but should +// still complain about other unstable flags. +//@[force_stable_disallow_additional_flags] act-as-stable +//@[force_stable_disallow_additional_flags] compile-flags: -Z mir-opt-level=3 + +// Here rustc should not allow using unstable language features. +//@[force_stable_disallow_unstable_features] act-as-stable +#![crate_type = "lib"] // Note: `rustc_attrs` is a perma-unstable internal feature that is unlikely to change, which is // used as a proxy to check `RUSTC_BOOTSTRAP` versus stability checking logic. #![feature(rustc_attrs)] +//[force_stable_disallow_unstable_features]~^ ERROR: `#![feature]` may not be used on the //[force_stable]~? RAW the option `Z` is only accepted on the nightly compiler diff --git a/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err b/tests/ui/compile-flags/print-request-help-stable-unstable.nightly.stderr similarity index 100% rename from tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err rename to tests/ui/compile-flags/print-request-help-stable-unstable.nightly.stderr diff --git a/tests/ui/compile-flags/print-request-help-stable-unstable.rs b/tests/ui/compile-flags/print-request-help-stable-unstable.rs new file mode 100644 index 0000000000000..f75996f28e6bd --- /dev/null +++ b/tests/ui/compile-flags/print-request-help-stable-unstable.rs @@ -0,0 +1,12 @@ +//! Check that unstable print requests are omitted from help if compiler is in stable channel. +//! +//! Issue: + +//@ ignore-backends: gcc +//@ compile-flags: --print xxx +// +//@ revisions: stable nightly +//@[stable] act-as-stable +//@[nightly] only-nightly + +//~? ERROR unknown print request: `xxx` diff --git a/tests/run-make/print-request-help-stable-unstable/stable-invalid-print-request-help.err b/tests/ui/compile-flags/print-request-help-stable-unstable.stable.stderr similarity index 100% rename from tests/run-make/print-request-help-stable-unstable/stable-invalid-print-request-help.err rename to tests/ui/compile-flags/print-request-help-stable-unstable.stable.stderr diff --git a/tests/run-make/const-destruct-stable-toolchain/const-drop-nightly.stderr b/tests/ui/consts/const-destruct-stable-toolchain.nightly.stderr similarity index 91% rename from tests/run-make/const-destruct-stable-toolchain/const-drop-nightly.stderr rename to tests/ui/consts/const-destruct-stable-toolchain.nightly.stderr index 52f68743b6275..2d05a300165aa 100644 --- a/tests/run-make/const-destruct-stable-toolchain/const-drop-nightly.stderr +++ b/tests/ui/consts/const-destruct-stable-toolchain.nightly.stderr @@ -1,5 +1,5 @@ error[E0493]: destructor of `T` cannot be evaluated at compile-time - --> const-drop.rs:1:24 + --> $DIR/const-destruct-stable-toolchain.rs:12:24 | LL | const fn const_drop(_: T) {} | ^ - value is dropped here diff --git a/tests/ui/consts/const-destruct-stable-toolchain.rs b/tests/ui/consts/const-destruct-stable-toolchain.rs new file mode 100644 index 0000000000000..ad16a74508e4c --- /dev/null +++ b/tests/ui/consts/const-destruct-stable-toolchain.rs @@ -0,0 +1,15 @@ +// Test that the suggestion to constrain a type parameter that is dropped in a const +// function with a `[const] Destruct` bound is only offered on nightly, since the bound +// requires an unstable feature. +// +//@ needs-target-std +//@ ignore-backends: gcc +// +//@ revisions: stable nightly +// +//@[stable] act-as-stable +//@[nightly] only-nightly +const fn const_drop(_: T) {} +//~^ ERROR: destructor of `T` cannot be evaluated at compile-time [E0493] + +fn main() {} diff --git a/tests/ui/consts/const-destruct-stable-toolchain.stable.stderr b/tests/ui/consts/const-destruct-stable-toolchain.stable.stderr new file mode 100644 index 0000000000000..4755afcd3f1e3 --- /dev/null +++ b/tests/ui/consts/const-destruct-stable-toolchain.stable.stderr @@ -0,0 +1,11 @@ +error[E0493]: destructor of `T` cannot be evaluated at compile-time + --> $DIR/const-destruct-stable-toolchain.rs:12:24 + | +LL | const fn const_drop(_: T) {} + | ^ - value is dropped here + | | + | the destructor for this type cannot be evaluated in constant functions + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0493`. diff --git a/tests/ui/eii/cfg_on_eii.stdout b/tests/ui/eii/cfg_on_eii.stdout index f6ed2aa90ed97..e49f3cdbb8c45 100644 --- a/tests/ui/eii/cfg_on_eii.stdout +++ b/tests/ui/eii/cfg_on_eii.stdout @@ -52,13 +52,14 @@ const _: () = extern "Rust" { #[attr = CfgAttrTrace([Bool(true, $DIR/cfg_on_eii.rs:13:12: 13:16 (#0))])] #[attr = Deprecated {deprecation: Deprecation {since: Unspecified, - note: bar#0}}] + note: bar#0}, + is_inner: false}] #[attr = RustcEiiForeignItem] fn cfg_attr_on_eii(); } #[attr = CfgAttrTrace([Bool(true, $DIR/cfg_on_eii.rs:13:12: 13:16 (#0))])] #[attr = Deprecated {deprecation: Deprecation {since: Unspecified, -note: bar#0}}] +note: bar#0}, is_inner: false}] #[attr = RustcMacroTransparency(SemiOpaque)] #[attr = RustcBuiltinMacro {builtin_name: "eii_shared_macro", helper_attrs: []}] diff --git a/tests/ui/enum-discriminant/invalid-niche-discriminant.normal.stderr b/tests/ui/enum-discriminant/invalid-niche-discriminant.normal.stderr deleted file mode 100644 index 9c66c1782e4dc..0000000000000 --- a/tests/ui/enum-discriminant/invalid-niche-discriminant.normal.stderr +++ /dev/null @@ -1,35 +0,0 @@ -error[E0732]: `#[repr(inttype)]` must be specified for enums with explicit discriminants and non-unit variants - --> $DIR/invalid-niche-discriminant.rs:11:1 - | -LL | enum E { - | ^^^^^^ -... -LL | S0 { - | -- non-unit discriminant declared here -... -LL | Bar = { - | ___________- -LL | | let x = 1; -LL | | 3 -LL | | }, - | |_____- explicit discriminant specified here - -error[E0599]: no variant named `S1` found for enum `E` - --> $DIR/invalid-niche-discriminant.rs:23:18 - | -LL | enum E { - | ------ variant `S1` not found here -... -LL | static C: E = E::S1 { u: 23 }; - | ^^ - | -help: there is a variant with a similar name - | -LL - static C: E = E::S1 { u: 23 }; -LL + static C: E = E::S0 { u: 23 }; - | - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0599, E0732. -For more information about an error, try `rustc --explain E0599`. diff --git a/tests/ui/enum-discriminant/invalid-niche-discriminant.with_delayed.stderr b/tests/ui/enum-discriminant/invalid-niche-discriminant.with_delayed.stderr deleted file mode 100644 index 20f118655a17a..0000000000000 --- a/tests/ui/enum-discriminant/invalid-niche-discriminant.with_delayed.stderr +++ /dev/null @@ -1,47 +0,0 @@ -error[E0732]: `#[repr(inttype)]` must be specified for enums with explicit discriminants and non-unit variants - --> $DIR/invalid-niche-discriminant.rs:11:1 - | -LL | enum E { - | ^^^^^^ -... -LL | S0 { - | -- non-unit discriminant declared here -... -LL | Bar = { - | ___________- -LL | | let x = 1; -LL | | 3 -LL | | }, - | |_____- explicit discriminant specified here - -error: variant 1 has discriminant 3 in niche-encoded type - --> $DIR/invalid-niche-discriminant.rs:11:1 - | -LL | enum E { - | ^^^^^^ - -error[E0599]: no variant named `S1` found for enum `E` - --> $DIR/invalid-niche-discriminant.rs:23:18 - | -LL | enum E { - | ------ variant `S1` not found here -... -LL | static C: E = E::S1 { u: 23 }; - | ^^ - | -help: there is a variant with a similar name - | -LL - static C: E = E::S1 { u: 23 }; -LL + static C: E = E::S0 { u: 23 }; - | - -error: `Res::Err` but no error emitted - --> $DIR/invalid-niche-discriminant.rs:23:15 - | -LL | static C: E = E::S1 { u: 23 }; - | ^^^^^ - -error: aborting due to 4 previous errors - -Some errors have detailed explanations: E0599, E0732. -For more information about an error, try `rustc --explain E0599`. diff --git a/tests/ui/feature-gates/feature-gate-autodiff-use.stderr b/tests/ui/feature-gates/feature-gate-autodiff-use.nightly.stderr similarity index 91% rename from tests/ui/feature-gates/feature-gate-autodiff-use.stderr rename to tests/ui/feature-gates/feature-gate-autodiff-use.nightly.stderr index 94b9a065e491e..ca724632063d4 100644 --- a/tests/ui/feature-gates/feature-gate-autodiff-use.stderr +++ b/tests/ui/feature-gates/feature-gate-autodiff-use.nightly.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `autodiff` - --> $DIR/feature-gate-autodiff-use.rs:11:3 + --> $DIR/feature-gate-autodiff-use.rs:16:3 | LL | #[autodiff_reverse(dfoo)] | ^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | #[autodiff_reverse(dfoo)] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `autodiff` - --> $DIR/feature-gate-autodiff-use.rs:5:5 + --> $DIR/feature-gate-autodiff-use.rs:9:5 | LL | use std::autodiff::autodiff_reverse; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/feature-gates/feature-gate-autodiff-use.rs b/tests/ui/feature-gates/feature-gate-autodiff-use.rs index 85608be4ac04a..d7336e50df1f3 100644 --- a/tests/ui/feature-gates/feature-gate-autodiff-use.rs +++ b/tests/ui/feature-gates/feature-gate-autodiff-use.rs @@ -1,16 +1,22 @@ +//@ revisions: nightly stable +//@[nightly] only-nightly +//@[stable] act-as-stable + // This checks that without enabling the autodiff feature, we can't import std::autodiff::autodiff; #![crate_type = "lib"] use std::autodiff::autodiff_reverse; -//~^ ERROR use of unstable library feature `autodiff` -//| NOTE see issue #124509 for more information -//| HELP add `#![feature(autodiff)]` to the crate attributes to enable -//| NOTE this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +//[stable]~^ ERROR use of unstable library feature `autodiff` +//[nightly]~^^ ERROR use of unstable library feature `autodiff` +//[nightly]~| NOTE see issue #124509 for more information +//[nightly]~| HELP add `#![feature(autodiff)]` to the crate attributes to enable +//[nightly]~| NOTE this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date #[autodiff_reverse(dfoo)] -//~^ ERROR use of unstable library feature `autodiff` [E0658] -//| NOTE see issue #124509 for more information -//| HELP add `#![feature(autodiff)]` to the crate attributes to enable -//| NOTE this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +//[stable]~^ ERROR use of unstable library feature `autodiff` [E0658] +//[nightly]~^^ ERROR use of unstable library feature `autodiff` [E0658] +//[nightly]~| NOTE see issue #124509 for more information +//[nightly]~| HELP add `#![feature(autodiff)]` to the crate attributes to enable +//[nightly]~| NOTE this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date fn foo() {} diff --git a/tests/ui/feature-gates/feature-gate-autodiff-use.stable.stderr b/tests/ui/feature-gates/feature-gate-autodiff-use.stable.stderr new file mode 100644 index 0000000000000..2ac3c82b644ef --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-autodiff-use.stable.stderr @@ -0,0 +1,19 @@ +error[E0658]: use of unstable library feature `autodiff` + --> $DIR/feature-gate-autodiff-use.rs:16:3 + | +LL | #[autodiff_reverse(dfoo)] + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #124509 for more information + +error[E0658]: use of unstable library feature `autodiff` + --> $DIR/feature-gate-autodiff-use.rs:9:5 + | +LL | use std::autodiff::autodiff_reverse; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #124509 for more information + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/fn/fn-trait-use-named-params-issue-140169.rs b/tests/ui/fn/fn-trait-use-named-params-issue-140169.rs index 036627ae3d913..f832cdb2d9b12 100644 --- a/tests/ui/fn/fn-trait-use-named-params-issue-140169.rs +++ b/tests/ui/fn/fn-trait-use-named-params-issue-140169.rs @@ -6,7 +6,13 @@ fn f4(_: impl Fn(aaaa: u8, vvvv: u8)) {} //~| ERROR named parameters in parenthesized generic argument lists are experimental fn f5(_: impl Fn(u8, ...)) {} //~^ ERROR `Trait(...)` syntax does not support c_variadic parameters -fn f6(_: impl Fn(u8, #[allow(unused_attributes)] u8)) {} -//~^ ERROR `Trait(...)` syntax does not support attributes in parameters +fn f6(f: impl Fn(#[cfg(false)] u8)) { f() } //~ ERROR named parameters in parenthesized generic argument lists are experimental +fn f7(_: impl Fn(#[inline] u8)) {} +//~^ ERROR named parameters in parenthesized generic argument lists are experimental +//~| ERROR the `inline` attribute cannot be used on function params +//~| ERROR: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters +fn f8(_: impl Fn(u8, #[allow(unused_attributes)] u8)) {} +//~^ ERROR named parameters in parenthesized generic argument lists are experimental + fn main(){} diff --git a/tests/ui/fn/fn-trait-use-named-params-issue-140169.stderr b/tests/ui/fn/fn-trait-use-named-params-issue-140169.stderr index ac7758e9d5373..99e3b87e2e37c 100644 --- a/tests/ui/fn/fn-trait-use-named-params-issue-140169.stderr +++ b/tests/ui/fn/fn-trait-use-named-params-issue-140169.stderr @@ -4,11 +4,11 @@ error: `Trait(...)` syntax does not support c_variadic parameters LL | fn f5(_: impl Fn(u8, ...)) {} | ^^^ help: remove the `...` -error: `Trait(...)` syntax does not support attributes in parameters - --> $DIR/fn-trait-use-named-params-issue-140169.rs:9:22 +error: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters + --> $DIR/fn-trait-use-named-params-issue-140169.rs:10:18 | -LL | fn f6(_: impl Fn(u8, #[allow(unused_attributes)] u8)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the attributes +LL | fn f7(_: impl Fn(#[inline] u8)) {} + | ^^^^^^^^^ error[E0658]: named parameters in parenthesized generic argument lists are experimental --> $DIR/fn-trait-use-named-params-issue-140169.rs:2:22 @@ -50,6 +50,44 @@ LL | fn f4(_: impl Fn(aaaa: u8, vvvv: u8)) {} = help: add `#![feature(named_fn_trait_parameters)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 6 previous errors +error[E0658]: named parameters in parenthesized generic argument lists are experimental + --> $DIR/fn-trait-use-named-params-issue-140169.rs:9:18 + | +LL | fn f6(f: impl Fn(#[cfg(false)] u8)) { f() } + | ^^^^^^^^^^^^^ + | + = note: see issue #158499 for more information + = help: add `#![feature(named_fn_trait_parameters)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: named parameters in parenthesized generic argument lists are experimental + --> $DIR/fn-trait-use-named-params-issue-140169.rs:10:18 + | +LL | fn f7(_: impl Fn(#[inline] u8)) {} + | ^^^^^^^^^ + | + = note: see issue #158499 for more information + = help: add `#![feature(named_fn_trait_parameters)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: named parameters in parenthesized generic argument lists are experimental + --> $DIR/fn-trait-use-named-params-issue-140169.rs:14:22 + | +LL | fn f8(_: impl Fn(u8, #[allow(unused_attributes)] u8)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #158499 for more information + = help: add `#![feature(named_fn_trait_parameters)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: the `inline` attribute cannot be used on function params + --> $DIR/fn-trait-use-named-params-issue-140169.rs:10:20 + | +LL | fn f7(_: impl Fn(#[inline] u8)) {} + | ^^^^^^ + | + = help: the `inline` attribute can only be applied to functions + +error: aborting due to 10 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/fn/fn-trait-use-named-params.rs b/tests/ui/fn/fn-trait-use-named-params.rs new file mode 100644 index 0000000000000..0f1e1a73cb1d2 --- /dev/null +++ b/tests/ui/fn/fn-trait-use-named-params.rs @@ -0,0 +1,23 @@ +#![feature(named_fn_trait_parameters)] + +fn f1(f: impl Fn(#[cfg(false)] x: u8)) { f() } +fn f2(_: impl Fn(#[inline] x: u8)) {} +//~^ ERROR the `inline` attribute cannot be used on function params +//~| ERROR: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters +fn f3(_: impl Fn(#[cfg_attr(true, inline)] a1: usize)) {} +//~^ ERROR the `inline` attribute cannot be used on function params +//~| ERROR: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters +fn f4(_: impl Fn(/** */ x: u8)) {} +//~^ ERROR documentation comments cannot be applied to function parameters + +fn f5(_: impl Fn(#[forbid(non_snake_case)] NON_SNAKE: u8)) {} +fn f6(_: impl Fn(#[forbid(keyword_idents_2024)] gen: u8)) {} +//~^ ERROR `gen` is a keyword in the 2024 edition [keyword_idents_2024] +//~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! +fn f7(_: impl Fn(#[allow(keyword_idents_2024)] gen: u8)) {} +fn f8(_: fn(#[rustc_splat] a: usize)) {} +//~^ ERROR the `rustc_splat` attribute is an experimental feature + + + +fn main(){} diff --git a/tests/ui/fn/fn-trait-use-named-params.stderr b/tests/ui/fn/fn-trait-use-named-params.stderr new file mode 100644 index 0000000000000..6b22fa58c2ffc --- /dev/null +++ b/tests/ui/fn/fn-trait-use-named-params.stderr @@ -0,0 +1,62 @@ +error: `gen` is a keyword in the 2024 edition + --> $DIR/fn-trait-use-named-params.rs:14:49 + | +LL | fn f6(_: impl Fn(#[forbid(keyword_idents_2024)] gen: u8)) {} + | ^^^ help: you can use a raw identifier to stay compatible: `r#gen` + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + = note: for more information, see +note: the lint level is defined here + --> $DIR/fn-trait-use-named-params.rs:14:27 + | +LL | fn f6(_: impl Fn(#[forbid(keyword_idents_2024)] gen: u8)) {} + | ^^^^^^^^^^^^^^^^^^^ + +error: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters + --> $DIR/fn-trait-use-named-params.rs:4:18 + | +LL | fn f2(_: impl Fn(#[inline] x: u8)) {} + | ^^^^^^^^^ + +error: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters + --> $DIR/fn-trait-use-named-params.rs:7:35 + | +LL | fn f3(_: impl Fn(#[cfg_attr(true, inline)] a1: usize)) {} + | ^^^^^^ + +error: documentation comments cannot be applied to function parameters + --> $DIR/fn-trait-use-named-params.rs:10:18 + | +LL | fn f4(_: impl Fn(/** */ x: u8)) {} + | ^^^^^^ doc comments are not allowed here + +error: the `inline` attribute cannot be used on function params + --> $DIR/fn-trait-use-named-params.rs:4:20 + | +LL | fn f2(_: impl Fn(#[inline] x: u8)) {} + | ^^^^^^ + | + = help: the `inline` attribute can only be applied to functions + +error: the `inline` attribute cannot be used on function params + --> $DIR/fn-trait-use-named-params.rs:7:35 + | +LL | fn f3(_: impl Fn(#[cfg_attr(true, inline)] a1: usize)) {} + | ^^^^^^ + | + = help: the `inline` attribute can only be applied to functions + +error[E0658]: the `rustc_splat` attribute is an experimental feature + --> $DIR/fn-trait-use-named-params.rs:18:15 + | +LL | fn f8(_: fn(#[rustc_splat] a: usize)) {} + | ^^^^^^^^^^^ + | + = note: see issue #153629 for more information + = help: add `#![feature(splat)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: the `rustc_splat` attribute is experimental + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/lifetimes/auxiliary/anon_lifetime.rs b/tests/ui/lifetimes/auxiliary/anon_lifetime.rs new file mode 100644 index 0000000000000..34cdd7c40fe12 --- /dev/null +++ b/tests/ui/lifetimes/auxiliary/anon_lifetime.rs @@ -0,0 +1,22 @@ +use std::pin::Pin; +use std::future::Future; + +pub trait Service: Sized + Send + Sync + 'static { + /// The type of response returned by the service. + type Response: Send + 'static; + + /// The type of error returned by the service. + type Error: Send + 'static; + + /// Serve a response or error for the given request, + /// using the given context. + fn serve( + &self, + ctx: Context, + req: Request, + ) -> impl Future> + Send + '_; +} + +pub struct Context { + state: S, +} diff --git a/tests/ui/lifetimes/do-not-suggest-anon-lifetime.rs b/tests/ui/lifetimes/do-not-suggest-anon-lifetime.rs new file mode 100644 index 0000000000000..5e4f5f191f850 --- /dev/null +++ b/tests/ui/lifetimes/do-not-suggest-anon-lifetime.rs @@ -0,0 +1,22 @@ +//@ aux-build:anon_lifetime.rs +//@ edition:2024 +extern crate anon_lifetime; +use anon_lifetime::{Service, Context}; + +struct Foo; + +impl Service for Foo { + type Response = (); + type Error = (); + + fn serve( + &self, + _ctx: Context, + _req: Request, + ) -> impl Future> { + //~^ ERROR: the parameter type `S` may not live long enough + async { todo!() } + } +} + +fn main() {} diff --git a/tests/ui/lifetimes/do-not-suggest-anon-lifetime.stderr b/tests/ui/lifetimes/do-not-suggest-anon-lifetime.stderr new file mode 100644 index 0000000000000..b23ffd83ce877 --- /dev/null +++ b/tests/ui/lifetimes/do-not-suggest-anon-lifetime.stderr @@ -0,0 +1,20 @@ +error[E0311]: the parameter type `S` may not live long enough + --> $DIR/do-not-suggest-anon-lifetime.rs:16:10 + | +LL | ) -> impl Future> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `impl Future>::Response, >::Error>>` will meet its required lifetime bounds... + | + ::: $DIR/auxiliary/anon_lifetime.rs:14:9 + | +LL | &self, + | - the parameter type `S` must be valid for the anonymous lifetime as defined here... + | +note: ...that is required by this bound + --> $DIR/auxiliary/anon_lifetime.rs:17:77 + | +LL | ) -> impl Future> + Send + '_; + | ^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0311`. diff --git a/tests/ui/lint/invalid-nan-comparison-suggestion.fixed b/tests/ui/lint/invalid-nan-comparison-suggestion.fixed deleted file mode 100644 index 2d88c274080b6..0000000000000 --- a/tests/ui/lint/invalid-nan-comparison-suggestion.fixed +++ /dev/null @@ -1,50 +0,0 @@ -//@ check-pass -//@ run-rustfix - -#![feature(f16, f128)] - -fn main() { - let x = 5f16; - let _ = x.is_nan(); - //~^ WARN incorrect NaN comparison - let _ = !x.is_nan(); - //~^ WARN incorrect NaN comparison - - let x = 5f32; - let _ = x.is_nan(); - //~^ WARN incorrect NaN comparison - let _ = !x.is_nan(); - //~^ WARN incorrect NaN comparison - - let x = 5f64; - let _ = x.is_nan(); - //~^ WARN incorrect NaN comparison - let _ = !x.is_nan(); - //~^ WARN incorrect NaN comparison - - let x = 5f128; - let _ = x.is_nan(); - //~^ WARN incorrect NaN comparison - let _ = !x.is_nan(); - //~^ WARN incorrect NaN comparison - - let b = &2.3f32; - if !b.is_nan() {} - //~^ WARN incorrect NaN comparison - - let b = &2.3f32; - if !b.is_nan() {} - //~^ WARN incorrect NaN comparison - - let _ = - !b.is_nan(); - - #[allow(unused_macros)] - macro_rules! nan { () => { f32::NAN }; } - macro_rules! number { () => { 5f32 }; } - - let _ = number!().is_nan(); - //~^ WARN incorrect NaN comparison - let _ = !number!().is_nan(); - //~^ WARN incorrect NaN comparison -} diff --git a/tests/ui/lint/invalid-nan-comparison-suggestion.rs b/tests/ui/lint/invalid-nan-comparison-suggestion.rs deleted file mode 100644 index 9175344786948..0000000000000 --- a/tests/ui/lint/invalid-nan-comparison-suggestion.rs +++ /dev/null @@ -1,53 +0,0 @@ -//@ check-pass -//@ run-rustfix - -#![feature(f16, f128)] - -fn main() { - let x = 5f16; - let _ = x == f16::NAN; - //~^ WARN incorrect NaN comparison - let _ = x != f16::NAN; - //~^ WARN incorrect NaN comparison - - let x = 5f32; - let _ = x == f32::NAN; - //~^ WARN incorrect NaN comparison - let _ = x != f32::NAN; - //~^ WARN incorrect NaN comparison - - let x = 5f64; - let _ = x == f64::NAN; - //~^ WARN incorrect NaN comparison - let _ = x != f64::NAN; - //~^ WARN incorrect NaN comparison - - let x = 5f128; - let _ = x == f128::NAN; - //~^ WARN incorrect NaN comparison - let _ = x != f128::NAN; - //~^ WARN incorrect NaN comparison - - let b = &2.3f32; - if b != &f32::NAN {} - //~^ WARN incorrect NaN comparison - - let b = &2.3f32; - if b != { &f32::NAN } {} - //~^ WARN incorrect NaN comparison - - let _ = - b != { - //~^ WARN incorrect NaN comparison - &f32::NAN - }; - - #[allow(unused_macros)] - macro_rules! nan { () => { f32::NAN }; } - macro_rules! number { () => { 5f32 }; } - - let _ = nan!() == number!(); - //~^ WARN incorrect NaN comparison - let _ = number!() != nan!(); - //~^ WARN incorrect NaN comparison -} diff --git a/tests/ui/lint/invalid-nan-comparison-suggestion.stderr b/tests/ui/lint/invalid-nan-comparison-suggestion.stderr deleted file mode 100644 index f9b1428de464c..0000000000000 --- a/tests/ui/lint/invalid-nan-comparison-suggestion.stderr +++ /dev/null @@ -1,165 +0,0 @@ -warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison-suggestion.rs:8:13 - | -LL | let _ = x == f16::NAN; - | ^^^^^^^^^^^^^ - | - = note: `#[warn(invalid_nan_comparisons)]` on by default -help: use `f32::is_nan()` or `f64::is_nan()` instead - | -LL - let _ = x == f16::NAN; -LL + let _ = x.is_nan(); - | - -warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison-suggestion.rs:10:13 - | -LL | let _ = x != f16::NAN; - | ^^^^^^^^^^^^^ - | -help: use `f32::is_nan()` or `f64::is_nan()` instead - | -LL - let _ = x != f16::NAN; -LL + let _ = !x.is_nan(); - | - -warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison-suggestion.rs:14:13 - | -LL | let _ = x == f32::NAN; - | ^^^^^^^^^^^^^ - | -help: use `f32::is_nan()` or `f64::is_nan()` instead - | -LL - let _ = x == f32::NAN; -LL + let _ = x.is_nan(); - | - -warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison-suggestion.rs:16:13 - | -LL | let _ = x != f32::NAN; - | ^^^^^^^^^^^^^ - | -help: use `f32::is_nan()` or `f64::is_nan()` instead - | -LL - let _ = x != f32::NAN; -LL + let _ = !x.is_nan(); - | - -warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison-suggestion.rs:20:13 - | -LL | let _ = x == f64::NAN; - | ^^^^^^^^^^^^^ - | -help: use `f32::is_nan()` or `f64::is_nan()` instead - | -LL - let _ = x == f64::NAN; -LL + let _ = x.is_nan(); - | - -warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison-suggestion.rs:22:13 - | -LL | let _ = x != f64::NAN; - | ^^^^^^^^^^^^^ - | -help: use `f32::is_nan()` or `f64::is_nan()` instead - | -LL - let _ = x != f64::NAN; -LL + let _ = !x.is_nan(); - | - -warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison-suggestion.rs:26:13 - | -LL | let _ = x == f128::NAN; - | ^^^^^^^^^^^^^^ - | -help: use `f32::is_nan()` or `f64::is_nan()` instead - | -LL - let _ = x == f128::NAN; -LL + let _ = x.is_nan(); - | - -warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison-suggestion.rs:28:13 - | -LL | let _ = x != f128::NAN; - | ^^^^^^^^^^^^^^ - | -help: use `f32::is_nan()` or `f64::is_nan()` instead - | -LL - let _ = x != f128::NAN; -LL + let _ = !x.is_nan(); - | - -warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison-suggestion.rs:32:8 - | -LL | if b != &f32::NAN {} - | ^^^^^^^^^^^^^^ - | -help: use `f32::is_nan()` or `f64::is_nan()` instead - | -LL - if b != &f32::NAN {} -LL + if !b.is_nan() {} - | - -warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison-suggestion.rs:36:8 - | -LL | if b != { &f32::NAN } {} - | ^^^^^^^^^^^^^^^^^^ - | -help: use `f32::is_nan()` or `f64::is_nan()` instead - | -LL - if b != { &f32::NAN } {} -LL + if !b.is_nan() {} - | - -warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison-suggestion.rs:40:9 - | -LL | / b != { -LL | | -LL | | &f32::NAN -LL | | }; - | |_________^ - | -help: use `f32::is_nan()` or `f64::is_nan()` instead - | -LL - b != { -LL - -LL - &f32::NAN -LL - }; -LL + !b.is_nan(); - | - -warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison-suggestion.rs:49:13 - | -LL | let _ = nan!() == number!(); - | ^^^^^^^^^^^^^^^^^^^ - | -help: use `f32::is_nan()` or `f64::is_nan()` instead - | -LL - let _ = nan!() == number!(); -LL + let _ = number!().is_nan(); - | - -warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison-suggestion.rs:51:13 - | -LL | let _ = number!() != nan!(); - | ^^^^^^^^^^^^^^^^^^^ - | -help: use `f32::is_nan()` or `f64::is_nan()` instead - | -LL - let _ = number!() != nan!(); -LL + let _ = !number!().is_nan(); - | - -warning: 13 warnings emitted - diff --git a/tests/ui/lint/invalid-nan-comparison.rs b/tests/ui/lint/invalid-nan-comparisons/basic.rs similarity index 100% rename from tests/ui/lint/invalid-nan-comparison.rs rename to tests/ui/lint/invalid-nan-comparisons/basic.rs diff --git a/tests/ui/lint/invalid-nan-comparison.stderr b/tests/ui/lint/invalid-nan-comparisons/basic.stderr similarity index 64% rename from tests/ui/lint/invalid-nan-comparison.stderr rename to tests/ui/lint/invalid-nan-comparisons/basic.stderr index 486d2a9636c95..77cd6fc111e75 100644 --- a/tests/ui/lint/invalid-nan-comparison.stderr +++ b/tests/ui/lint/invalid-nan-comparisons/basic.stderr @@ -1,302 +1,302 @@ warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison.rs:12:20 + --> $DIR/basic.rs:12:20 | LL | const TEST: bool = 5f32 == f32::NAN; | ^^^^^^^^^^^^^^^^ | = note: `#[warn(invalid_nan_comparisons)]` on by default -help: use `f32::is_nan()` or `f64::is_nan()` instead +help: use the `is_nan` method instead | LL - const TEST: bool = 5f32 == f32::NAN; -LL + const TEST: bool = 5f32.is_nan(); +LL + const TEST: bool = ::is_nan(5f32); | warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison.rs:18:5 + --> $DIR/basic.rs:18:5 | LL | x == f16::NAN; | ^^^^^^^^^^^^^ | -help: use `f32::is_nan()` or `f64::is_nan()` instead +help: use the `is_nan` method instead | LL - x == f16::NAN; -LL + x.is_nan(); +LL + ::is_nan(x); | warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison.rs:20:5 + --> $DIR/basic.rs:20:5 | LL | x != f16::NAN; | ^^^^^^^^^^^^^ | -help: use `f32::is_nan()` or `f64::is_nan()` instead +help: use the `is_nan` method instead | LL - x != f16::NAN; -LL + !x.is_nan(); +LL + !::is_nan(x); | warning: incorrect NaN comparison, NaN is not orderable - --> $DIR/invalid-nan-comparison.rs:22:5 + --> $DIR/basic.rs:22:5 | LL | x < f16::NAN; | ^^^^^^^^^^^^ warning: incorrect NaN comparison, NaN is not orderable - --> $DIR/invalid-nan-comparison.rs:24:5 + --> $DIR/basic.rs:24:5 | LL | x > f16::NAN; | ^^^^^^^^^^^^ warning: incorrect NaN comparison, NaN is not orderable - --> $DIR/invalid-nan-comparison.rs:26:5 + --> $DIR/basic.rs:26:5 | LL | x <= f16::NAN; | ^^^^^^^^^^^^^ warning: incorrect NaN comparison, NaN is not orderable - --> $DIR/invalid-nan-comparison.rs:28:5 + --> $DIR/basic.rs:28:5 | LL | x >= f16::NAN; | ^^^^^^^^^^^^^ warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison.rs:30:5 + --> $DIR/basic.rs:30:5 | LL | number!() == f16::NAN; | ^^^^^^^^^^^^^^^^^^^^^ | -help: use `f32::is_nan()` or `f64::is_nan()` instead +help: use the `is_nan` method instead | LL - number!() == f16::NAN; -LL + number!().is_nan(); +LL + ::is_nan(number!()); | warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison.rs:32:5 + --> $DIR/basic.rs:32:5 | LL | f16::NAN != number!(); | ^^^^^^^^^^^^^^^^^^^^^ | -help: use `f32::is_nan()` or `f64::is_nan()` instead +help: use the `is_nan` method instead | LL - f16::NAN != number!(); -LL + !number!().is_nan(); +LL + !::is_nan(number!()); | warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison.rs:39:5 + --> $DIR/basic.rs:39:5 | LL | x == f32::NAN; | ^^^^^^^^^^^^^ | -help: use `f32::is_nan()` or `f64::is_nan()` instead +help: use the `is_nan` method instead | LL - x == f32::NAN; -LL + x.is_nan(); +LL + ::is_nan(x); | warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison.rs:41:5 + --> $DIR/basic.rs:41:5 | LL | x != f32::NAN; | ^^^^^^^^^^^^^ | -help: use `f32::is_nan()` or `f64::is_nan()` instead +help: use the `is_nan` method instead | LL - x != f32::NAN; -LL + !x.is_nan(); +LL + !::is_nan(x); | warning: incorrect NaN comparison, NaN is not orderable - --> $DIR/invalid-nan-comparison.rs:43:5 + --> $DIR/basic.rs:43:5 | LL | x < f32::NAN; | ^^^^^^^^^^^^ warning: incorrect NaN comparison, NaN is not orderable - --> $DIR/invalid-nan-comparison.rs:45:5 + --> $DIR/basic.rs:45:5 | LL | x > f32::NAN; | ^^^^^^^^^^^^ warning: incorrect NaN comparison, NaN is not orderable - --> $DIR/invalid-nan-comparison.rs:47:5 + --> $DIR/basic.rs:47:5 | LL | x <= f32::NAN; | ^^^^^^^^^^^^^ warning: incorrect NaN comparison, NaN is not orderable - --> $DIR/invalid-nan-comparison.rs:49:5 + --> $DIR/basic.rs:49:5 | LL | x >= f32::NAN; | ^^^^^^^^^^^^^ warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison.rs:51:5 + --> $DIR/basic.rs:51:5 | LL | number!() == f32::NAN; | ^^^^^^^^^^^^^^^^^^^^^ | -help: use `f32::is_nan()` or `f64::is_nan()` instead +help: use the `is_nan` method instead | LL - number!() == f32::NAN; -LL + number!().is_nan(); +LL + ::is_nan(number!()); | warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison.rs:53:5 + --> $DIR/basic.rs:53:5 | LL | f32::NAN != number!(); | ^^^^^^^^^^^^^^^^^^^^^ | -help: use `f32::is_nan()` or `f64::is_nan()` instead +help: use the `is_nan` method instead | LL - f32::NAN != number!(); -LL + !number!().is_nan(); +LL + !::is_nan(number!()); | warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison.rs:60:5 + --> $DIR/basic.rs:60:5 | LL | x == f64::NAN; | ^^^^^^^^^^^^^ | -help: use `f32::is_nan()` or `f64::is_nan()` instead +help: use the `is_nan` method instead | LL - x == f64::NAN; -LL + x.is_nan(); +LL + ::is_nan(x); | warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison.rs:62:5 + --> $DIR/basic.rs:62:5 | LL | x != f64::NAN; | ^^^^^^^^^^^^^ | -help: use `f32::is_nan()` or `f64::is_nan()` instead +help: use the `is_nan` method instead | LL - x != f64::NAN; -LL + !x.is_nan(); +LL + !::is_nan(x); | warning: incorrect NaN comparison, NaN is not orderable - --> $DIR/invalid-nan-comparison.rs:64:5 + --> $DIR/basic.rs:64:5 | LL | x < f64::NAN; | ^^^^^^^^^^^^ warning: incorrect NaN comparison, NaN is not orderable - --> $DIR/invalid-nan-comparison.rs:66:5 + --> $DIR/basic.rs:66:5 | LL | x > f64::NAN; | ^^^^^^^^^^^^ warning: incorrect NaN comparison, NaN is not orderable - --> $DIR/invalid-nan-comparison.rs:68:5 + --> $DIR/basic.rs:68:5 | LL | x <= f64::NAN; | ^^^^^^^^^^^^^ warning: incorrect NaN comparison, NaN is not orderable - --> $DIR/invalid-nan-comparison.rs:70:5 + --> $DIR/basic.rs:70:5 | LL | x >= f64::NAN; | ^^^^^^^^^^^^^ warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison.rs:72:5 + --> $DIR/basic.rs:72:5 | LL | number!() == f64::NAN; | ^^^^^^^^^^^^^^^^^^^^^ | -help: use `f32::is_nan()` or `f64::is_nan()` instead +help: use the `is_nan` method instead | LL - number!() == f64::NAN; -LL + number!().is_nan(); +LL + ::is_nan(number!()); | warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison.rs:74:5 + --> $DIR/basic.rs:74:5 | LL | f64::NAN != number!(); | ^^^^^^^^^^^^^^^^^^^^^ | -help: use `f32::is_nan()` or `f64::is_nan()` instead +help: use the `is_nan` method instead | LL - f64::NAN != number!(); -LL + !number!().is_nan(); +LL + !::is_nan(number!()); | warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison.rs:81:5 + --> $DIR/basic.rs:81:5 | LL | x == f128::NAN; | ^^^^^^^^^^^^^^ | -help: use `f32::is_nan()` or `f64::is_nan()` instead +help: use the `is_nan` method instead | LL - x == f128::NAN; -LL + x.is_nan(); +LL + ::is_nan(x); | warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison.rs:83:5 + --> $DIR/basic.rs:83:5 | LL | x != f128::NAN; | ^^^^^^^^^^^^^^ | -help: use `f32::is_nan()` or `f64::is_nan()` instead +help: use the `is_nan` method instead | LL - x != f128::NAN; -LL + !x.is_nan(); +LL + !::is_nan(x); | warning: incorrect NaN comparison, NaN is not orderable - --> $DIR/invalid-nan-comparison.rs:85:5 + --> $DIR/basic.rs:85:5 | LL | x < f128::NAN; | ^^^^^^^^^^^^^ warning: incorrect NaN comparison, NaN is not orderable - --> $DIR/invalid-nan-comparison.rs:87:5 + --> $DIR/basic.rs:87:5 | LL | x > f128::NAN; | ^^^^^^^^^^^^^ warning: incorrect NaN comparison, NaN is not orderable - --> $DIR/invalid-nan-comparison.rs:89:5 + --> $DIR/basic.rs:89:5 | LL | x <= f128::NAN; | ^^^^^^^^^^^^^^ warning: incorrect NaN comparison, NaN is not orderable - --> $DIR/invalid-nan-comparison.rs:91:5 + --> $DIR/basic.rs:91:5 | LL | x >= f128::NAN; | ^^^^^^^^^^^^^^ warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison.rs:93:5 + --> $DIR/basic.rs:93:5 | LL | number!() == f128::NAN; | ^^^^^^^^^^^^^^^^^^^^^^ | -help: use `f32::is_nan()` or `f64::is_nan()` instead +help: use the `is_nan` method instead | LL - number!() == f128::NAN; -LL + number!().is_nan(); +LL + ::is_nan(number!()); | warning: incorrect NaN comparison, NaN cannot be directly compared to itself - --> $DIR/invalid-nan-comparison.rs:95:5 + --> $DIR/basic.rs:95:5 | LL | f128::NAN != number!(); | ^^^^^^^^^^^^^^^^^^^^^^ | -help: use `f32::is_nan()` or `f64::is_nan()` instead +help: use the `is_nan` method instead | LL - f128::NAN != number!(); -LL + !number!().is_nan(); +LL + !::is_nan(number!()); | warning: 33 warnings emitted diff --git a/tests/ui/lint/invalid-nan-comparisons/custom-comparisons.rs b/tests/ui/lint/invalid-nan-comparisons/custom-comparisons.rs new file mode 100644 index 0000000000000..ccdb7e0d31859 --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparisons/custom-comparisons.rs @@ -0,0 +1,73 @@ +//@ check-pass + +#![deny(invalid_nan_comparisons)] + +use std::cmp::Ordering; + +struct NotFloat; + +impl PartialEq for NotFloat { + fn eq(&self, other: &f64) -> bool { + other.is_nan() + } +} + +impl PartialEq for f64 { + fn eq(&self, other: &NotFloat) -> bool { + other == self + } +} + +impl PartialOrd for NotFloat { + fn partial_cmp(&self, other: &f64) -> Option { + other.is_nan().then_some(Ordering::Equal) + } +} + +impl PartialOrd for f64 { + fn partial_cmp(&self, other: &NotFloat) -> Option { + other.partial_cmp(self).map(Ordering::reverse) + } +} + +fn main() { + let value = NotFloat; + let _ = value == f64::NAN; + let _ = value != f64::NAN; + let _ = value < f64::NAN; + let _ = value <= f64::NAN; + let _ = value > f64::NAN; + let _ = value >= f64::NAN; + + let _ = f64::NAN == value; + let _ = f64::NAN != value; + let _ = f64::NAN < value; + let _ = f64::NAN <= value; + let _ = f64::NAN > value; + let _ = f64::NAN >= value; + + let _ = &value == &f64::NAN; + let _ = &value != &f64::NAN; + let _ = &value < &f64::NAN; + let _ = &value <= &f64::NAN; + let _ = &value > &f64::NAN; + let _ = &value >= &f64::NAN; + + let _ = &f64::NAN == &value; + let _ = &f64::NAN != &value; + let _ = &f64::NAN < &value; + let _ = &f64::NAN <= &value; + let _ = &f64::NAN > &value; + let _ = &f64::NAN >= &value; + + generic(value); +} + +fn generic>(value: T) { + let _ = value == f64::NAN; + let _ = value != f64::NAN; + let _ = value < f64::NAN; + let _ = value <= f64::NAN; + let _ = value > f64::NAN; + let _ = value >= f64::NAN; +} diff --git a/tests/ui/lint/invalid-nan-comparisons/no-std.fixed b/tests/ui/lint/invalid-nan-comparisons/no-std.fixed new file mode 100644 index 0000000000000..3e35d14d223d7 --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparisons/no-std.fixed @@ -0,0 +1,18 @@ +//@ check-pass +//@ run-rustfix +//@ compile-flags: --crate-type=lib + +#![no_std] + +pub type MacroFloat = ::core::primitive::f64; + +#[allow(non_camel_case_types)] +pub struct f64; + +pub fn check() { + #[allow(unused_macros)] + macro_rules! nan { () => { $crate::MacroFloat::NAN }; } + let b = &3.3; + let _ = !::core::primitive::f64::is_nan(*b); + //~^ WARN incorrect NaN comparison +} diff --git a/tests/ui/lint/invalid-nan-comparisons/no-std.rs b/tests/ui/lint/invalid-nan-comparisons/no-std.rs new file mode 100644 index 0000000000000..7ace5b976c890 --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparisons/no-std.rs @@ -0,0 +1,18 @@ +//@ check-pass +//@ run-rustfix +//@ compile-flags: --crate-type=lib + +#![no_std] + +pub type MacroFloat = ::core::primitive::f64; + +#[allow(non_camel_case_types)] +pub struct f64; + +pub fn check() { + #[allow(unused_macros)] + macro_rules! nan { () => { $crate::MacroFloat::NAN }; } + let b = &3.3; + let _ = b != &nan!(); + //~^ WARN incorrect NaN comparison +} diff --git a/tests/ui/lint/invalid-nan-comparisons/no-std.stderr b/tests/ui/lint/invalid-nan-comparisons/no-std.stderr new file mode 100644 index 0000000000000..b10909e157020 --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparisons/no-std.stderr @@ -0,0 +1,15 @@ +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/no-std.rs:16:13 + | +LL | let _ = b != &nan!(); + | ^^^^^^^^^^^^ + | + = note: `#[warn(invalid_nan_comparisons)]` on by default +help: use the `is_nan` method instead + | +LL - let _ = b != &nan!(); +LL + let _ = !::core::primitive::f64::is_nan(*b); + | + +warning: 1 warning emitted + diff --git a/tests/ui/lint/invalid-nan-comparisons/no-suggestion.rs b/tests/ui/lint/invalid-nan-comparisons/no-suggestion.rs new file mode 100644 index 0000000000000..afe3cb7cfd259 --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparisons/no-suggestion.rs @@ -0,0 +1,28 @@ +//@ check-pass + +#![feature(f16, f128)] + +fn main() { + let mut effects = 0; + let _ = 3.3 == { + effects += 1; + f64::NAN + }; + //~^^^^ WARN incorrect NaN comparison + let _ = effects; + + macro_rules! scoped_nan { () => {{ type Float = f64; Float::NAN }}; } + let _ = 3.3 == scoped_nan!(); + //~^ WARN incorrect NaN comparison + + macro_rules! nan16 { () => { f16::NAN }; } + let _ = 3.3 == nan16!(); + //~^ WARN incorrect NaN comparison + macro_rules! nan128 { () => { f128::NAN }; } + let _ = 3.3 != nan128!(); + //~^ WARN incorrect NaN comparison + + macro_rules! compare { () => { 3.3 != f64::NAN }; } + //~^ WARN incorrect NaN comparison + let _ = compare!(); +} diff --git a/tests/ui/lint/invalid-nan-comparisons/no-suggestion.stderr b/tests/ui/lint/invalid-nan-comparisons/no-suggestion.stderr new file mode 100644 index 0000000000000..278f4d225aec4 --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparisons/no-suggestion.stderr @@ -0,0 +1,50 @@ +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/no-suggestion.rs:7:13 + | +LL | let _ = 3.3 == { + | _____________^ +LL | | effects += 1; +LL | | f64::NAN +LL | | }; + | |_____^ + | + = help: use the `is_nan` method instead + = note: `#[warn(invalid_nan_comparisons)]` on by default + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/no-suggestion.rs:15:13 + | +LL | let _ = 3.3 == scoped_nan!(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: use the `is_nan` method instead + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/no-suggestion.rs:19:13 + | +LL | let _ = 3.3 == nan16!(); + | ^^^^^^^^^^^^^^^ + | + = help: use the `is_nan` method instead + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/no-suggestion.rs:22:13 + | +LL | let _ = 3.3 != nan128!(); + | ^^^^^^^^^^^^^^^^ + | + = help: use the `is_nan` method instead + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/no-suggestion.rs:25:36 + | +LL | macro_rules! compare { () => { 3.3 != f64::NAN }; } + | ^^^^^^^^^^^^^^^ +LL | +LL | let _ = compare!(); + | ---------- in this macro invocation + | + = help: use the `is_nan` method instead + +warning: 5 warnings emitted + diff --git a/tests/ui/lint/invalid-nan-comparisons/suggestion.fixed b/tests/ui/lint/invalid-nan-comparisons/suggestion.fixed new file mode 100644 index 0000000000000..be5f477e67992 --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparisons/suggestion.fixed @@ -0,0 +1,215 @@ +//@ check-pass +//@ run-rustfix + +#![feature(f16, f128)] + +extern crate core; + +fn main() { + let x = 5f16; + let _ = ::is_nan(x); + //~^ WARN incorrect NaN comparison + let _ = !::is_nan(x); + //~^ WARN incorrect NaN comparison + + let x = 5f32; + let _ = ::is_nan(x); + //~^ WARN incorrect NaN comparison + let _ = !::is_nan(x); + //~^ WARN incorrect NaN comparison + + let x = 5f64; + let _ = ::is_nan(x); + //~^ WARN incorrect NaN comparison + let _ = !::is_nan(x); + //~^ WARN incorrect NaN comparison + + let x = 5f128; + let _ = ::is_nan(x); + //~^ WARN incorrect NaN comparison + let _ = !::is_nan(x); + //~^ WARN incorrect NaN comparison + + let b = &2.3f32; + if !::is_nan(*b) {} + //~^ WARN incorrect NaN comparison + + let b = &2.3f32; + if !::is_nan(*b) {} + //~^ WARN incorrect NaN comparison + + let _ = + !::is_nan(*b); + + // Issue #164143: an unsuffixed float is constrained by the `NAN` operand. + // Suggesting `.is_nan()` drops that constraint and does not compile. + let b = &3.3; + if !::is_nan(*b) {} + //~^ WARN incorrect NaN comparison + + let mut m = &3.3; + if !::is_nan(*m) { + //~^ WARN incorrect NaN comparison + m = &1.0; + } + let _ = m; + + let unary = &(-3.3); + if !::is_nan(*unary) {} + //~^ WARN incorrect NaN comparison + + let block = &{ 3.3 }; + if !::is_nan(*block) {} + //~^ WARN incorrect NaN comparison + + let (tuple,) = (&3.3,); + if !::is_nan(*tuple) {} + //~^ WARN incorrect NaN comparison + + let arithmetic = &(3.3 + 0.0); + if !::is_nan(*arithmetic) {} + //~^ WARN incorrect NaN comparison + + let x = 1.0f64; + if !::is_nan(*&x) {} + //~^ WARN incorrect NaN comparison + let p = &x; + let _ = ::is_nan(*p); + //~^ WARN incorrect NaN comparison + let _ = ::is_nan(-x); + //~^ WARN incorrect NaN comparison + + shadowed::check(); + nested_operands(); + macro_paths(); + method_resolution::check(); + + let _ = ::is_nan(3.3); + //~^ WARN incorrect NaN comparison + + let _ = !::is_nan(*&3.3); + //~^ WARN incorrect NaN comparison + + let suffixed = 3.3f64; + let copied = suffixed; + let _ = !::is_nan(copied); + //~^ WARN incorrect NaN comparison + + let ascribed: f64 = 3.3; + let _ = !::is_nan(ascribed); + //~^ WARN incorrect NaN comparison + + parameter(1.0); + parameter_ref(&1.0); + let _ = |x: f64| !::is_nan(x); //~ WARN incorrect NaN comparison + + #[allow(unused_macros)] + macro_rules! nan { () => { f32::NAN }; } + macro_rules! number { () => { 5f32 }; } + + let _ = ::std::primitive::f32::is_nan(number!()); + //~^ WARN incorrect NaN comparison + let _ = !::std::primitive::f32::is_nan(number!()); + //~^ WARN incorrect NaN comparison +} + +fn parameter(x: f64) { + let _ = ::is_nan(x); + //~^ WARN incorrect NaN comparison +} + +fn parameter_ref(x: &f32) { + if !::is_nan(*x) {} + //~^ WARN incorrect NaN comparison +} + +fn nested_operands() { + let x = 1.0f64; + let _ = ::is_nan(*(&x as &f64)); + //~^ WARN incorrect NaN comparison + let _ = !::is_nan(*{ &{ 3.3 } }); + //~^ WARN incorrect NaN comparison + let _ = ::is_nan(***&&&3.3); + //~^ WARN incorrect NaN comparison + let b = &&&3.3; + let _ = !::is_nan(***b); + //~^ WARN incorrect NaN comparison + let b = &mut 3.3; + let _ = !::is_nan(*b); + //~^ WARN incorrect NaN comparison + let b = &3.3; + let _ = ::is_nan(*b); + //~^ WARN incorrect NaN comparison + let b = &3.3; + let _ = !::is_nan(*b); + //~^ WARN incorrect NaN comparison + let b = &&3.3; + let _ = ::is_nan(**b); + //~^ WARN incorrect NaN comparison + + let b = &(-3.3); + let _ = !::is_nan(*b); + //~^ WARN incorrect NaN comparison + let b = &(3.3 + 0.0); + let _ = ::is_nan(*b); + //~^ WARN incorrect NaN comparison + + macro_rules! borrowed { () => { &3.3 }; } + let _ = !::is_nan(*borrowed!()); + //~^ WARN incorrect NaN comparison +} + +pub type MacroFloat = f64; + +fn macro_paths() { + #[allow(unused_macros)] + macro_rules! nan { () => { $crate::MacroFloat::NAN }; } + let b = &3.3; + let _ = !::std::primitive::f64::is_nan(*b); + //~^ WARN incorrect NaN comparison +} + +mod shadowed { + #[allow(dead_code, non_camel_case_types)] + struct f64; + use ::core::primitive::f64 as Float; + + pub fn check() { + let b = &3.3; + if !<::core::primitive::f64>::is_nan(*b) {} + //~^ WARN incorrect NaN comparison + let c = &4.4; + if !::is_nan(*c) {} + //~^ WARN incorrect NaN comparison + let d = &5.5; + if !::is_nan(*d) {} + //~^ WARN incorrect NaN comparison + } +} + +mod method_resolution { + mod f64 { + pub fn is_nan(_: ::core::primitive::f64) {} + } + + trait IsNan { + fn is_nan(self); + } + + impl IsNan for &f64 { + fn is_nan(self) {} + } + + pub fn check() { + let _: () = f64::is_nan(5.0); + let _: () = (&5f64).is_nan(); + + let x = 5f64; + let _: bool = ::is_nan(x); + //~^ WARN incorrect NaN comparison + + let x = &5f64; + let _: bool = ::is_nan(*x); + //~^ WARN incorrect NaN comparison + } +} diff --git a/tests/ui/lint/invalid-nan-comparisons/suggestion.rs b/tests/ui/lint/invalid-nan-comparisons/suggestion.rs new file mode 100644 index 0000000000000..96b7618e921e9 --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparisons/suggestion.rs @@ -0,0 +1,218 @@ +//@ check-pass +//@ run-rustfix + +#![feature(f16, f128)] + +extern crate core; + +fn main() { + let x = 5f16; + let _ = x == f16::NAN; + //~^ WARN incorrect NaN comparison + let _ = x != f16::NAN; + //~^ WARN incorrect NaN comparison + + let x = 5f32; + let _ = x == f32::NAN; + //~^ WARN incorrect NaN comparison + let _ = x != f32::NAN; + //~^ WARN incorrect NaN comparison + + let x = 5f64; + let _ = x == f64::NAN; + //~^ WARN incorrect NaN comparison + let _ = x != f64::NAN; + //~^ WARN incorrect NaN comparison + + let x = 5f128; + let _ = x == f128::NAN; + //~^ WARN incorrect NaN comparison + let _ = x != f128::NAN; + //~^ WARN incorrect NaN comparison + + let b = &2.3f32; + if b != &f32::NAN {} + //~^ WARN incorrect NaN comparison + + let b = &2.3f32; + if b != { &f32::NAN } {} + //~^ WARN incorrect NaN comparison + + let _ = + b != { + //~^ WARN incorrect NaN comparison + &f32::NAN + }; + + // Issue #164143: an unsuffixed float is constrained by the `NAN` operand. + // Suggesting `.is_nan()` drops that constraint and does not compile. + let b = &3.3; + if b != &f64::NAN {} + //~^ WARN incorrect NaN comparison + + let mut m = &3.3; + if m != &f64::NAN { + //~^ WARN incorrect NaN comparison + m = &1.0; + } + let _ = m; + + let unary = &(-3.3); + if unary != &f64::NAN {} + //~^ WARN incorrect NaN comparison + + let block = &{ 3.3 }; + if block != &f64::NAN {} + //~^ WARN incorrect NaN comparison + + let (tuple,) = (&3.3,); + if tuple != &f64::NAN {} + //~^ WARN incorrect NaN comparison + + let arithmetic = &(3.3 + 0.0); + if arithmetic != &f64::NAN {} + //~^ WARN incorrect NaN comparison + + let x = 1.0f64; + if &x != &f64::NAN {} + //~^ WARN incorrect NaN comparison + let p = &x; + let _ = *p == f64::NAN; + //~^ WARN incorrect NaN comparison + let _ = -x == f64::NAN; + //~^ WARN incorrect NaN comparison + + shadowed::check(); + nested_operands(); + macro_paths(); + method_resolution::check(); + + let _ = 3.3 == f64::NAN; + //~^ WARN incorrect NaN comparison + + let _ = &3.3 != &f64::NAN; + //~^ WARN incorrect NaN comparison + + let suffixed = 3.3f64; + let copied = suffixed; + let _ = copied != f64::NAN; + //~^ WARN incorrect NaN comparison + + let ascribed: f64 = 3.3; + let _ = ascribed != f64::NAN; + //~^ WARN incorrect NaN comparison + + parameter(1.0); + parameter_ref(&1.0); + let _ = |x: f64| x != f64::NAN; //~ WARN incorrect NaN comparison + + #[allow(unused_macros)] + macro_rules! nan { () => { f32::NAN }; } + macro_rules! number { () => { 5f32 }; } + + let _ = nan!() == number!(); + //~^ WARN incorrect NaN comparison + let _ = number!() != nan!(); + //~^ WARN incorrect NaN comparison +} + +fn parameter(x: f64) { + let _ = x == f64::NAN; + //~^ WARN incorrect NaN comparison +} + +fn parameter_ref(x: &f32) { + if x != &f32::NAN {} + //~^ WARN incorrect NaN comparison +} + +fn nested_operands() { + let x = 1.0f64; + let _ = &x as &f64 == &f64::NAN; + //~^ WARN incorrect NaN comparison + let _ = { &{ 3.3 } } != &f64::NAN; + //~^ WARN incorrect NaN comparison + let _ = &&&3.3 == &&&f64::NAN; + //~^ WARN incorrect NaN comparison + let b = &&&3.3; + let _ = &&&f64::NAN != b; + //~^ WARN incorrect NaN comparison + let b = &mut 3.3; + let _ = b != &f64::NAN; + //~^ WARN incorrect NaN comparison + let b = &3.3; + let _ = &(::NAN) == b; + //~^ WARN incorrect NaN comparison + let b = &3.3; + let _ = b != &{ f64::NAN }; + //~^ WARN incorrect NaN comparison + let b = &&3.3; + let _ = &{ &{ f64::NAN } } == b; + //~^ WARN incorrect NaN comparison + + let b = &(-3.3); + let _ = b != &f16::NAN; + //~^ WARN incorrect NaN comparison + let b = &(3.3 + 0.0); + let _ = b == &f128::NAN; + //~^ WARN incorrect NaN comparison + + macro_rules! borrowed { () => { &3.3 }; } + let _ = borrowed!() != &f64::NAN; + //~^ WARN incorrect NaN comparison +} + +pub type MacroFloat = f64; + +fn macro_paths() { + #[allow(unused_macros)] + macro_rules! nan { () => { $crate::MacroFloat::NAN }; } + let b = &3.3; + let _ = b != &nan!(); + //~^ WARN incorrect NaN comparison +} + +mod shadowed { + #[allow(dead_code, non_camel_case_types)] + struct f64; + use ::core::primitive::f64 as Float; + + pub fn check() { + let b = &3.3; + if b != &::core::primitive::f64::NAN {} + //~^ WARN incorrect NaN comparison + let c = &4.4; + if c != &Float::NAN {} + //~^ WARN incorrect NaN comparison + let d = &5.5; + if d != &Float::r#NAN {} + //~^ WARN incorrect NaN comparison + } +} + +mod method_resolution { + mod f64 { + pub fn is_nan(_: ::core::primitive::f64) {} + } + + trait IsNan { + fn is_nan(self); + } + + impl IsNan for &f64 { + fn is_nan(self) {} + } + + pub fn check() { + let _: () = f64::is_nan(5.0); + let _: () = (&5f64).is_nan(); + + let x = 5f64; + let _: bool = x == f64::NAN; + //~^ WARN incorrect NaN comparison + + let x = &5f64; + let _: bool = x == &f64::NAN; + //~^ WARN incorrect NaN comparison + } +} diff --git a/tests/ui/lint/invalid-nan-comparisons/suggestion.stderr b/tests/ui/lint/invalid-nan-comparisons/suggestion.stderr new file mode 100644 index 0000000000000..50d5684dbb2c1 --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparisons/suggestion.stderr @@ -0,0 +1,561 @@ +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:10:13 + | +LL | let _ = x == f16::NAN; + | ^^^^^^^^^^^^^ + | + = note: `#[warn(invalid_nan_comparisons)]` on by default +help: use the `is_nan` method instead + | +LL - let _ = x == f16::NAN; +LL + let _ = ::is_nan(x); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:12:13 + | +LL | let _ = x != f16::NAN; + | ^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = x != f16::NAN; +LL + let _ = !::is_nan(x); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:16:13 + | +LL | let _ = x == f32::NAN; + | ^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = x == f32::NAN; +LL + let _ = ::is_nan(x); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:18:13 + | +LL | let _ = x != f32::NAN; + | ^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = x != f32::NAN; +LL + let _ = !::is_nan(x); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:22:13 + | +LL | let _ = x == f64::NAN; + | ^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = x == f64::NAN; +LL + let _ = ::is_nan(x); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:24:13 + | +LL | let _ = x != f64::NAN; + | ^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = x != f64::NAN; +LL + let _ = !::is_nan(x); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:28:13 + | +LL | let _ = x == f128::NAN; + | ^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = x == f128::NAN; +LL + let _ = ::is_nan(x); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:30:13 + | +LL | let _ = x != f128::NAN; + | ^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = x != f128::NAN; +LL + let _ = !::is_nan(x); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:34:8 + | +LL | if b != &f32::NAN {} + | ^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - if b != &f32::NAN {} +LL + if !::is_nan(*b) {} + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:38:8 + | +LL | if b != { &f32::NAN } {} + | ^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - if b != { &f32::NAN } {} +LL + if !::is_nan(*b) {} + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:42:9 + | +LL | / b != { +LL | | +LL | | &f32::NAN +LL | | }; + | |_________^ + | +help: use the `is_nan` method instead + | +LL - b != { +LL - +LL - &f32::NAN +LL - }; +LL + !::is_nan(*b); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:50:8 + | +LL | if b != &f64::NAN {} + | ^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - if b != &f64::NAN {} +LL + if !::is_nan(*b) {} + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:54:8 + | +LL | if m != &f64::NAN { + | ^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - if m != &f64::NAN { +LL + if !::is_nan(*m) { + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:61:8 + | +LL | if unary != &f64::NAN {} + | ^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - if unary != &f64::NAN {} +LL + if !::is_nan(*unary) {} + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:65:8 + | +LL | if block != &f64::NAN {} + | ^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - if block != &f64::NAN {} +LL + if !::is_nan(*block) {} + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:69:8 + | +LL | if tuple != &f64::NAN {} + | ^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - if tuple != &f64::NAN {} +LL + if !::is_nan(*tuple) {} + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:73:8 + | +LL | if arithmetic != &f64::NAN {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - if arithmetic != &f64::NAN {} +LL + if !::is_nan(*arithmetic) {} + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:77:8 + | +LL | if &x != &f64::NAN {} + | ^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - if &x != &f64::NAN {} +LL + if !::is_nan(*&x) {} + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:80:13 + | +LL | let _ = *p == f64::NAN; + | ^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = *p == f64::NAN; +LL + let _ = ::is_nan(*p); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:82:13 + | +LL | let _ = -x == f64::NAN; + | ^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = -x == f64::NAN; +LL + let _ = ::is_nan(-x); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:90:13 + | +LL | let _ = 3.3 == f64::NAN; + | ^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = 3.3 == f64::NAN; +LL + let _ = ::is_nan(3.3); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:93:13 + | +LL | let _ = &3.3 != &f64::NAN; + | ^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = &3.3 != &f64::NAN; +LL + let _ = !::is_nan(*&3.3); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:98:13 + | +LL | let _ = copied != f64::NAN; + | ^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = copied != f64::NAN; +LL + let _ = !::is_nan(copied); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:102:13 + | +LL | let _ = ascribed != f64::NAN; + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = ascribed != f64::NAN; +LL + let _ = !::is_nan(ascribed); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:107:22 + | +LL | let _ = |x: f64| x != f64::NAN; + | ^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = |x: f64| x != f64::NAN; +LL + let _ = |x: f64| !::is_nan(x); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:113:13 + | +LL | let _ = nan!() == number!(); + | ^^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = nan!() == number!(); +LL + let _ = ::std::primitive::f32::is_nan(number!()); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:115:13 + | +LL | let _ = number!() != nan!(); + | ^^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = number!() != nan!(); +LL + let _ = !::std::primitive::f32::is_nan(number!()); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:120:13 + | +LL | let _ = x == f64::NAN; + | ^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = x == f64::NAN; +LL + let _ = ::is_nan(x); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:125:8 + | +LL | if x != &f32::NAN {} + | ^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - if x != &f32::NAN {} +LL + if !::is_nan(*x) {} + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:131:13 + | +LL | let _ = &x as &f64 == &f64::NAN; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = &x as &f64 == &f64::NAN; +LL + let _ = ::is_nan(*(&x as &f64)); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:133:13 + | +LL | let _ = { &{ 3.3 } } != &f64::NAN; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = { &{ 3.3 } } != &f64::NAN; +LL + let _ = !::is_nan(*{ &{ 3.3 } }); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:135:13 + | +LL | let _ = &&&3.3 == &&&f64::NAN; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = &&&3.3 == &&&f64::NAN; +LL + let _ = ::is_nan(***&&&3.3); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:138:13 + | +LL | let _ = &&&f64::NAN != b; + | ^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = &&&f64::NAN != b; +LL + let _ = !::is_nan(***b); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:141:13 + | +LL | let _ = b != &f64::NAN; + | ^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = b != &f64::NAN; +LL + let _ = !::is_nan(*b); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:144:13 + | +LL | let _ = &(::NAN) == b; + | ^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = &(::NAN) == b; +LL + let _ = ::is_nan(*b); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:147:13 + | +LL | let _ = b != &{ f64::NAN }; + | ^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = b != &{ f64::NAN }; +LL + let _ = !::is_nan(*b); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:150:13 + | +LL | let _ = &{ &{ f64::NAN } } == b; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = &{ &{ f64::NAN } } == b; +LL + let _ = ::is_nan(**b); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:154:13 + | +LL | let _ = b != &f16::NAN; + | ^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = b != &f16::NAN; +LL + let _ = !::is_nan(*b); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:157:13 + | +LL | let _ = b == &f128::NAN; + | ^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = b == &f128::NAN; +LL + let _ = ::is_nan(*b); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:161:13 + | +LL | let _ = borrowed!() != &f64::NAN; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = borrowed!() != &f64::NAN; +LL + let _ = !::is_nan(*borrowed!()); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:171:13 + | +LL | let _ = b != &nan!(); + | ^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _ = b != &nan!(); +LL + let _ = !::std::primitive::f64::is_nan(*b); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:182:12 + | +LL | if b != &::core::primitive::f64::NAN {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - if b != &::core::primitive::f64::NAN {} +LL + if !<::core::primitive::f64>::is_nan(*b) {} + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:185:12 + | +LL | if c != &Float::NAN {} + | ^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - if c != &Float::NAN {} +LL + if !::is_nan(*c) {} + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:188:12 + | +LL | if d != &Float::r#NAN {} + | ^^^^^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - if d != &Float::r#NAN {} +LL + if !::is_nan(*d) {} + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:211:23 + | +LL | let _: bool = x == f64::NAN; + | ^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _: bool = x == f64::NAN; +LL + let _: bool = ::is_nan(x); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/suggestion.rs:215:23 + | +LL | let _: bool = x == &f64::NAN; + | ^^^^^^^^^^^^^^ + | +help: use the `is_nan` method instead + | +LL - let _: bool = x == &f64::NAN; +LL + let _: bool = ::is_nan(*x); + | + +warning: 46 warnings emitted + diff --git a/tests/ui/unpretty/deprecated-attr.stdout b/tests/ui/unpretty/deprecated-attr.stdout index 4ba5b0b366df9..c7d825f1f29c2 100644 --- a/tests/ui/unpretty/deprecated-attr.stdout +++ b/tests/ui/unpretty/deprecated-attr.stdout @@ -5,28 +5,30 @@ use ::std::prelude::rust_2015::*; //@ check-pass //@ edition: 2015 -#[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}] +#[attr = Deprecated {deprecation: Deprecation {since: Unspecified}, +is_inner: false}] struct PlainDeprecated; #[attr = Deprecated {deprecation: Deprecation {since: Unspecified, -note: here's why this is deprecated#0}}] +note: here's why this is deprecated#0}, is_inner: false}] struct DirectNote; #[attr = Deprecated {deprecation: Deprecation {since: Unspecified, -note: here's why this is deprecated#0}}] +note: here's why this is deprecated#0}, is_inner: false}] struct ExplicitNote; #[attr = Deprecated {deprecation: Deprecation {since: NonStandard("1.2.3"), -note: here's why this is deprecated#0}}] +note: here's why this is deprecated#0}, is_inner: false}] struct SinceAndNote; #[attr = Deprecated {deprecation: Deprecation {since: NonStandard("1.2.3"), -note: here's why this is deprecated#0}}] +note: here's why this is deprecated#0}, is_inner: false}] struct FlippedOrder; fn f() { // Attribute is ignored here (with a warning), but still preserved in HIR - #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}] + #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}, + is_inner: false}] 0 }