diff options
Diffstat (limited to 'compiler')
92 files changed, 1581 insertions, 1102 deletions
diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index 180130c7be4..849141c34f7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -1,3 +1,4 @@ +use rustc_ast::AttrStyle; use rustc_errors::DiagArgValue; use rustc_hir::attrs::MacroUseArgs; @@ -133,3 +134,65 @@ impl<S: Stage> NoArgsAttributeParser<S> for AllowInternalUnsafeParser { ]); const CREATE: fn(Span) -> AttributeKind = |span| AttributeKind::AllowInternalUnsafe(span); } + +pub(crate) struct MacroExportParser; + +impl<S: Stage> SingleAttributeParser<S> for MacroExportParser { + const PATH: &[Symbol] = &[sym::macro_export]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn; + const TEMPLATE: AttributeTemplate = template!(Word, List: &["local_inner_macros"]); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + Allow(Target::MacroDef), + Error(Target::WherePredicate), + Error(Target::Crate), + ]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + let suggestions = || { + <Self as SingleAttributeParser<S>>::TEMPLATE + .suggestions(AttrStyle::Inner, "macro_export") + }; + let local_inner_macros = match args { + ArgParser::NoArgs => false, + ArgParser::List(list) => { + let Some(l) = list.single() else { + let span = cx.attr_span; + cx.emit_lint( + AttributeLintKind::InvalidMacroExportArguments { + suggestions: suggestions(), + }, + span, + ); + return None; + }; + match l.meta_item().and_then(|i| i.path().word_sym()) { + Some(sym::local_inner_macros) => true, + _ => { + let span = cx.attr_span; + cx.emit_lint( + AttributeLintKind::InvalidMacroExportArguments { + suggestions: suggestions(), + }, + span, + ); + return None; + } + } + } + ArgParser::NameValue(_) => { + let span = cx.attr_span; + let suggestions = suggestions(); + cx.emit_err(IllFormedAttributeInputLint { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + span, + }); + return None; + } + }; + Some(AttributeKind::MacroExport { span: cx.attr_span, local_inner_macros }) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index ee5b7322b02..4c32bb87a24 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -40,7 +40,7 @@ use crate::attributes::lint_helpers::{ }; use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; use crate::attributes::macro_attrs::{ - AllowInternalUnsafeParser, MacroEscapeParser, MacroUseParser, + AllowInternalUnsafeParser, MacroEscapeParser, MacroExportParser, MacroUseParser, }; use crate::attributes::must_use::MustUseParser; use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; @@ -183,6 +183,7 @@ attribute_parsers!( Single<LinkOrdinalParser>, Single<LinkSectionParser>, Single<LinkageParser>, + Single<MacroExportParser>, Single<MoveSizeLimitParser>, Single<MustUseParser>, Single<ObjcClassParser>, diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs index b1a971eec32..ab8ba0daf1f 100644 --- a/compiler/rustc_attr_parsing/src/lints.rs +++ b/compiler/rustc_attr_parsing/src/lints.rs @@ -31,6 +31,18 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<L::Id>, lint_emi }, ); } + AttributeLintKind::InvalidMacroExportArguments { suggestions } => lint_emitter + .emit_node_span_lint( + rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS, + *id, + *span, + session_diagnostics::IllFormedAttributeInput { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + }, + ), AttributeLintKind::EmptyAttribute { first_span } => lint_emitter.emit_node_span_lint( rustc_session::lint::builtin::UNUSED_ATTRIBUTES, *id, diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 51089e5a1d3..82c59d5a3a2 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -63,8 +63,8 @@ pub fn inject( if sess.is_test_crate() { let panic_strategy = match (panic_strategy, sess.opts.unstable_opts.panic_abort_tests) { - (PanicStrategy::Abort, true) => PanicStrategy::Abort, - (PanicStrategy::Abort, false) => { + (PanicStrategy::Abort | PanicStrategy::ImmediateAbort, true) => panic_strategy, + (PanicStrategy::Abort | PanicStrategy::ImmediateAbort, false) => { if panic_strategy == platform_panic_strategy { // Silently allow compiling with panic=abort on these platforms, // but with old behavior (abort if a test fails). @@ -287,10 +287,8 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> Box<ast::Item> { let ecx = &cx.ext_cx; let test_ident = Ident::new(sym::test, sp); - let runner_name = match cx.panic_strategy { - PanicStrategy::Unwind => "test_main_static", - PanicStrategy::Abort => "test_main_static_abort", - }; + let runner_name = + if cx.panic_strategy.unwinds() { "test_main_static" } else { "test_main_static_abort" }; // test::test_main_static(...) let mut test_runner = cx.test_runner.clone().unwrap_or_else(|| { diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 17e2e028b16..a14881c502c 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -698,8 +698,12 @@ fn reg_class_to_gcc(reg_class: InlineAsmRegClass) -> &'static str { InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b", InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => "f", InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => "v", - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr) - | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => { + InlineAsmRegClass::PowerPC( + PowerPCInlineAsmRegClass::cr + | PowerPCInlineAsmRegClass::ctr + | PowerPCInlineAsmRegClass::lr + | PowerPCInlineAsmRegClass::xer, + ) => { unreachable!("clobber-only") } InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r", @@ -777,8 +781,12 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => { cx.type_vector(cx.type_i32(), 4) } - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr) - | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => { + InlineAsmRegClass::PowerPC( + PowerPCInlineAsmRegClass::cr + | PowerPCInlineAsmRegClass::ctr + | PowerPCInlineAsmRegClass::lr + | PowerPCInlineAsmRegClass::xer, + ) => { unreachable!("clobber-only") } InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(), diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index e9d72e457a0..e8672f49580 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -15,9 +15,9 @@ use rustc_middle::mir::mono::Visibility; use rustc_middle::ty::TyCtxt; use rustc_session::config::DebugInfo; use rustc_span::Symbol; +use rustc_target::spec::RelocModel; #[cfg(feature = "master")] use rustc_target::spec::SymbolVisibility; -use rustc_target::spec::{PanicStrategy, RelocModel}; use crate::builder::Builder; use crate::context::CodegenCx; @@ -101,7 +101,7 @@ pub fn compile_codegen_unit( // Instantiate monomorphizations without filling out definitions yet... let context = new_context(tcx); - if tcx.sess.panic_strategy() == PanicStrategy::Unwind { + if tcx.sess.panic_strategy().unwinds() { context.add_command_line_option("-fexceptions"); context.add_driver_option("-fexceptions"); } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 84fa56cf903..a915f5d6418 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -29,7 +29,6 @@ use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Instance, Ty}; use rustc_span::{Span, Symbol, sym}; use rustc_target::callconv::{ArgAbi, PassMode}; -use rustc_target::spec::PanicStrategy; #[cfg(feature = "master")] use crate::abi::FnAbiGccExt; @@ -1334,7 +1333,7 @@ fn try_intrinsic<'a, 'b, 'gcc, 'tcx>( _catch_func: RValue<'gcc>, dest: PlaceRef<'tcx, RValue<'gcc>>, ) { - if bx.sess().panic_strategy() == PanicStrategy::Abort { + if !bx.sess().panic_strategy().unwinds() { bx.call(bx.type_void(), None, None, try_func, &[data], None, None); // Return 0 unconditionally from the intrinsic call; // we can never unwind. diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index b79176e9098..cc09fa5b69b 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -340,8 +340,8 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { attrs.push(llvm::AttributeKind::WillReturn.create_attr(self.cx.llcx)); } else if options.contains(InlineAsmOptions::NOMEM) { attrs.push(llvm::MemoryEffects::InaccessibleMemOnly.create_attr(self.cx.llcx)); - } else { - // LLVM doesn't have an attribute to represent ReadOnly + SideEffect + } else if options.contains(InlineAsmOptions::READONLY) { + attrs.push(llvm::MemoryEffects::ReadOnlyNotPure.create_attr(self.cx.llcx)); } attributes::apply_to_callsite(result, llvm::AttributePlace::Function, &{ attrs }); @@ -662,7 +662,12 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) -> PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b", PowerPC(PowerPCInlineAsmRegClass::freg) => "f", PowerPC(PowerPCInlineAsmRegClass::vreg) => "v", - PowerPC(PowerPCInlineAsmRegClass::cr) | PowerPC(PowerPCInlineAsmRegClass::xer) => { + PowerPC( + PowerPCInlineAsmRegClass::cr + | PowerPCInlineAsmRegClass::ctr + | PowerPCInlineAsmRegClass::lr + | PowerPCInlineAsmRegClass::xer, + ) => { unreachable!("clobber-only") } RiscV(RiscVInlineAsmRegClass::reg) => "r", @@ -830,7 +835,12 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &' PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(), PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(), PowerPC(PowerPCInlineAsmRegClass::vreg) => cx.type_vector(cx.type_i32(), 4), - PowerPC(PowerPCInlineAsmRegClass::cr) | PowerPC(PowerPCInlineAsmRegClass::xer) => { + PowerPC( + PowerPCInlineAsmRegClass::cr + | PowerPCInlineAsmRegClass::ctr + | PowerPCInlineAsmRegClass::lr + | PowerPCInlineAsmRegClass::xer, + ) => { unreachable!("clobber-only") } RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(), diff --git a/compiler/rustc_codegen_llvm/src/back/command_line_args.rs b/compiler/rustc_codegen_llvm/src/back/command_line_args.rs new file mode 100644 index 00000000000..b14713969b3 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/back/command_line_args.rs @@ -0,0 +1,37 @@ +#[cfg(test)] +mod tests; + +/// Joins command-line arguments into a single space-separated string, quoting +/// and escaping individual arguments as necessary. +/// +/// The result is intended to be informational, for embedding in debug metadata, +/// and might not be properly quoted/escaped for actual command-line use. +pub(crate) fn quote_command_line_args(args: &[String]) -> String { + // Start with a decent-sized buffer, since rustc invocations tend to be long. + let mut buf = String::with_capacity(128); + + for arg in args { + if !buf.is_empty() { + buf.push(' '); + } + + print_arg_quoted(&mut buf, arg); + } + + buf +} + +/// Equivalent to LLVM's `sys::printArg` with quoting always enabled +/// (see llvm/lib/Support/Program.cpp). +fn print_arg_quoted(buf: &mut String, arg: &str) { + buf.reserve(arg.len() + 2); + + buf.push('"'); + for ch in arg.chars() { + if matches!(ch, '"' | '\\' | '$') { + buf.push('\\'); + } + buf.push(ch); + } + buf.push('"'); +} diff --git a/compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs b/compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs new file mode 100644 index 00000000000..69641fed3bc --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs @@ -0,0 +1,25 @@ +#[test] +fn quote_command_line_args() { + use super::quote_command_line_args; + + struct Case<'a> { + args: &'a [&'a str], + expected: &'a str, + } + + let cases = &[ + Case { args: &[], expected: "" }, + Case { args: &["--hello", "world"], expected: r#""--hello" "world""# }, + Case { args: &["--hello world"], expected: r#""--hello world""# }, + Case { + args: &["plain", "$dollar", "spa ce", r"back\slash", r#""quote""#, "plain"], + expected: r#""plain" "\$dollar" "spa ce" "back\\slash" "\"quote\"" "plain""#, + }, + ]; + + for &Case { args, expected } in cases { + let args = args.iter().copied().map(str::to_owned).collect::<Vec<_>>(); + let actual = quote_command_line_args(&args); + assert_eq!(actual, expected, "args {args:?}"); + } +} diff --git a/compiler/rustc_codegen_llvm/src/back/mod.rs b/compiler/rustc_codegen_llvm/src/back/mod.rs index 6cb89f80ab8..fe3883e8c73 100644 --- a/compiler/rustc_codegen_llvm/src/back/mod.rs +++ b/compiler/rustc_codegen_llvm/src/back/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod archive; +mod command_line_args; pub(crate) mod lto; pub(crate) mod owned_target_machine; mod profiling; diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs index 6d8178320fe..d5228f0e0de 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -1,4 +1,3 @@ -use std::assert_matches::assert_matches; use std::ffi::CStr; use std::marker::PhantomData; use std::ptr::NonNull; @@ -39,13 +38,10 @@ impl OwnedTargetMachine { output_obj_file: &CStr, debug_info_compression: &CStr, use_emulated_tls: bool, - args_cstr_buff: &[u8], + argv0: &str, + command_line_args: &str, use_wasm_eh: bool, ) -> Result<Self, LlvmError<'static>> { - // The argument list is passed as the concatenation of one or more C strings. - // This implies that there must be a last byte, and it must be 0. - assert_matches!(args_cstr_buff, [.., b'\0'], "the last byte must be a NUL terminator"); - // SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed to data let tm_ptr = unsafe { llvm::LLVMRustCreateTargetMachine( @@ -70,8 +66,10 @@ impl OwnedTargetMachine { output_obj_file.as_ptr(), debug_info_compression.as_ptr(), use_emulated_tls, - args_cstr_buff.as_ptr(), - args_cstr_buff.len(), + argv0.as_ptr(), + argv0.len(), + command_line_args.as_ptr(), + command_line_args.len(), use_wasm_eh, ) }; diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index bda81fbd19e..1950b8288d1 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -31,6 +31,7 @@ use rustc_span::{BytePos, InnerSpan, Pos, SpanData, SyntaxContext, sym}; use rustc_target::spec::{CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel}; use tracing::{debug, trace}; +use crate::back::command_line_args::quote_command_line_args; use crate::back::lto::ThinBuffer; use crate::back::owned_target_machine::OwnedTargetMachine; use crate::back::profiling::{ @@ -203,6 +204,9 @@ pub(crate) fn target_machine_factory( optlvl: config::OptLevel, target_features: &[String], ) -> TargetMachineFactoryFn<LlvmCodegenBackend> { + // Self-profile timer for creating a _factory_. + let _prof_timer = sess.prof.generic_activity("target_machine_factory"); + let reloc_model = to_llvm_relocation_model(sess.relocation_model()); let (opt_level, _) = to_llvm_opt_settings(optlvl); @@ -249,23 +253,18 @@ pub(crate) fn target_machine_factory( let use_emulated_tls = matches!(sess.tls_model(), TlsModel::Emulated); - // copy the exe path, followed by path all into one buffer - // null terminating them so we can use them as null terminated strings - let args_cstr_buff = { - let mut args_cstr_buff: Vec<u8> = Vec::new(); - let exe_path = std::env::current_exe().unwrap_or_default(); - let exe_path_str = exe_path.into_os_string().into_string().unwrap_or_default(); - - args_cstr_buff.extend_from_slice(exe_path_str.as_bytes()); - args_cstr_buff.push(0); - - for arg in sess.expanded_args.iter() { - args_cstr_buff.extend_from_slice(arg.as_bytes()); - args_cstr_buff.push(0); - } - - args_cstr_buff - }; + // Command-line information to be included in the target machine. + // This seems to only be used for embedding in PDB debuginfo files. + // FIXME(Zalathar): Maybe skip this for non-PDB targets? + let argv0 = std::env::current_exe() + .unwrap_or_default() + .into_os_string() + .into_string() + .unwrap_or_default(); + let command_line_args = quote_command_line_args(&sess.expanded_args); + // Self-profile counter for the number of bytes produced by command-line quoting. + // Values are summed, so the summary result is cumulative across all TM factories. + sess.prof.artifact_size("quoted_command_line_args", "-", command_line_args.len() as u64); let debuginfo_compression = sess.opts.debuginfo_compression.to_string(); match sess.opts.debuginfo_compression { @@ -288,7 +287,11 @@ pub(crate) fn target_machine_factory( let use_wasm_eh = wants_wasm_eh(sess); + let prof = SelfProfilerRef::clone(&sess.prof); Arc::new(move |config: TargetMachineFactoryConfig| { + // Self-profile timer for invoking a factory to create a target machine. + let _prof_timer = prof.generic_activity("target_machine_factory_inner"); + let path_to_cstring_helper = |path: Option<PathBuf>| -> CString { let path = path.unwrap_or_default(); let path = path_mapping @@ -323,7 +326,8 @@ pub(crate) fn target_machine_factory( &output_obj_file, &debuginfo_compression, use_emulated_tls, - &args_cstr_buff, + &argv0, + &command_line_args, use_wasm_eh, ) }) diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index e7f4a357048..50398a32142 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -18,7 +18,6 @@ use rustc_middle::{bug, span_bug}; use rustc_span::{Span, Symbol, sym}; use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate}; use rustc_target::callconv::PassMode; -use rustc_target::spec::PanicStrategy; use tracing::debug; use crate::abi::FnAbiLlvmExt; @@ -674,7 +673,7 @@ fn catch_unwind_intrinsic<'ll, 'tcx>( catch_func: &'ll Value, dest: PlaceRef<'tcx, &'ll Value>, ) { - if bx.sess().panic_strategy() == PanicStrategy::Abort { + if !bx.sess().panic_strategy().unwinds() { let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); bx.call(try_func_ty, None, None, try_func, &[data], None, None); // Return 0 unconditionally from the intrinsic call; diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 6fb23d09843..13bdb7cb1a2 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -45,6 +45,7 @@ use rustc_middle::util::Providers; use rustc_session::Session; use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; use rustc_span::Symbol; +use rustc_target::spec::{RelocModel, TlsModel}; mod abi; mod allocator; @@ -244,16 +245,7 @@ impl CodegenBackend for LlvmCodegenBackend { match req.kind { PrintKind::RelocationModels => { writeln!(out, "Available relocation models:").unwrap(); - for name in &[ - "static", - "pic", - "pie", - "dynamic-no-pic", - "ropi", - "rwpi", - "ropi-rwpi", - "default", - ] { + for name in RelocModel::ALL.iter().map(RelocModel::desc).chain(["default"]) { writeln!(out, " {name}").unwrap(); } writeln!(out).unwrap(); @@ -267,9 +259,7 @@ impl CodegenBackend for LlvmCodegenBackend { } PrintKind::TlsModels => { writeln!(out, "Available TLS models:").unwrap(); - for name in - &["global-dynamic", "local-dynamic", "initial-exec", "local-exec", "emulated"] - { + for name in TlsModel::ALL.iter().map(TlsModel::desc) { writeln!(out, " {name}").unwrap(); } writeln!(out).unwrap(); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 1124ebc3d44..fd972f371df 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -710,6 +710,7 @@ pub(crate) enum MemoryEffects { None, ReadOnly, InaccessibleMemOnly, + ReadOnlyNotPure, } /// LLVMOpcode @@ -2491,8 +2492,10 @@ unsafe extern "C" { OutputObjFile: *const c_char, DebugInfoCompression: *const c_char, UseEmulatedTls: bool, - ArgsCstrBuff: *const c_uchar, // See "PTR_LEN_STR". - ArgsCstrBuffLen: usize, + Argv0: *const c_uchar, // See "PTR_LEN_STR". + Argv0Len: size_t, + CommandLineArgs: *const c_uchar, // See "PTR_LEN_STR". + CommandLineArgsLen: size_t, UseWasmEH: bool, ) -> *mut TargetMachine; diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 8461c8b03d5..3b920168e06 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -106,7 +106,7 @@ unsafe fn configure_llvm(sess: &Session) { if sess.target.os == "emscripten" && !sess.opts.unstable_opts.emscripten_wasm_eh - && sess.panic_strategy() == PanicStrategy::Unwind + && sess.panic_strategy().unwinds() { add("-enable-emscripten-cxx-exceptions", false); } @@ -217,27 +217,16 @@ impl<'a> IntoIterator for LLVMFeature<'a> { /// Rust can also be build with an external precompiled version of LLVM which might lead to failures /// if the oldest tested / supported LLVM version doesn't yet support the relevant intrinsics. pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFeature<'a>> { - let arch = if sess.target.arch == "x86_64" { - "x86" - } else if sess.target.arch == "arm64ec" { - "aarch64" - } else if sess.target.arch == "sparc64" { - "sparc" - } else if sess.target.arch == "powerpc64" { - "powerpc" - } else { - &*sess.target.arch + let raw_arch = &*sess.target.arch; + let arch = match raw_arch { + "x86_64" => "x86", + "arm64ec" => "aarch64", + "sparc64" => "sparc", + "powerpc64" => "powerpc", + _ => raw_arch, }; + let (major, _, _) = get_version(); match (arch, s) { - ("x86", "sse4.2") => Some(LLVMFeature::with_dependencies( - "sse4.2", - smallvec![TargetFeatureFoldStrength::EnableOnly("crc32")], - )), - ("x86", "pclmulqdq") => Some(LLVMFeature::new("pclmul")), - ("x86", "rdrand") => Some(LLVMFeature::new("rdrnd")), - ("x86", "bmi1") => Some(LLVMFeature::new("bmi")), - ("x86", "cmpxchg16b") => Some(LLVMFeature::new("cx16")), - ("x86", "lahfsahf") => Some(LLVMFeature::new("sahf")), ("aarch64", "rcpc2") => Some(LLVMFeature::new("rcpc-immo")), ("aarch64", "dpb") => Some(LLVMFeature::new("ccpp")), ("aarch64", "dpb2") => Some(LLVMFeature::new("ccdp")), @@ -260,14 +249,23 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea ("aarch64", "fpmr") => None, // only existed in 18 ("arm", "fp16") => Some(LLVMFeature::new("fullfp16")), // Filter out features that are not supported by the current LLVM version - ("loongarch32" | "loongarch64", "32s") if get_version().0 < 21 => None, + ("loongarch32" | "loongarch64", "32s") if major < 21 => None, + ("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")), + ("sparc", "leoncasa") => Some(LLVMFeature::new("hasleoncasa")), + ("x86", "sse4.2") => Some(LLVMFeature::with_dependencies( + "sse4.2", + smallvec![TargetFeatureFoldStrength::EnableOnly("crc32")], + )), + ("x86", "pclmulqdq") => Some(LLVMFeature::new("pclmul")), + ("x86", "rdrand") => Some(LLVMFeature::new("rdrnd")), + ("x86", "bmi1") => Some(LLVMFeature::new("bmi")), + ("x86", "cmpxchg16b") => Some(LLVMFeature::new("cx16")), + ("x86", "lahfsahf") => Some(LLVMFeature::new("sahf")), // Enable the evex512 target feature if an avx512 target feature is enabled. ("x86", s) if s.starts_with("avx512") => Some(LLVMFeature::with_dependencies( s, smallvec![TargetFeatureFoldStrength::EnableOnly("evex512")], )), - ("sparc", "leoncasa") => Some(LLVMFeature::new("hasleoncasa")), - ("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")), ("x86", "avx10.1") => Some(LLVMFeature::new("avx10.1-512")), ("x86", "avx10.2") => Some(LLVMFeature::new("avx10.2-512")), ("x86", "apxf") => Some(LLVMFeature::with_dependencies( diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 5462163f4ff..9c5a3d839ce 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -8,9 +8,7 @@ edition = "2024" ar_archive_writer = "0.5" bitflags = "2.4.1" bstr = "1.11.3" -# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version -# per crate", so if you change this, you need to also change it in `rustc_llvm` and `rustc_windows_rc`. -cc = "=1.2.16" +find-msvc-tools = "0.1.2" itertools = "0.12" pathdiff = "0.2.0" regex = "1.4" diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 1dd65d38a2b..91c3806df4c 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -8,8 +8,6 @@ codegen_ssa_aix_strip_not_used = using host's `strip` binary to cross-compile to codegen_ssa_archive_build_failure = failed to build archive at `{$path}`: {$error} -codegen_ssa_autodiff_without_lto = using the autodiff feature requires using fat-lto - codegen_ssa_bare_instruction_set = `#[instruction_set]` requires an argument codegen_ssa_binary_output_to_tty = option `-o` or `--emit` is used to write binary output type `{$shorthand}` to stdout, but stdout is a tty diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 48b01ea2df1..d6c304c1b14 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -9,7 +9,7 @@ use std::path::{Path, PathBuf}; use std::process::{Output, Stdio}; use std::{env, fmt, fs, io, mem, str}; -use cc::windows_registry; +use find_msvc_tools; use itertools::Itertools; use regex::Regex; use rustc_arena::TypedArena; @@ -47,8 +47,8 @@ use rustc_span::Symbol; use rustc_target::spec::crt_objects::CrtObjects; use rustc_target::spec::{ BinaryFormat, Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, - LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel, - SanitizerSet, SplitDebuginfo, + LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, RelocModel, RelroLevel, SanitizerSet, + SplitDebuginfo, }; use tracing::{debug, info, warn}; @@ -877,9 +877,9 @@ fn link_natively( // All Microsoft `link.exe` linking ror codes are // four digit numbers in the range 1000 to 9999 inclusive if is_msvc_link_exe && (code < 1000 || code > 9999) { - let is_vs_installed = windows_registry::find_vs_version().is_ok(); + let is_vs_installed = find_msvc_tools::find_vs_version().is_ok(); let has_linker = - windows_registry::find_tool(&sess.target.arch, "link.exe").is_some(); + find_msvc_tools::find_tool(&sess.target.arch, "link.exe").is_some(); sess.dcx().emit_note(errors::LinkExeUnexpectedError); @@ -2512,10 +2512,10 @@ fn add_order_independent_options( if sess.target.os == "emscripten" { cmd.cc_arg(if sess.opts.unstable_opts.emscripten_wasm_eh { "-fwasm-exceptions" - } else if sess.panic_strategy() == PanicStrategy::Abort { - "-sDISABLE_EXCEPTION_CATCHING=1" - } else { + } else if sess.panic_strategy().unwinds() { "-sDISABLE_EXCEPTION_CATCHING=0" + } else { + "-sDISABLE_EXCEPTION_CATCHING=1" }); } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index a2efd420a32..624ab1b5084 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -4,7 +4,7 @@ use std::io::prelude::*; use std::path::{Path, PathBuf}; use std::{env, io, iter, mem, str}; -use cc::windows_registry; +use find_msvc_tools; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::{ find_native_static_library, try_find_native_dynamic_library, try_find_native_static_library, @@ -53,7 +53,7 @@ pub(crate) fn get_linker<'a>( self_contained: bool, target_cpu: &'a str, ) -> Box<dyn Linker + 'a> { - let msvc_tool = windows_registry::find_tool(&sess.target.arch, "link.exe"); + let msvc_tool = find_msvc_tools::find_tool(&sess.target.arch, "link.exe"); // If our linker looks like a batch script on Windows then to execute this // we'll need to spawn `cmd` explicitly. This is primarily done to handle @@ -117,7 +117,6 @@ pub(crate) fn get_linker<'a>( if sess.target.is_like_msvc && let Some(ref tool) = msvc_tool { - cmd.args(tool.args()); for (k, v) in tool.env() { if k == "PATH" { new_path.extend(env::split_paths(v)); @@ -845,6 +844,11 @@ impl<'a> Linker for GccLinker<'a> { self.sess.dcx().emit_fatal(errors::VersionScriptWriteFailure { error }); } self.link_arg("--dynamic-list").link_arg(path); + } else if self.sess.target.is_like_wasm { + self.link_arg("--no-export-dynamic"); + for (sym, _) in symbols { + self.link_arg("--export").link_arg(sym); + } } else { // Write an LD version script let res: io::Result<()> = try { diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index fb5a8205140..d5c30c5c7a6 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -38,10 +38,6 @@ pub(crate) struct CguNotRecorded<'a> { } #[derive(Diagnostic)] -#[diag(codegen_ssa_autodiff_without_lto)] -pub struct AutodiffWithoutLto; - -#[derive(Diagnostic)] #[diag(codegen_ssa_unknown_reuse_kind)] pub(crate) struct UnknownReuseKind { #[primary_span] diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 31784cabf4a..e7239ebfecf 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -228,6 +228,9 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, "{asm_name}:").unwrap(); writeln!(end).unwrap(); + // emit a label starting with `func_end` for `cargo asm` and other tooling that might + // pattern match on assembly generated by LLVM. + writeln!(end, ".Lfunc_end_{asm_name}:").unwrap(); writeln!(end, ".size {asm_name}, . - {asm_name}").unwrap(); writeln!(end, ".popsection").unwrap(); if !arch_suffix.is_empty() { @@ -246,6 +249,7 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, "{asm_name}:").unwrap(); writeln!(end).unwrap(); + writeln!(end, ".Lfunc_end_{asm_name}:").unwrap(); writeln!(end, ".popsection").unwrap(); if !arch_suffix.is_empty() { writeln!(end, "{}", arch_suffix).unwrap(); @@ -263,6 +267,7 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, "{asm_name}:").unwrap(); writeln!(end).unwrap(); + writeln!(end, ".Lfunc_end_{asm_name}:").unwrap(); writeln!(end, ".popsection").unwrap(); if !arch_suffix.is_empty() { writeln!(end, "{}", arch_suffix).unwrap(); @@ -287,6 +292,7 @@ fn prefix_and_suffix<'tcx>( writeln!(end).unwrap(); // .size is ignored for function symbols, so we can skip it writeln!(end, "end_function").unwrap(); + writeln!(end, ".Lfunc_end_{asm_name}:").unwrap(); } BinaryFormat::Xcoff => { // the LLVM XCOFFAsmParser is extremely incomplete and does not implement many of the diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 4f875cf99ec..8dab520cf36 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -206,7 +206,7 @@ impl Callbacks for TimePassesCallbacks { // time because it will mess up the --print output. See #64339. // self.time_passes = (config.opts.prints.is_empty() && config.opts.unstable_opts.time_passes) - .then(|| config.opts.unstable_opts.time_passes_format); + .then_some(config.opts.unstable_opts.time_passes_format); config.opts.trimmed_def_paths = true; } } @@ -439,8 +439,9 @@ fn make_input(early_dcx: &EarlyDiagCtxt, free_matches: &[String]) -> Option<Inpu "when UNSTABLE_RUSTDOC_TEST_PATH is set \ UNSTABLE_RUSTDOC_TEST_LINE also needs to be set", ); - let line = isize::from_str_radix(&line, 10) - .expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be an number"); + let line = line + .parse::<isize>() + .expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be a number"); FileName::doc_test_source_code(PathBuf::from(path), line) } Err(_) => FileName::anon_source_code(&input), @@ -474,8 +475,7 @@ fn handle_explain(early_dcx: &EarlyDiagCtxt, registry: Registry, code: &str, col let mut text = String::new(); // Slice off the leading newline and print. for line in description.lines() { - let indent_level = - line.find(|c: char| !c.is_whitespace()).unwrap_or_else(|| line.len()); + let indent_level = line.find(|c: char| !c.is_whitespace()).unwrap_or(line.len()); let dedented_line = &line[indent_level..]; if dedented_line.starts_with("```") { is_in_code_block = !is_in_code_block; @@ -547,7 +547,7 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) { // The pager failed. Try to print pretty output to stdout. if let Some((bufwtr, mdbuf)) = &pretty_data - && bufwtr.print(&mdbuf).is_ok() + && bufwtr.print(mdbuf).is_ok() { return; } @@ -598,8 +598,7 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) { match sess.io.input { - Input::File(ref ifile) => { - let path = &(*ifile); + Input::File(ref path) => { let mut v = Vec::new(); locator::list_file_metadata( &sess.target, @@ -833,7 +832,7 @@ fn print_crate_info( SupportedCrateTypes => { let supported_crate_types = CRATE_TYPES .iter() - .filter(|(_, crate_type)| !invalid_output_for_target(&sess, *crate_type)) + .filter(|(_, crate_type)| !invalid_output_for_target(sess, *crate_type)) .filter(|(_, crate_type)| *crate_type != CrateType::Sdylib) .map(|(crate_type_sym, _)| *crate_type_sym) .collect::<BTreeSet<_>>(); @@ -1434,7 +1433,7 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&DiagCtxt)) eprintln!(); if let Some(ice_path) = ice_path() - && let Ok(mut out) = File::options().create(true).append(true).open(&ice_path) + && let Ok(mut out) = File::options().create(true).append(true).open(ice_path) { // The current implementation always returns `Some`. let location = info.location().unwrap(); @@ -1510,7 +1509,7 @@ fn report_ice( let file = if let Some(path) = ice_path() { // Create the ICE dump target file. - match crate::fs::File::options().create(true).append(true).open(&path) { + match crate::fs::File::options().create(true).append(true).open(path) { Ok(mut file) => { dcx.emit_note(session_diagnostics::IcePath { path: path.clone() }); if FIRST_PANIC.swap(false, Ordering::SeqCst) { diff --git a/compiler/rustc_driver_impl/src/print.rs b/compiler/rustc_driver_impl/src/print.rs index 70de55320f7..3f107eb7a61 100644 --- a/compiler/rustc_driver_impl/src/print.rs +++ b/compiler/rustc_driver_impl/src/print.rs @@ -14,7 +14,7 @@ macro_rules! safe_println { } pub(crate) fn print(args: fmt::Arguments<'_>) { - if let Err(_) = io::stdout().write_fmt(args) { + if io::stdout().write_fmt(args).is_err() { rustc_errors::FatalError.raise(); } } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 88f88f30a8c..3956125bace 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -942,9 +942,9 @@ impl SyntaxExtension { .unwrap_or_default(); let allow_internal_unsafe = find_attr!(attrs, AttributeKind::AllowInternalUnsafe(_)); - let local_inner_macros = ast::attr::find_by_name(attrs, sym::macro_export) - .and_then(|macro_export| macro_export.meta_item_list()) - .is_some_and(|l| ast::attr::list_contains_name(&l, sym::local_inner_macros)); + let local_inner_macros = + *find_attr!(attrs, AttributeKind::MacroExport {local_inner_macros: l, ..} => l) + .unwrap_or(&false); let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, !is_local); tracing::debug!(?name, ?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe); diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 946265eba8b..1d147a0385c 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -702,7 +702,7 @@ pub fn compile_declarative_macro( kinds |= MacroKinds::DERIVE; let derive_keyword_span = p.prev_token.span; if !features.macro_derive() { - feature_err(sess, sym::macro_attr, span, "`macro_rules!` derives are unstable") + feature_err(sess, sym::macro_derive, span, "`macro_rules!` derives are unstable") .emit(); } if let Some(guar) = check_no_eof(sess, &p, "expected `()` after `derive`") { diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 0784675b177..8ab43ff2582 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -551,6 +551,9 @@ pub enum AttributeKind { /// Represents `#[macro_escape]`. MacroEscape(Span), + /// Represents [`#[macro_export]`](https://doc.rust-lang.org/reference/macros-by-example.html#r-macro.decl.scope.path). + MacroExport { span: Span, local_inner_macros: bool }, + /// Represents `#[rustc_macro_transparency]`. MacroTransparency(Transparency), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 563e7a58c6d..8e443405074 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -56,6 +56,7 @@ impl AttributeKind { Linkage(..) => No, LoopMatch(..) => No, MacroEscape(..) => No, + MacroExport { .. } => Yes, MacroTransparency(..) => Yes, MacroUse { .. } => No, Marker(..) => No, diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index 0b24052b453..b7a0a6a0c19 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -31,9 +31,34 @@ pub struct AttributeLint<Id> { #[derive(Clone, Debug, HashStable_Generic)] pub enum AttributeLintKind { - UnusedDuplicate { this: Span, other: Span, warning: bool }, - IllFormedAttributeInput { suggestions: Vec<String> }, - EmptyAttribute { first_span: Span }, - InvalidTarget { name: AttrPath, target: Target, applied: Vec<String>, only: &'static str }, - InvalidStyle { name: AttrPath, is_used_as_inner: bool, target: Target, target_span: Span }, + UnusedDuplicate { + this: Span, + other: Span, + warning: bool, + }, + IllFormedAttributeInput { + suggestions: Vec<String>, + }, + EmptyAttribute { + first_span: Span, + }, + + /// Copy of `IllFormedAttributeInput` + /// specifically for the `invalid_macro_export_arguments` lint until that is removed, + /// see <https://github.com/rust-lang/rust/pull/143857#issuecomment-3079175663> + InvalidMacroExportArguments { + suggestions: Vec<String>, + }, + InvalidTarget { + name: AttrPath, + target: Target, + applied: Vec<String>, + only: &'static str, + }, + InvalidStyle { + name: AttrPath, + is_used_as_inner: bool, + target: Target, + target_span: Span, + }, } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index aa2d27ab809..6faa67f6a90 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -64,83 +64,151 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi // it's usually worth updating that intrinsic's documentation // to note that it's safe to call, since // safe extern fns are otherwise unprecedented. - sym::abort + + // tidy-alphabetical-start + | sym::abort + | sym::add_with_overflow + | sym::aggregate_raw_ptr + | sym::align_of | sym::assert_inhabited - | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid + | sym::assert_zero_valid + | sym::autodiff + | sym::bitreverse + | sym::black_box | sym::box_new | sym::breakpoint - | sym::size_of - | sym::align_of - | sym::needs_drop + | sym::bswap | sym::caller_location - | sym::add_with_overflow - | sym::sub_with_overflow - | sym::mul_with_overflow | sym::carrying_mul_add - | sym::wrapping_add - | sym::wrapping_sub - | sym::wrapping_mul - | sym::saturating_add - | sym::saturating_sub - | sym::rotate_left - | sym::rotate_right - | sym::ctpop + | sym::ceilf16 + | sym::ceilf32 + | sym::ceilf64 + | sym::ceilf128 + | sym::cold_path + | sym::const_eval_select + | sym::contract_check_ensures + | sym::contract_check_requires + | sym::contract_checks + | sym::cosf16 + | sym::cosf32 + | sym::cosf64 + | sym::cosf128 | sym::ctlz + | sym::ctpop | sym::cttz - | sym::bswap - | sym::bitreverse - | sym::three_way_compare | sym::discriminant_value - | sym::type_id - | sym::type_id_eq - | sym::select_unpredictable - | sym::cold_path - | sym::ptr_guaranteed_cmp - | sym::minnumf16 - | sym::minnumf32 - | sym::minnumf64 - | sym::minnumf128 - | sym::minimumf16 - | sym::minimumf32 - | sym::minimumf64 - | sym::minimumf128 - | sym::maxnumf16 - | sym::maxnumf32 - | sym::maxnumf64 - | sym::maxnumf128 + | sym::exp2f16 + | sym::exp2f32 + | sym::exp2f64 + | sym::exp2f128 + | sym::expf16 + | sym::expf32 + | sym::expf64 + | sym::expf128 + | sym::fadd_algebraic + | sym::fdiv_algebraic + | sym::floorf16 + | sym::floorf32 + | sym::floorf64 + | sym::floorf128 + | sym::fmaf16 + | sym::fmaf32 + | sym::fmaf64 + | sym::fmaf128 + | sym::fmul_algebraic + | sym::fmuladdf16 + | sym::fmuladdf32 + | sym::fmuladdf64 + | sym::fmuladdf128 + | sym::forget + | sym::frem_algebraic + | sym::fsub_algebraic + | sym::is_val_statically_known + | sym::log2f16 + | sym::log2f32 + | sym::log2f64 + | sym::log2f128 + | sym::log10f16 + | sym::log10f32 + | sym::log10f64 + | sym::log10f128 + | sym::logf16 + | sym::logf32 + | sym::logf64 + | sym::logf128 | sym::maximumf16 | sym::maximumf32 | sym::maximumf64 | sym::maximumf128 - | sym::rustc_peek - | sym::type_name - | sym::forget - | sym::black_box - | sym::variant_count - | sym::is_val_statically_known + | sym::maxnumf16 + | sym::maxnumf32 + | sym::maxnumf64 + | sym::maxnumf128 + | sym::minimumf16 + | sym::minimumf32 + | sym::minimumf64 + | sym::minimumf128 + | sym::minnumf16 + | sym::minnumf32 + | sym::minnumf64 + | sym::minnumf128 + | sym::mul_with_overflow + | sym::needs_drop + | sym::powf16 + | sym::powf32 + | sym::powf64 + | sym::powf128 + | sym::powif16 + | sym::powif32 + | sym::powif64 + | sym::powif128 + | sym::prefetch_read_data + | sym::prefetch_read_instruction + | sym::prefetch_write_data + | sym::prefetch_write_instruction + | sym::ptr_guaranteed_cmp | sym::ptr_mask - | sym::aggregate_raw_ptr | sym::ptr_metadata - | sym::ub_checks - | sym::contract_checks - | sym::contract_check_requires - | sym::contract_check_ensures - | sym::fadd_algebraic - | sym::fsub_algebraic - | sym::fmul_algebraic - | sym::fdiv_algebraic - | sym::frem_algebraic + | sym::rotate_left + | sym::rotate_right | sym::round_ties_even_f16 | sym::round_ties_even_f32 | sym::round_ties_even_f64 | sym::round_ties_even_f128 - | sym::autodiff - | sym::prefetch_read_data - | sym::prefetch_write_data - | sym::prefetch_read_instruction - | sym::prefetch_write_instruction - | sym::const_eval_select => hir::Safety::Safe, + | sym::roundf16 + | sym::roundf32 + | sym::roundf64 + | sym::roundf128 + | sym::rustc_peek + | sym::saturating_add + | sym::saturating_sub + | sym::select_unpredictable + | sym::sinf16 + | sym::sinf32 + | sym::sinf64 + | sym::sinf128 + | sym::size_of + | sym::sqrtf16 + | sym::sqrtf32 + | sym::sqrtf64 + | sym::sqrtf128 + | sym::sub_with_overflow + | sym::three_way_compare + | sym::truncf16 + | sym::truncf32 + | sym::truncf64 + | sym::truncf128 + | sym::type_id + | sym::type_id_eq + | sym::type_name + | sym::ub_checks + | sym::variant_count + | sym::wrapping_add + | sym::wrapping_mul + | sym::wrapping_sub + // tidy-alphabetical-end + => hir::Safety::Safe, _ => hir::Safety::Unsafe, }; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 5088c63702e..7867c1c3b48 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -85,6 +85,12 @@ pub(crate) fn validate_cmse_abi<'tcx>( return; }; + // An `extern "cmse-nonsecure-entry"` function cannot be c-variadic. We run + // into https://github.com/rust-lang/rust/issues/132142 if we don't explicitly bail. + if decl.c_variadic { + return; + } + match is_valid_cmse_inputs(tcx, fn_sig) { Ok(Ok(())) => {} Ok(Err(index)) => { diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 7f5397a7926..43a23822fd1 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -254,7 +254,7 @@ fn typeck_with_inspect<'tcx>( } fcx.select_obligations_where_possible(|_| {}); - if let None = fcx.infcx.tainted_by_errors() { + if fcx.infcx.tainted_by_errors().is_none() { fcx.report_ambiguity_errors(); } @@ -295,7 +295,7 @@ fn infer_type_if_missing<'tcx>(fcx: &FnCtxt<'_, 'tcx>, node: Node<'tcx>) -> Opti } else if let Node::AnonConst(_) = node { let id = tcx.local_def_id_to_hir_id(def_id); match tcx.parent_hir_node(id) { - Node::Ty(&hir::Ty { kind: hir::TyKind::Typeof(ref anon_const), span, .. }) + Node::Ty(&hir::Ty { kind: hir::TyKind::Typeof(anon_const), span, .. }) if anon_const.hir_id == id => { Some(fcx.next_ty_var(span)) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index a39ac0fcb6e..024b9ee08c2 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2194,7 +2194,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_associated_call_syntax( &self, err: &mut Diag<'_>, - static_candidates: &Vec<CandidateSource>, + static_candidates: &[CandidateSource], rcvr_ty: Ty<'tcx>, source: SelfSource<'tcx>, item_name: Ident, @@ -2422,7 +2422,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let span_included = match parent_expr.kind { hir::ExprKind::Struct(_, eps, _) => { - eps.len() > 0 && eps.last().is_some_and(|ep| ep.span.contains(span)) + eps.last().is_some_and(|ep| ep.span.contains(span)) } // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. hir::ExprKind::Call(func, ..) => func.span.contains(span), @@ -2484,7 +2484,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { simplify_type(tcx, ty, TreatParams::InstantiateWithInfer) .and_then(|simp| { tcx.incoherent_impls(simp) - .into_iter() + .iter() .find_map(|&id| self.associated_value(id, item_name)) }) .is_some() @@ -2617,7 +2617,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Node::Expr(call_expr) = self.tcx.parent_hir_node(seg1.hir_id) && let ControlFlow::Break(Some(expr)) = - (LetVisitor { ident_name: seg1.ident.name }).visit_body(&body) + (LetVisitor { ident_name: seg1.ident.name }).visit_body(body) && let Some(self_ty) = self.node_ty_opt(expr.hir_id) { let probe = self.lookup_probe_for_diagnostic( @@ -2960,14 +2960,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect::<Vec<_>>() .into(); for pred in &local_preds { - match pred.self_ty().kind() { - ty::Adt(def, _) => { - local_spans.push_span_label( - self.tcx.def_span(def.did()), - format!("must implement `{}`", pred.trait_ref.print_trait_sugared()), - ); - } - _ => {} + if let ty::Adt(def, _) = pred.self_ty().kind() { + local_spans.push_span_label( + self.tcx.def_span(def.did()), + format!("must implement `{}`", pred.trait_ref.print_trait_sugared()), + ); } } if local_spans.primary_span().is_some() { @@ -3006,14 +3003,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect::<Vec<_>>() .into(); for pred in &foreign_preds { - match pred.self_ty().kind() { - ty::Adt(def, _) => { - foreign_spans.push_span_label( - self.tcx.def_span(def.did()), - format!("not implement `{}`", pred.trait_ref.print_trait_sugared()), - ); - } - _ => {} + if let ty::Adt(def, _) = pred.self_ty().kind() { + foreign_spans.push_span_label( + self.tcx.def_span(def.did()), + format!("not implement `{}`", pred.trait_ref.print_trait_sugared()), + ); } } if foreign_spans.primary_span().is_some() { @@ -3595,7 +3589,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // would take care of them. && !skippable.contains(&Some(pick.item.container_id(self.tcx))) // Do not suggest pinning when the method is directly on `Pin`. - && pick.item.impl_container(self.tcx).map_or(true, |did| { + && pick.item.impl_container(self.tcx).is_none_or(|did| { match self.tcx.type_of(did).skip_binder().kind() { ty::Adt(def, _) => Some(def.did()) != self.tcx.lang_items().pin_type(), _ => true, @@ -3653,7 +3647,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { vec![ ( rcvr.span.shrink_to_lo(), - format!("let mut pinned = std::pin::pin!("), + "let mut pinned = std::pin::pin!(".to_string(), ), ( rcvr.span.shrink_to_hi(), @@ -4128,7 +4122,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let trait_span = self.tcx.def_span(trait_def_id); let mut multi_span: MultiSpan = trait_span.into(); - multi_span.push_span_label(trait_span, format!("this is the trait that is needed")); + multi_span.push_span_label(trait_span, "this is the trait that is needed".to_string()); let descr = self.tcx.associated_item(item_def_id).descr(); let rcvr_ty = rcvr_ty.map(|t| format!("`{t}`")).unwrap_or_else(|| "the receiver".to_string()); @@ -4146,7 +4140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } multi_span.push_span_label( self.tcx.def_span(def_id), - format!("this is the trait that was imported"), + "this is the trait that was imported".to_string(), ); } err.span_note(multi_span, msg); diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 05443537943..a8e8582c51c 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -566,7 +566,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_ty, lhs_expr, lhs_ty, - |lhs_ty, rhs_ty| is_compatible_after_call(lhs_ty, rhs_ty), + is_compatible_after_call, ) { // Cool } @@ -1170,8 +1170,8 @@ fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> { } } -/// Returns `true` if this is a built-in arithmetic operation (e.g., u32 -/// + u32, i16x4 == i16x4) and false if these types would have to be +/// Returns `true` if this is a built-in arithmetic operation (e.g., +/// u32 + u32, i16x4 == i16x4) and false if these types would have to be /// overloaded to be legal. There are two reasons that we distinguish /// builtin operations from overloaded ones (vs trying to drive /// everything uniformly through the trait system and intrinsics or @@ -1191,7 +1191,7 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, category: BinOpCategory) // (See https://github.com/rust-lang/rust/issues/57447.) let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs)); - match category.into() { + match category { BinOpCategory::Shortcircuit => true, BinOpCategory::Shift => { lhs.references_error() diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index f735c0a4160..46accb76a18 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -47,7 +47,7 @@ You can read more about trait objects in the Trait Objects section of the Refere https://doc.rust-lang.org/reference/types.html#trait-objects"; fn is_number(text: &str) -> bool { - text.chars().all(|c: char| c.is_digit(10)) + text.chars().all(|c: char| c.is_ascii_digit()) } /// Information about the expected type at the top level of type checking a pattern. @@ -262,8 +262,9 @@ enum InheritedRefMatchRule { /// pattern matches a given type: /// - If the underlying type is not a reference, a reference pattern may eat the inherited reference; /// - If the underlying type is a reference, a reference pattern matches if it can eat either one - /// of the underlying and inherited references. E.g. a `&mut` pattern is allowed if either the - /// underlying type is `&mut` or the inherited reference is `&mut`. + /// of the underlying and inherited references. E.g. a `&mut` pattern is allowed if either the + /// underlying type is `&mut` or the inherited reference is `&mut`. + /// /// If `false`, a reference pattern is only matched against the underlying type. /// This is `false` for stable Rust and `true` for both the `ref_pat_eat_one_layer_2024` and /// `ref_pat_eat_one_layer_2024_structural` feature gates. @@ -1069,7 +1070,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { if !self.tcx.features().mut_ref() { feature_err( - &self.tcx.sess, + self.tcx.sess, sym::mut_ref, pat.span.until(ident.span), "binding cannot be both mutable and by-reference", @@ -1487,31 +1488,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { opt_def_id: Option<hir::def_id::DefId>, ident: Ident, ) -> bool { - match opt_def_id { - Some(def_id) => match self.tcx.hir_get_if_local(def_id) { - Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Const(_, _, _, body_id), - .. - })) => match self.tcx.hir_node(body_id.hir_id) { - hir::Node::Expr(expr) => { - if hir::is_range_literal(expr) { - let span = self.tcx.hir_span(body_id.hir_id); - if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) { - e.span_suggestion_verbose( - ident.span, - "you may want to move the range into the match block", - snip, - Applicability::MachineApplicable, - ); - return true; - } - } - } - _ => (), - }, - _ => (), - }, - _ => (), + if let Some(def_id) = opt_def_id + && let Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Const(_, _, _, body_id), + .. + })) = self.tcx.hir_get_if_local(def_id) + && let hir::Node::Expr(expr) = self.tcx.hir_node(body_id.hir_id) + && hir::is_range_literal(expr) + { + let span = self.tcx.hir_span(body_id.hir_id); + if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) { + e.span_suggestion_verbose( + ident.span, + "you may want to move the range into the match block", + snip, + Applicability::MachineApplicable, + ); + return true; + } } false } @@ -1529,7 +1523,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(span) = self.tcx.hir_res_span(pat_res) { e.span_label(span, format!("{} defined here", res.descr())); - if let [hir::PathSegment { ident, .. }] = &*segments { + if let [hir::PathSegment { ident, .. }] = segments { e.span_label( pat_span, format!( @@ -1557,17 +1551,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => (None, None), }; - let is_range = match type_def_id.and_then(|id| self.tcx.as_lang_item(id)) { + let is_range = matches!( + type_def_id.and_then(|id| self.tcx.as_lang_item(id)), Some( LangItem::Range - | LangItem::RangeFrom - | LangItem::RangeTo - | LangItem::RangeFull - | LangItem::RangeInclusiveStruct - | LangItem::RangeToInclusive, - ) => true, - _ => false, - }; + | LangItem::RangeFrom + | LangItem::RangeTo + | LangItem::RangeFull + | LangItem::RangeInclusiveStruct + | LangItem::RangeToInclusive, + ) + ); if is_range { if !self.maybe_suggest_range_literal(&mut e, item_def_id, *ident) { let msg = "constants only support matching by type, \ diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 06acff06a51..0a7b458f3fb 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -421,7 +421,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Not all upvars are captured by ref, so use // `apply_capture_kind_on_capture_ty` to ensure that we // compute the right captured type. - return apply_capture_kind_on_capture_ty( + apply_capture_kind_on_capture_ty( self.tcx, upvar_ty, capture, @@ -430,7 +430,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { self.tcx.lifetimes.re_erased }, - ); + ) }, ), ); @@ -529,11 +529,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // process any deferred resolutions. let deferred_call_resolutions = self.remove_deferred_call_resolutions(closure_def_id); for deferred_call_resolution in deferred_call_resolutions { - deferred_call_resolution.resolve(&mut FnCtxt::new( - self, - self.param_env, - closure_def_id, - )); + deferred_call_resolution.resolve(&FnCtxt::new(self, self.param_env, closure_def_id)); } } @@ -1493,7 +1489,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Notation: /// - Ty(place): Type of place /// - `(a, b)`: Represents the function parameters `base_path_ty` and `captured_by_move_projs` - /// respectively. + /// respectively. /// ```ignore (illustrative) /// (Ty(w), [ &[p, x], &[c] ]) /// // | @@ -2179,9 +2175,10 @@ fn restrict_precision_for_unsafe( (place, curr_mode) } -/// Truncate projections so that following rules are obeyed by the captured `place`: +/// Truncate projections so that the following rules are obeyed by the captured `place`: /// - No Index projections are captured, since arrays are captured completely. -/// - No unsafe block is required to capture `place` +/// - No unsafe block is required to capture `place`. +/// /// Returns the truncated place and updated capture mode. fn restrict_capture_precision( place: Place<'_>, diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 90301d1b391..6192420898f 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -220,7 +220,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // to use builtin indexing because the index type is known to be // usize-ish fn fix_index_builtin_expr(&mut self, e: &hir::Expr<'_>) { - if let hir::ExprKind::Index(ref base, ref index, _) = e.kind { + if let hir::ExprKind::Index(base, index, _) = e.kind { // All valid indexing looks like this; might encounter non-valid indexes at this point. let base_ty = self.typeck_results.expr_ty_adjusted(base); if let ty::Ref(_, base_ty_inner, _) = *base_ty.kind() { @@ -583,7 +583,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } if let Err(err) = opaque_type_has_defining_use_args( - &self.fcx, + self.fcx, opaque_type_key, hidden_type.span, DefiningScopeKind::HirTypeck, @@ -792,7 +792,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn visit_potentially_region_dependent_goals(&mut self) { let obligations = self.fcx.take_hir_typeck_potentially_region_dependent_goals(); - if let None = self.fcx.tainted_by_errors() { + if self.fcx.tainted_by_errors().is_none() { for obligation in obligations { let (predicate, mut cause) = self.fcx.resolve_vars_if_possible((obligation.predicate, obligation.cause)); diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 684bd34f909..f7649c5f5c5 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -645,7 +645,7 @@ impl<T: Idx> ChunkedBitSet<T> { }; #[cfg(not(feature = "nightly"))] let mut words = { - // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#63291). + // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#129396). let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed(); // SAFETY: `words` can safely be all zeroes. let words = unsafe { words.assume_init() }; @@ -708,7 +708,7 @@ impl<T: Idx> ChunkedBitSet<T> { }; #[cfg(not(feature = "nightly"))] let mut words = { - // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#63291). + // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#129396). let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed(); // SAFETY: `words` can safely be all zeroes. let words = unsafe { words.assume_init() }; diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index cc680838e7e..9d647209c6f 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -1,9 +1,9 @@ // tidy-alphabetical-start #![cfg_attr(all(feature = "nightly", test), feature(stmt_expr_attributes))] +#![cfg_attr(bootstrap, feature(new_zeroed_alloc))] #![cfg_attr(feature = "nightly", allow(internal_features))] #![cfg_attr(feature = "nightly", feature(extend_one, step_trait, test))] #![cfg_attr(feature = "nightly", feature(new_range_api))] -#![cfg_attr(feature = "nightly", feature(new_zeroed_alloc))] // tidy-alphabetical-end pub mod bit_set; diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl index 6f6666f8c76..1f5c5e74d97 100644 --- a/compiler/rustc_interface/messages.ftl +++ b/compiler/rustc_interface/messages.ftl @@ -47,7 +47,7 @@ interface_out_dir_error = failed to find or create the directory specified by `--out-dir` interface_proc_macro_crate_panic_abort = - building proc macro crate with `panic=abort` may crash the compiler should the proc-macro panic + building proc macro crate with `panic=abort` or `panic=immediate-abort` may crash the compiler should the proc-macro panic interface_temps_dir_error = failed to find or create the directory specified by `--temps-dir` diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index d39219bfd66..761a5c80918 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -42,7 +42,6 @@ use rustc_span::{ DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym, }; -use rustc_target::spec::PanicStrategy; use rustc_trait_selection::{solve, traits}; use tracing::{info, instrument}; @@ -192,7 +191,7 @@ fn configure_and_expand( unsafe { env::set_var( "PATH", - &env::join_paths( + env::join_paths( new_path.iter().filter(|p| env::join_paths(iter::once(p)).is_ok()), ) .unwrap(), @@ -282,7 +281,7 @@ fn configure_and_expand( feature_err(sess, sym::export_stable, DUMMY_SP, "`sdylib` crate type is unstable").emit(); } - if is_proc_macro_crate && sess.panic_strategy() == PanicStrategy::Abort { + if is_proc_macro_crate && !sess.panic_strategy().unwinds() { sess.dcx().emit_warn(errors::ProcMacroCratePanicAbort); } @@ -446,7 +445,7 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) { let prev_source = sess.psess.source_map().span_to_prev_source(first_span); let ferris_fix = prev_source .map_or(FerrisFix::SnakeCase, |source| { - let mut source_before_ferris = source.trim_end().split_whitespace().rev(); + let mut source_before_ferris = source.split_whitespace().rev(); match source_before_ferris.next() { Some("struct" | "trait" | "mod" | "union" | "type" | "enum") => { FerrisFix::PascalCase @@ -500,7 +499,7 @@ fn env_var_os<'tcx>(tcx: TyCtxt<'tcx>, key: &'tcx OsStr) -> Option<&'tcx OsStr> // properly change-tracked. tcx.sess.psess.env_depinfo.borrow_mut().insert(( Symbol::intern(&key.to_string_lossy()), - value.as_ref().and_then(|value| value.to_str()).map(|value| Symbol::intern(&value)), + value.as_ref().and_then(|value| value.to_str()).map(|value| Symbol::intern(value)), )); value_tcx @@ -824,7 +823,7 @@ pub fn write_dep_info(tcx: TyCtxt<'_>) { let outputs = tcx.output_filenames(()); let output_paths = - generated_output_paths(tcx, &outputs, sess.io.output_file.is_some(), crate_name); + generated_output_paths(tcx, outputs, sess.io.output_file.is_some(), crate_name); // Ensure the source file isn't accidentally overwritten during compilation. if let Some(input_path) = sess.io.input.opt_path() { @@ -847,7 +846,7 @@ pub fn write_dep_info(tcx: TyCtxt<'_>) { } } - write_out_deps(tcx, &outputs, &output_paths); + write_out_deps(tcx, outputs, &output_paths); let only_dep_info = sess.opts.output_types.contains_key(&OutputType::DepInfo) && sess.opts.output_types.len() == 1; @@ -1303,7 +1302,7 @@ pub(crate) fn parse_crate_name( let rustc_hir::Attribute::Parsed(AttributeKind::CrateName { name, name_span, .. }) = AttributeParser::parse_limited_should_emit( sess, - &attrs, + attrs, sym::crate_name, DUMMY_SP, rustc_ast::node_id::CRATE_NODE_ID, diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 370e886c525..280214ab418 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -94,7 +94,7 @@ impl Linker { &rlink_file, &codegen_results, &self.metadata, - &*self.output_filenames, + &self.output_filenames, ) .unwrap_or_else(|error| { sess.dcx().emit_fatal(FailedWritingFile { path: &rlink_file, error }) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 061c764e619..26e09c95e76 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -243,7 +243,7 @@ pub(crate) fn run_in_thread_pool_with_globals< let query_map = rustc_span::set_session_globals_then(unsafe { &*(session_globals as *const SessionGlobals) }, || { // Ensure there was no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. - QueryCtxt::new(tcx).collect_active_jobs().ok().expect("failed to collect active queries in deadlock handler") + QueryCtxt::new(tcx).collect_active_jobs().expect("failed to collect active queries in deadlock handler") }); break_query_cycles(query_map, ®istry); }) @@ -561,7 +561,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu } Some(out_file.clone()) }; - if sess.io.output_dir != None { + if sess.io.output_dir.is_some() { sess.dcx().emit_warn(errors::IgnoringOutDir); } diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index dca22b986ff..b10be22dc38 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -1,11 +1,12 @@ use rustc_errors::MultiSpan; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{self, Visitor, VisitorExt}; -use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind}; +use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind, find_attr}; use rustc_middle::ty::TyCtxt; use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::def_id::{DefId, LOCAL_CRATE}; -use rustc_span::{ExpnKind, Span, kw, sym}; +use rustc_span::{ExpnKind, Span, kw}; use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag}; use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; @@ -241,7 +242,10 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { ) } ItemKind::Macro(_, _macro, _kinds) - if cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) => + if find_attr!( + cx.tcx.get_all_attrs(item.owner_id.def_id), + AttributeKind::MacroExport { .. } + ) => { cx.emit_span_lint( NON_LOCAL_DEFINITIONS, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index b79075ac09b..3d0974d5d28 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4191,8 +4191,13 @@ declare_lint! { /// You can't have multiple arguments in a `#[macro_export(..)]`, or mention arguments other than `local_inner_macros`. /// pub INVALID_MACRO_EXPORT_ARGUMENTS, - Warn, + Deny, "\"invalid_parameter\" isn't a valid argument for `#[macro_export]`", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseError, + reference: "issue #57571 <https://github.com/rust-lang/rust/issues/57571>", + report_in_deps: true, + }; } declare_lint! { diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml index 0dfd1b13df5..ad93c745381 100644 --- a/compiler/rustc_llvm/Cargo.toml +++ b/compiler/rustc_llvm/Cargo.toml @@ -10,8 +10,7 @@ libc = "0.2.73" [build-dependencies] # tidy-alphabetical-start -# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version -# per crate", so if you change this, you need to also change it in `rustc_codegen_ssa` and `rustc_windows_rc`. +# `cc` updates often break things, so we pin it here. cc = "=1.2.16" # tidy-alphabetical-end diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index ab5d5c03e81..7518b40799b 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -271,8 +271,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( bool TrapUnreachable, bool Singlethread, bool VerboseAsm, bool EmitStackSizeSection, bool RelaxELFRelocations, bool UseInitArray, const char *SplitDwarfFile, const char *OutputObjFile, - const char *DebugInfoCompression, bool UseEmulatedTls, - const char *ArgsCstrBuff, size_t ArgsCstrBuffLen, bool UseWasmEH) { + const char *DebugInfoCompression, bool UseEmulatedTls, const char *Argv0, + size_t Argv0Len, const char *CommandLineArgs, size_t CommandLineArgsLen, + bool UseWasmEH) { auto OptLevel = fromRust(RustOptLevel); auto RM = fromRust(RustReloc); @@ -343,25 +344,10 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.EmitStackSizeSection = EmitStackSizeSection; - if (ArgsCstrBuff != nullptr) { - size_t buffer_offset = 0; - assert(ArgsCstrBuff[ArgsCstrBuffLen - 1] == '\0'); - auto Arg0 = std::string(ArgsCstrBuff); - buffer_offset = Arg0.size() + 1; - - std::string CommandlineArgs; - raw_string_ostream OS(CommandlineArgs); - ListSeparator LS(" "); - for (StringRef Arg : split(StringRef(ArgsCstrBuff + buffer_offset, - ArgsCstrBuffLen - buffer_offset), - '\0')) { - OS << LS; - sys::printArg(OS, Arg, /*Quote=*/true); - } - OS.flush(); - Options.MCOptions.Argv0 = Arg0; - Options.MCOptions.CommandlineArgs = CommandlineArgs; - } + if (Argv0 != nullptr) + Options.MCOptions.Argv0 = {Argv0, Argv0Len}; + if (CommandLineArgs != nullptr) + Options.MCOptions.CommandlineArgs = {CommandLineArgs, CommandLineArgsLen}; #if LLVM_VERSION_GE(21, 0) TargetMachine *TM = TheTarget->createTargetMachine(Trip, CPU, Feature, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 64151962321..414274f24fb 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -553,6 +553,7 @@ enum class LLVMRustMemoryEffects { None, ReadOnly, InaccessibleMemOnly, + ReadOnlyNotPure, }; extern "C" LLVMAttributeRef @@ -568,6 +569,10 @@ LLVMRustCreateMemoryEffectsAttr(LLVMContextRef C, case LLVMRustMemoryEffects::InaccessibleMemOnly: return wrap(Attribute::getWithMemoryEffects( *unwrap(C), MemoryEffects::inaccessibleMemOnly())); + case LLVMRustMemoryEffects::ReadOnlyNotPure: + return wrap(Attribute::getWithMemoryEffects( + *unwrap(C), + MemoryEffects::readOnly() | MemoryEffects::inaccessibleMemOnly())); default: report_fatal_error("bad MemoryEffects."); } diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index e104be2c466..e624bfc5b8b 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -98,6 +98,13 @@ metadata_full_metadata_not_found = metadata_global_alloc_required = no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait +metadata_incompatible_with_immediate_abort = + the crate `{$crate_name}` was compiled with a panic strategy which is incompatible with `immediate-abort` + +metadata_incompatible_with_immediate_abort_core = + the crate `core` was compiled with a panic strategy which is incompatible with `immediate-abort` + .help = consider building the standard library from source with `cargo build -Zbuild-std` + metadata_incompatible_panic_in_drop_strategy = the crate `{$crate_name}` is compiled with the panic-in-drop strategy `{$found_strategy}` which is incompatible with this crate's strategy of `{$desired_strategy}` diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 9e23da88f5e..7650acbd292 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -1027,6 +1027,10 @@ impl CStore { let name = match desired_strategy { PanicStrategy::Unwind => sym::panic_unwind, PanicStrategy::Abort => sym::panic_abort, + PanicStrategy::ImmediateAbort => { + // Immediate-aborting panics don't use a runtime. + return; + } }; info!("panic runtime not found -- loading {}", name); diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index fb9c2e23b71..8054a48d37a 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -61,11 +61,13 @@ use rustc_session::config::CrateType; use rustc_session::cstore::CrateDepKind; use rustc_session::cstore::LinkagePreference::{self, RequireDynamic, RequireStatic}; use rustc_span::sym; +use rustc_target::spec::PanicStrategy; use tracing::info; use crate::creader::CStore; use crate::errors::{ - BadPanicStrategy, CrateDepMultiple, IncompatiblePanicInDropStrategy, LibRequired, + BadPanicStrategy, CrateDepMultiple, IncompatiblePanicInDropStrategy, + IncompatibleWithImmediateAbort, IncompatibleWithImmediateAbortCore, LibRequired, NonStaticCrateDep, RequiredPanicStrategy, RlibRequired, RustcDriverHelp, RustcLibRequired, TwoPanicRuntimes, }; @@ -402,15 +404,43 @@ fn activate_injected_dep( /// there's only going to be one panic runtime in the output. fn verify_ok(tcx: TyCtxt<'_>, list: &DependencyList) { let sess = &tcx.sess; + let list: Vec<_> = list + .iter_enumerated() + .filter_map( + |(cnum, linkage)| if *linkage == Linkage::NotLinked { None } else { Some(cnum) }, + ) + .collect(); if list.is_empty() { return; } - let mut panic_runtime = None; - for (cnum, linkage) in list.iter_enumerated() { - if let Linkage::NotLinked = *linkage { - continue; + let desired_strategy = sess.panic_strategy(); + + // If we are panic=immediate-abort, make sure everything in the dependency tree has also been + // compiled with immediate-abort. + if list + .iter() + .any(|cnum| tcx.required_panic_strategy(*cnum) == Some(PanicStrategy::ImmediateAbort)) + { + let mut invalid_crates = Vec::new(); + for cnum in list.iter().copied() { + if tcx.required_panic_strategy(cnum) != Some(PanicStrategy::ImmediateAbort) { + invalid_crates.push(cnum); + // If core is incompatible, it's very likely that we'd emit an error for every + // sysroot crate, so instead of doing that emit a single fatal error that suggests + // using build-std. + if tcx.crate_name(cnum) == sym::core { + sess.dcx().emit_fatal(IncompatibleWithImmediateAbortCore); + } + } } + for cnum in invalid_crates { + sess.dcx() + .emit_err(IncompatibleWithImmediateAbort { crate_name: tcx.crate_name(cnum) }); + } + } + let mut panic_runtime = None; + for cnum in list.iter().copied() { if tcx.is_panic_runtime(cnum) { if let Some((prev, _)) = panic_runtime { let prev_name = tcx.crate_name(prev); @@ -430,8 +460,6 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &DependencyList) { // only one, but we perform validation here that all the panic strategy // compilation modes for the whole DAG are valid. if let Some((runtime_cnum, found_strategy)) = panic_runtime { - let desired_strategy = sess.panic_strategy(); - // First up, validate that our selected panic runtime is indeed exactly // our same strategy. if found_strategy != desired_strategy { @@ -445,10 +473,7 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &DependencyList) { // strategy. If the dep isn't linked, we ignore it, and if our strategy // is abort then it's compatible with everything. Otherwise all crates' // panic strategy must match our own. - for (cnum, linkage) in list.iter_enumerated() { - if let Linkage::NotLinked = *linkage { - continue; - } + for cnum in list.iter().copied() { if cnum == runtime_cnum || tcx.is_compiler_builtins(cnum) { continue; } diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index e5a4fd48353..abfd078f746 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -76,6 +76,16 @@ pub struct RequiredPanicStrategy { } #[derive(Diagnostic)] +#[diag(metadata_incompatible_with_immediate_abort)] +pub struct IncompatibleWithImmediateAbort { + pub crate_name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(metadata_incompatible_with_immediate_abort_core)] +pub struct IncompatibleWithImmediateAbortCore; + +#[derive(Diagnostic)] #[diag(metadata_incompatible_panic_in_drop_strategy)] pub struct IncompatiblePanicInDropStrategy { pub crate_name: Symbol, diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs index 93264f02cc2..a0e4c288c4a 100644 --- a/compiler/rustc_middle/src/middle/lang_items.rs +++ b/compiler/rustc_middle/src/middle/lang_items.rs @@ -98,5 +98,6 @@ pub fn required(tcx: TyCtxt<'_>, lang_item: LangItem) -> bool { lang_item != LangItem::EhPersonality && lang_item != LangItem::EhCatchTypeinfo } PanicStrategy::Unwind => true, + PanicStrategy::ImmediateAbort => false, } } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index c477e65f5d6..47b45c58b9f 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -16,7 +16,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable, extension}; use rustc_session::config::OptLevel; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use rustc_target::callconv::FnAbi; -use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, PanicStrategy, Target, X86Abi}; +use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, X86Abi}; use tracing::debug; use {rustc_abi as abi, rustc_hir as hir}; @@ -1198,7 +1198,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: ExternAbi) // // Note that this is true regardless ABI specified on the function -- a `extern "C-unwind"` // function defined in Rust is also required to abort. - if tcx.sess.panic_strategy() == PanicStrategy::Abort && !tcx.is_foreign_item(did) { + if !tcx.sess.panic_strategy().unwinds() && !tcx.is_foreign_item(did) { return false; } @@ -1206,7 +1206,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: ExternAbi) // // This is not part of `codegen_fn_attrs` as it can differ between crates // and therefore cannot be computed in core. - if tcx.sess.opts.unstable_opts.panic_in_drop == PanicStrategy::Abort + if !tcx.sess.opts.unstable_opts.panic_in_drop.unwinds() && tcx.is_lang_item(did, LangItem::DropInPlace) { return false; @@ -1245,7 +1245,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: ExternAbi) | RiscvInterruptS | RustInvalid | Unadjusted => false, - Rust | RustCall | RustCold => tcx.sess.panic_strategy() == PanicStrategy::Unwind, + Rust | RustCall | RustCold => tcx.sess.panic_strategy().unwinds(), } } diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 08c43a4648c..511c1960e40 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start either = "1" +hashbrown = "0.15" itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index c1cd2788348..c5cd06f170c 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -86,7 +86,6 @@ use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::source_map::dummy_spanned; use rustc_span::symbol::sym; use rustc_span::{DUMMY_SP, Span}; -use rustc_target::spec::PanicStrategy; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::TyCtxtInferExt as _; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; @@ -1149,7 +1148,7 @@ fn can_return<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, typing_env: ty::Typing fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { // Nothing can unwind when landing pads are off. - if tcx.sess.panic_strategy() == PanicStrategy::Abort { + if !tcx.sess.panic_strategy().unwinds() { return false; } diff --git a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs index abbff1c48dd..7c66783548e 100644 --- a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs +++ b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs @@ -101,12 +101,15 @@ fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool { } fn required_panic_strategy(tcx: TyCtxt<'_>, _: LocalCrate) -> Option<PanicStrategy> { + let local_strategy = tcx.sess.panic_strategy(); + if tcx.is_panic_runtime(LOCAL_CRATE) { - return Some(tcx.sess.panic_strategy()); + return Some(local_strategy); } - if tcx.sess.panic_strategy() == PanicStrategy::Abort { - return Some(PanicStrategy::Abort); + match local_strategy { + PanicStrategy::Abort | PanicStrategy::ImmediateAbort => return Some(local_strategy), + _ => {} } for def_id in tcx.hir_body_owners() { diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 30e68a3f650..ebec3d12500 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -85,8 +85,10 @@ //! that contain `AllocId`s. use std::borrow::Cow; +use std::hash::{Hash, Hasher}; use either::Either; +use hashbrown::hash_table::{Entry, HashTable}; use itertools::Itertools as _; use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx}; use rustc_const_eval::const_eval::DummyMachine; @@ -94,7 +96,7 @@ use rustc_const_eval::interpret::{ ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar, intern_const_alloc_for_constprop, }; -use rustc_data_structures::fx::{FxIndexSet, MutableValues}; +use rustc_data_structures::fx::FxHasher; use rustc_data_structures::graph::dominators::Dominators; use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; @@ -152,9 +154,29 @@ impl<'tcx> crate::MirPass<'tcx> for GVN { } newtype_index! { + /// This represents a `Value` in the symbolic execution. + #[debug_format = "_v{}"] struct VnIndex {} } +/// Marker type to forbid hashing and comparing opaque values. +/// This struct should only be constructed by `ValueSet::insert_unique` to ensure we use that +/// method to create non-unifiable values. It will ICE if used in `ValueSet::insert`. +#[derive(Copy, Clone, Debug, Eq)] +struct VnOpaque; +impl PartialEq for VnOpaque { + fn eq(&self, _: &VnOpaque) -> bool { + // ICE if we try to compare unique values + unreachable!() + } +} +impl Hash for VnOpaque { + fn hash<T: Hasher>(&self, _: &mut T) { + // ICE if we try to hash unique values + unreachable!() + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] enum AddressKind { Ref(BorrowKind), @@ -166,15 +188,17 @@ enum Value<'tcx> { // Root values. /// Used to represent values we know nothing about. /// The `usize` is a counter incremented by `new_opaque`. - Opaque(usize), + Opaque(VnOpaque), /// Evaluated or unevaluated constant value. Constant { value: Const<'tcx>, /// Some constants do not have a deterministic value. To avoid merging two instances of the /// same `Const`, we assign them an additional integer index. - // `disambiguator` is 0 iff the constant is deterministic. - disambiguator: usize, + // `disambiguator` is `None` iff the constant is deterministic. + disambiguator: Option<VnOpaque>, }, + + // Aggregates. /// An aggregate value, either tuple/closure/struct/enum. /// This does not contain unions, as we cannot reason with the value. Aggregate(VariantIdx, Vec<VnIndex>), @@ -192,7 +216,7 @@ enum Value<'tcx> { place: Place<'tcx>, kind: AddressKind, /// Give each borrow and pointer a different provenance, so we don't merge them. - provenance: usize, + provenance: VnOpaque, }, // Extractions. @@ -211,6 +235,107 @@ enum Value<'tcx> { }, } +/// Stores and deduplicates pairs of `(Value, Ty)` into in `VnIndex` numbered values. +/// +/// This data structure is mostly a partial reimplementation of `FxIndexMap<VnIndex, (Value, Ty)>`. +/// We do not use a regular `FxIndexMap` to skip hashing values that are unique by construction, +/// like opaque values, address with provenance and non-deterministic constants. +struct ValueSet<'tcx> { + indices: HashTable<VnIndex>, + hashes: IndexVec<VnIndex, u64>, + values: IndexVec<VnIndex, Value<'tcx>>, + types: IndexVec<VnIndex, Ty<'tcx>>, +} + +impl<'tcx> ValueSet<'tcx> { + fn new(num_values: usize) -> ValueSet<'tcx> { + ValueSet { + indices: HashTable::with_capacity(num_values), + hashes: IndexVec::with_capacity(num_values), + values: IndexVec::with_capacity(num_values), + types: IndexVec::with_capacity(num_values), + } + } + + /// Insert a `(Value, Ty)` pair without hashing or deduplication. + /// This always creates a new `VnIndex`. + #[inline] + fn insert_unique( + &mut self, + ty: Ty<'tcx>, + value: impl FnOnce(VnOpaque) -> Value<'tcx>, + ) -> VnIndex { + let value = value(VnOpaque); + + debug_assert!(match value { + Value::Opaque(_) | Value::Address { .. } => true, + Value::Constant { disambiguator, .. } => disambiguator.is_some(), + _ => false, + }); + + let index = self.hashes.push(0); + let _index = self.types.push(ty); + debug_assert_eq!(index, _index); + let _index = self.values.push(value); + debug_assert_eq!(index, _index); + index + } + + /// Insert a `(Value, Ty)` pair to be deduplicated. + /// Returns `true` as second tuple field if this value did not exist previously. + #[allow(rustc::pass_by_value)] // closures take `&VnIndex` + fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> (VnIndex, bool) { + debug_assert!(match value { + Value::Opaque(_) | Value::Address { .. } => false, + Value::Constant { disambiguator, .. } => disambiguator.is_none(), + _ => true, + }); + + let hash: u64 = { + let mut h = FxHasher::default(); + value.hash(&mut h); + ty.hash(&mut h); + h.finish() + }; + + let eq = |index: &VnIndex| self.values[*index] == value && self.types[*index] == ty; + let hasher = |index: &VnIndex| self.hashes[*index]; + match self.indices.entry(hash, eq, hasher) { + Entry::Occupied(entry) => { + let index = *entry.get(); + (index, false) + } + Entry::Vacant(entry) => { + let index = self.hashes.push(hash); + entry.insert(index); + let _index = self.values.push(value); + debug_assert_eq!(index, _index); + let _index = self.types.push(ty); + debug_assert_eq!(index, _index); + (index, true) + } + } + } + + /// Return the `Value` associated with the given `VnIndex`. + #[inline] + fn value(&self, index: VnIndex) -> &Value<'tcx> { + &self.values[index] + } + + /// Return the type associated with the given `VnIndex`. + #[inline] + fn ty(&self, index: VnIndex) -> Ty<'tcx> { + self.types[index] + } + + /// Replace the value associated with `index` with an opaque value. + #[inline] + fn forget(&mut self, index: VnIndex) { + self.values[index] = Value::Opaque(VnOpaque); + } +} + struct VnState<'body, 'tcx> { tcx: TyCtxt<'tcx>, ecx: InterpCx<'tcx, DummyMachine>, @@ -221,11 +346,9 @@ struct VnState<'body, 'tcx> { /// Locals that are assigned that value. // This vector does not hold all the values of `VnIndex` that we create. rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>, - values: FxIndexSet<(Value<'tcx>, Ty<'tcx>)>, + values: ValueSet<'tcx>, /// Values evaluated as constants if possible. evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>, - /// Counter to generate different values. - next_opaque: usize, /// Cache the deref values. derefs: Vec<VnIndex>, ssa: &'body SsaLocals, @@ -256,9 +379,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { is_coroutine: body.coroutine.is_some(), locals: IndexVec::from_elem(None, local_decls), rev_locals: IndexVec::with_capacity(num_values), - values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()), + values: ValueSet::new(num_values), evaluated: IndexVec::with_capacity(num_values), - next_opaque: 1, derefs: Vec::new(), ssa, dominators, @@ -272,8 +394,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { #[instrument(level = "trace", skip(self), ret)] fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> VnIndex { - let (index, new) = self.values.insert_full((value, ty)); - let index = VnIndex::from_usize(index); + let (index, new) = self.values.insert(ty, value); if new { // Grow `evaluated` and `rev_locals` here to amortize the allocations. let evaluated = self.eval_to_const(index); @@ -285,18 +406,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { index } - fn next_opaque(&mut self) -> usize { - let next_opaque = self.next_opaque; - self.next_opaque += 1; - next_opaque - } - /// Create a new `Value` for which we have no information at all, except that it is distinct /// from all the others. #[instrument(level = "trace", skip(self), ret)] fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex { - let value = Value::Opaque(self.next_opaque()); - self.insert(ty, value) + let index = self.values.insert_unique(ty, Value::Opaque); + let _index = self.evaluated.push(None); + debug_assert_eq!(index, _index); + let _index = self.rev_locals.push(SmallVec::new()); + debug_assert_eq!(index, _index); + index } /// Create a new `Value::Address` distinct from all the others. @@ -309,18 +428,49 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()), }; - let value = Value::Address { place, kind, provenance: self.next_opaque() }; - self.insert(ty, value) + let index = + self.values.insert_unique(ty, |provenance| Value::Address { place, kind, provenance }); + let evaluated = self.eval_to_const(index); + let _index = self.evaluated.push(evaluated); + debug_assert_eq!(index, _index); + let _index = self.rev_locals.push(SmallVec::new()); + debug_assert_eq!(index, _index); + index + } + + #[instrument(level = "trace", skip(self), ret)] + fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex { + let (index, new) = if value.is_deterministic() { + // The constant is deterministic, no need to disambiguate. + let constant = Value::Constant { value, disambiguator: None }; + self.values.insert(value.ty(), constant) + } else { + // Multiple mentions of this constant will yield different values, + // so assign a different `disambiguator` to ensure they do not get the same `VnIndex`. + let index = self.values.insert_unique(value.ty(), |disambiguator| Value::Constant { + value, + disambiguator: Some(disambiguator), + }); + (index, true) + }; + if new { + let evaluated = self.eval_to_const(index); + let _index = self.evaluated.push(evaluated); + debug_assert_eq!(index, _index); + let _index = self.rev_locals.push(SmallVec::new()); + debug_assert_eq!(index, _index); + } + index } #[inline] fn get(&self, index: VnIndex) -> &Value<'tcx> { - &self.values.get_index(index.as_usize()).unwrap().0 + self.values.value(index) } #[inline] fn ty(&self, index: VnIndex) -> Ty<'tcx> { - self.values.get_index(index.as_usize()).unwrap().1 + self.values.ty(index) } /// Record that `local` is assigned `value`. `local` must be SSA. @@ -331,33 +481,18 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { self.rev_locals[value].push(local); } - fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex { - let disambiguator = if value.is_deterministic() { - // The constant is deterministic, no need to disambiguate. - 0 - } else { - // Multiple mentions of this constant will yield different values, - // so assign a different `disambiguator` to ensure they do not get the same `VnIndex`. - let disambiguator = self.next_opaque(); - // `disambiguator: 0` means deterministic. - debug_assert_ne!(disambiguator, 0); - disambiguator - }; - self.insert(value.ty(), Value::Constant { value, disambiguator }) - } - fn insert_bool(&mut self, flag: bool) -> VnIndex { // Booleans are deterministic. let value = Const::from_bool(self.tcx, flag); debug_assert!(value.is_deterministic()); - self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: 0 }) + self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: None }) } fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex { // Scalars are deterministic. let value = Const::from_scalar(self.tcx, scalar, ty); debug_assert!(value.is_deterministic()); - self.insert(ty, Value::Constant { value, disambiguator: 0 }) + self.insert(ty, Value::Constant { value, disambiguator: None }) } fn insert_tuple(&mut self, ty: Ty<'tcx>, values: Vec<VnIndex>) -> VnIndex { @@ -372,8 +507,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn invalidate_derefs(&mut self) { for deref in std::mem::take(&mut self.derefs) { - let opaque = self.next_opaque(); - self.values.get_index_mut2(deref.index()).unwrap().0 = Value::Opaque(opaque); + self.values.forget(deref); } } @@ -1572,7 +1706,7 @@ impl<'tcx> VnState<'_, 'tcx> { // This was already constant in MIR, do not change it. If the constant is not // deterministic, adding an additional mention of it in MIR will not give the same value as // the former mention. - if let Value::Constant { value, disambiguator: 0 } = *self.get(index) { + if let Value::Constant { value, disambiguator: None } = *self.get(index) { debug_assert!(value.is_deterministic()); return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); } diff --git a/compiler/rustc_mir_transform/src/impossible_predicates.rs b/compiler/rustc_mir_transform/src/impossible_predicates.rs index b03518de00a..883ee32bdec 100644 --- a/compiler/rustc_mir_transform/src/impossible_predicates.rs +++ b/compiler/rustc_mir_transform/src/impossible_predicates.rs @@ -28,6 +28,7 @@ use rustc_middle::mir::{Body, START_BLOCK, TerminatorKind}; use rustc_middle::ty::{TyCtxt, TypeFlags, TypeVisitableExt}; +use rustc_span::def_id::DefId; use rustc_trait_selection::traits; use tracing::trace; @@ -35,23 +36,29 @@ use crate::pass_manager::MirPass; pub(crate) struct ImpossiblePredicates; +fn has_impossible_predicates(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + let predicates = tcx.predicates_of(def_id).instantiate_identity(tcx); + tracing::trace!(?predicates); + let predicates = predicates.predicates.into_iter().filter(|p| { + !p.has_type_flags( + // Only consider global clauses to simplify. + TypeFlags::HAS_FREE_LOCAL_NAMES + // Clauses that refer to unevaluated constants as they cause cycles. + | TypeFlags::HAS_CT_PROJECTION, + ) + }); + let predicates: Vec<_> = traits::elaborate(tcx, predicates).collect(); + tracing::trace!(?predicates); + predicates.references_error() || traits::impossible_predicates(tcx, predicates) +} + impl<'tcx> MirPass<'tcx> for ImpossiblePredicates { #[tracing::instrument(level = "trace", skip(self, tcx, body))] fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { tracing::trace!(def_id = ?body.source.def_id()); - let predicates = tcx.predicates_of(body.source.def_id()).instantiate_identity(tcx); - tracing::trace!(?predicates); - let predicates = predicates.predicates.into_iter().filter(|p| { - !p.has_type_flags( - // Only consider global clauses to simplify. - TypeFlags::HAS_FREE_LOCAL_NAMES - // Clauses that refer to unevaluated constants as they cause cycles. - | TypeFlags::HAS_CT_PROJECTION, - ) - }); - let predicates: Vec<_> = traits::elaborate(tcx, predicates).collect(); - tracing::trace!(?predicates); - if predicates.references_error() || traits::impossible_predicates(tcx, predicates) { + let impossible = body.tainted_by_errors.is_some() + || has_impossible_predicates(tcx, body.source.def_id()); + if impossible { trace!("found unsatisfiable predicates"); // Clear the body to only contain a single `unreachable` statement. let bbs = body.basic_blocks.as_mut(); diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index 6f61215cee2..b9d6e74ecae 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -195,10 +195,10 @@ fn compute_replacement<'tcx>( // including DEF. This violates the DEF dominates USE condition, and so is impossible. let is_constant_place = |place: Place<'_>| { // We only allow `Deref` as the first projection, to avoid surprises. - if place.projection.first() == Some(&PlaceElem::Deref) { + if let Some((&PlaceElem::Deref, rest)) = place.projection.split_first() { // `place == (*some_local).xxx`, it is constant only if `some_local` is constant. // We approximate constness using SSAness. - ssa.is_ssa(place.local) && place.projection[1..].iter().all(PlaceElem::is_stable_offset) + ssa.is_ssa(place.local) && rest.iter().all(PlaceElem::is_stable_offset) } else { storage_live.has_single_storage(place.local) && place.projection[..].iter().all(PlaceElem::is_stable_offset) @@ -206,7 +206,7 @@ fn compute_replacement<'tcx>( }; let mut can_perform_opt = |target: Place<'tcx>, loc: Location| { - if target.projection.first() == Some(&PlaceElem::Deref) { + if target.is_indirect_first_projection() { // We are creating a reborrow. As `place.local` is a reference, removing the storage // statements should not make it much harder for LLVM to optimize. storage_to_remove.insert(target.local); @@ -266,7 +266,7 @@ fn compute_replacement<'tcx>( Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { let mut place = *place; // Try to see through `place` in order to collapse reborrow chains. - if place.projection.first() == Some(&PlaceElem::Deref) + if let Some((&PlaceElem::Deref, rest)) = place.projection.split_first() && let Value::Pointer(target, inner_needs_unique) = targets[place.local] // Only see through immutable reference and pointers, as we do not know yet if // mutable references are fully replaced. @@ -274,7 +274,7 @@ fn compute_replacement<'tcx>( // Only collapse chain if the pointee is definitely live. && can_perform_opt(target, location) { - place = target.project_deeper(&place.projection[1..], tcx); + place = target.project_deeper(rest, tcx); } assert_ne!(place.local, local); if is_constant_place(place) { @@ -323,7 +323,7 @@ fn compute_replacement<'tcx>( return; } - if place.projection.first() != Some(&PlaceElem::Deref) { + if !place.is_indirect_first_projection() { // This is not a dereference, nothing to do. return; } @@ -392,20 +392,15 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { } fn visit_var_debug_info(&mut self, debuginfo: &mut VarDebugInfo<'tcx>) { - // If the debuginfo is a pointer to another place: - // - if it's a reborrow, see through it; - // - if it's a direct borrow, increase `debuginfo.references`. + // If the debuginfo is a pointer to another place + // and it's a reborrow: see through it while let VarDebugInfoContents::Place(ref mut place) = debuginfo.value && place.projection.is_empty() && let Value::Pointer(target, _) = self.targets[place.local] - && target.projection.iter().all(|p| p.can_use_in_debuginfo()) + && let &[PlaceElem::Deref] = &target.projection[..] { - if let Some((&PlaceElem::Deref, rest)) = target.projection.split_last() { - *place = Place::from(target.local).project_deeper(rest, self.tcx); - self.any_replacement = true; - } else { - break; - } + *place = Place::from(target.local); + self.any_replacement = true; } // Simplify eventual projections left inside `debuginfo`. @@ -414,9 +409,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { fn visit_place(&mut self, place: &mut Place<'tcx>, ctxt: PlaceContext, loc: Location) { loop { - if place.projection.first() != Some(&PlaceElem::Deref) { - return; - } + let Some((&PlaceElem::Deref, rest)) = place.projection.split_first() else { return }; let Value::Pointer(target, _) = self.targets[place.local] else { return }; @@ -432,7 +425,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { return; } - *place = target.project_deeper(&place.projection[1..], self.tcx); + *place = target.project_deeper(rest, self.tcx); self.any_replacement = true; } } diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 5b6d7ffb511..b53c1f6d202 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -1,7 +1,6 @@ use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; -use rustc_target::spec::PanicStrategy; use tracing::debug; use crate::patch::MirPatch; @@ -13,7 +12,7 @@ pub(super) struct RemoveNoopLandingPads; impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.panic_strategy() != PanicStrategy::Abort + sess.panic_strategy().unwinds() } fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index 4b4ec4956eb..b25671d676b 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -57,7 +57,7 @@ enum CanonicalizeMode { }, } -pub struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> { +pub(super) struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> { delegate: &'a D, // Immutable field. @@ -83,7 +83,7 @@ pub struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> { } impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { - pub fn canonicalize_response<T: TypeFoldable<I>>( + pub(super) fn canonicalize_response<T: TypeFoldable<I>>( delegate: &'a D, max_input_universe: ty::UniverseIndex, variables: &'a mut Vec<I::GenericArg>, @@ -112,7 +112,6 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { let (max_universe, variables) = canonicalizer.finalize(); Canonical { max_universe, variables, value } } - fn canonicalize_param_env( delegate: &'a D, variables: &'a mut Vec<I::GenericArg>, @@ -195,7 +194,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { /// /// We want to keep the option of canonicalizing `'static` to an existential /// variable in the future by changing the way we detect global where-bounds. - pub fn canonicalize_input<P: TypeFoldable<I>>( + pub(super) fn canonicalize_input<P: TypeFoldable<I>>( delegate: &'a D, variables: &'a mut Vec<I::GenericArg>, input: QueryInput<I, P>, diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs new file mode 100644 index 00000000000..e3520e238ed --- /dev/null +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -0,0 +1,364 @@ +//! Canonicalization is used to separate some goal from its context, +//! throwing away unnecessary information in the process. +//! +//! This is necessary to cache goals containing inference variables +//! and placeholders without restricting them to the current `InferCtxt`. +//! +//! Canonicalization is fairly involved, for more details see the relevant +//! section of the [rustc-dev-guide][c]. +//! +//! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html + +use std::iter; + +use canonicalizer::Canonicalizer; +use rustc_index::IndexVec; +use rustc_type_ir::inherent::*; +use rustc_type_ir::relate::solver_relating::RelateExt; +use rustc_type_ir::{ + self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner, + TypeFoldable, +}; +use tracing::instrument; + +use crate::delegate::SolverDelegate; +use crate::resolve::eager_resolve_vars; +use crate::solve::{ + CanonicalInput, CanonicalResponse, Certainty, ExternalConstraintsData, Goal, + NestedNormalizationGoals, PredefinedOpaquesData, QueryInput, Response, inspect, +}; + +pub mod canonicalizer; + +trait ResponseT<I: Interner> { + fn var_values(&self) -> CanonicalVarValues<I>; +} + +impl<I: Interner> ResponseT<I> for Response<I> { + fn var_values(&self) -> CanonicalVarValues<I> { + self.var_values + } +} + +impl<I: Interner, T> ResponseT<I> for inspect::State<I, T> { + fn var_values(&self) -> CanonicalVarValues<I> { + self.var_values + } +} + +/// Canonicalizes the goal remembering the original values +/// for each bound variable. +/// +/// This expects `goal` and `opaque_types` to be eager resolved. +pub(super) fn canonicalize_goal<D, I>( + delegate: &D, + goal: Goal<I, I::Predicate>, + opaque_types: Vec<(ty::OpaqueTypeKey<I>, I::Ty)>, +) -> (Vec<I::GenericArg>, CanonicalInput<I, I::Predicate>) +where + D: SolverDelegate<Interner = I>, + I: Interner, +{ + let mut orig_values = Default::default(); + let canonical = Canonicalizer::canonicalize_input( + delegate, + &mut orig_values, + QueryInput { + goal, + predefined_opaques_in_body: delegate + .cx() + .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), + }, + ); + let query_input = ty::CanonicalQueryInput { canonical, typing_mode: delegate.typing_mode() }; + (orig_values, query_input) +} + +pub(super) fn canonicalize_response<D, I, T>( + delegate: &D, + max_input_universe: ty::UniverseIndex, + value: T, +) -> ty::Canonical<I, T> +where + D: SolverDelegate<Interner = I>, + I: Interner, + T: TypeFoldable<I>, +{ + let mut orig_values = Default::default(); + let canonical = + Canonicalizer::canonicalize_response(delegate, max_input_universe, &mut orig_values, value); + canonical +} + +/// After calling a canonical query, we apply the constraints returned +/// by the query using this function. +/// +/// This happens in three steps: +/// - we instantiate the bound variables of the query response +/// - we unify the `var_values` of the response with the `original_values` +/// - we apply the `external_constraints` returned by the query, returning +/// the `normalization_nested_goals` +pub(super) fn instantiate_and_apply_query_response<D, I>( + delegate: &D, + param_env: I::ParamEnv, + original_values: &[I::GenericArg], + response: CanonicalResponse<I>, + span: I::Span, +) -> (NestedNormalizationGoals<I>, Certainty) +where + D: SolverDelegate<Interner = I>, + I: Interner, +{ + let instantiation = + compute_query_response_instantiation_values(delegate, &original_values, &response, span); + + let Response { var_values, external_constraints, certainty } = + delegate.instantiate_canonical(response, instantiation); + + unify_query_var_values(delegate, param_env, &original_values, var_values, span); + + let ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } = + &*external_constraints; + + register_region_constraints(delegate, region_constraints, span); + register_new_opaque_types(delegate, opaque_types, span); + + (normalization_nested_goals.clone(), certainty) +} + +/// This returns the canonical variable values to instantiate the bound variables of +/// the canonical response. This depends on the `original_values` for the +/// bound variables. +fn compute_query_response_instantiation_values<D, I, T>( + delegate: &D, + original_values: &[I::GenericArg], + response: &Canonical<I, T>, + span: I::Span, +) -> CanonicalVarValues<I> +where + D: SolverDelegate<Interner = I>, + I: Interner, + T: ResponseT<I>, +{ + // FIXME: Longterm canonical queries should deal with all placeholders + // created inside of the query directly instead of returning them to the + // caller. + let prev_universe = delegate.universe(); + let universes_created_in_query = response.max_universe.index(); + for _ in 0..universes_created_in_query { + delegate.create_next_universe(); + } + + let var_values = response.value.var_values(); + assert_eq!(original_values.len(), var_values.len()); + + // If the query did not make progress with constraining inference variables, + // we would normally create a new inference variables for bound existential variables + // only then unify this new inference variable with the inference variable from + // the input. + // + // We therefore instantiate the existential variable in the canonical response with the + // inference variable of the input right away, which is more performant. + let mut opt_values = IndexVec::from_elem_n(None, response.variables.len()); + for (original_value, result_value) in iter::zip(original_values, var_values.var_values.iter()) { + match result_value.kind() { + ty::GenericArgKind::Type(t) => { + // We disable the instantiation guess for inference variables + // and only use it for placeholders. We need to handle the + // `sub_root` of type inference variables which would make this + // more involved. They are also a lot rarer than region variables. + if let ty::Bound(debruijn, b) = t.kind() + && !matches!( + response.variables.get(b.var().as_usize()).unwrap(), + CanonicalVarKind::Ty { .. } + ) + { + assert_eq!(debruijn, ty::INNERMOST); + opt_values[b.var()] = Some(*original_value); + } + } + ty::GenericArgKind::Lifetime(r) => { + if let ty::ReBound(debruijn, br) = r.kind() { + assert_eq!(debruijn, ty::INNERMOST); + opt_values[br.var()] = Some(*original_value); + } + } + ty::GenericArgKind::Const(c) => { + if let ty::ConstKind::Bound(debruijn, bv) = c.kind() { + assert_eq!(debruijn, ty::INNERMOST); + opt_values[bv.var()] = Some(*original_value); + } + } + } + } + CanonicalVarValues::instantiate(delegate.cx(), response.variables, |var_values, kind| { + if kind.universe() != ty::UniverseIndex::ROOT { + // A variable from inside a binder of the query. While ideally these shouldn't + // exist at all (see the FIXME at the start of this method), we have to deal with + // them for now. + delegate.instantiate_canonical_var(kind, span, &var_values, |idx| { + prev_universe + idx.index() + }) + } else if kind.is_existential() { + // As an optimization we sometimes avoid creating a new inference variable here. + // + // All new inference variables we create start out in the current universe of the caller. + // This is conceptually wrong as these inference variables would be able to name + // more placeholders then they should be able to. However the inference variables have + // to "come from somewhere", so by equating them with the original values of the caller + // later on, we pull them down into their correct universe again. + if let Some(v) = opt_values[ty::BoundVar::from_usize(var_values.len())] { + v + } else { + delegate.instantiate_canonical_var(kind, span, &var_values, |_| prev_universe) + } + } else { + // For placeholders which were already part of the input, we simply map this + // universal bound variable back the placeholder of the input. + original_values[kind.expect_placeholder_index()] + } + }) +} + +/// Unify the `original_values` with the `var_values` returned by the canonical query.. +/// +/// This assumes that this unification will always succeed. This is the case when +/// applying a query response right away. However, calling a canonical query, doing any +/// other kind of trait solving, and only then instantiating the result of the query +/// can cause the instantiation to fail. This is not supported and we ICE in this case. +/// +/// We always structurally instantiate aliases. Relating aliases needs to be different +/// depending on whether the alias is *rigid* or not. We're only really able to tell +/// whether an alias is rigid by using the trait solver. When instantiating a response +/// from the solver we assume that the solver correctly handled aliases and therefore +/// always relate them structurally here. +#[instrument(level = "trace", skip(delegate))] +fn unify_query_var_values<D, I>( + delegate: &D, + param_env: I::ParamEnv, + original_values: &[I::GenericArg], + var_values: CanonicalVarValues<I>, + span: I::Span, +) where + D: SolverDelegate<Interner = I>, + I: Interner, +{ + assert_eq!(original_values.len(), var_values.len()); + + for (&orig, response) in iter::zip(original_values, var_values.var_values.iter()) { + let goals = + delegate.eq_structurally_relating_aliases(param_env, orig, response, span).unwrap(); + assert!(goals.is_empty()); + } +} + +fn register_region_constraints<D, I>( + delegate: &D, + outlives: &[ty::OutlivesPredicate<I, I::GenericArg>], + span: I::Span, +) where + D: SolverDelegate<Interner = I>, + I: Interner, +{ + for &ty::OutlivesPredicate(lhs, rhs) in outlives { + match lhs.kind() { + ty::GenericArgKind::Lifetime(lhs) => delegate.sub_regions(rhs, lhs, span), + ty::GenericArgKind::Type(lhs) => delegate.register_ty_outlives(lhs, rhs, span), + ty::GenericArgKind::Const(_) => panic!("const outlives: {lhs:?}: {rhs:?}"), + } + } +} + +fn register_new_opaque_types<D, I>( + delegate: &D, + opaque_types: &[(ty::OpaqueTypeKey<I>, I::Ty)], + span: I::Span, +) where + D: SolverDelegate<Interner = I>, + I: Interner, +{ + for &(key, ty) in opaque_types { + let prev = delegate.register_hidden_type_in_storage(key, ty, span); + // We eagerly resolve inference variables when computing the query response. + // This can cause previously distinct opaque type keys to now be structurally equal. + // + // To handle this, we store any duplicate entries in a separate list to check them + // at the end of typeck/borrowck. We could alternatively eagerly equate the hidden + // types here. However, doing so is difficult as it may result in nested goals and + // any errors may make it harder to track the control flow for diagnostics. + if let Some(prev) = prev { + delegate.add_duplicate_opaque_type(key, prev, span); + } + } +} + +/// Used by proof trees to be able to recompute intermediate actions while +/// evaluating a goal. The `var_values` not only include the bound variables +/// of the query input, but also contain all unconstrained inference vars +/// created while evaluating this goal. +pub fn make_canonical_state<D, I, T>( + delegate: &D, + var_values: &[I::GenericArg], + max_input_universe: ty::UniverseIndex, + data: T, +) -> inspect::CanonicalState<I, T> +where + D: SolverDelegate<Interner = I>, + I: Interner, + T: TypeFoldable<I>, +{ + let var_values = CanonicalVarValues { var_values: delegate.cx().mk_args(var_values) }; + let state = inspect::State { var_values, data }; + let state = eager_resolve_vars(delegate, state); + Canonicalizer::canonicalize_response(delegate, max_input_universe, &mut vec![], state) +} + +// FIXME: needs to be pub to be accessed by downstream +// `rustc_trait_selection::solve::inspect::analyse`. +pub fn instantiate_canonical_state<D, I, T>( + delegate: &D, + span: I::Span, + param_env: I::ParamEnv, + orig_values: &mut Vec<I::GenericArg>, + state: inspect::CanonicalState<I, T>, +) -> T +where + D: SolverDelegate<Interner = I>, + I: Interner, + T: TypeFoldable<I>, +{ + // In case any fresh inference variables have been created between `state` + // and the previous instantiation, extend `orig_values` for it. + orig_values.extend( + state.value.var_values.var_values.as_slice()[orig_values.len()..] + .iter() + .map(|&arg| delegate.fresh_var_for_kind_with_span(arg, span)), + ); + + let instantiation = + compute_query_response_instantiation_values(delegate, orig_values, &state, span); + + let inspect::State { var_values, data } = delegate.instantiate_canonical(state, instantiation); + + unify_query_var_values(delegate, param_env, orig_values, var_values, span); + data +} + +pub fn response_no_constraints_raw<I: Interner>( + cx: I, + max_universe: ty::UniverseIndex, + variables: I::CanonicalVarKinds, + certainty: Certainty, +) -> CanonicalResponse<I> { + ty::Canonical { + max_universe, + variables, + value: Response { + var_values: ty::CanonicalVarValues::make_identity(cx, variables), + // FIXME: maybe we should store the "no response" version in cx, like + // we do for cx.types and stuff. + external_constraints: cx.mk_external_constraints(ExternalConstraintsData::default()), + certainty, + }, + } +} diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs index d3965e14c68..5fa29b7d9f8 100644 --- a/compiler/rustc_next_trait_solver/src/lib.rs +++ b/compiler/rustc_next_trait_solver/src/lib.rs @@ -10,7 +10,7 @@ #![allow(rustc::usage_of_type_ir_traits)] // tidy-alphabetical-end -pub mod canonicalizer; +pub mod canonical; pub mod coherence; pub mod delegate; pub mod placeholder; diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs deleted file mode 100644 index 889588afe61..00000000000 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ /dev/null @@ -1,517 +0,0 @@ -//! Canonicalization is used to separate some goal from its context, -//! throwing away unnecessary information in the process. -//! -//! This is necessary to cache goals containing inference variables -//! and placeholders without restricting them to the current `InferCtxt`. -//! -//! Canonicalization is fairly involved, for more details see the relevant -//! section of the [rustc-dev-guide][c]. -//! -//! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html - -use std::iter; - -use rustc_index::IndexVec; -use rustc_type_ir::data_structures::HashSet; -use rustc_type_ir::inherent::*; -use rustc_type_ir::relate::solver_relating::RelateExt; -use rustc_type_ir::solve::OpaqueTypesJank; -use rustc_type_ir::{ - self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner, - TypeFoldable, -}; -use tracing::{debug, instrument, trace}; - -use crate::canonicalizer::Canonicalizer; -use crate::delegate::SolverDelegate; -use crate::resolve::eager_resolve_vars; -use crate::solve::eval_ctxt::CurrentGoalKind; -use crate::solve::{ - CanonicalInput, CanonicalResponse, Certainty, EvalCtxt, ExternalConstraintsData, Goal, - MaybeCause, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryInput, - QueryResult, Response, inspect, response_no_constraints_raw, -}; - -trait ResponseT<I: Interner> { - fn var_values(&self) -> CanonicalVarValues<I>; -} - -impl<I: Interner> ResponseT<I> for Response<I> { - fn var_values(&self) -> CanonicalVarValues<I> { - self.var_values - } -} - -impl<I: Interner, T> ResponseT<I> for inspect::State<I, T> { - fn var_values(&self) -> CanonicalVarValues<I> { - self.var_values - } -} - -impl<D, I> EvalCtxt<'_, D> -where - D: SolverDelegate<Interner = I>, - I: Interner, -{ - /// Canonicalizes the goal remembering the original values - /// for each bound variable. - /// - /// This expects `goal` and `opaque_types` to be eager resolved. - pub(super) fn canonicalize_goal( - delegate: &D, - goal: Goal<I, I::Predicate>, - opaque_types: Vec<(ty::OpaqueTypeKey<I>, I::Ty)>, - ) -> (Vec<I::GenericArg>, CanonicalInput<I, I::Predicate>) { - let mut orig_values = Default::default(); - let canonical = Canonicalizer::canonicalize_input( - delegate, - &mut orig_values, - QueryInput { - goal, - predefined_opaques_in_body: delegate - .cx() - .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), - }, - ); - let query_input = - ty::CanonicalQueryInput { canonical, typing_mode: delegate.typing_mode() }; - (orig_values, query_input) - } - - /// To return the constraints of a canonical query to the caller, we canonicalize: - /// - /// - `var_values`: a map from bound variables in the canonical goal to - /// the values inferred while solving the instantiated goal. - /// - `external_constraints`: additional constraints which aren't expressible - /// using simple unification of inference variables. - /// - /// This takes the `shallow_certainty` which represents whether we're confident - /// that the final result of the current goal only depends on the nested goals. - /// - /// In case this is `Certainty::Maybe`, there may still be additional nested goals - /// or inference constraints required for this candidate to be hold. The candidate - /// always requires all already added constraints and nested goals. - #[instrument(level = "trace", skip(self), ret)] - pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response( - &mut self, - shallow_certainty: Certainty, - ) -> QueryResult<I> { - self.inspect.make_canonical_response(shallow_certainty); - - let goals_certainty = self.try_evaluate_added_goals()?; - assert_eq!( - self.tainted, - Ok(()), - "EvalCtxt is tainted -- nested goals may have been dropped in a \ - previous call to `try_evaluate_added_goals!`" - ); - - // We only check for leaks from universes which were entered inside - // of the query. - self.delegate.leak_check(self.max_input_universe).map_err(|NoSolution| { - trace!("failed the leak check"); - NoSolution - })?; - - let (certainty, normalization_nested_goals) = - match (self.current_goal_kind, shallow_certainty) { - // When normalizing, we've replaced the expected term with an unconstrained - // inference variable. This means that we dropped information which could - // have been important. We handle this by instead returning the nested goals - // to the caller, where they are then handled. We only do so if we do not - // need to recompute the `NormalizesTo` goal afterwards to avoid repeatedly - // uplifting its nested goals. This is the case if the `shallow_certainty` is - // `Certainty::Yes`. - (CurrentGoalKind::NormalizesTo, Certainty::Yes) => { - let goals = std::mem::take(&mut self.nested_goals); - // As we return all ambiguous nested goals, we can ignore the certainty - // returned by `self.try_evaluate_added_goals()`. - if goals.is_empty() { - assert!(matches!(goals_certainty, Certainty::Yes)); - } - ( - Certainty::Yes, - NestedNormalizationGoals( - goals.into_iter().map(|(s, g, _)| (s, g)).collect(), - ), - ) - } - _ => { - let certainty = shallow_certainty.and(goals_certainty); - (certainty, NestedNormalizationGoals::empty()) - } - }; - - if let Certainty::Maybe { - cause: cause @ MaybeCause::Overflow { keep_constraints: false, .. }, - opaque_types_jank, - } = certainty - { - // If we have overflow, it's probable that we're substituting a type - // into itself infinitely and any partial substitutions in the query - // response are probably not useful anyways, so just return an empty - // query response. - // - // This may prevent us from potentially useful inference, e.g. - // 2 candidates, one ambiguous and one overflow, which both - // have the same inference constraints. - // - // Changing this to retain some constraints in the future - // won't be a breaking change, so this is good enough for now. - return Ok(self.make_ambiguous_response_no_constraints(cause, opaque_types_jank)); - } - - let external_constraints = - self.compute_external_query_constraints(certainty, normalization_nested_goals); - let (var_values, mut external_constraints) = - eager_resolve_vars(self.delegate, (self.var_values, external_constraints)); - - // Remove any trivial or duplicated region constraints once we've resolved regions - let mut unique = HashSet::default(); - external_constraints.region_constraints.retain(|outlives| { - outlives.0.as_region().is_none_or(|re| re != outlives.1) && unique.insert(*outlives) - }); - - let canonical = Canonicalizer::canonicalize_response( - self.delegate, - self.max_input_universe, - &mut Default::default(), - Response { - var_values, - certainty, - external_constraints: self.cx().mk_external_constraints(external_constraints), - }, - ); - - // HACK: We bail with overflow if the response would have too many non-region - // inference variables. This tends to only happen if we encounter a lot of - // ambiguous alias types which get replaced with fresh inference variables - // during generalization. This prevents hangs caused by an exponential blowup, - // see tests/ui/traits/next-solver/coherence-alias-hang.rs. - match self.current_goal_kind { - // We don't do so for `NormalizesTo` goals as we erased the expected term and - // bailing with overflow here would prevent us from detecting a type-mismatch, - // causing a coherence error in diesel, see #131969. We still bail with overflow - // when later returning from the parent AliasRelate goal. - CurrentGoalKind::NormalizesTo => {} - CurrentGoalKind::Misc | CurrentGoalKind::CoinductiveTrait => { - let num_non_region_vars = canonical - .variables - .iter() - .filter(|c| !c.is_region() && c.is_existential()) - .count(); - if num_non_region_vars > self.cx().recursion_limit() { - debug!(?num_non_region_vars, "too many inference variables -> overflow"); - return Ok(self.make_ambiguous_response_no_constraints( - MaybeCause::Overflow { - suggest_increasing_limit: true, - keep_constraints: false, - }, - OpaqueTypesJank::AllGood, - )); - } - } - } - - Ok(canonical) - } - - /// Constructs a totally unconstrained, ambiguous response to a goal. - /// - /// Take care when using this, since often it's useful to respond with - /// ambiguity but return constrained variables to guide inference. - pub(in crate::solve) fn make_ambiguous_response_no_constraints( - &self, - cause: MaybeCause, - opaque_types_jank: OpaqueTypesJank, - ) -> CanonicalResponse<I> { - response_no_constraints_raw( - self.cx(), - self.max_input_universe, - self.variables, - Certainty::Maybe { cause, opaque_types_jank }, - ) - } - - /// Computes the region constraints and *new* opaque types registered when - /// proving a goal. - /// - /// If an opaque was already constrained before proving this goal, then the - /// external constraints do not need to record that opaque, since if it is - /// further constrained by inference, that will be passed back in the var - /// values. - #[instrument(level = "trace", skip(self), ret)] - fn compute_external_query_constraints( - &self, - certainty: Certainty, - normalization_nested_goals: NestedNormalizationGoals<I>, - ) -> ExternalConstraintsData<I> { - // We only return region constraints once the certainty is `Yes`. This - // is necessary as we may drop nested goals on ambiguity, which may result - // in unconstrained inference variables in the region constraints. It also - // prevents us from emitting duplicate region constraints, avoiding some - // unnecessary work. This slightly weakens the leak check in case it uses - // region constraints from an ambiguous nested goal. This is tested in both - // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs` and - // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`. - let region_constraints = if certainty == Certainty::Yes { - self.delegate.make_deduplicated_outlives_constraints() - } else { - Default::default() - }; - - // We only return *newly defined* opaque types from canonical queries. - // - // Constraints for any existing opaque types are already tracked by changes - // to the `var_values`. - let opaque_types = self - .delegate - .clone_opaque_types_added_since(self.initial_opaque_types_storage_num_entries); - - ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } - } - - /// After calling a canonical query, we apply the constraints returned - /// by the query using this function. - /// - /// This happens in three steps: - /// - we instantiate the bound variables of the query response - /// - we unify the `var_values` of the response with the `original_values` - /// - we apply the `external_constraints` returned by the query, returning - /// the `normalization_nested_goals` - pub(super) fn instantiate_and_apply_query_response( - delegate: &D, - param_env: I::ParamEnv, - original_values: &[I::GenericArg], - response: CanonicalResponse<I>, - span: I::Span, - ) -> (NestedNormalizationGoals<I>, Certainty) { - let instantiation = Self::compute_query_response_instantiation_values( - delegate, - &original_values, - &response, - span, - ); - - let Response { var_values, external_constraints, certainty } = - delegate.instantiate_canonical(response, instantiation); - - Self::unify_query_var_values(delegate, param_env, &original_values, var_values, span); - - let ExternalConstraintsData { - region_constraints, - opaque_types, - normalization_nested_goals, - } = &*external_constraints; - - Self::register_region_constraints(delegate, region_constraints, span); - Self::register_new_opaque_types(delegate, opaque_types, span); - - (normalization_nested_goals.clone(), certainty) - } - - /// This returns the canonical variable values to instantiate the bound variables of - /// the canonical response. This depends on the `original_values` for the - /// bound variables. - fn compute_query_response_instantiation_values<T: ResponseT<I>>( - delegate: &D, - original_values: &[I::GenericArg], - response: &Canonical<I, T>, - span: I::Span, - ) -> CanonicalVarValues<I> { - // FIXME: Longterm canonical queries should deal with all placeholders - // created inside of the query directly instead of returning them to the - // caller. - let prev_universe = delegate.universe(); - let universes_created_in_query = response.max_universe.index(); - for _ in 0..universes_created_in_query { - delegate.create_next_universe(); - } - - let var_values = response.value.var_values(); - assert_eq!(original_values.len(), var_values.len()); - - // If the query did not make progress with constraining inference variables, - // we would normally create a new inference variables for bound existential variables - // only then unify this new inference variable with the inference variable from - // the input. - // - // We therefore instantiate the existential variable in the canonical response with the - // inference variable of the input right away, which is more performant. - let mut opt_values = IndexVec::from_elem_n(None, response.variables.len()); - for (original_value, result_value) in - iter::zip(original_values, var_values.var_values.iter()) - { - match result_value.kind() { - ty::GenericArgKind::Type(t) => { - // We disable the instantiation guess for inference variables - // and only use it for placeholders. We need to handle the - // `sub_root` of type inference variables which would make this - // more involved. They are also a lot rarer than region variables. - if let ty::Bound(debruijn, b) = t.kind() - && !matches!( - response.variables.get(b.var().as_usize()).unwrap(), - CanonicalVarKind::Ty { .. } - ) - { - assert_eq!(debruijn, ty::INNERMOST); - opt_values[b.var()] = Some(*original_value); - } - } - ty::GenericArgKind::Lifetime(r) => { - if let ty::ReBound(debruijn, br) = r.kind() { - assert_eq!(debruijn, ty::INNERMOST); - opt_values[br.var()] = Some(*original_value); - } - } - ty::GenericArgKind::Const(c) => { - if let ty::ConstKind::Bound(debruijn, bv) = c.kind() { - assert_eq!(debruijn, ty::INNERMOST); - opt_values[bv.var()] = Some(*original_value); - } - } - } - } - CanonicalVarValues::instantiate(delegate.cx(), response.variables, |var_values, kind| { - if kind.universe() != ty::UniverseIndex::ROOT { - // A variable from inside a binder of the query. While ideally these shouldn't - // exist at all (see the FIXME at the start of this method), we have to deal with - // them for now. - delegate.instantiate_canonical_var(kind, span, &var_values, |idx| { - prev_universe + idx.index() - }) - } else if kind.is_existential() { - // As an optimization we sometimes avoid creating a new inference variable here. - // - // All new inference variables we create start out in the current universe of the caller. - // This is conceptually wrong as these inference variables would be able to name - // more placeholders then they should be able to. However the inference variables have - // to "come from somewhere", so by equating them with the original values of the caller - // later on, we pull them down into their correct universe again. - if let Some(v) = opt_values[ty::BoundVar::from_usize(var_values.len())] { - v - } else { - delegate.instantiate_canonical_var(kind, span, &var_values, |_| prev_universe) - } - } else { - // For placeholders which were already part of the input, we simply map this - // universal bound variable back the placeholder of the input. - original_values[kind.expect_placeholder_index()] - } - }) - } - - /// Unify the `original_values` with the `var_values` returned by the canonical query.. - /// - /// This assumes that this unification will always succeed. This is the case when - /// applying a query response right away. However, calling a canonical query, doing any - /// other kind of trait solving, and only then instantiating the result of the query - /// can cause the instantiation to fail. This is not supported and we ICE in this case. - /// - /// We always structurally instantiate aliases. Relating aliases needs to be different - /// depending on whether the alias is *rigid* or not. We're only really able to tell - /// whether an alias is rigid by using the trait solver. When instantiating a response - /// from the solver we assume that the solver correctly handled aliases and therefore - /// always relate them structurally here. - #[instrument(level = "trace", skip(delegate))] - fn unify_query_var_values( - delegate: &D, - param_env: I::ParamEnv, - original_values: &[I::GenericArg], - var_values: CanonicalVarValues<I>, - span: I::Span, - ) { - assert_eq!(original_values.len(), var_values.len()); - - for (&orig, response) in iter::zip(original_values, var_values.var_values.iter()) { - let goals = - delegate.eq_structurally_relating_aliases(param_env, orig, response, span).unwrap(); - assert!(goals.is_empty()); - } - } - - fn register_region_constraints( - delegate: &D, - outlives: &[ty::OutlivesPredicate<I, I::GenericArg>], - span: I::Span, - ) { - for &ty::OutlivesPredicate(lhs, rhs) in outlives { - match lhs.kind() { - ty::GenericArgKind::Lifetime(lhs) => delegate.sub_regions(rhs, lhs, span), - ty::GenericArgKind::Type(lhs) => delegate.register_ty_outlives(lhs, rhs, span), - ty::GenericArgKind::Const(_) => panic!("const outlives: {lhs:?}: {rhs:?}"), - } - } - } - - fn register_new_opaque_types( - delegate: &D, - opaque_types: &[(ty::OpaqueTypeKey<I>, I::Ty)], - span: I::Span, - ) { - for &(key, ty) in opaque_types { - let prev = delegate.register_hidden_type_in_storage(key, ty, span); - // We eagerly resolve inference variables when computing the query response. - // This can cause previously distinct opaque type keys to now be structurally equal. - // - // To handle this, we store any duplicate entries in a separate list to check them - // at the end of typeck/borrowck. We could alternatively eagerly equate the hidden - // types here. However, doing so is difficult as it may result in nested goals and - // any errors may make it harder to track the control flow for diagnostics. - if let Some(prev) = prev { - delegate.add_duplicate_opaque_type(key, prev, span); - } - } - } -} - -/// Used by proof trees to be able to recompute intermediate actions while -/// evaluating a goal. The `var_values` not only include the bound variables -/// of the query input, but also contain all unconstrained inference vars -/// created while evaluating this goal. -pub(in crate::solve) fn make_canonical_state<D, T, I>( - delegate: &D, - var_values: &[I::GenericArg], - max_input_universe: ty::UniverseIndex, - data: T, -) -> inspect::CanonicalState<I, T> -where - D: SolverDelegate<Interner = I>, - I: Interner, - T: TypeFoldable<I>, -{ - let var_values = CanonicalVarValues { var_values: delegate.cx().mk_args(var_values) }; - let state = inspect::State { var_values, data }; - let state = eager_resolve_vars(delegate, state); - Canonicalizer::canonicalize_response(delegate, max_input_universe, &mut vec![], state) -} - -// FIXME: needs to be pub to be accessed by downstream -// `rustc_trait_selection::solve::inspect::analyse`. -pub fn instantiate_canonical_state<D, I, T: TypeFoldable<I>>( - delegate: &D, - span: I::Span, - param_env: I::ParamEnv, - orig_values: &mut Vec<I::GenericArg>, - state: inspect::CanonicalState<I, T>, -) -> T -where - D: SolverDelegate<Interner = I>, - I: Interner, -{ - // In case any fresh inference variables have been created between `state` - // and the previous instantiation, extend `orig_values` for it. - orig_values.extend( - state.value.var_values.var_values.as_slice()[orig_values.len()..] - .iter() - .map(|&arg| delegate.fresh_var_for_kind_with_span(arg, span)), - ); - - let instantiation = - EvalCtxt::compute_query_response_instantiation_values(delegate, orig_values, &state, span); - - let inspect::State { var_values, data } = delegate.instantiate_canonical(state, instantiation); - - EvalCtxt::unify_query_var_values(delegate, param_env, orig_values, var_values, span); - data -} diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 5df7c92d881..bb86357a85f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -17,6 +17,10 @@ use rustc_type_ir::{ use tracing::{debug, instrument, trace}; use super::has_only_region_constraints; +use crate::canonical::{ + canonicalize_goal, canonicalize_response, instantiate_and_apply_query_response, + response_no_constraints_raw, +}; use crate::coherence; use crate::delegate::SolverDelegate; use crate::placeholder::BoundVarReplacer; @@ -24,12 +28,11 @@ use crate::resolve::eager_resolve_vars; use crate::solve::search_graph::SearchGraph; use crate::solve::ty::may_use_unstable_feature; use crate::solve::{ - CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluation, GoalSource, - GoalStalledOn, HasChanged, NestedNormalizationGoals, NoSolution, QueryInput, QueryResult, - inspect, + CanonicalInput, CanonicalResponse, Certainty, ExternalConstraintsData, FIXPOINT_STEP_LIMIT, + Goal, GoalEvaluation, GoalSource, GoalStalledOn, HasChanged, MaybeCause, + NestedNormalizationGoals, NoSolution, QueryInput, QueryResult, Response, inspect, }; -pub(super) mod canonical; mod probe; /// The kind of goal we're currently proving. @@ -464,8 +467,7 @@ where let opaque_types = self.delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); - let (orig_values, canonical_goal) = - Self::canonicalize_goal(self.delegate, goal, opaque_types); + let (orig_values, canonical_goal) = canonicalize_goal(self.delegate, goal, opaque_types); let canonical_result = self.search_graph.evaluate_goal( self.cx(), canonical_goal, @@ -480,7 +482,7 @@ where let has_changed = if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No }; - let (normalization_nested_goals, certainty) = Self::instantiate_and_apply_query_response( + let (normalization_nested_goals, certainty) = instantiate_and_apply_query_response( self.delegate, goal.param_env, &orig_values, @@ -1223,6 +1225,198 @@ where vec![] } } + + /// To return the constraints of a canonical query to the caller, we canonicalize: + /// + /// - `var_values`: a map from bound variables in the canonical goal to + /// the values inferred while solving the instantiated goal. + /// - `external_constraints`: additional constraints which aren't expressible + /// using simple unification of inference variables. + /// + /// This takes the `shallow_certainty` which represents whether we're confident + /// that the final result of the current goal only depends on the nested goals. + /// + /// In case this is `Certainty::Maybe`, there may still be additional nested goals + /// or inference constraints required for this candidate to be hold. The candidate + /// always requires all already added constraints and nested goals. + #[instrument(level = "trace", skip(self), ret)] + pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response( + &mut self, + shallow_certainty: Certainty, + ) -> QueryResult<I> { + self.inspect.make_canonical_response(shallow_certainty); + + let goals_certainty = self.try_evaluate_added_goals()?; + assert_eq!( + self.tainted, + Ok(()), + "EvalCtxt is tainted -- nested goals may have been dropped in a \ + previous call to `try_evaluate_added_goals!`" + ); + + // We only check for leaks from universes which were entered inside + // of the query. + self.delegate.leak_check(self.max_input_universe).map_err(|NoSolution| { + trace!("failed the leak check"); + NoSolution + })?; + + let (certainty, normalization_nested_goals) = + match (self.current_goal_kind, shallow_certainty) { + // When normalizing, we've replaced the expected term with an unconstrained + // inference variable. This means that we dropped information which could + // have been important. We handle this by instead returning the nested goals + // to the caller, where they are then handled. We only do so if we do not + // need to recompute the `NormalizesTo` goal afterwards to avoid repeatedly + // uplifting its nested goals. This is the case if the `shallow_certainty` is + // `Certainty::Yes`. + (CurrentGoalKind::NormalizesTo, Certainty::Yes) => { + let goals = std::mem::take(&mut self.nested_goals); + // As we return all ambiguous nested goals, we can ignore the certainty + // returned by `self.try_evaluate_added_goals()`. + if goals.is_empty() { + assert!(matches!(goals_certainty, Certainty::Yes)); + } + ( + Certainty::Yes, + NestedNormalizationGoals( + goals.into_iter().map(|(s, g, _)| (s, g)).collect(), + ), + ) + } + _ => { + let certainty = shallow_certainty.and(goals_certainty); + (certainty, NestedNormalizationGoals::empty()) + } + }; + + if let Certainty::Maybe { + cause: cause @ MaybeCause::Overflow { keep_constraints: false, .. }, + opaque_types_jank, + } = certainty + { + // If we have overflow, it's probable that we're substituting a type + // into itself infinitely and any partial substitutions in the query + // response are probably not useful anyways, so just return an empty + // query response. + // + // This may prevent us from potentially useful inference, e.g. + // 2 candidates, one ambiguous and one overflow, which both + // have the same inference constraints. + // + // Changing this to retain some constraints in the future + // won't be a breaking change, so this is good enough for now. + return Ok(self.make_ambiguous_response_no_constraints(cause, opaque_types_jank)); + } + + let external_constraints = + self.compute_external_query_constraints(certainty, normalization_nested_goals); + let (var_values, mut external_constraints) = + eager_resolve_vars(self.delegate, (self.var_values, external_constraints)); + + // Remove any trivial or duplicated region constraints once we've resolved regions + let mut unique = HashSet::default(); + external_constraints.region_constraints.retain(|outlives| { + outlives.0.as_region().is_none_or(|re| re != outlives.1) && unique.insert(*outlives) + }); + + let canonical = canonicalize_response( + self.delegate, + self.max_input_universe, + Response { + var_values, + certainty, + external_constraints: self.cx().mk_external_constraints(external_constraints), + }, + ); + + // HACK: We bail with overflow if the response would have too many non-region + // inference variables. This tends to only happen if we encounter a lot of + // ambiguous alias types which get replaced with fresh inference variables + // during generalization. This prevents hangs caused by an exponential blowup, + // see tests/ui/traits/next-solver/coherence-alias-hang.rs. + match self.current_goal_kind { + // We don't do so for `NormalizesTo` goals as we erased the expected term and + // bailing with overflow here would prevent us from detecting a type-mismatch, + // causing a coherence error in diesel, see #131969. We still bail with overflow + // when later returning from the parent AliasRelate goal. + CurrentGoalKind::NormalizesTo => {} + CurrentGoalKind::Misc | CurrentGoalKind::CoinductiveTrait => { + let num_non_region_vars = canonical + .variables + .iter() + .filter(|c| !c.is_region() && c.is_existential()) + .count(); + if num_non_region_vars > self.cx().recursion_limit() { + debug!(?num_non_region_vars, "too many inference variables -> overflow"); + return Ok(self.make_ambiguous_response_no_constraints( + MaybeCause::Overflow { + suggest_increasing_limit: true, + keep_constraints: false, + }, + OpaqueTypesJank::AllGood, + )); + } + } + } + + Ok(canonical) + } + + /// Constructs a totally unconstrained, ambiguous response to a goal. + /// + /// Take care when using this, since often it's useful to respond with + /// ambiguity but return constrained variables to guide inference. + pub(in crate::solve) fn make_ambiguous_response_no_constraints( + &self, + cause: MaybeCause, + opaque_types_jank: OpaqueTypesJank, + ) -> CanonicalResponse<I> { + response_no_constraints_raw( + self.cx(), + self.max_input_universe, + self.variables, + Certainty::Maybe { cause, opaque_types_jank }, + ) + } + + /// Computes the region constraints and *new* opaque types registered when + /// proving a goal. + /// + /// If an opaque was already constrained before proving this goal, then the + /// external constraints do not need to record that opaque, since if it is + /// further constrained by inference, that will be passed back in the var + /// values. + #[instrument(level = "trace", skip(self), ret)] + fn compute_external_query_constraints( + &self, + certainty: Certainty, + normalization_nested_goals: NestedNormalizationGoals<I>, + ) -> ExternalConstraintsData<I> { + // We only return region constraints once the certainty is `Yes`. This + // is necessary as we may drop nested goals on ambiguity, which may result + // in unconstrained inference variables in the region constraints. It also + // prevents us from emitting duplicate region constraints, avoiding some + // unnecessary work. This slightly weakens the leak check in case it uses + // region constraints from an ambiguous nested goal. This is tested in both + // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs` and + // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`. + let region_constraints = if certainty == Certainty::Yes { + self.delegate.make_deduplicated_outlives_constraints() + } else { + Default::default() + }; + + // We only return *newly defined* opaque types from canonical queries. + // + // Constraints for any existing opaque types are already tracked by changes + // to the `var_values`. + let opaque_types = self + .delegate + .clone_opaque_types_added_since(self.initial_opaque_types_storage_num_entries); + + ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } + } } /// Eagerly replace aliases with inference variables, emitting `AliasRelate` @@ -1363,7 +1557,7 @@ pub(super) fn evaluate_root_goal_for_proof_tree<D: SolverDelegate<Interner = I>, let opaque_types = delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(delegate, (goal, opaque_types)); - let (orig_values, canonical_goal) = EvalCtxt::canonicalize_goal(delegate, goal, opaque_types); + let (orig_values, canonical_goal) = canonicalize_goal(delegate, goal, opaque_types); let (canonical_result, final_revision) = delegate.cx().evaluate_root_goal_for_proof_tree_raw(canonical_goal); @@ -1380,7 +1574,7 @@ pub(super) fn evaluate_root_goal_for_proof_tree<D: SolverDelegate<Interner = I>, Ok(response) => response, }; - let (normalization_nested_goals, _certainty) = EvalCtxt::instantiate_and_apply_query_response( + let (normalization_nested_goals, _certainty) = instantiate_and_apply_query_response( delegate, goal.param_env, &proof_tree.orig_values, diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs index 2675ed0d0da..4369148baf9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs @@ -10,8 +10,8 @@ use derive_where::derive_where; use rustc_type_ir::inherent::*; use rustc_type_ir::{self as ty, Interner}; +use crate::canonical; use crate::delegate::SolverDelegate; -use crate::solve::eval_ctxt::canonical; use crate::solve::{Certainty, Goal, GoalSource, QueryResult, inspect}; /// We need to know whether to build a prove tree while evaluating. We diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs index 0d8c0060126..65f32f1947f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs @@ -2,5 +2,3 @@ pub use rustc_type_ir::solve::inspect::*; mod build; pub(in crate::solve) use build::*; - -pub use crate::solve::eval_ctxt::canonical::instantiate_canonical_state; diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index fb900b592d1..afb86aaf8ab 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -380,25 +380,6 @@ where } } -fn response_no_constraints_raw<I: Interner>( - cx: I, - max_universe: ty::UniverseIndex, - variables: I::CanonicalVarKinds, - certainty: Certainty, -) -> CanonicalResponse<I> { - ty::Canonical { - max_universe, - variables, - value: Response { - var_values: ty::CanonicalVarValues::make_identity(cx, variables), - // FIXME: maybe we should store the "no response" version in cx, like - // we do for cx.types and stuff. - external_constraints: cx.mk_external_constraints(ExternalConstraintsData::default()), - certainty, - }, - } -} - /// The result of evaluating a goal. pub struct GoalEvaluation<I: Interner> { /// The goal we've evaluated. This is the input goal, but potentially with its diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 289325d7055..aa9dfc9a9a2 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -6,6 +6,7 @@ use rustc_type_ir::search_graph::{self, PathKind}; use rustc_type_ir::solve::{CanonicalInput, Certainty, NoSolution, QueryResult}; use rustc_type_ir::{Interner, TypingMode}; +use crate::canonical::response_no_constraints_raw; use crate::delegate::SolverDelegate; use crate::solve::{ EvalCtxt, FIXPOINT_STEP_LIMIT, has_no_inference_or_external_constraints, inspect, @@ -127,7 +128,7 @@ fn response_no_constraints<I: Interner>( input: CanonicalInput<I>, certainty: Certainty, ) -> QueryResult<I> { - Ok(super::response_no_constraints_raw( + Ok(response_no_constraints_raw( cx, input.canonical.max_universe, input.canonical.variables, diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 75537caa894..6cd68380e46 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -349,10 +349,6 @@ passes_invalid_attr_at_crate_level = passes_invalid_attr_at_crate_level_item = the inner attribute doesn't annotate this {$kind} -passes_invalid_macro_export_arguments = invalid `#[macro_export]` argument - -passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments - passes_lang_item_fn = {$name -> [panic_impl] `#[panic_handler]` *[other] `{$name}` lang item @@ -392,9 +388,6 @@ passes_loop_match_attr = `#[loop_match]` should be applied to a loop .label = not a loop -passes_macro_export = - `#[macro_export]` only has an effect on macro definitions - passes_macro_export_on_decl_macro = `#[macro_export]` has no effect on declarative macro definitions .note = declarative macros follow the same exporting rules as regular items diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4d5a8447695..94c9e71ce77 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -38,8 +38,8 @@ use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; use rustc_session::lint; use rustc_session::lint::builtin::{ - CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS, - MALFORMED_DIAGNOSTIC_ATTRIBUTES, MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, + CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_ATTRIBUTES, + MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, }; use rustc_session::parse::feature_err; use rustc_span::edition::Edition; @@ -217,7 +217,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }, Attribute::Parsed(AttributeKind::Link(_, attr_span)) => { self.check_link(hir_id, *attr_span, span, target) - } + }, + Attribute::Parsed(AttributeKind::MacroExport { span, .. }) => { + self.check_macro_export(hir_id, *span, target) + }, Attribute::Parsed( AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect @@ -331,7 +334,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::rustc_has_incoherent_inherent_impls, ..] => { self.check_has_incoherent_inherent_impls(attr, span, target) } - [sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target), [sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => { self.check_autodiff(hir_id, attr, span, target) } @@ -1850,45 +1852,22 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) { + fn check_macro_export(&self, hir_id: HirId, attr_span: Span, target: Target) { if target != Target::MacroDef { + return; + } + + // special case when `#[macro_export]` is applied to a macro 2.0 + let (_, macro_definition, _) = self.tcx.hir_node(hir_id).expect_item().expect_macro(); + let is_decl_macro = !macro_definition.macro_rules; + + if is_decl_macro { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), - errors::MacroExport::Normal, + attr_span, + errors::MacroExport::OnDeclMacro, ); - } else if let Some(meta_item_list) = attr.meta_item_list() - && !meta_item_list.is_empty() - { - if meta_item_list.len() > 1 { - self.tcx.emit_node_span_lint( - INVALID_MACRO_EXPORT_ARGUMENTS, - hir_id, - attr.span(), - errors::MacroExport::TooManyItems, - ); - } else if !meta_item_list[0].has_name(sym::local_inner_macros) { - self.tcx.emit_node_span_lint( - INVALID_MACRO_EXPORT_ARGUMENTS, - hir_id, - meta_item_list[0].span(), - errors::MacroExport::InvalidArgument, - ); - } - } else { - // special case when `#[macro_export]` is applied to a macro 2.0 - let (_, macro_definition, _) = self.tcx.hir_node(hir_id).expect_item().expect_macro(); - let is_decl_macro = !macro_definition.macro_rules; - - if is_decl_macro { - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - attr.span(), - errors::MacroExport::OnDeclMacro, - ); - } } } @@ -2253,7 +2232,9 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { // In the long run, the checks should be harmonized. if let ItemKind::Macro(_, macro_def, _) = item.kind { let def_id = item.owner_id.to_def_id(); - if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) { + if macro_def.macro_rules + && !find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::MacroExport { .. }) + { check_non_exported_macro_for_invalid_attrs(self.tcx, item); } } @@ -2384,7 +2365,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { // which were unsuccessfully resolved due to cannot determine // resolution for the attribute macro error. const ATTRS_TO_CHECK: &[Symbol] = &[ - sym::macro_export, sym::rustc_main, sym::derive, sym::test, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 2da4b6f52cf..cd8935f6b2f 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -530,18 +530,9 @@ pub(crate) struct RustcForceInlineCoro { #[derive(LintDiagnostic)] pub(crate) enum MacroExport { - #[diag(passes_macro_export)] - Normal, - #[diag(passes_macro_export_on_decl_macro)] #[note] OnDeclMacro, - - #[diag(passes_invalid_macro_export_arguments)] - InvalidArgument, - - #[diag(passes_invalid_macro_export_arguments_too_many_items)] - TooManyItems, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 297df7c2c97..81ada79dd43 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -29,7 +29,8 @@ use rustc_span::{ SourceFileHashAlgorithm, Symbol, sym, }; use rustc_target::spec::{ - FramePointer, LinkSelfContainedComponents, LinkerFeatures, SplitDebuginfo, Target, TargetTuple, + FramePointer, LinkSelfContainedComponents, LinkerFeatures, PanicStrategy, SplitDebuginfo, + Target, TargetTuple, }; use tracing::debug; @@ -1509,6 +1510,11 @@ impl Options { pub fn get_symbol_mangling_version(&self) -> SymbolManglingVersion { self.cg.symbol_mangling_version.unwrap_or(SymbolManglingVersion::Legacy) } + + #[inline] + pub fn autodiff_enabled(&self) -> bool { + self.unstable_opts.autodiff.contains(&AutoDiff::Enable) + } } impl UnstableOptions { @@ -2794,6 +2800,12 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M } } + if !unstable_options_enabled && cg.panic == Some(PanicStrategy::ImmediateAbort) { + early_dcx.early_fatal( + "`-Cpanic=immediate-abort` requires `-Zunstable-options` and a nightly compiler", + ) + } + let crate_name = matches.opt_str("crate-name"); let unstable_features = UnstableFeatures::from_environment(crate_name.as_deref()); // Parse any `-l` flags, which link to native libraries. diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 8f63ce6f0ae..f3d91ce4a5d 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -125,7 +125,9 @@ pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) { None | Some(_), ) => disallow(cfg, "-Z sanitizer=cfi"), (sym::proc_macro, None) => disallow(cfg, "--crate-type proc-macro"), - (sym::panic, Some(sym::abort | sym::unwind)) => disallow(cfg, "-C panic"), + (sym::panic, Some(sym::abort | sym::unwind | sym::immediate_abort)) => { + disallow(cfg, "-C panic") + } (sym::target_feature, Some(_)) => disallow(cfg, "-C target-feature"), (sym::unix, None) | (sym::windows, None) @@ -203,7 +205,14 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg { ins_none!(sym::overflow_checks); } + // We insert a cfg for the name of session's panic strategy. + // Since the ImmediateAbort strategy is new, it also sets cfg(panic="abort"), so that code + // which is trying to detect whether unwinding is enabled by checking for cfg(panic="abort") + // does not need to be updated. ins_sym!(sym::panic, sess.panic_strategy().desc_symbol()); + if sess.panic_strategy() == PanicStrategy::ImmediateAbort { + ins_sym!(sym::panic, PanicStrategy::Abort.desc_symbol()); + } // JUSTIFICATION: before wrapper fn is available #[allow(rustc::bad_opt_access)] @@ -374,11 +383,13 @@ impl CheckCfg { ins!(sym::overflow_checks, no_values); - ins!(sym::panic, empty_values).extend(&PanicStrategy::all()); + ins!(sym::panic, empty_values) + .extend(PanicStrategy::ALL.iter().map(PanicStrategy::desc_symbol)); ins!(sym::proc_macro, no_values); - ins!(sym::relocation_model, empty_values).extend(RelocModel::all()); + ins!(sym::relocation_model, empty_values) + .extend(RelocModel::ALL.iter().map(RelocModel::desc_symbol)); let sanitize_values = SanitizerSet::all() .into_iter() diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 69facde6936..3b9d8117786 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -802,7 +802,7 @@ mod desc { pub(crate) const parse_threads: &str = parse_number; pub(crate) const parse_time_passes_format: &str = "`text` (default) or `json`"; pub(crate) const parse_passes: &str = "a space-separated list of passes, or `all`"; - pub(crate) const parse_panic_strategy: &str = "either `unwind` or `abort`"; + pub(crate) const parse_panic_strategy: &str = "either `unwind`, `abort`, or `immediate-abort`"; pub(crate) const parse_on_broken_pipe: &str = "either `kill`, `error`, or `inherit`"; pub(crate) const parse_patchable_function_entry: &str = "either two comma separated integers (total_nops,prefix_nops), with prefix_nops <= total_nops, or one integer (total_nops)"; pub(crate) const parse_opt_panic_strategy: &str = parse_panic_strategy; @@ -1165,6 +1165,7 @@ pub mod parse { match v { Some("unwind") => *slot = Some(PanicStrategy::Unwind), Some("abort") => *slot = Some(PanicStrategy::Abort), + Some("immediate-abort") => *slot = Some(PanicStrategy::ImmediateAbort), _ => return false, } true @@ -1174,6 +1175,7 @@ pub mod parse { match v { Some("unwind") => *slot = PanicStrategy::Unwind, Some("abort") => *slot = PanicStrategy::Abort, + Some("immediate-abort") => *slot = PanicStrategy::ImmediateAbort, _ => return false, } true diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 3525c7c1d1a..25b46241c52 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -600,6 +600,13 @@ impl Session { /// Calculates the flavor of LTO to use for this compilation. pub fn lto(&self) -> config::Lto { + // Autodiff currently requires fat-lto to have access to the llvm-ir of all (indirectly) used functions and types. + // fat-lto is the easiest solution to this requirement, but quite expensive. + // FIXME(autodiff): Make autodiff also work with embed-bc instead of fat-lto. + if self.opts.autodiff_enabled() { + return config::Lto::Fat; + } + // If our target has codegen requirements ignore the command line if self.target.requires_lto { return config::Lto::Fat; @@ -770,9 +777,11 @@ impl Session { // Otherwise, we can defer to the `-C force-unwind-tables=<yes/no>` // value, if it is provided, or disable them, if not. self.target.requires_uwtable - || self.opts.cg.force_unwind_tables.unwrap_or( - self.panic_strategy() == PanicStrategy::Unwind || self.target.default_uwtable, - ) + || self + .opts + .cg + .force_unwind_tables + .unwrap_or(self.panic_strategy().unwinds() || self.target.default_uwtable) } /// Returns the number of query threads that should be used for this @@ -1222,7 +1231,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } // KCFI requires panic=abort - if sess.is_sanitizer_kcfi_enabled() && sess.panic_strategy() != PanicStrategy::Abort { + if sess.is_sanitizer_kcfi_enabled() && sess.panic_strategy().unwinds() { sess.dcx().emit_err(errors::SanitizerKcfiRequiresPanicAbort); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4fef65f46b1..4e48c96afd6 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -794,6 +794,7 @@ symbols! { ctlz, ctlz_nonzero, ctpop, + ctr, cttz, cttz_nonzero, custom_attribute, @@ -1195,6 +1196,7 @@ symbols! { if_let_rescope, if_while_or_patterns, ignore, + immediate_abort: "immediate-abort", impl_header_lifetime_elision, impl_lint_pass, impl_trait_in_assoc_type, @@ -1333,6 +1335,7 @@ symbols! { loongarch_target_feature, loop_break_value, loop_match, + lr, lt, m68k_target_feature, macro_at_most_once_rep, diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index e06f881e4b1..0601613567e 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -1260,11 +1260,12 @@ impl InlineAsmClobberAbi { v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, - // cr0-cr1, cr5-cr7, xer + // cr0-cr1, cr5-cr7, ctr, lr, xer cr0, cr1, cr5, cr6, cr7, + ctr, + lr, xer, - // lr and ctr are reserved } }, InlineAsmClobberAbi::S390x => clobbered_regs! { diff --git a/compiler/rustc_target/src/asm/powerpc.rs b/compiler/rustc_target/src/asm/powerpc.rs index f3934afa6d9..2348a0fd202 100644 --- a/compiler/rustc_target/src/asm/powerpc.rs +++ b/compiler/rustc_target/src/asm/powerpc.rs @@ -13,6 +13,8 @@ def_reg_class! { freg, vreg, cr, + ctr, + lr, xer, } } @@ -56,7 +58,7 @@ impl PowerPCInlineAsmRegClass { altivec: VecI8(16), VecI16(8), VecI32(4), VecF32(4); vsx: F32, F64, VecI64(2), VecF64(2); }, - Self::cr | Self::xer => &[], + Self::cr | Self::ctr | Self::lr | Self::xer => &[], } } } @@ -195,6 +197,8 @@ def_regs! { cr5: cr = ["cr5"], cr6: cr = ["cr6"], cr7: cr = ["cr7"], + ctr: ctr = ["ctr"], + lr: lr = ["lr"], xer: xer = ["xer"], #error = ["r1", "1", "sp"] => "the stack pointer cannot be used as an operand for inline asm", @@ -206,10 +210,6 @@ def_regs! { "r30 is used internally by LLVM and cannot be used as an operand for inline asm", #error = ["r31", "31", "fp"] => "the frame pointer cannot be used as an operand for inline asm", - #error = ["lr"] => - "the link register cannot be used as an operand for inline asm", - #error = ["ctr"] => - "the counter register cannot be used as an operand for inline asm", #error = ["vrsave"] => "the vrsave register cannot be used as an operand for inline asm", } @@ -247,6 +247,8 @@ impl PowerPCInlineAsmReg { (v24, "24"), (v25, "25"), (v26, "26"), (v27, "27"), (v28, "28"), (v29, "29"), (v30, "30"), (v31, "31"); (cr, "cr"); (cr0, "0"), (cr1, "1"), (cr2, "2"), (cr3, "3"), (cr4, "4"), (cr5, "5"), (cr6, "6"), (cr7, "7"); + (ctr, "ctr"); + (lr, "lr"); (xer, "xer"); } } diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index b3fe1fffcce..8c6a77cba8b 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -76,10 +76,10 @@ fn find_relative_libdir(sysroot: &Path) -> std::borrow::Cow<'static, str> { macro_rules! target_spec_enum { ( $( #[$attr:meta] )* - pub enum $name:ident { + pub enum $Name:ident { $( $( #[$variant_attr:meta] )* - $variant:ident = $string:literal, + $Variant:ident = $string:literal, )* } parse_error_type = $parse_error_type:literal; @@ -87,20 +87,20 @@ macro_rules! target_spec_enum { $( #[$attr] )* #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] #[derive(schemars::JsonSchema)] - pub enum $name { + pub enum $Name { $( $( #[$variant_attr] )* #[serde(rename = $string)] // for JSON schema generation only - $variant, + $Variant, )* } - impl FromStr for $name { + impl FromStr for $Name { type Err = String; fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(match s { - $( $string => Self::$variant, )* + $( $string => Self::$Variant, )* _ => { let all = [$( concat!("'", $string, "'") ),*].join(", "); return Err(format!("invalid {}: '{s}'. allowed values: {all}", $parse_error_type)); @@ -109,24 +109,25 @@ macro_rules! target_spec_enum { } } - impl $name { + impl $Name { + pub const ALL: &'static [$Name] = &[ $( $Name::$Variant, )* ]; pub fn desc(&self) -> &'static str { match self { - $( Self::$variant => $string, )* + $( Self::$Variant => $string, )* } } } - impl crate::json::ToJson for $name { + impl crate::json::ToJson for $Name { fn to_json(&self) -> crate::json::Json { self.desc().to_json() } } - crate::json::serde_deserialize_from_str!($name); + crate::json::serde_deserialize_from_str!($Name); - impl std::fmt::Display for $name { + impl std::fmt::Display for $Name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(self.desc()) } diff --git a/compiler/rustc_target/src/spec/base/mod.rs b/compiler/rustc_target/src/spec/base/mod.rs index be15da7329d..6ab8597a4ec 100644 --- a/compiler/rustc_target/src/spec/base/mod.rs +++ b/compiler/rustc_target/src/spec/base/mod.rs @@ -21,6 +21,7 @@ pub(crate) mod linux_uclibc; pub(crate) mod linux_wasm; pub(crate) mod lynxos178; pub(crate) mod managarm_mlibc; +pub(crate) mod motor; pub(crate) mod msvc; pub(crate) mod netbsd; pub(crate) mod nto_qnx; diff --git a/compiler/rustc_target/src/spec/base/motor.rs b/compiler/rustc_target/src/spec/base/motor.rs new file mode 100644 index 00000000000..18485b2cef2 --- /dev/null +++ b/compiler/rustc_target/src/spec/base/motor.rs @@ -0,0 +1,34 @@ +use crate::spec::{ + Cc, FramePointer, LinkerFlavor, Lld, PanicStrategy, StackProbeType, TargetOptions, +}; + +pub(crate) fn opts() -> TargetOptions { + let pre_link_args = TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &[ + "-e", + "motor_start", + "--no-undefined", + "--error-unresolved-symbols", + "--no-undefined-version", + "-u", + "__rust_abort", + ], + ); + TargetOptions { + os: "motor".into(), + executables: true, + // TLS is false below because if true, the compiler assumes + // we handle TLS at the ELF loading level, which we don't. + // We use "OS level" TLS (see thread/local.rs in stdlib). + has_thread_local: false, + frame_pointer: FramePointer::NonLeaf, + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::No), + main_needs_argc_argv: true, + panic_strategy: PanicStrategy::Abort, + pre_link_args, + stack_probes: StackProbeType::Inline, + supports_stack_protector: true, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/base/wasm.rs b/compiler/rustc_target/src/spec/base/wasm.rs index 88e7af5e669..7ede45766ea 100644 --- a/compiler/rustc_target/src/spec/base/wasm.rs +++ b/compiler/rustc_target/src/spec/base/wasm.rs @@ -81,11 +81,6 @@ pub(crate) fn options() -> TargetOptions { // threaded model which will legalize atomics to normal operations. singlethread: true, - // Symbol visibility takes care of this for the WebAssembly. - // Additionally the only known linker, LLD, doesn't support the script - // arguments just yet - limit_rdylib_exports: false, - // we use the LLD shipped with the Rust toolchain by default linker: Some("rust-lld".into()), linker_flavor: LinkerFlavor::WasmLld(Cc::No), diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 07fb1ce63f7..4a82a8bd888 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -834,6 +834,7 @@ crate::target_spec_enum! { pub enum PanicStrategy { Unwind = "unwind", Abort = "abort", + ImmediateAbort = "immediate-abort", } parse_error_type = "panic strategy"; @@ -852,11 +853,12 @@ impl PanicStrategy { match *self { PanicStrategy::Unwind => sym::unwind, PanicStrategy::Abort => sym::abort, + PanicStrategy::ImmediateAbort => sym::immediate_abort, } } - pub const fn all() -> [Symbol; 2] { - [Self::Abort.desc_symbol(), Self::Unwind.desc_symbol()] + pub fn unwinds(self) -> bool { + matches!(self, PanicStrategy::Unwind) } } @@ -974,18 +976,6 @@ impl RelocModel { RelocModel::RopiRwpi => sym::ropi_rwpi, } } - - pub const fn all() -> [Symbol; 7] { - [ - RelocModel::Static.desc_symbol(), - RelocModel::Pic.desc_symbol(), - RelocModel::Pie.desc_symbol(), - RelocModel::DynamicNoPic.desc_symbol(), - RelocModel::Ropi.desc_symbol(), - RelocModel::Rwpi.desc_symbol(), - RelocModel::RopiRwpi.desc_symbol(), - ] - } } crate::target_spec_enum! { @@ -1658,6 +1648,7 @@ supported_targets! { ("aarch64-unknown-hermit", aarch64_unknown_hermit), ("riscv64gc-unknown-hermit", riscv64gc_unknown_hermit), ("x86_64-unknown-hermit", x86_64_unknown_hermit), + ("x86_64-unknown-motor", x86_64_unknown_motor), ("x86_64-unikraft-linux-musl", x86_64_unikraft_linux_musl), diff --git a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs index d42e097b0fd..38c3c7dfaa1 100644 --- a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs @@ -5,8 +5,6 @@ pub(crate) fn target() -> Target { base.cpu = "mips64r2".into(); base.features = "+mips64r2,+xgot".into(); base.max_atomic_width = Some(64); - // FIXME(compiler-team#422): musl targets should be dynamically linked by default. - base.crt_static_default = true; Target { // LLVM doesn't recognize "muslabi64" yet. llvm_target: "mips64el-unknown-linux-musl".into(), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_motor.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_motor.rs new file mode 100644 index 00000000000..0fd43357a76 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_motor.rs @@ -0,0 +1,38 @@ +use crate::spec::{ + CodeModel, LinkSelfContainedDefault, LldFlavor, RelocModel, RelroLevel, Target, base, +}; + +pub(crate) fn target() -> Target { + let mut base = base::motor::opts(); + base.cpu = "x86-64".into(); + base.max_atomic_width = Some(64); + base.code_model = Some(CodeModel::Small); + + // We want fully static relocatable binaries. It was surprisingly + // difficult to make it happen reliably, especially various + // linker-related options below. Mostly trial and error. + base.position_independent_executables = true; + base.relro_level = RelroLevel::Full; + base.static_position_independent_executables = true; + base.relocation_model = RelocModel::Pic; + base.lld_flavor_json = LldFlavor::Ld; + base.link_self_contained = LinkSelfContainedDefault::True; + base.dynamic_linking = false; + base.crt_static_default = true; + base.crt_static_respected = true; + + Target { + llvm_target: "x86_64-unknown-none-elf".into(), + metadata: crate::spec::TargetMetadata { + description: Some("Motor OS".into()), + tier: Some(3), + host_tools: None, + std: None, + }, + pointer_width: 64, + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), + arch: "x86_64".into(), + options: base, + } +} diff --git a/compiler/rustc_thread_pool/src/latch.rs b/compiler/rustc_thread_pool/src/latch.rs index 18d654d9f78..58dabaf35c0 100644 --- a/compiler/rustc_thread_pool/src/latch.rs +++ b/compiler/rustc_thread_pool/src/latch.rs @@ -388,13 +388,17 @@ impl Latch for CountLatch { #[inline] unsafe fn set(this: *const Self) { if unsafe { (*this).counter.fetch_sub(1, Ordering::SeqCst) == 1 } { - // NOTE: Once we call `set` on the internal `latch`, + // SAFETY: Once we call `set` on the internal `latch`, // the target may proceed and invalidate `this`! match unsafe { &(*this).kind } { CountLatchKind::Stealing { latch, registry, worker_index } => { let registry = Arc::clone(registry); + let worker_index = *worker_index; + // SAFETY: We don't use any references from `this` after this call. if unsafe { CoreLatch::set(latch) } { - registry.notify_worker_latch_is_set(*worker_index); + // We **must not** access any part of `this` anymore, which + // is why we read and shadowed these fields beforehand. + registry.notify_worker_latch_is_set(worker_index); } } CountLatchKind::Blocking { latch } => unsafe { LockLatch::set(latch) }, diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 086a7a44786..c010add0fc5 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -18,9 +18,9 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, NoSolution, QueryResult}; use rustc_middle::ty::{TyCtxt, VisitorResult, try_visit}; use rustc_middle::{bug, ty}; +use rustc_next_trait_solver::canonical::instantiate_canonical_state; use rustc_next_trait_solver::resolve::eager_resolve_vars; -use rustc_next_trait_solver::solve::inspect::{self, instantiate_canonical_state}; -use rustc_next_trait_solver::solve::{MaybeCause, SolverDelegateEvalExt as _}; +use rustc_next_trait_solver::solve::{MaybeCause, SolverDelegateEvalExt as _, inspect}; use rustc_span::Span; use tracing::instrument; diff --git a/compiler/rustc_windows_rc/Cargo.toml b/compiler/rustc_windows_rc/Cargo.toml index 080acd35c38..13f716897fa 100644 --- a/compiler/rustc_windows_rc/Cargo.toml +++ b/compiler/rustc_windows_rc/Cargo.toml @@ -5,7 +5,5 @@ edition = "2024" [dependencies] #tidy-alphabetical-start -# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version -# per crate", so if you change this, you need to also change it in `rustc_llvm` and `rustc_codegen_ssa`. -cc = "=1.2.16" +find-msvc-tools = "0.1.2" #tidy-alphabetical-end diff --git a/compiler/rustc_windows_rc/src/lib.rs b/compiler/rustc_windows_rc/src/lib.rs index caa5e5ef276..15afaf7b94b 100644 --- a/compiler/rustc_windows_rc/src/lib.rs +++ b/compiler/rustc_windows_rc/src/lib.rs @@ -2,9 +2,7 @@ //! //! Uses values from the `CFG_VERSION` and `CFG_RELEASE` environment variables //! to set the product and file version information in the Windows resource file. -use std::{env, ffi, fs, path, process}; - -use cc::windows_registry; +use std::{env, fs, path, process}; /// The template for the Windows resource file. const RESOURCE_TEMPLATE: &str = include_str!("../rustc.rc.in"); @@ -35,8 +33,14 @@ pub fn compile_windows_resource_file( resources_dir.push("resources"); fs::create_dir_all(&resources_dir).unwrap(); - let resource_compiler = - find_resource_compiler(&env::var("CARGO_CFG_TARGET_ARCH").unwrap()).expect("found rc.exe"); + let resource_compiler = if let Ok(path) = env::var("RUSTC_WINDOWS_RC") { + path.into() + } else { + find_msvc_tools::find_tool(&env::var("CARGO_CFG_TARGET_ARCH").unwrap(), "rc.exe") + .expect("found rc.exe") + .path() + .to_owned() + }; let rc_path = resources_dir.join(file_stem.with_extension("rc")); @@ -131,28 +135,3 @@ fn parse_version(version: &str) -> Option<ResourceVersion> { Some(ResourceVersion { major, minor, patch, build: 0 }) } } - -/// Find the Windows SDK resource compiler `rc.exe` for the given architecture or target triple. -/// Returns `None` if the tool could not be found. -fn find_resource_compiler(arch_or_target: &str) -> Option<path::PathBuf> { - find_windows_sdk_tool(arch_or_target, "rc.exe") -} - -/// Find a Windows SDK tool for the given architecture or target triple. -/// Returns `None` if the tool could not be found. -fn find_windows_sdk_tool(arch_or_target: &str, tool_name: &str) -> Option<path::PathBuf> { - // windows_registry::find_tool can only find MSVC tools, not Windows SDK tools, but - // cc does include the Windows SDK tools in the PATH environment of MSVC tools. - - let msvc_linker = windows_registry::find_tool(arch_or_target, "link.exe")?; - let path = &msvc_linker.env().iter().find(|(k, _)| k == "PATH")?.1; - find_tool_in_path(tool_name, path) -} - -/// Find a tool in the directories in a given PATH-like string. -fn find_tool_in_path<P: AsRef<ffi::OsStr>>(tool_name: &str, path: P) -> Option<path::PathBuf> { - env::split_paths(path.as_ref()).find_map(|p| { - let tool_path = p.join(tool_name); - if tool_path.try_exists().unwrap_or(false) { Some(tool_path) } else { None } - }) -} |
