diff options
48 files changed, 844 insertions, 332 deletions
diff --git a/Cargo.lock b/Cargo.lock index 6505aa52c4e..b8fe1ebaf80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,7 +214,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9792d37ca5173d7e7f4fe453739a0671d0557915a030a383d6b866476bbc3e71" dependencies = [ - "object", + "object 0.32.2", ] [[package]] @@ -281,7 +281,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.32.2", "rustc-demangle", ] @@ -2636,11 +2636,22 @@ dependencies = [ "memchr", "rustc-std-workspace-alloc", "rustc-std-workspace-core", - "ruzstd", + "ruzstd 0.5.0", "wasmparser", ] [[package]] +name = "object" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7090bae93f8585aad99e595b7073c5de9ba89fbd6b4e9f0cdd7a10177273ac8" +dependencies = [ + "flate2", + "memchr", + "ruzstd 0.6.0", +] + +[[package]] name = "odht" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3323,6 +3334,7 @@ dependencies = [ name = "run_make_support" version = "0.0.0" dependencies = [ + "object 0.34.0", "wasmparser", ] @@ -3634,7 +3646,7 @@ dependencies = [ "itertools 0.12.1", "libc", "measureme", - "object", + "object 0.32.2", "rustc-demangle", "rustc_ast", "rustc_attr", @@ -3670,7 +3682,7 @@ dependencies = [ "itertools 0.12.1", "jobserver", "libc", - "object", + "object 0.32.2", "pathdiff", "regex", "rustc_arena", @@ -3686,6 +3698,7 @@ dependencies = [ "rustc_macros", "rustc_metadata", "rustc_middle", + "rustc_monomorphize", "rustc_query_system", "rustc_serialize", "rustc_session", @@ -4630,7 +4643,7 @@ name = "rustc_target" version = "0.0.0" dependencies = [ "bitflags 2.4.2", - "object", + "object 0.32.2", "rustc_abi", "rustc_data_structures", "rustc_feature", @@ -4898,6 +4911,17 @@ dependencies = [ ] [[package]] +name = "ruzstd" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5174a470eeb535a721ae9fdd6e291c2411a906b96592182d05217591d5c5cf7b" +dependencies = [ + "byteorder", + "derive_more", + "twox-hash", +] + +[[package]] name = "ryu" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5200,7 +5224,7 @@ dependencies = [ "hermit-abi", "libc", "miniz_oxide", - "object", + "object 0.32.2", "panic_abort", "panic_unwind", "profiler_builtins", @@ -5517,7 +5541,7 @@ checksum = "4db52ee8fec06e119b692ef3dd2c4cf621a99204c1b8c47407870ed050305b9b" dependencies = [ "gimli", "hashbrown", - "object", + "object 0.32.2", "tracing", ] diff --git a/Cargo.toml b/Cargo.toml index 5dd315ef2f7..e12c968e205 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,15 +66,6 @@ exclude = [ ] [profile.release.package.compiler_builtins] -# The compiler-builtins crate cannot reference libcore, and its own CI will -# verify that this is the case. This requires, however, that the crate is built -# without overflow checks and debug assertions. Forcefully disable debug -# assertions and overflow checks here which should ensure that even if these -# assertions are enabled for libstd we won't enable them for compiler_builtins -# which should ensure we still link everything correctly. -debug-assertions = false -overflow-checks = false - # For compiler-builtins we always use a high number of codegen units. # The goal here is to place every single intrinsic into its own object # file to avoid symbol clashes with the system libgcc if possible. Note diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 6e846d721f2..b33587f8948 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -8,8 +8,11 @@ use std::borrow::Cow; use cranelift_codegen::ir::SigRef; use cranelift_module::ModuleError; +use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty::layout::FnAbiOf; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_session::Session; use rustc_span::source_map::Spanned; use rustc_target::abi::call::{Conv, FnAbi}; @@ -372,6 +375,17 @@ pub(crate) fn codegen_terminator_call<'tcx>( ty::Instance::expect_resolve(fx.tcx, ty::ParamEnv::reveal_all(), def_id, fn_args) .polymorphize(fx.tcx); + if is_call_from_compiler_builtins_to_upstream_monomorphization(fx.tcx, instance) { + if target.is_some() { + let caller = with_no_trimmed_paths!(fx.tcx.def_path_str(fx.instance.def_id())); + let callee = with_no_trimmed_paths!(fx.tcx.def_path_str(def_id)); + fx.tcx.dcx().emit_err(CompilerBuiltinsCannotCall { caller, callee }); + } else { + fx.bcx.ins().trap(TrapCode::User(0)); + return; + } + } + if fx.tcx.symbol_name(instance).name.starts_with("llvm.") { crate::intrinsics::codegen_llvm_intrinsic_call( fx, diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 2415c2c90b2..047dc56a32e 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -8,6 +8,7 @@ use rustc_index::IndexVec; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization; use crate::constant::ConstantCx; use crate::debuginfo::FunctionDebugContext; @@ -999,6 +1000,12 @@ fn codegen_panic_inner<'tcx>( let def_id = fx.tcx.require_lang_item(lang_item, span); let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx); + + if is_call_from_compiler_builtins_to_upstream_monomorphization(fx.tcx, instance) { + fx.bcx.ins().trap(TrapCode::User(0)); + return; + } + let symbol_name = fx.tcx.symbol_name(instance).name; fx.lib_call( diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 7e2e1f7c6ac..a59a39074f8 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -21,6 +21,7 @@ extern crate rustc_hir; extern crate rustc_incremental; extern crate rustc_index; extern crate rustc_metadata; +extern crate rustc_monomorphize; extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 7851b9e8e03..baf10622a6d 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -25,6 +25,7 @@ rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } +rustc_monomorphize = { path = "../rustc_monomorphize" } rustc_query_system = { path = "../rustc_query_system" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 5ba66d1be43..d159fe58d3e 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -16,6 +16,9 @@ codegen_ssa_cgu_not_recorded = codegen_ssa_check_installed_visual_studio = please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option. +codegen_ssa_compiler_builtins_cannot_call = + `compiler_builtins` cannot call functions through upstream monomorphizations; encountered invalid call from `{$caller}` to `{$callee}` + codegen_ssa_copy_path = could not copy {$from} to {$to}: {$error} codegen_ssa_copy_path_buf = unable to copy {$source_file} to {$output_path}: {$error} diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 44a2434238d..71fca403def 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -2,6 +2,7 @@ use rustc_hir::LangItem; use rustc_middle::mir; +use rustc_middle::ty::Instance; use rustc_middle::ty::{self, layout::TyAndLayout, Ty, TyCtxt}; use rustc_span::Span; @@ -120,11 +121,11 @@ pub fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &Bx, span: Option<Span>, li: LangItem, -) -> (Bx::FnAbiOfResult, Bx::Value) { +) -> (Bx::FnAbiOfResult, Bx::Value, Instance<'tcx>) { let tcx = bx.tcx(); let def_id = tcx.require_lang_item(li, span); let instance = ty::Instance::mono(tcx, def_id); - (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance)) + (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance), instance) } // To avoid UB from LLVM, these two functions mask RHS with an diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 3572ee301c8..b843d1bdf23 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1030,3 +1030,10 @@ pub struct FailedToGetLayout<'tcx> { pub struct ErrorCreatingRemarkDir { pub error: std::io::Error, } + +#[derive(Diagnostic)] +#[diag(codegen_ssa_compiler_builtins_cannot_call)] +pub struct CompilerBuiltinsCannotCall { + pub caller: String, + pub callee: String, +} diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 02e7bb05b77..dcc27a4f0e5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -5,6 +5,7 @@ use super::{CachedLlbb, FunctionCx, LocalRef}; use crate::base; use crate::common::{self, IntPredicate}; +use crate::errors::CompilerBuiltinsCannotCall; use crate::meth; use crate::traits::*; use crate::MemFlags; @@ -16,6 +17,7 @@ use rustc_middle::mir::{self, AssertKind, BasicBlock, SwitchTargets, UnwindTermi use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty}; +use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_session::config::OptLevel; use rustc_span::{source_map::Spanned, sym, Span}; use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode, Reg}; @@ -157,8 +159,28 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>, mut unwind: mir::UnwindAction, copied_constant_arguments: &[PlaceRef<'tcx, <Bx as BackendTypes>::Value>], + instance: Option<Instance<'tcx>>, mergeable_succ: bool, ) -> MergingSucc { + let tcx = bx.tcx(); + if let Some(instance) = instance { + if is_call_from_compiler_builtins_to_upstream_monomorphization(tcx, instance) { + if destination.is_some() { + let caller = with_no_trimmed_paths!(tcx.def_path_str(fx.instance.def_id())); + let callee = with_no_trimmed_paths!(tcx.def_path_str(instance.def_id())); + tcx.dcx().emit_err(CompilerBuiltinsCannotCall { caller, callee }); + } else { + info!( + "compiler_builtins call to diverging function {:?} replaced with abort", + instance.def_id() + ); + bx.abort(); + bx.unreachable(); + return MergingSucc::False; + } + } + } + // If there is a cleanup block and the function we're calling can unwind, then // do an invoke, otherwise do a call. let fn_ty = bx.fn_decl_backend_type(fn_abi); @@ -480,6 +502,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ty = location.ty(self.mir, bx.tcx()).ty; let ty = self.monomorphize(ty); let drop_fn = Instance::resolve_drop_in_place(bx.tcx(), ty); + let instance = drop_fn.clone(); if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { // we don't actually need to drop anything. @@ -582,6 +605,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { Some((ReturnDest::Nothing, target)), unwind, &[], + Some(instance), mergeable_succ, ) } @@ -658,10 +682,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } }; - let (fn_abi, llfn) = common::build_langcall(bx, Some(span), lang_item); + let (fn_abi, llfn, instance) = common::build_langcall(bx, Some(span), lang_item); // Codegen the actual panic invoke/call. - let merging_succ = helper.do_call(self, bx, fn_abi, llfn, &args, None, unwind, &[], false); + let merging_succ = + helper.do_call(self, bx, fn_abi, llfn, &args, None, unwind, &[], Some(instance), false); assert_eq!(merging_succ, MergingSucc::False); MergingSucc::False } @@ -677,7 +702,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.set_debug_loc(bx, terminator.source_info); // Obtain the panic entry point. - let (fn_abi, llfn) = common::build_langcall(bx, Some(span), reason.lang_item()); + let (fn_abi, llfn, instance) = common::build_langcall(bx, Some(span), reason.lang_item()); // Codegen the actual panic invoke/call. let merging_succ = helper.do_call( @@ -689,6 +714,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { None, mir::UnwindAction::Unreachable, &[], + Some(instance), false, ); assert_eq!(merging_succ, MergingSucc::False); @@ -738,7 +764,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let msg = bx.const_str(&msg_str); // Obtain the panic entry point. - let (fn_abi, llfn) = + let (fn_abi, llfn, instance) = common::build_langcall(bx, Some(source_info.span), LangItem::PanicNounwind); // Codegen the actual panic invoke/call. @@ -751,6 +777,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { target.as_ref().map(|bb| (ReturnDest::Nothing, *bb)), unwind, &[], + Some(instance), mergeable_succ, ) } else { @@ -798,6 +825,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ty::FnPtr(_) => (None, Some(callee.immediate())), _ => bug!("{} is not callable", callee.layout.ty), }; + let def = instance.map(|i| i.def); if let Some(ty::InstanceDef::DropGlue(_, None)) = def { @@ -1106,6 +1134,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { destination, unwind, &copied_constant_arguments, + instance, mergeable_succ, ) } @@ -1664,11 +1693,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.set_debug_loc(&mut bx, mir::SourceInfo::outermost(self.mir.span)); - let (fn_abi, fn_ptr) = common::build_langcall(&bx, None, reason.lang_item()); - let fn_ty = bx.fn_decl_backend_type(fn_abi); + let (fn_abi, fn_ptr, instance) = common::build_langcall(&bx, None, reason.lang_item()); + if is_call_from_compiler_builtins_to_upstream_monomorphization(bx.tcx(), instance) { + bx.abort(); + } else { + let fn_ty = bx.fn_decl_backend_type(fn_abi); - let llret = bx.call(fn_ty, None, Some(fn_abi), fn_ptr, &[], funclet.as_ref()); - bx.apply_attrs_to_cleanup_callsite(llret); + let llret = bx.call(fn_ty, None, Some(fn_abi), fn_ptr, &[], funclet.as_ref()); + bx.apply_attrs_to_cleanup_callsite(llret); + } bx.unreachable(); diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index 087836ca37d..e2e95cede60 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -62,7 +62,8 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let msg = bx.const_str(&msg_str); // Obtain the panic entry point. - let (fn_abi, llfn) = common::build_langcall(bx, None, LangItem::PanicNounwind); + let (fn_abi, llfn, _instance) = + common::build_langcall(bx, None, LangItem::PanicNounwind); // Generate the call. // Cannot use `do_call` since we don't have a MIR terminator so we can't create a `TerminationCodegenHelper`. diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs new file mode 100644 index 00000000000..ba2e2a1e353 --- /dev/null +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -0,0 +1,193 @@ +use crate::interpret::{self, HasStaticRootDefId, ImmTy, Immediate, InterpCx, PointerArithmetic}; +use rustc_middle::mir::interpret::{AllocId, ConstAllocation, InterpResult}; +use rustc_middle::mir::*; +use rustc_middle::query::TyCtxtAt; +use rustc_middle::ty; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_span::def_id::DefId; + +/// Macro for machine-specific `InterpError` without allocation. +/// (These will never be shown to the user, but they help diagnose ICEs.) +pub macro throw_machine_stop_str($($tt:tt)*) {{ + // We make a new local type for it. The type itself does not carry any information, + // but its vtable (for the `MachineStopType` trait) does. + #[derive(Debug)] + struct Zst; + // Printing this type shows the desired string. + impl std::fmt::Display for Zst { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, $($tt)*) + } + } + + impl rustc_middle::mir::interpret::MachineStopType for Zst { + fn diagnostic_message(&self) -> rustc_errors::DiagMessage { + self.to_string().into() + } + + fn add_args( + self: Box<Self>, + _: &mut dyn FnMut(rustc_errors::DiagArgName, rustc_errors::DiagArgValue), + ) {} + } + throw_machine_stop!(Zst) +}} + +pub struct DummyMachine; + +impl HasStaticRootDefId for DummyMachine { + fn static_def_id(&self) -> Option<rustc_hir::def_id::LocalDefId> { + None + } +} + +impl<'mir, 'tcx: 'mir> interpret::Machine<'mir, 'tcx> for DummyMachine { + interpret::compile_time_machine!(<'mir, 'tcx>); + type MemoryKind = !; + const PANIC_ON_ALLOC_FAIL: bool = true; + + #[inline(always)] + fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { + false // no reason to enforce alignment + } + + fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool { + false + } + + fn before_access_global( + _tcx: TyCtxtAt<'tcx>, + _machine: &Self, + _alloc_id: AllocId, + alloc: ConstAllocation<'tcx>, + _static_def_id: Option<DefId>, + is_write: bool, + ) -> InterpResult<'tcx> { + if is_write { + throw_machine_stop_str!("can't write to global"); + } + + // If the static allocation is mutable, then we can't const prop it as its content + // might be different at runtime. + if alloc.inner().mutability.is_mut() { + throw_machine_stop_str!("can't access mutable globals in ConstProp"); + } + + Ok(()) + } + + fn find_mir_or_eval_fn( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _instance: ty::Instance<'tcx>, + _abi: rustc_target::spec::abi::Abi, + _args: &[interpret::FnArg<'tcx, Self::Provenance>], + _destination: &interpret::MPlaceTy<'tcx, Self::Provenance>, + _target: Option<BasicBlock>, + _unwind: UnwindAction, + ) -> interpret::InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> { + unimplemented!() + } + + fn panic_nounwind( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _msg: &str, + ) -> interpret::InterpResult<'tcx> { + unimplemented!() + } + + fn call_intrinsic( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _instance: ty::Instance<'tcx>, + _args: &[interpret::OpTy<'tcx, Self::Provenance>], + _destination: &interpret::MPlaceTy<'tcx, Self::Provenance>, + _target: Option<BasicBlock>, + _unwind: UnwindAction, + ) -> interpret::InterpResult<'tcx> { + unimplemented!() + } + + fn assert_panic( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _msg: &rustc_middle::mir::AssertMessage<'tcx>, + _unwind: UnwindAction, + ) -> interpret::InterpResult<'tcx> { + unimplemented!() + } + + fn binary_ptr_op( + ecx: &InterpCx<'mir, 'tcx, Self>, + bin_op: BinOp, + left: &interpret::ImmTy<'tcx, Self::Provenance>, + right: &interpret::ImmTy<'tcx, Self::Provenance>, + ) -> interpret::InterpResult<'tcx, (ImmTy<'tcx, Self::Provenance>, bool)> { + use rustc_middle::mir::BinOp::*; + Ok(match bin_op { + Eq | Ne | Lt | Le | Gt | Ge => { + // Types can differ, e.g. fn ptrs with different `for`. + assert_eq!(left.layout.abi, right.layout.abi); + let size = ecx.pointer_size(); + // Just compare the bits. ScalarPairs are compared lexicographically. + // We thus always compare pairs and simply fill scalars up with 0. + // If the pointer has provenance, `to_bits` will return `Err` and we bail out. + let left = match **left { + Immediate::Scalar(l) => (l.to_bits(size)?, 0), + Immediate::ScalarPair(l1, l2) => (l1.to_bits(size)?, l2.to_bits(size)?), + Immediate::Uninit => panic!("we should never see uninit data here"), + }; + let right = match **right { + Immediate::Scalar(r) => (r.to_bits(size)?, 0), + Immediate::ScalarPair(r1, r2) => (r1.to_bits(size)?, r2.to_bits(size)?), + Immediate::Uninit => panic!("we should never see uninit data here"), + }; + let res = match bin_op { + Eq => left == right, + Ne => left != right, + Lt => left < right, + Le => left <= right, + Gt => left > right, + Ge => left >= right, + _ => bug!(), + }; + (ImmTy::from_bool(res, *ecx.tcx), false) + } + + // Some more operations are possible with atomics. + // The return value always has the provenance of the *left* operand. + Add | Sub | BitOr | BitAnd | BitXor => { + throw_machine_stop_str!("pointer arithmetic is not handled") + } + + _ => span_bug!(ecx.cur_span(), "Invalid operator on pointers: {:?}", bin_op), + }) + } + + fn expose_ptr( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ptr: interpret::Pointer<Self::Provenance>, + ) -> interpret::InterpResult<'tcx> { + unimplemented!() + } + + fn init_frame_extra( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _frame: interpret::Frame<'mir, 'tcx, Self::Provenance>, + ) -> interpret::InterpResult< + 'tcx, + interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, + > { + unimplemented!() + } + + fn stack<'a>( + _ecx: &'a InterpCx<'mir, 'tcx, Self>, + ) -> &'a [interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] { + // Return an empty stack instead of panicking, as `cur_span` uses it to evaluate constants. + &[] + } + + fn stack_mut<'a>( + _ecx: &'a mut InterpCx<'mir, 'tcx, Self>, + ) -> &'a mut Vec<interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>> { + unimplemented!() + } +} diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 5a1c7cc4209..16bd0296247 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -3,7 +3,7 @@ use either::{Left, Right}; use rustc_hir::def::DefKind; use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo}; use rustc_middle::mir::{self, ConstAlloc, ConstValue}; -use rustc_middle::query::TyCtxtAt; +use rustc_middle::query::{Key, TyCtxtAt}; use rustc_middle::traits::Reveal; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -243,6 +243,24 @@ pub(crate) fn turn_into_const_value<'tcx>( op_to_const(&ecx, &mplace.into(), /* for diagnostics */ false) } +/// Computes the tag (if any) for a given type and variant. +#[instrument(skip(tcx), level = "debug")] +pub fn tag_for_variant_provider<'tcx>( + tcx: TyCtxt<'tcx>, + (ty, variant_index): (Ty<'tcx>, abi::VariantIdx), +) -> Option<ty::ScalarInt> { + assert!(ty.is_enum()); + + let ecx = InterpCx::new( + tcx, + ty.default_span(tcx), + ty::ParamEnv::reveal_all(), + crate::const_eval::DummyMachine, + ); + + ecx.tag_for_variant(ty, variant_index).unwrap().map(|(tag, _tag_field)| tag) +} + #[instrument(skip(tcx), level = "debug")] pub fn eval_to_const_value_raw_provider<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index d0d6adbfad0..b768c429070 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -7,12 +7,14 @@ use rustc_middle::ty::{self, Ty}; use crate::interpret::format_interp_error; +mod dummy_machine; mod error; mod eval_queries; mod fn_queries; mod machine; mod valtrees; +pub use dummy_machine::*; pub use error::*; pub use eval_queries::*; pub use fn_queries::*; diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index 6d4f6d0cb3c..40469c6632c 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -2,7 +2,7 @@ use rustc_middle::mir; use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, ScalarInt, Ty}; use rustc_target::abi::{self, TagEncoding}; use rustc_target::abi::{VariantIdx, Variants}; @@ -28,78 +28,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { throw_ub!(UninhabitedEnumVariantWritten(variant_index)) } - match dest.layout().variants { - abi::Variants::Single { index } => { - assert_eq!(index, variant_index); - } - abi::Variants::Multiple { - tag_encoding: TagEncoding::Direct, - tag: tag_layout, - tag_field, - .. - } => { + match self.tag_for_variant(dest.layout().ty, variant_index)? { + Some((tag, tag_field)) => { // No need to validate that the discriminant here because the - // `TyAndLayout::for_variant()` call earlier already checks the variant is valid. - - let discr_val = dest - .layout() - .ty - .discriminant_for_variant(*self.tcx, variant_index) - .unwrap() - .val; - - // raw discriminants for enums are isize or bigger during - // their computation, but the in-memory tag is the smallest possible - // representation - let size = tag_layout.size(self); - let tag_val = size.truncate(discr_val); - + // `TyAndLayout::for_variant()` call earlier already checks the + // variant is valid. let tag_dest = self.project_field(dest, tag_field)?; - self.write_scalar(Scalar::from_uint(tag_val, size), &tag_dest)?; + self.write_scalar(tag, &tag_dest) } - abi::Variants::Multiple { - tag_encoding: - TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start }, - tag: tag_layout, - tag_field, - .. - } => { - // No need to validate that the discriminant here because the - // `TyAndLayout::for_variant()` call earlier already checks the variant is valid. - - if variant_index != untagged_variant { - let variants_start = niche_variants.start().as_u32(); - let variant_index_relative = variant_index - .as_u32() - .checked_sub(variants_start) - .expect("overflow computing relative variant idx"); - // We need to use machine arithmetic when taking into account `niche_start`: - // tag_val = variant_index_relative + niche_start_val - let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?; - let niche_start_val = ImmTy::from_uint(niche_start, tag_layout); - let variant_index_relative_val = - ImmTy::from_uint(variant_index_relative, tag_layout); - let tag_val = self.wrapping_binary_op( - mir::BinOp::Add, - &variant_index_relative_val, - &niche_start_val, - )?; - // Write result. - let niche_dest = self.project_field(dest, tag_field)?; - self.write_immediate(*tag_val, &niche_dest)?; - } else { - // The untagged variant is implicitly encoded simply by having a value that is - // outside the niche variants. But what if the data stored here does not - // actually encode this variant? That would be bad! So let's double-check... - let actual_variant = self.read_discriminant(&dest.to_op(self)?)?; - if actual_variant != variant_index { - throw_ub!(InvalidNichedEnumVariantWritten { enum_ty: dest.layout().ty }); - } + None => { + // No need to write the tag here, because an untagged variant is + // implicitly encoded. For `Niche`-optimized enums, it's by + // simply by having a value that is outside the niche variants. + // But what if the data stored here does not actually encode + // this variant? That would be bad! So let's double-check... + let actual_variant = self.read_discriminant(&dest.to_op(self)?)?; + if actual_variant != variant_index { + throw_ub!(InvalidNichedEnumVariantWritten { enum_ty: dest.layout().ty }); } + Ok(()) } } - - Ok(()) } /// Read discriminant, return the runtime value as well as the variant index. @@ -277,4 +226,77 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; Ok(ImmTy::from_scalar(discr_value, discr_layout)) } + + /// Computes the tag value and its field number (if any) of a given variant + /// of type `ty`. + pub(crate) fn tag_for_variant( + &self, + ty: Ty<'tcx>, + variant_index: VariantIdx, + ) -> InterpResult<'tcx, Option<(ScalarInt, usize)>> { + match self.layout_of(ty)?.variants { + abi::Variants::Single { index } => { + assert_eq!(index, variant_index); + Ok(None) + } + + abi::Variants::Multiple { + tag_encoding: TagEncoding::Direct, + tag: tag_layout, + tag_field, + .. + } => { + // raw discriminants for enums are isize or bigger during + // their computation, but the in-memory tag is the smallest possible + // representation + let discr = self.discriminant_for_variant(ty, variant_index)?; + let discr_size = discr.layout.size; + let discr_val = discr.to_scalar().to_bits(discr_size)?; + let tag_size = tag_layout.size(self); + let tag_val = tag_size.truncate(discr_val); + let tag = ScalarInt::try_from_uint(tag_val, tag_size).unwrap(); + Ok(Some((tag, tag_field))) + } + + abi::Variants::Multiple { + tag_encoding: TagEncoding::Niche { untagged_variant, .. }, + .. + } if untagged_variant == variant_index => { + // The untagged variant is implicitly encoded simply by having a + // value that is outside the niche variants. + Ok(None) + } + + abi::Variants::Multiple { + tag_encoding: + TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start }, + tag: tag_layout, + tag_field, + .. + } => { + assert!(variant_index != untagged_variant); + let variants_start = niche_variants.start().as_u32(); + let variant_index_relative = variant_index + .as_u32() + .checked_sub(variants_start) + .expect("overflow computing relative variant idx"); + // We need to use machine arithmetic when taking into account `niche_start`: + // tag_val = variant_index_relative + niche_start_val + let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?; + let niche_start_val = ImmTy::from_uint(niche_start, tag_layout); + let variant_index_relative_val = + ImmTy::from_uint(variant_index_relative, tag_layout); + let tag = self + .wrapping_binary_op( + mir::BinOp::Add, + &variant_index_relative_val, + &niche_start_val, + )? + .to_scalar() + .try_to_int() + .unwrap(); + Ok(Some((tag, tag_field))) + } + } + } } diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 1e7ee208af1..633caf8d092 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -40,6 +40,7 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { const_eval::provide(providers); + providers.tag_for_variant = const_eval::tag_for_variant_provider; providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider; providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider; providers.eval_static_initializer = const_eval::eval_static_initializer_provider; diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs index 0f5ba26337a..dcab571eedf 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs @@ -435,6 +435,22 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { &self, num_params_to_take: usize, ) -> String { + let is_in_a_method_call = self + .tcx + .hir() + .parent_iter(self.path_segment.hir_id) + .skip(1) + .find_map(|(_, node)| match node { + hir::Node::Expr(expr) => Some(expr), + _ => None, + }) + .is_some_and(|expr| { + matches!( + expr.kind, + hir::ExprKind::MethodCall(hir::PathSegment { args: Some(_), .. }, ..) + ) + }); + let fn_sig = self.tcx.hir().get_if_local(self.def_id).and_then(hir::Node::fn_sig); let is_used_in_input = |def_id| { fn_sig.is_some_and(|fn_sig| { @@ -453,14 +469,17 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { .skip(self.params_offset + self.num_provided_type_or_const_args()) .take(num_params_to_take) .map(|param| match param.kind { - // This is being inferred from the item's inputs, no need to set it. - ty::GenericParamDefKind::Type { .. } if is_used_in_input(param.def_id) => { - "_".to_string() + // If it's in method call (turbofish), it might be inferred from the expression (e.g. `.collect::<Vec<_>>()`) + // If it is being inferred from the item's inputs, no need to set it. + ty::GenericParamDefKind::Type { .. } + if is_in_a_method_call || is_used_in_input(param.def_id) => + { + "_" } - _ => param.name.to_string(), + _ => param.name.as_str(), }) - .collect::<Vec<_>>() - .join(", ") + .intersperse(", ") + .collect() } fn get_unbound_associated_types(&self) -> Vec<String> { diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 220da19a29d..0ca958302f7 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -103,6 +103,8 @@ hir_typeck_no_associated_item = no {$item_kind} named `{$item_name}` found for { *[other] {" "}in the current scope } +hir_typeck_note_caller_chooses_ty_for_ty_param = the caller chooses a type for `{$ty_param_name}` which can be different from `{$found_ty}` + hir_typeck_note_edition_guide = for more on editions, read https://doc.rust-lang.org/edition-guide hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expected_ty}` to `{$expr_ty}` diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index df21b84f92e..eee4ac5ad23 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::Ty; use rustc_span::{ edition::{Edition, LATEST_STABLE_EDITION}, symbol::Ident, - Span, + Span, Symbol, }; #[derive(Diagnostic)] @@ -614,3 +614,10 @@ pub struct SuggestConvertViaMethod<'tcx> { pub expected: Ty<'tcx>, pub found: Ty<'tcx>, } + +#[derive(Subdiagnostic)] +#[note(hir_typeck_note_caller_chooses_ty_for_ty_param)] +pub struct NoteCallerChoosesTyForTyParam<'tcx> { + pub ty_param_name: Symbol, + pub found_ty: Ty<'tcx>, +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 21f52f72080..e4ff7317f2e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -889,7 +889,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.dcx(), errors::ExpectedReturnTypeLabel::Other { span: hir_ty.span, expected }, ); - self.try_suggest_return_impl_trait(err, expected, ty, fn_id); + self.try_suggest_return_impl_trait(err, expected, found, fn_id); + self.note_caller_chooses_ty_for_ty_param(err, expected, found); return true; } } @@ -899,6 +900,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + fn note_caller_chooses_ty_for_ty_param( + &self, + diag: &mut Diag<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + if let ty::Param(expected_ty_as_param) = expected.kind() { + diag.subdiagnostic( + self.dcx(), + errors::NoteCallerChoosesTyForTyParam { + ty_param_name: expected_ty_as_param.name, + found_ty: found, + }, + ); + } + } + /// check whether the return type is a generic type with a trait bound /// only suggest this if the generic param is not present in the arguments /// if this is true, hint them towards changing the return type to `impl Trait` diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 33ee3371605..d3da49c26a2 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -234,6 +234,7 @@ trivial! { Option<rustc_middle::middle::stability::DeprecationEntry>, Option<rustc_middle::ty::Destructor>, Option<rustc_middle::ty::ImplTraitInTraitData>, + Option<rustc_middle::ty::ScalarInt>, Option<rustc_span::def_id::CrateNum>, Option<rustc_span::def_id::DefId>, Option<rustc_span::def_id::LocalDefId>, diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 69d3974184d..3b1d1a04d6f 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -13,6 +13,7 @@ use rustc_query_system::query::DefIdCacheSelector; use rustc_query_system::query::{DefaultCacheSelector, SingleCacheSelector, VecCacheSelector}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi; /// Placeholder for `CrateNum`'s "local" counterpart #[derive(Copy, Clone, Debug)] @@ -502,6 +503,14 @@ impl<'tcx> Key for (DefId, Ty<'tcx>, GenericArgsRef<'tcx>, ty::ParamEnv<'tcx>) { } } +impl<'tcx> Key for (Ty<'tcx>, abi::VariantIdx) { + type CacheSelector = DefaultCacheSelector<Self>; + + fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + impl<'tcx> Key for (ty::Predicate<'tcx>, traits::WellFormedLoc) { type CacheSelector = DefaultCacheSelector<Self>; diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 10d92583a55..3984b3b61c2 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1062,6 +1062,13 @@ rustc_queries! { } } + /// Computes the tag (if any) for a given type and variant. + query tag_for_variant( + key: (Ty<'tcx>, abi::VariantIdx) + ) -> Option<ty::ScalarInt> { + desc { "computing variant tag for enum" } + } + /// Evaluates a constant and returns the computed allocation. /// /// **Do not use this** directly, use the `eval_to_const_value` or `eval_to_valtree` instead. diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 4748e961019..65574f5702b 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -10,6 +10,7 @@ use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::FiniteBitSet; use rustc_macros::HashStable; use rustc_middle::ty::normalize_erasing_regions::NormalizationError; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::Symbol; use std::assert_matches::assert_matches; @@ -172,6 +173,11 @@ impl<'tcx> Instance<'tcx> { // If this a non-generic instance, it cannot be a shared monomorphization. self.args.non_erasable_generics(tcx, self.def_id()).next()?; + // compiler_builtins cannot use upstream monomorphizations. + if tcx.is_compiler_builtins(LOCAL_CRATE) { + return None; + } + match self.def { InstanceDef::Item(def) => tcx .upstream_monomorphizations_for(def) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 595ef71cc32..8997caee152 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -345,11 +345,16 @@ impl<'tcx> SizeSkeleton<'tcx> { ty::Array(inner, len) if len.ty() == tcx.types.usize && tcx.features().transmute_generic_consts => { + let len_eval = len.try_eval_target_usize(tcx, param_env); + if len_eval == Some(0) { + return Ok(SizeSkeleton::Known(Size::from_bytes(0))); + } + match SizeSkeleton::compute(inner, tcx, param_env)? { // This may succeed because the multiplication of two types may overflow // but a single size of a nested array will not. SizeSkeleton::Known(s) => { - if let Some(c) = len.try_eval_target_usize(tcx, param_env) { + if let Some(c) = len_eval { let size = s .bytes() .checked_mul(c) diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 3389305e7ee..3e9c1459f1c 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -2,52 +2,22 @@ //! //! Currently, this pass only propagates scalar values. -use rustc_const_eval::interpret::{ - HasStaticRootDefId, ImmTy, Immediate, InterpCx, OpTy, PlaceTy, PointerArithmetic, Projectable, -}; +use rustc_const_eval::const_eval::{throw_machine_stop_str, DummyMachine}; +use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, PlaceTy, Projectable}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; -use rustc_middle::mir::interpret::{AllocId, ConstAllocation, InterpResult, Scalar}; +use rustc_middle::mir::interpret::{InterpResult, Scalar}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; -use rustc_middle::query::TyCtxtAt; -use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::value_analysis::{ Map, PlaceIndex, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace, }; use rustc_mir_dataflow::{lattice::FlatSet, Analysis, Results, ResultsVisitor}; -use rustc_span::def_id::DefId; use rustc_span::DUMMY_SP; use rustc_target::abi::{Abi, FieldIdx, Size, VariantIdx, FIRST_VARIANT}; -/// Macro for machine-specific `InterpError` without allocation. -/// (These will never be shown to the user, but they help diagnose ICEs.) -pub(crate) macro throw_machine_stop_str($($tt:tt)*) {{ - // We make a new local type for it. The type itself does not carry any information, - // but its vtable (for the `MachineStopType` trait) does. - #[derive(Debug)] - struct Zst; - // Printing this type shows the desired string. - impl std::fmt::Display for Zst { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, $($tt)*) - } - } - - impl rustc_middle::mir::interpret::MachineStopType for Zst { - fn diagnostic_message(&self) -> rustc_errors::DiagMessage { - self.to_string().into() - } - - fn add_args( - self: Box<Self>, - _: &mut dyn FnMut(rustc_errors::DiagArgName, rustc_errors::DiagArgValue), - ) {} - } - throw_machine_stop!(Zst) -}} - // These constants are somewhat random guesses and have not been optimized. // If `tcx.sess.mir_opt_level() >= 4`, we ignore the limits (this can become very expensive). const BLOCK_LIMIT: usize = 100; @@ -888,165 +858,3 @@ impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> { } } } - -pub(crate) struct DummyMachine; - -impl HasStaticRootDefId for DummyMachine { - fn static_def_id(&self) -> Option<rustc_hir::def_id::LocalDefId> { - None - } -} - -impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for DummyMachine { - rustc_const_eval::interpret::compile_time_machine!(<'mir, 'tcx>); - type MemoryKind = !; - const PANIC_ON_ALLOC_FAIL: bool = true; - - #[inline(always)] - fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { - false // no reason to enforce alignment - } - - fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool { - false - } - - fn before_access_global( - _tcx: TyCtxtAt<'tcx>, - _machine: &Self, - _alloc_id: AllocId, - alloc: ConstAllocation<'tcx>, - _static_def_id: Option<DefId>, - is_write: bool, - ) -> InterpResult<'tcx> { - if is_write { - throw_machine_stop_str!("can't write to global"); - } - - // If the static allocation is mutable, then we can't const prop it as its content - // might be different at runtime. - if alloc.inner().mutability.is_mut() { - throw_machine_stop_str!("can't access mutable globals in ConstProp"); - } - - Ok(()) - } - - fn find_mir_or_eval_fn( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _instance: ty::Instance<'tcx>, - _abi: rustc_target::spec::abi::Abi, - _args: &[rustc_const_eval::interpret::FnArg<'tcx, Self::Provenance>], - _destination: &rustc_const_eval::interpret::MPlaceTy<'tcx, Self::Provenance>, - _target: Option<BasicBlock>, - _unwind: UnwindAction, - ) -> interpret::InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> { - unimplemented!() - } - - fn panic_nounwind( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _msg: &str, - ) -> interpret::InterpResult<'tcx> { - unimplemented!() - } - - fn call_intrinsic( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _instance: ty::Instance<'tcx>, - _args: &[rustc_const_eval::interpret::OpTy<'tcx, Self::Provenance>], - _destination: &rustc_const_eval::interpret::MPlaceTy<'tcx, Self::Provenance>, - _target: Option<BasicBlock>, - _unwind: UnwindAction, - ) -> interpret::InterpResult<'tcx> { - unimplemented!() - } - - fn assert_panic( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _msg: &rustc_middle::mir::AssertMessage<'tcx>, - _unwind: UnwindAction, - ) -> interpret::InterpResult<'tcx> { - unimplemented!() - } - - fn binary_ptr_op( - ecx: &InterpCx<'mir, 'tcx, Self>, - bin_op: BinOp, - left: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>, - right: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>, - ) -> interpret::InterpResult<'tcx, (ImmTy<'tcx, Self::Provenance>, bool)> { - use rustc_middle::mir::BinOp::*; - Ok(match bin_op { - Eq | Ne | Lt | Le | Gt | Ge => { - // Types can differ, e.g. fn ptrs with different `for`. - assert_eq!(left.layout.abi, right.layout.abi); - let size = ecx.pointer_size(); - // Just compare the bits. ScalarPairs are compared lexicographically. - // We thus always compare pairs and simply fill scalars up with 0. - // If the pointer has provenance, `to_bits` will return `Err` and we bail out. - let left = match **left { - Immediate::Scalar(l) => (l.to_bits(size)?, 0), - Immediate::ScalarPair(l1, l2) => (l1.to_bits(size)?, l2.to_bits(size)?), - Immediate::Uninit => panic!("we should never see uninit data here"), - }; - let right = match **right { - Immediate::Scalar(r) => (r.to_bits(size)?, 0), - Immediate::ScalarPair(r1, r2) => (r1.to_bits(size)?, r2.to_bits(size)?), - Immediate::Uninit => panic!("we should never see uninit data here"), - }; - let res = match bin_op { - Eq => left == right, - Ne => left != right, - Lt => left < right, - Le => left <= right, - Gt => left > right, - Ge => left >= right, - _ => bug!(), - }; - (ImmTy::from_bool(res, *ecx.tcx), false) - } - - // Some more operations are possible with atomics. - // The return value always has the provenance of the *left* operand. - Add | Sub | BitOr | BitAnd | BitXor => { - throw_machine_stop_str!("pointer arithmetic is not handled") - } - - _ => span_bug!(ecx.cur_span(), "Invalid operator on pointers: {:?}", bin_op), - }) - } - - fn expose_ptr( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _ptr: interpret::Pointer<Self::Provenance>, - ) -> interpret::InterpResult<'tcx> { - unimplemented!() - } - - fn init_frame_extra( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _frame: rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance>, - ) -> interpret::InterpResult< - 'tcx, - rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, - > { - unimplemented!() - } - - fn stack<'a>( - _ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] - { - // Return an empty stack instead of panicking, as `cur_span` uses it to evaluate constants. - &[] - } - - fn stack_mut<'a>( - _ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec< - rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, - > { - unimplemented!() - } -} diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 87dff49e0be..36c13b48ce9 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -82,6 +82,7 @@ //! Second, when writing constants in MIR, we do not write `Const::Slice` or `Const` //! that contain `AllocId`s. +use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{intern_const_alloc_for_constprop, MemoryKind}; use rustc_const_eval::interpret::{ImmTy, InterpCx, OpTy, Projectable, Scalar}; use rustc_data_structures::fx::FxIndexSet; @@ -101,7 +102,6 @@ use rustc_target::abi::{self, Abi, Size, VariantIdx, FIRST_VARIANT}; use smallvec::SmallVec; use std::borrow::Cow; -use crate::dataflow_const_prop::DummyMachine; use crate::ssa::{AssignedValue, SsaLocals}; use either::Either; diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 116d6f48456..a458297210d 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -36,6 +36,7 @@ //! cost by `MAX_COST`. use rustc_arena::DroplessArena; +use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable}; use rustc_data_structures::fx::FxHashSet; use rustc_index::bit_set::BitSet; @@ -50,7 +51,6 @@ use rustc_span::DUMMY_SP; use rustc_target::abi::{TagEncoding, Variants}; use crate::cost_checker::CostChecker; -use crate::dataflow_const_prop::DummyMachine; pub struct JumpThreading; diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index f19b78a3a5c..6b13725b386 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -6,6 +6,7 @@ use std::fmt::Debug; +use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ format_interp_error, ImmTy, InterpCx, InterpResult, Projectable, Scalar, }; @@ -20,7 +21,6 @@ use rustc_middle::ty::{self, ConstInt, ParamEnv, ScalarInt, Ty, TyCtxt, TypeVisi use rustc_span::Span; use rustc_target::abi::{Abi, FieldIdx, HasDataLayout, Size, TargetDataLayout, VariantIdx}; -use crate::dataflow_const_prop::DummyMachine; use crate::errors::{AssertLint, AssertLintKind}; use crate::MirLint; diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 1a18a84b358..673cd968e28 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1107,7 +1107,7 @@ fn visit_instance_use<'tcx>( /// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we /// can just link to the upstream crate and therefore don't need a mono item. -fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> bool { +pub(crate) fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> bool { let Some(def_id) = instance.def.def_id_if_not_guaranteed_local_codegen() else { return true; }; diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 7f36ae91f1a..4ec842e8f85 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -11,7 +11,10 @@ use rustc_hir::lang_items::LangItem; use rustc_middle::query::{Providers, TyCtxtAt}; use rustc_middle::traits; use rustc_middle::ty::adjustment::CustomCoerceUnsized; +use rustc_middle::ty::Instance; +use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{self, Ty}; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::ErrorGuaranteed; mod collector; @@ -20,6 +23,8 @@ mod partitioning; mod polymorphize; mod util; +use collector::should_codegen_locally; + rustc_fluent_macro::fluent_messages! { "../messages.ftl" } fn custom_coerce_unsize_info<'tcx>( @@ -45,6 +50,23 @@ fn custom_coerce_unsize_info<'tcx>( } } +/// Returns whether a call from the current crate to the [`Instance`] would produce a call +/// from `compiler_builtins` to a symbol the linker must resolve. +/// +/// Such calls from `compiler_bultins` are effectively impossible for the linker to handle. Some +/// linkers will optimize such that dead calls to unresolved symbols are not an error, but this is +/// not guaranteed. So we used this function in codegen backends to ensure we do not generate any +/// unlinkable calls. +pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, +) -> bool { + !instance.def_id().is_local() + && tcx.is_compiler_builtins(LOCAL_CRATE) + && tcx.codegen_fn_attrs(instance.def_id()).link_name.is_none() + && !should_codegen_locally(tcx, &instance) +} + pub fn provide(providers: &mut Providers) { partitioning::provide(providers); polymorphize::provide(providers); diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 9a43d67d435..f6bc224c7e7 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -174,10 +174,10 @@ pub(crate) mod rustc { use crate::layout::rustc::{Def, Ref}; use rustc_middle::ty::layout::LayoutError; - use rustc_middle::ty::util::Discr; use rustc_middle::ty::AdtDef; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::ParamEnv; + use rustc_middle::ty::ScalarInt; use rustc_middle::ty::VariantDef; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::ErrorGuaranteed; @@ -331,14 +331,15 @@ pub(crate) mod rustc { trace!(?adt_def, "treeifying enum"); let mut tree = Tree::uninhabited(); - for (idx, discr) in adt_def.discriminants(tcx) { + for (idx, variant) in adt_def.variants().iter_enumerated() { + let tag = tcx.tag_for_variant((ty, idx)); tree = tree.or(Self::from_repr_c_variant( ty, *adt_def, args_ref, &layout_summary, - Some(discr), - adt_def.variant(idx), + tag, + variant, tcx, )?); } @@ -393,7 +394,7 @@ pub(crate) mod rustc { adt_def: AdtDef<'tcx>, args_ref: GenericArgsRef<'tcx>, layout_summary: &LayoutSummary, - discr: Option<Discr<'tcx>>, + tag: Option<ScalarInt>, variant_def: &'tcx VariantDef, tcx: TyCtxt<'tcx>, ) -> Result<Self, Err> { @@ -403,9 +404,6 @@ pub(crate) mod rustc { let min_align = repr.align.unwrap_or(Align::ONE); let max_align = repr.pack.unwrap_or(Align::MAX); - let clamp = - |align: Align| align.clamp(min_align, max_align).bytes().try_into().unwrap(); - let variant_span = trace_span!( "treeifying variant", min_align = ?min_align, @@ -419,17 +417,12 @@ pub(crate) mod rustc { ) .unwrap(); - // The layout of the variant is prefixed by the discriminant, if any. - if let Some(discr) = discr { - trace!(?discr, "treeifying discriminant"); - let discr_layout = alloc::Layout::from_size_align( - layout_summary.discriminant_size, - clamp(layout_summary.discriminant_align), - ) - .unwrap(); - trace!(?discr_layout, "computed discriminant layout"); - variant_layout = variant_layout.extend(discr_layout).unwrap().0; - tree = tree.then(Self::from_discr(discr, tcx, layout_summary.discriminant_size)); + // The layout of the variant is prefixed by the tag, if any. + if let Some(tag) = tag { + let tag_layout = + alloc::Layout::from_size_align(tag.size().bytes_usize(), 1).unwrap(); + tree = tree.then(Self::from_tag(tag, tcx)); + variant_layout = variant_layout.extend(tag_layout).unwrap().0; } // Next come fields. @@ -469,18 +462,19 @@ pub(crate) mod rustc { Ok(tree) } - pub fn from_discr(discr: Discr<'tcx>, tcx: TyCtxt<'tcx>, size: usize) -> Self { + pub fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self { use rustc_target::abi::Endian; - + let size = tag.size(); + let bits = tag.to_bits(size).unwrap(); let bytes: [u8; 16]; let bytes = match tcx.data_layout.endian { Endian::Little => { - bytes = discr.val.to_le_bytes(); - &bytes[..size] + bytes = bits.to_le_bytes(); + &bytes[..size.bytes_usize()] } Endian::Big => { - bytes = discr.val.to_be_bytes(); - &bytes[bytes.len() - size..] + bytes = bits.to_be_bytes(); + &bytes[bytes.len() - size.bytes_usize()..] } }; Self::Seq(bytes.iter().map(|&b| Self::from_bits(b)).collect()) diff --git a/src/tools/clippy/tests/ui/builtin_type_shadow.stderr b/src/tools/clippy/tests/ui/builtin_type_shadow.stderr index 1e15cdee772..033204af925 100644 --- a/src/tools/clippy/tests/ui/builtin_type_shadow.stderr +++ b/src/tools/clippy/tests/ui/builtin_type_shadow.stderr @@ -19,6 +19,7 @@ LL | 42 | = note: expected type parameter `u32` found type `{integer}` + = note: the caller chooses a type for `u32` which can be different from `i32` error: aborting due to 2 previous errors diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 958aef69572..d8bb8c643d1 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -4,4 +4,5 @@ version = "0.0.0" edition = "2021" [dependencies] +object = "0.34.0" wasmparser = "0.118.2" diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 674860f8413..e5e7b559c92 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -2,6 +2,7 @@ use std::env; use std::path::{Path, PathBuf}; use std::process::{Command, Output}; +pub use object; pub use wasmparser; pub fn out_dir() -> PathBuf { diff --git a/tests/run-make/compiler-builtins/rmake.rs b/tests/run-make/compiler-builtins/rmake.rs new file mode 100644 index 00000000000..e7a5e8addbe --- /dev/null +++ b/tests/run-make/compiler-builtins/rmake.rs @@ -0,0 +1,142 @@ +//! The compiler_builtins library is special. It can call functions in core, but it must not +//! require linkage against a build of core. If it ever does, building the standard library *may* +//! result in linker errors, depending on whether the linker in use applies optimizations first or +//! resolves symbols first. So the portable and safe approach is to forbid such a linkage +//! requirement entirely. +//! +//! In addition, whether compiler_builtins requires linkage against core can depend on optimization +//! settings. Turning off optimizations and enabling debug assertions tends to produce the most +//! dependence on core that is possible, so that is the configuration we test here. + +#![deny(warnings)] + +extern crate run_make_support; + +use run_make_support::object; +use run_make_support::object::read::archive::ArchiveFile; +use run_make_support::object::read::Object; +use run_make_support::object::ObjectSection; +use run_make_support::object::ObjectSymbol; +use run_make_support::object::RelocationTarget; +use run_make_support::out_dir; +use std::collections::HashSet; + +const MANIFEST: &str = r#" +[package] +name = "scratch" +version = "0.1.0" +edition = "2021" + +[lib] +path = "lib.rs""#; + +fn main() { + let target_dir = out_dir().join("target"); + let target = std::env::var("TARGET").unwrap(); + if target.starts_with("wasm") || target.starts_with("nvptx") { + // wasm and nvptx targets don't produce rlib files that object can parse. + return; + } + + println!("Testing compiler_builtins for {}", target); + + // Set up the tiniest Cargo project: An empty no_std library. Just enough to run -Zbuild-std. + let manifest_path = out_dir().join("Cargo.toml"); + std::fs::write(&manifest_path, MANIFEST.as_bytes()).unwrap(); + std::fs::write(out_dir().join("lib.rs"), b"#![no_std]").unwrap(); + + let path = std::env::var("PATH").unwrap(); + let rustc = std::env::var("RUSTC").unwrap(); + let bootstrap_cargo = std::env::var("BOOTSTRAP_CARGO").unwrap(); + let status = std::process::Command::new(bootstrap_cargo) + .args([ + "build", + "--manifest-path", + manifest_path.to_str().unwrap(), + "-Zbuild-std=core", + "--target", + &target, + ]) + .env_clear() + .env("PATH", path) + .env("RUSTC", rustc) + .env("RUSTFLAGS", "-Copt-level=0 -Cdebug-assertions=yes") + .env("CARGO_TARGET_DIR", &target_dir) + .env("RUSTC_BOOTSTRAP", "1") + .status() + .unwrap(); + + assert!(status.success()); + + let rlibs_path = target_dir.join(target).join("debug").join("deps"); + let compiler_builtins_rlib = std::fs::read_dir(rlibs_path) + .unwrap() + .find_map(|e| { + let path = e.unwrap().path(); + let file_name = path.file_name().unwrap().to_str().unwrap(); + if file_name.starts_with("libcompiler_builtins") && file_name.ends_with(".rlib") { + Some(path) + } else { + None + } + }) + .unwrap(); + + // rlib files are archives, where the archive members each a CGU, and we also have one called + // lib.rmeta which is the encoded metadata. Each of the CGUs is an object file. + let data = std::fs::read(compiler_builtins_rlib).unwrap(); + + let mut defined_symbols = HashSet::new(); + let mut undefined_relocations = HashSet::new(); + + let archive = ArchiveFile::parse(&*data).unwrap(); + for member in archive.members() { + let member = member.unwrap(); + if member.name() == b"lib.rmeta" { + continue; + } + let data = member.data(&*data).unwrap(); + let object = object::File::parse(&*data).unwrap(); + + // Record all defined symbols in this CGU. + for symbol in object.symbols() { + if !symbol.is_undefined() { + let name = symbol.name().unwrap(); + defined_symbols.insert(name); + } + } + + // Find any relocations against undefined symbols. Calls within this CGU are relocations + // against a defined symbol. + for (_offset, relocation) in object.sections().flat_map(|section| section.relocations()) { + let RelocationTarget::Symbol(symbol_index) = relocation.target() else { + continue; + }; + let symbol = object.symbol_by_index(symbol_index).unwrap(); + if symbol.is_undefined() { + let name = symbol.name().unwrap(); + undefined_relocations.insert(name); + } + } + } + + // We can have symbols in the compiler_builtins rlib that are actually from core, if they were + // monomorphized in the compiler_builtins crate. This is totally fine, because though the call + // is to a function in core, it's resolved internally. + // + // It is normal to have relocations against symbols not defined in the rlib for things like + // unwinding, or math functions provided the target's platform libraries. Finding these is not + // a problem, we want to specifically ban relocations against core which are not resolved + // internally. + undefined_relocations + .retain(|symbol| !defined_symbols.contains(symbol) && symbol.contains("core")); + + if !undefined_relocations.is_empty() { + panic!( + "compiler_builtins must not link against core, but it does. \n\ + These symbols may be undefined in a debug build of compiler_builtins:\n\ + {:?}", + undefined_relocations + ); + } +} diff --git a/tests/ui/generics/generic-type-less-params-with-defaults.rs b/tests/ui/generics/generic-type-less-params-with-defaults.rs index 6b877ab8aee..d04b1c80d34 100644 --- a/tests/ui/generics/generic-type-less-params-with-defaults.rs +++ b/tests/ui/generics/generic-type-less-params-with-defaults.rs @@ -5,7 +5,23 @@ struct Heap; struct Vec<T, A = Heap>( marker::PhantomData<(T,A)>); +struct HashMap<K, V, S = ()>(marker::PhantomData<(K,V,S)>); + fn main() { let _: Vec; //~^ ERROR missing generics for struct `Vec` + //~| SUGGESTION <T> + + let _x = (1..10).collect::<HashMap>(); + //~^ ERROR missing generics for struct `HashMap` + //~| SUGGESTION <_, _> + + ().extend::<[(); 0]>({ + fn not_the_extend() { + let _: Vec; + //~^ ERROR missing generics for struct `Vec` + //~| SUGGESTION <T> + } + [] + }); } diff --git a/tests/ui/generics/generic-type-less-params-with-defaults.stderr b/tests/ui/generics/generic-type-less-params-with-defaults.stderr index 6f79b09f6cd..a6771a6d5c8 100644 --- a/tests/ui/generics/generic-type-less-params-with-defaults.stderr +++ b/tests/ui/generics/generic-type-less-params-with-defaults.stderr @@ -1,5 +1,5 @@ error[E0107]: missing generics for struct `Vec` - --> $DIR/generic-type-less-params-with-defaults.rs:9:12 + --> $DIR/generic-type-less-params-with-defaults.rs:11:12 | LL | let _: Vec; | ^^^ expected at least 1 generic argument @@ -14,6 +14,38 @@ help: add missing generic argument LL | let _: Vec<T>; | +++ -error: aborting due to 1 previous error +error[E0107]: missing generics for struct `HashMap` + --> $DIR/generic-type-less-params-with-defaults.rs:15:32 + | +LL | let _x = (1..10).collect::<HashMap>(); + | ^^^^^^^ expected at least 2 generic arguments + | +note: struct defined here, with at least 2 generic parameters: `K`, `V` + --> $DIR/generic-type-less-params-with-defaults.rs:8:8 + | +LL | struct HashMap<K, V, S = ()>(marker::PhantomData<(K,V,S)>); + | ^^^^^^^ - - +help: add missing generic arguments + | +LL | let _x = (1..10).collect::<HashMap<_, _>>(); + | ++++++ + +error[E0107]: missing generics for struct `Vec` + --> $DIR/generic-type-less-params-with-defaults.rs:21:20 + | +LL | let _: Vec; + | ^^^ expected at least 1 generic argument + | +note: struct defined here, with at least 1 generic parameter: `T` + --> $DIR/generic-type-less-params-with-defaults.rs:5:8 + | +LL | struct Vec<T, A = Heap>( + | ^^^ - +help: add missing generic argument + | +LL | let _: Vec<T>; + | +++ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/return/return-impl-trait-bad.stderr b/tests/ui/return/return-impl-trait-bad.stderr index a015b9f53af..6277c5feb20 100644 --- a/tests/ui/return/return-impl-trait-bad.stderr +++ b/tests/ui/return/return-impl-trait-bad.stderr @@ -10,6 +10,7 @@ LL | "this should not suggest impl Trait" | = note: expected type parameter `T` found reference `&'static str` + = note: the caller chooses a type for `T` which can be different from `&'static str` error[E0308]: mismatched types --> $DIR/return-impl-trait-bad.rs:9:5 @@ -23,6 +24,7 @@ LL | "this will not suggest it, because that would probably be wrong" | = note: expected type parameter `T` found reference `&'static str` + = note: the caller chooses a type for `T` which can be different from `&'static str` error[E0308]: mismatched types --> $DIR/return-impl-trait-bad.rs:17:5 @@ -37,6 +39,7 @@ LL | "don't suggest this, because Option<T> places additional constraints" | = note: expected type parameter `T` found reference `&'static str` + = note: the caller chooses a type for `T` which can be different from `&'static str` error[E0308]: mismatched types --> $DIR/return-impl-trait-bad.rs:28:5 @@ -53,6 +56,7 @@ LL | "don't suggest this, because the generic param is used in the bound." | = note: expected type parameter `T` found reference `&'static str` + = note: the caller chooses a type for `T` which can be different from `&'static str` error: aborting due to 4 previous errors diff --git a/tests/ui/return/return-impl-trait.stderr b/tests/ui/return/return-impl-trait.stderr index 707f014a16f..114ae0c2445 100644 --- a/tests/ui/return/return-impl-trait.stderr +++ b/tests/ui/return/return-impl-trait.stderr @@ -12,6 +12,7 @@ LL | () | = note: expected type parameter `T` found unit type `()` + = note: the caller chooses a type for `T` which can be different from `()` error[E0308]: mismatched types --> $DIR/return-impl-trait.rs:23:5 @@ -28,6 +29,7 @@ LL | () | = note: expected type parameter `T` found unit type `()` + = note: the caller chooses a type for `T` which can be different from `()` error: aborting due to 2 previous errors diff --git a/tests/ui/return/return-ty-mismatch-note.rs b/tests/ui/return/return-ty-mismatch-note.rs new file mode 100644 index 00000000000..352bc2a1637 --- /dev/null +++ b/tests/ui/return/return-ty-mismatch-note.rs @@ -0,0 +1,21 @@ +// Checks existence of a note for "a caller chooses ty for ty param" upon return ty mismatch. + +fn f<T>() -> (T,) { + (0,) //~ ERROR mismatched types +} + +fn g<U, V>() -> (U, V) { + (0, "foo") + //~^ ERROR mismatched types + //~| ERROR mismatched types +} + +fn h() -> u8 { + 0u8 +} + +fn main() { + f::<()>(); + g::<(), ()>; + let _ = h(); +} diff --git a/tests/ui/return/return-ty-mismatch-note.stderr b/tests/ui/return/return-ty-mismatch-note.stderr new file mode 100644 index 00000000000..135903da5c2 --- /dev/null +++ b/tests/ui/return/return-ty-mismatch-note.stderr @@ -0,0 +1,36 @@ +error[E0308]: mismatched types + --> $DIR/return-ty-mismatch-note.rs:4:6 + | +LL | fn f<T>() -> (T,) { + | - expected this type parameter +LL | (0,) + | ^ expected type parameter `T`, found integer + | + = note: expected type parameter `T` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/return-ty-mismatch-note.rs:8:6 + | +LL | fn g<U, V>() -> (U, V) { + | - expected this type parameter +LL | (0, "foo") + | ^ expected type parameter `U`, found integer + | + = note: expected type parameter `U` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/return-ty-mismatch-note.rs:8:9 + | +LL | fn g<U, V>() -> (U, V) { + | - expected this type parameter +LL | (0, "foo") + | ^^^^^ expected type parameter `V`, found `&str` + | + = note: expected type parameter `V` + found reference `&'static str` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr b/tests/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr index afbb9c32d51..2c4be26a82b 100644 --- a/tests/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr +++ b/tests/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr @@ -10,6 +10,7 @@ LL | t.clone() | = note: expected type parameter `_` found reference `&_` + = note: the caller chooses a type for `T` which can be different from `&T` note: `T` does not implement `Clone`, so `&T` was cloned instead --> $DIR/clone-on-unconstrained-borrowed-type-param.rs:3:5 | diff --git a/tests/ui/trait-bounds/restrict-assoc-type-of-generic-bound.stderr b/tests/ui/trait-bounds/restrict-assoc-type-of-generic-bound.stderr index 5024ad21892..7aa32557af2 100644 --- a/tests/ui/trait-bounds/restrict-assoc-type-of-generic-bound.stderr +++ b/tests/ui/trait-bounds/restrict-assoc-type-of-generic-bound.stderr @@ -10,6 +10,7 @@ LL | return a.bar(); | = note: expected type parameter `B` found associated type `<A as MyTrait>::T` + = note: the caller chooses a type for `B` which can be different from `<A as MyTrait>::T` help: consider further restricting this bound | LL | pub fn foo<A: MyTrait<T = B>, B>(a: A) -> B { diff --git a/tests/ui/transmute/transmute-zst-generics.rs b/tests/ui/transmute/transmute-zst-generics.rs new file mode 100644 index 00000000000..9aeb21923ea --- /dev/null +++ b/tests/ui/transmute/transmute-zst-generics.rs @@ -0,0 +1,34 @@ +//@ run-pass + +// Transmuting to/from ZSTs that contain generics. + +#![feature(transmute_generic_consts)] + +// Verify non-generic ZST -> generic ZST transmute +unsafe fn cast_zst0<T>(from: ()) -> [T; 0] { + ::std::mem::transmute::<(), [T; 0]>(from) +} + +// Verify generic ZST -> non-generic ZST transmute +unsafe fn cast_zst1<T>(from: [T; 0]) -> () { + ::std::mem::transmute::<[T; 0], ()>(from) +} + +// Verify transmute with generic compound types +unsafe fn cast_zst2<T>(from: ()) -> [(T, T); 0] { + ::std::mem::transmute::<(), [(T, T); 0]>(from) +} + +// Verify transmute with ZST propagation through arrays +unsafe fn cast_zst3<T>(from: ()) -> [[T; 0]; 8] { + ::std::mem::transmute::<(), [[T; 0]; 8]>(from) +} + +pub fn main() { + unsafe { + let _: [u32; 0] = cast_zst0(()); + let _ = cast_zst1::<u32>([]); + let _: [(u32, u32); 0] = cast_zst2(()); + let _: [[u32; 0]; 8] = cast_zst3(()); + }; +} diff --git a/tests/ui/type/type-parameter-names.stderr b/tests/ui/type/type-parameter-names.stderr index 8e3e2388c6c..be9000a99e4 100644 --- a/tests/ui/type/type-parameter-names.stderr +++ b/tests/ui/type/type-parameter-names.stderr @@ -13,6 +13,7 @@ LL | x found type parameter `Foo` = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters + = note: the caller chooses a type for `Bar` which can be different from `Foo` error: aborting due to 1 previous error diff --git a/tests/ui/type/type-params-in-different-spaces-3.stderr b/tests/ui/type/type-params-in-different-spaces-3.stderr index 58783fe1ff0..3c0c022f112 100644 --- a/tests/ui/type/type-params-in-different-spaces-3.stderr +++ b/tests/ui/type/type-params-in-different-spaces-3.stderr @@ -14,6 +14,7 @@ LL | u found type parameter `X` = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters + = note: the caller chooses a type for `Self` which can be different from `X` error: aborting due to 1 previous error diff --git a/tests/ui/typeck/issue-13853.stderr b/tests/ui/typeck/issue-13853.stderr index 0683c782933..45363c87d29 100644 --- a/tests/ui/typeck/issue-13853.stderr +++ b/tests/ui/typeck/issue-13853.stderr @@ -9,6 +9,7 @@ LL | self.iter() | = note: expected type parameter `I` found struct `std::slice::Iter<'_, N>` + = note: the caller chooses a type for `I` which can be different from `std::slice::Iter<'_, N>` error[E0599]: no method named `iter` found for reference `&G` in the current scope --> $DIR/issue-13853.rs:27:23 |
