diff --git a/.mailmap b/.mailmap index d70a9f2aed68..f37ac7609e00 100644 --- a/.mailmap +++ b/.mailmap @@ -129,7 +129,7 @@ Clement Miao Clément Renault Cliff Dyer Clinton Ryan -Corey Richardson Elaine "See More" Nemo +ember arlynx Crazycolorz5 csmoe <35686186+csmoe@users.noreply.github.com> Cyryl Płotnicki diff --git a/Cargo.lock b/Cargo.lock index ef5811c6901d..537571ee6b54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -878,12 +878,12 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.0" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a011bbe2c35ce9c1f143b7af6f94f29a167beb4cd1d29e6740ce836f723120e" +checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" dependencies = [ "nix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2512,14 +2512,13 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" [[package]] name = "nix" -version = "0.26.2" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "cfg-if", "libc", - "static_assertions", ] [[package]] @@ -2641,11 +2640,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.55" +version = "0.10.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" +checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -2673,9 +2672,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.90" +version = "0.9.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" +checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae" dependencies = [ "cc", "libc", @@ -6143,6 +6142,15 @@ dependencies = [ "windows-targets 0.48.1", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -6173,6 +6181,21 @@ dependencies = [ "windows_x86_64_msvc 0.48.0", ] +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -6185,6 +6208,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -6197,6 +6226,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -6209,6 +6244,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -6221,6 +6262,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -6233,6 +6280,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -6245,6 +6298,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -6257,6 +6316,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winnow" version = "0.4.7" diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index eec128b5f1d5..18975a4e3b27 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -1,6 +1,6 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::graph::WithSuccessors; -use rustc_index::bit_set::HybridBitSet; +use rustc_index::bit_set::BitSet; use rustc_index::interval::IntervalSet; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives::for_liveness; @@ -135,7 +135,7 @@ struct LivenessResults<'me, 'typeck, 'flow, 'tcx> { cx: LivenessContext<'me, 'typeck, 'flow, 'tcx>, /// Set of points that define the current local. - defs: HybridBitSet, + defs: BitSet, /// Points where the current variable is "use live" -- meaning /// that there is a future "full use" that may use its value. @@ -158,7 +158,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { let num_points = cx.elements.num_points(); LivenessResults { cx, - defs: HybridBitSet::new_empty(num_points), + defs: BitSet::new_empty(num_points), use_live_at: IntervalSet::new(num_points), drop_live_at: IntervalSet::new(num_points), drop_locations: vec![], diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 881c0c0b56b6..0afd6d0e670b 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -682,7 +682,6 @@ fn codegen_stmt<'tcx>( args, ty::ClosureKind::FnOnce, ) - .expect("failed to normalize and resolve closure during codegen") .polymorphize(fx.tcx); let func_ref = fx.get_function_ref(instance); let func_addr = fx.bcx.ins().func_addr(fx.pointer_type, func_ref); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 02b51dfe5bf7..266505d3f269 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -435,7 +435,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args, ty::ClosureKind::FnOnce, ) - .expect("failed to normalize and resolve closure during codegen") .polymorphize(bx.cx().tcx()); OperandValue::Immediate(bx.cx().get_fn_addr(instance)) } diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 4a426ed16e54..e7e8b2b36006 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -46,8 +46,8 @@ const_eval_dangling_int_pointer = {$bad_pointer_message}: {$pointer} is a dangling pointer (it has no provenance) const_eval_dangling_null_pointer = {$bad_pointer_message}: null pointer is a dangling pointer (it has no provenance) -const_eval_dangling_ptr_in_final = encountered dangling pointer in final constant +const_eval_dangling_ptr_in_final = encountered dangling pointer in final value of {const_eval_intern_kind} const_eval_dead_local = accessing a dead local variable const_eval_dealloc_immutable = @@ -134,6 +134,14 @@ const_eval_interior_mutable_data_refer = This would make multiple uses of a constant to be able to see different values and allow circumventing the `Send` and `Sync` requirements for shared mutable data, which is unsound. +const_eval_intern_kind = {$kind -> + [static] static + [static_mut] mutable static + [const] constant + [promoted] promoted + *[other] {""} +} + const_eval_invalid_align = align has to be a power of 2 @@ -205,6 +213,8 @@ const_eval_modified_global = const_eval_mut_deref = mutation through a reference is not allowed in {const_eval_const_context}s +const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind} + const_eval_non_const_fmt_macro_call = cannot call non-const formatting macro in {const_eval_const_context}s @@ -327,7 +337,7 @@ const_eval_too_many_caller_args = const_eval_transient_mut_borrow = mutable references are not allowed in {const_eval_const_context}s -const_eval_transient_mut_borrow_raw = raw mutable references are not allowed in {const_eval_const_context}s +const_eval_transient_mut_raw = raw mutable pointers are not allowed in {const_eval_const_context}s const_eval_try_block_from_output_non_const = `try` block cannot convert `{$ty}` to the result in {const_eval_const_context}s @@ -341,21 +351,21 @@ const_eval_unallowed_heap_allocations = const_eval_unallowed_inline_asm = inline assembly is not allowed in {const_eval_const_context}s -const_eval_unallowed_mutable_refs = - mutable references are not allowed in the final value of {const_eval_const_context}s +const_eval_unallowed_mutable_raw = + raw mutable pointers are not allowed in the final value of {const_eval_const_context}s .teach_note = + References in statics and constants may only refer to immutable values. + + Statics are shared everywhere, and if they refer to mutable data one might violate memory safety since holding multiple mutable references to shared data is not allowed. If you really want global mutable state, try using static mut or a global UnsafeCell. -const_eval_unallowed_mutable_refs_raw = - raw mutable references are not allowed in the final value of {const_eval_const_context}s +const_eval_unallowed_mutable_refs = + mutable references are not allowed in the final value of {const_eval_const_context}s .teach_note = - References in statics and constants may only refer to immutable values. - - Statics are shared everywhere, and if they refer to mutable data one might violate memory safety since holding multiple mutable references to shared data is not allowed. @@ -392,9 +402,6 @@ const_eval_unstable_in_stable = .unstable_sugg = if it is not part of the public API, make this function unstably const .bypass_sugg = otherwise `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks -const_eval_unsupported_untyped_pointer = unsupported untyped pointer in constant - .note = memory only reachable via raw pointers is not supported - const_eval_unterminated_c_string = reading a null-terminated string starting at {$pointer} with no null found before end of allocation @@ -406,7 +413,6 @@ const_eval_upcast_mismatch = ## The `front_matter`s here refer to either `const_eval_front_matter_invalid_value` or `const_eval_front_matter_invalid_value_with_path`. ## (We'd love to sort this differently to make that more clear but tidy won't let us...) -const_eval_validation_box_to_mut = {$front_matter}: encountered a box pointing to mutable memory in a constant const_eval_validation_box_to_static = {$front_matter}: encountered a box pointing to a static variable in a constant const_eval_validation_box_to_uninhabited = {$front_matter}: encountered a box pointing to uninhabited type {$ty} const_eval_validation_dangling_box_no_provenance = {$front_matter}: encountered a dangling box ({$pointer} has no provenance) @@ -441,7 +447,8 @@ const_eval_validation_invalid_fn_ptr = {$front_matter}: encountered {$value}, bu const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer -const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in a `const` +const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in a `const` or `static` +const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!` const_eval_validation_null_box = {$front_matter}: encountered a null box const_eval_validation_null_fn_ptr = {$front_matter}: encountered a null function pointer @@ -451,7 +458,6 @@ const_eval_validation_out_of_range = {$front_matter}: encountered {$value}, but const_eval_validation_partial_pointer = {$front_matter}: encountered a partial pointer or a mix of pointers const_eval_validation_pointer_as_int = {$front_matter}: encountered a pointer, but {$expected} const_eval_validation_ptr_out_of_range = {$front_matter}: encountered a pointer, but expected something that cannot possibly fail to be {$in_range} -const_eval_validation_ref_to_mut = {$front_matter}: encountered a reference pointing to mutable memory in a constant const_eval_validation_ref_to_static = {$front_matter}: encountered a reference pointing to a static variable in a constant const_eval_validation_ref_to_uninhabited = {$front_matter}: encountered a reference pointing to uninhabited type {$ty} const_eval_validation_unaligned_box = {$front_matter}: encountered an unaligned box (required {$required_bytes} byte alignment but found {$found_bytes}) @@ -459,7 +465,7 @@ const_eval_validation_unaligned_ref = {$front_matter}: encountered an unaligned const_eval_validation_uninhabited_enum_variant = {$front_matter}: encountered an uninhabited enum variant const_eval_validation_uninhabited_val = {$front_matter}: encountered a value of uninhabited type `{$ty}` const_eval_validation_uninit = {$front_matter}: encountered uninitialized memory, but {$expected} -const_eval_validation_unsafe_cell = {$front_matter}: encountered `UnsafeCell` in a `const` +const_eval_validation_unsafe_cell = {$front_matter}: encountered `UnsafeCell` in read-only memory const_eval_write_through_immutable_pointer = writing through a pointer that was derived from a shared (immutable) reference 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 4236117d75b9..6a92ed9717de 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -293,6 +293,9 @@ pub fn eval_in_interpreter<'mir, 'tcx>( cid: GlobalId<'tcx>, is_static: bool, ) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> { + // `is_static` just means "in static", it could still be a promoted! + debug_assert_eq!(is_static, ecx.tcx.static_mutability(cid.instance.def_id()).is_some()); + let res = ecx.load_mir(cid.instance.def, cid.promoted); match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)) { Err(error) => { @@ -330,8 +333,7 @@ pub fn eval_in_interpreter<'mir, 'tcx>( Ok(mplace) => { // Since evaluation had no errors, validate the resulting constant. // This is a separate `try` block to provide more targeted error reporting. - let validation = - const_validate_mplace(&ecx, &mplace, is_static, cid.promoted.is_some()); + let validation = const_validate_mplace(&ecx, &mplace, cid); let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); @@ -350,22 +352,26 @@ pub fn eval_in_interpreter<'mir, 'tcx>( pub fn const_validate_mplace<'mir, 'tcx>( ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, mplace: &MPlaceTy<'tcx>, - is_static: bool, - is_promoted: bool, + cid: GlobalId<'tcx>, ) -> InterpResult<'tcx> { let mut ref_tracking = RefTracking::new(mplace.clone()); let mut inner = false; while let Some((mplace, path)) = ref_tracking.todo.pop() { - let mode = if is_static { - if is_promoted { - // Promoteds in statics are allowed to point to statics. - CtfeValidationMode::Const { inner, allow_static_ptrs: true } - } else { - // a `static` - CtfeValidationMode::Regular + let mode = match ecx.tcx.static_mutability(cid.instance.def_id()) { + Some(_) if cid.promoted.is_some() => { + // Promoteds in statics are consts that re allowed to point to statics. + CtfeValidationMode::Const { + allow_immutable_unsafe_cell: false, + allow_static_ptrs: true, + } + } + Some(mutbl) => CtfeValidationMode::Static { mutbl }, // a `static` + None => { + // In normal `const` (not promoted), the outermost allocation is always only copied, + // so having `UnsafeCell` in there is okay despite them being in immutable memory. + let allow_immutable_unsafe_cell = cid.promoted.is_none() && !inner; + CtfeValidationMode::Const { allow_immutable_unsafe_cell, allow_static_ptrs: false } } - } else { - CtfeValidationMode::Const { inner, allow_static_ptrs: false } }; ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?; inner = true; diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index f2e8821d6b65..d5a33b6f01e5 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -723,7 +723,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, && ty.is_freeze(*ecx.tcx, ecx.param_env) { let place = ecx.ref_to_mplace(val)?; - let new_place = place.map_provenance(|p| p.map(CtfeProvenance::as_immutable)); + let new_place = place.map_provenance(CtfeProvenance::as_immutable); Ok(ImmTy::from_immediate(new_place.to_ref(ecx), val.layout)) } else { Ok(val.clone()) diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 171cc89d6adb..4a654480ef5a 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use rustc_errors::{ DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, IntoDiagnostic, Level, @@ -13,12 +15,24 @@ use rustc_middle::ty::{self, Ty}; use rustc_span::Span; use rustc_target::abi::call::AdjustForForeignAbiError; use rustc_target::abi::{Size, WrappingRange}; +use rustc_type_ir::Mutability; + +use crate::interpret::InternKind; #[derive(Diagnostic)] #[diag(const_eval_dangling_ptr_in_final)] pub(crate) struct DanglingPtrInFinal { #[primary_span] pub span: Span, + pub kind: InternKind, +} + +#[derive(Diagnostic)] +#[diag(const_eval_mutable_ptr_in_final)] +pub(crate) struct MutablePtrInFinal { + #[primary_span] + pub span: Span, + pub kind: InternKind, } #[derive(Diagnostic)] @@ -100,8 +114,8 @@ pub(crate) struct TransientMutBorrowErr { } #[derive(Diagnostic)] -#[diag(const_eval_transient_mut_borrow_raw, code = "E0658")] -pub(crate) struct TransientMutBorrowErrRaw { +#[diag(const_eval_transient_mut_raw, code = "E0658")] +pub(crate) struct TransientMutRawErr { #[primary_span] pub span: Span, pub kind: ConstContext, @@ -142,8 +156,8 @@ pub(crate) struct UnallowedMutableRefs { } #[derive(Diagnostic)] -#[diag(const_eval_unallowed_mutable_refs_raw, code = "E0764")] -pub(crate) struct UnallowedMutableRefsRaw { +#[diag(const_eval_unallowed_mutable_raw, code = "E0764")] +pub(crate) struct UnallowedMutableRaw { #[primary_span] pub span: Span, pub kind: ConstContext, @@ -194,14 +208,6 @@ pub(crate) struct UnallowedInlineAsm { pub kind: ConstContext, } -#[derive(Diagnostic)] -#[diag(const_eval_unsupported_untyped_pointer)] -#[note] -pub(crate) struct UnsupportedUntypedPointer { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(const_eval_interior_mutable_data_refer, code = "E0492")] pub(crate) struct InteriorMutableDataRefer { @@ -615,18 +621,16 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { PtrToStatic { ptr_kind: PointerKind::Box } => const_eval_validation_box_to_static, PtrToStatic { ptr_kind: PointerKind::Ref } => const_eval_validation_ref_to_static, - PtrToMut { ptr_kind: PointerKind::Box } => const_eval_validation_box_to_mut, - PtrToMut { ptr_kind: PointerKind::Ref } => const_eval_validation_ref_to_mut, - PointerAsInt { .. } => const_eval_validation_pointer_as_int, PartialPointer => const_eval_validation_partial_pointer, MutableRefInConst => const_eval_validation_mutable_ref_in_const, + MutableRefToImmutable => const_eval_validation_mutable_ref_to_immutable, NullFnPtr => const_eval_validation_null_fn_ptr, NeverVal => const_eval_validation_never_val, NullablePtrOutOfRange { .. } => const_eval_validation_nullable_ptr_out_of_range, PtrOutOfRange { .. } => const_eval_validation_ptr_out_of_range, OutOfRange { .. } => const_eval_validation_out_of_range, - UnsafeCell => const_eval_validation_unsafe_cell, + UnsafeCellInImmutable => const_eval_validation_unsafe_cell, UninhabitedVal { .. } => const_eval_validation_uninhabited_val, InvalidEnumTag { .. } => const_eval_validation_invalid_enum_tag, UninhabitedEnumVariant => const_eval_validation_uninhabited_enum_variant, @@ -772,11 +776,11 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { } NullPtr { .. } | PtrToStatic { .. } - | PtrToMut { .. } | MutableRefInConst + | MutableRefToImmutable | NullFnPtr | NeverVal - | UnsafeCell + | UnsafeCellInImmutable | InvalidMetaSliceTooLarge { .. } | InvalidMetaTooLarge { .. } | DanglingPtrUseAfterFree { .. } @@ -905,3 +909,14 @@ impl ReportErrorExt for ResourceExhaustionInfo { } fn add_args(self, _: &DiagCtxt, _: &mut DiagnosticBuilder<'_, G>) {} } + +impl rustc_errors::IntoDiagnosticArg for InternKind { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Borrowed(match self { + InternKind::Static(Mutability::Not) => "static", + InternKind::Static(Mutability::Mut) => "static_mut", + InternKind::Constant => "const", + InternKind::Promoted => "promoted", + })) + } +} diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index d296ff5928b3..0cb5c634b22b 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -117,8 +117,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { def_id, args, ty::ClosureKind::FnOnce, - ) - .ok_or_else(|| err_inval!(TooGeneric))?; + ); let fn_ptr = self.fn_ptr(FnVal::Instance(instance)); self.write_pointer(fn_ptr, dest)?; } diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 202819ee6338..751fbfacaad0 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -5,30 +5,24 @@ //! //! In principle, this is not very complicated: we recursively walk the final value, follow all the //! pointers, and move all reachable allocations to the global `tcx` memory. The only complication -//! is picking the right mutability for the allocations in a `static` initializer: we want to make -//! as many allocations as possible immutable so LLVM can put them into read-only memory. At the -//! same time, we need to make memory that could be mutated by the program mutable to avoid -//! incorrect compilations. To achieve this, we do a type-based traversal of the final value, -//! tracking mutable and shared references and `UnsafeCell` to determine the current mutability. -//! (In principle, we could skip this type-based part for `const` and promoteds, as they need to be -//! always immutable. At least for `const` however we use this opportunity to reject any `const` -//! that contains allocations whose mutability we cannot identify.) +//! is picking the right mutability: the outermost allocation generally has a clear mutability, but +//! what about the other allocations it points to that have also been created with this value? We +//! don't want to do guesswork here. The rules are: `static`, `const`, and promoted can only create +//! immutable allocations that way. `static mut` can be initialized with expressions like `&mut 42`, +//! so all inner allocations are marked mutable. Some of them could potentially be made immutable, +//! but that would require relying on type information, and given how many ways Rust has to lie +//! about type information, we want to avoid doing that. -use super::validity::RefTracking; -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_ast::Mutability; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_middle::mir::interpret::{CtfeProvenance, InterpResult}; -use rustc_middle::ty::{self, layout::TyAndLayout, Ty}; +use rustc_middle::ty::layout::TyAndLayout; -use rustc_ast::Mutability; - -use super::{ - AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, Projectable, - ValueVisitor, -}; +use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy}; use crate::const_eval; -use crate::errors::{DanglingPtrInFinal, UnsupportedUntypedPointer}; +use crate::errors::{DanglingPtrInFinal, MutablePtrInFinal}; pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine< 'mir, @@ -41,271 +35,44 @@ pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine< MemoryMap = FxIndexMap, Allocation)>, >; -struct InternVisitor<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval::MemoryKind>> { - /// The ectx from which we intern. +/// Intern an allocation. Returns `Err` if the allocation does not exist in the local memory. +/// +/// `mutability` can be used to force immutable interning: if it is `Mutability::Not`, the +/// allocation is interned immutably; if it is `Mutability::Mut`, then the allocation *must be* +/// already mutable (as a sanity check). +/// +/// `recursive_alloc` is called for all recursively encountered allocations. +fn intern_shallow<'rt, 'mir, 'tcx, T, M: CompileTimeMachine<'mir, 'tcx, T>>( ecx: &'rt mut InterpCx<'mir, 'tcx, M>, - /// Previously encountered safe references. - ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, InternMode)>, - /// A list of all encountered allocations. After type-based interning, we traverse this list to - /// also intern allocations that are only referenced by a raw pointer or inside a union. - leftover_allocations: &'rt mut FxIndexSet, - /// The root kind of the value that we're looking at. This field is never mutated for a - /// particular allocation. It is primarily used to make as many allocations as possible - /// read-only so LLVM can place them in const memory. - mode: InternMode, - /// This field stores whether we are *currently* inside an `UnsafeCell`. This can affect - /// the intern mode of references we encounter. - inside_unsafe_cell: bool, -} - -#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)] -enum InternMode { - /// A static and its current mutability. Below shared references inside a `static mut`, - /// this is *immutable*, and below mutable references inside an `UnsafeCell`, this - /// is *mutable*. - Static(hir::Mutability), - /// A `const`. - Const, -} - -/// Signalling data structure to ensure we don't recurse -/// into the memory of other constants or statics -struct IsStaticOrFn; - -/// Intern an allocation without looking at its children. -/// `mode` is the mode of the environment where we found this pointer. -/// `mutability` is the mutability of the place to be interned; even if that says -/// `immutable` things might become mutable if `ty` is not frozen. -/// `ty` can be `None` if there is no potential interior mutability -/// to account for (e.g. for vtables). -fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval::MemoryKind>>( - ecx: &'rt mut InterpCx<'mir, 'tcx, M>, - leftover_allocations: &'rt mut FxIndexSet, alloc_id: AllocId, - mode: InternMode, - ty: Option>, -) -> Option { - trace!("intern_shallow {:?} with {:?}", alloc_id, mode); + mutability: Mutability, + mut recursive_alloc: impl FnMut(&InterpCx<'mir, 'tcx, M>, CtfeProvenance), +) -> Result<(), ()> { + trace!("intern_shallow {:?}", alloc_id); // remove allocation - let tcx = ecx.tcx; - let Some((kind, mut alloc)) = ecx.memory.alloc_map.remove(&alloc_id) else { - // Pointer not found in local memory map. It is either a pointer to the global - // map, or dangling. - // If the pointer is dangling (neither in local nor global memory), we leave it - // to validation to error -- it has the much better error messages, pointing out where - // in the value the dangling reference lies. - // The `span_delayed_bug` ensures that we don't forget such a check in validation. - if tcx.try_get_global_alloc(alloc_id).is_none() { - tcx.dcx().span_delayed_bug(ecx.tcx.span, "tried to intern dangling pointer"); - } - // treat dangling pointers like other statics - // just to stop trying to recurse into them - return Some(IsStaticOrFn); + let Some((_kind, mut alloc)) = ecx.memory.alloc_map.remove(&alloc_id) else { + return Err(()); }; - // This match is just a canary for future changes to `MemoryKind`, which most likely need - // changes in this function. - match kind { - MemoryKind::Stack - | MemoryKind::Machine(const_eval::MemoryKind::Heap) - | MemoryKind::CallerLocation => {} - } // Set allocation mutability as appropriate. This is used by LLVM to put things into // read-only memory, and also by Miri when evaluating other globals that // access this one. - if let InternMode::Static(mutability) = mode { - // For this, we need to take into account `UnsafeCell`. When `ty` is `None`, we assume - // no interior mutability. - let frozen = ty.map_or(true, |ty| ty.is_freeze(*ecx.tcx, ecx.param_env)); - // For statics, allocation mutability is the combination of place mutability and - // type mutability. - // The entire allocation needs to be mutable if it contains an `UnsafeCell` anywhere. - let immutable = mutability == Mutability::Not && frozen; - if immutable { + match mutability { + Mutability::Not => { alloc.mutability = Mutability::Not; - } else { - // Just making sure we are not "upgrading" an immutable allocation to mutable. + } + Mutability::Mut => { + // This must be already mutable, we won't "un-freeze" allocations ever. assert_eq!(alloc.mutability, Mutability::Mut); } - } else { - // No matter what, *constants are never mutable*. Mutating them is UB. - // See const_eval::machine::MemoryExtra::can_access_statics for why - // immutability is so important. - - // Validation will ensure that there is no `UnsafeCell` on an immutable allocation. - alloc.mutability = Mutability::Not; - }; + } + // record child allocations + for &(_, prov) in alloc.provenance().ptrs().iter() { + recursive_alloc(ecx, prov); + } // link the alloc id to the actual allocation - leftover_allocations.extend(alloc.provenance().ptrs().iter().map(|&(_, prov)| prov.alloc_id())); - let alloc = tcx.mk_const_alloc(alloc); - tcx.set_alloc_id_memory(alloc_id, alloc); - None -} - -impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval::MemoryKind>> - InternVisitor<'rt, 'mir, 'tcx, M> -{ - fn intern_shallow( - &mut self, - alloc_id: AllocId, - mode: InternMode, - ty: Option>, - ) -> Option { - intern_shallow(self.ecx, self.leftover_allocations, alloc_id, mode, ty) - } -} - -impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::MemoryKind>> - ValueVisitor<'mir, 'tcx, M> for InternVisitor<'rt, 'mir, 'tcx, M> -{ - type V = MPlaceTy<'tcx>; - - #[inline(always)] - fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> { - self.ecx - } - - fn visit_value(&mut self, mplace: &MPlaceTy<'tcx>) -> InterpResult<'tcx> { - // Handle Reference types, as these are the only types with provenance supported by const eval. - // Raw pointers (and boxes) are handled by the `leftover_allocations` logic. - let tcx = self.ecx.tcx; - let ty = mplace.layout.ty; - if let ty::Ref(_, referenced_ty, ref_mutability) = *ty.kind() { - let value = self.ecx.read_immediate(mplace)?; - let mplace = self.ecx.ref_to_mplace(&value)?; - assert_eq!(mplace.layout.ty, referenced_ty); - // Handle trait object vtables. - if let ty::Dynamic(_, _, ty::Dyn) = - tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind() - { - let ptr = mplace.meta().unwrap_meta().to_pointer(&tcx)?; - if let Some(prov) = ptr.provenance { - // Explicitly choose const mode here, since vtables are immutable, even - // if the reference of the fat pointer is mutable. - self.intern_shallow(prov.alloc_id(), InternMode::Const, None); - } else { - // Validation will error (with a better message) on an invalid vtable pointer. - // Let validation show the error message, but make sure it *does* error. - tcx.dcx() - .span_delayed_bug(tcx.span, "vtables pointers cannot be integer pointers"); - } - } - // Check if we have encountered this pointer+layout combination before. - // Only recurse for allocation-backed pointers. - if let Some(prov) = mplace.ptr().provenance { - // Compute the mode with which we intern this. Our goal here is to make as many - // statics as we can immutable so they can be placed in read-only memory by LLVM. - let ref_mode = match self.mode { - InternMode::Static(mutbl) => { - // In statics, merge outer mutability with reference mutability and - // take into account whether we are in an `UnsafeCell`. - - // The only way a mutable reference actually works as a mutable reference is - // by being in a `static mut` directly or behind another mutable reference. - // If there's an immutable reference or we are inside a `static`, then our - // mutable reference is equivalent to an immutable one. As an example: - // `&&mut Foo` is semantically equivalent to `&&Foo` - match ref_mutability { - _ if self.inside_unsafe_cell => { - // Inside an `UnsafeCell` is like inside a `static mut`, the "outer" - // mutability does not matter. - InternMode::Static(ref_mutability) - } - Mutability::Not => { - // A shared reference, things become immutable. - // We do *not* consider `freeze` here: `intern_shallow` considers - // `freeze` for the actual mutability of this allocation; the intern - // mode for references contained in this allocation is tracked more - // precisely when traversing the referenced data (by tracking - // `UnsafeCell`). This makes sure that `&(&i32, &Cell)` still - // has the left inner reference interned into a read-only - // allocation. - InternMode::Static(Mutability::Not) - } - Mutability::Mut => { - // Mutable reference. - InternMode::Static(mutbl) - } - } - } - InternMode::Const => { - // Ignore `UnsafeCell`, everything is immutable. Validity does some sanity - // checking for mutable references that we encounter -- they must all be - // ZST. - InternMode::Const - } - }; - match self.intern_shallow(prov.alloc_id(), ref_mode, Some(referenced_ty)) { - // No need to recurse, these are interned already and statics may have - // cycles, so we don't want to recurse there - Some(IsStaticOrFn) => {} - // intern everything referenced by this value. The mutability is taken from the - // reference. It is checked above that mutable references only happen in - // `static mut` - None => self.ref_tracking.track((mplace, ref_mode), || ()), - } - } - Ok(()) - } else { - // Not a reference. Check if we want to recurse. - let is_walk_needed = |mplace: &MPlaceTy<'tcx>| -> InterpResult<'tcx, bool> { - // ZSTs cannot contain pointers, we can avoid the interning walk. - if mplace.layout.is_zst() { - return Ok(false); - } - - // Now, check whether this allocation could contain references. - // - // Note, this check may sometimes not be cheap, so we only do it when the walk we'd like - // to avoid could be expensive: on the potentially larger types, arrays and slices, - // rather than on all aggregates unconditionally. - if matches!(mplace.layout.ty.kind(), ty::Array(..) | ty::Slice(..)) { - let Some((size, _align)) = self.ecx.size_and_align_of_mplace(mplace)? else { - // We do the walk if we can't determine the size of the mplace: we may be - // dealing with extern types here in the future. - return Ok(true); - }; - - // If there is no provenance in this allocation, it does not contain references - // that point to another allocation, and we can avoid the interning walk. - if let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr(), size)? { - if !alloc.has_provenance() { - return Ok(false); - } - } else { - // We're encountering a ZST here, and can avoid the walk as well. - return Ok(false); - } - } - - // In the general case, we do the walk. - Ok(true) - }; - - // If this allocation contains no references to intern, we avoid the potentially costly - // walk. - // - // We can do this before the checks for interior mutability below, because only references - // are relevant in that situation, and we're checking if there are any here. - if !is_walk_needed(mplace)? { - return Ok(()); - } - - if let Some(def) = mplace.layout.ty.ty_adt_def() { - if def.is_unsafe_cell() { - // We are crossing over an `UnsafeCell`, we can mutate again. This means that - // References we encounter inside here are interned as pointing to mutable - // allocations. - // Remember the `old` value to handle nested `UnsafeCell`. - let old = std::mem::replace(&mut self.inside_unsafe_cell, true); - let walked = self.walk_value(mplace); - self.inside_unsafe_cell = old; - return walked; - } - } - - self.walk_value(mplace) - } - } + let alloc = ecx.tcx.mk_const_alloc(alloc); + ecx.tcx.set_alloc_id_memory(alloc_id, alloc); + Ok(()) } /// How a constant value should be interned. @@ -332,122 +99,108 @@ pub fn intern_const_alloc_recursive< intern_kind: InternKind, ret: &MPlaceTy<'tcx>, ) -> Result<(), ErrorGuaranteed> { - let tcx = ecx.tcx; - let base_intern_mode = match intern_kind { - InternKind::Static(mutbl) => InternMode::Static(mutbl), - // `Constant` includes array lengths. - InternKind::Constant | InternKind::Promoted => InternMode::Const, + // We are interning recursively, and for mutability we are distinguishing the "root" allocation + // that we are starting in, and all other allocations that we are encountering recursively. + let (base_mutability, inner_mutability) = match intern_kind { + InternKind::Constant | InternKind::Promoted => { + // Completely immutable. Interning anything mutably here can only lead to unsoundness, + // since all consts are conceptually independent values but share the same underlying + // memory. + (Mutability::Not, Mutability::Not) + } + InternKind::Static(Mutability::Not) => { + ( + // Outermost allocation is mutable if `!Freeze`. + if ret.layout.ty.is_freeze(*ecx.tcx, ecx.param_env) { + Mutability::Not + } else { + Mutability::Mut + }, + // Inner allocations are never mutable. They can only arise via the "tail + // expression" / "outer scope" rule, and we treat them consistently with `const`. + Mutability::Not, + ) + } + InternKind::Static(Mutability::Mut) => { + // Just make everything mutable. We accept code like + // `static mut X = &mut [42]`, so even inner allocations need to be mutable. + (Mutability::Mut, Mutability::Mut) + } }; - // Type based interning. - // `ref_tracking` tracks typed references we have already interned and still need to crawl for - // more typed information inside them. - // `leftover_allocations` collects *all* allocations we see, because some might not - // be available in a typed way. They get interned at the end. - let mut ref_tracking = RefTracking::empty(); - let leftover_allocations = &mut FxIndexSet::default(); + // Initialize recursive interning. + let base_alloc_id = ret.ptr().provenance.unwrap().alloc_id(); + let mut todo = vec![(base_alloc_id, base_mutability)]; + // We need to distinguish "has just been interned" from "was already in `tcx`", + // so we track this in a separate set. + let mut just_interned = FxHashSet::default(); + // Whether we encountered a bad mutable pointer. + // We want to first report "dangling" and then "mutable", so we need to delay reporting these + // errors. + let mut found_bad_mutable_pointer = false; - // start with the outermost allocation - intern_shallow( - ecx, - leftover_allocations, - // The outermost allocation must exist, because we allocated it with - // `Memory::allocate`. - ret.ptr().provenance.unwrap().alloc_id(), - base_intern_mode, - Some(ret.layout.ty), - ); - - ref_tracking.track((ret.clone(), base_intern_mode), || ()); - - while let Some(((mplace, mode), _)) = ref_tracking.todo.pop() { - let res = InternVisitor { - ref_tracking: &mut ref_tracking, - ecx, - mode, - leftover_allocations, - inside_unsafe_cell: false, + // Keep interning as long as there are things to intern. + // We show errors if there are dangling pointers, or mutable pointers in immutable contexts + // (i.e., everything except for `static mut`). When these errors affect references, it is + // unfortunate that we show these errors here and not during validation, since validation can + // show much nicer errors. However, we do need these checks to be run on all pointers, including + // raw pointers, so we cannot rely on validation to catch them -- and since interning runs + // before validation, and interning doesn't know the type of anything, this means we can't show + // better errors. Maybe we should consider doing validation before interning in the future. + while let Some((alloc_id, mutability)) = todo.pop() { + if ecx.tcx.try_get_global_alloc(alloc_id).is_some() { + // Already interned. + debug_assert!(!ecx.memory.alloc_map.contains_key(&alloc_id)); + continue; } - .visit_value(&mplace); - // We deliberately *ignore* interpreter errors here. When there is a problem, the remaining - // references are "leftover"-interned, and later validation will show a proper error - // and point at the right part of the value causing the problem. - match res { - Ok(()) => {} - Err(error) => { - ecx.tcx.dcx().span_delayed_bug( - ecx.tcx.span, - format!( - "error during interning should later cause validation failure: {}", - ecx.format_error(error), - ), - ); + just_interned.insert(alloc_id); + intern_shallow(ecx, alloc_id, mutability, |ecx, prov| { + let alloc_id = prov.alloc_id(); + if intern_kind != InternKind::Promoted + && inner_mutability == Mutability::Not + && !prov.immutable() + { + if ecx.tcx.try_get_global_alloc(alloc_id).is_some() + && !just_interned.contains(&alloc_id) + { + // This is a pointer to some memory from another constant. We encounter mutable + // pointers to such memory since we do not always track immutability through + // these "global" pointers. Allowing them is harmless; the point of these checks + // during interning is to justify why we intern the *new* allocations immutably, + // so we can completely ignore existing allocations. We also don't need to add + // this to the todo list, since after all it is already interned. + return; + } + // Found a mutable pointer inside a const where inner allocations should be + // immutable. We exclude promoteds from this, since things like `&mut []` and + // `&None::>` lead to promotion that can produce mutable pointers. We rely + // on the promotion analysis not screwing up to ensure that it is sound to intern + // promoteds as immutable. + found_bad_mutable_pointer = true; } - } + // We always intern with `inner_mutability`, and furthermore we ensured above that if + // that is "immutable", then there are *no* mutable pointers anywhere in the newly + // interned memory -- justifying that we can indeed intern immutably. However this also + // means we can *not* easily intern immutably here if `prov.immutable()` is true and + // `inner_mutability` is `Mut`: there might be other pointers to that allocation, and + // we'd have to somehow check that they are *all* immutable before deciding that this + // allocation can be made immutable. In the future we could consider analyzing all + // pointers before deciding which allocations can be made immutable; but for now we are + // okay with losing some potential for immutability here. This can anyway only affect + // `static mut`. + todo.push((alloc_id, inner_mutability)); + }) + .map_err(|()| { + ecx.tcx.dcx().emit_err(DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind }) + })?; + } + if found_bad_mutable_pointer { + return Err(ecx + .tcx + .dcx() + .emit_err(MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind })); } - // Intern the rest of the allocations as mutable. These might be inside unions, padding, raw - // pointers, ... So we can't intern them according to their type rules - - let mut todo: Vec<_> = leftover_allocations.iter().cloned().collect(); - debug!(?todo); - debug!("dead_alloc_map: {:#?}", ecx.memory.dead_alloc_map); - while let Some(alloc_id) = todo.pop() { - if let Some((_, mut alloc)) = ecx.memory.alloc_map.remove(&alloc_id) { - // We can't call the `intern_shallow` method here, as its logic is tailored to safe - // references and a `leftover_allocations` set (where we only have a todo-list here). - // So we hand-roll the interning logic here again. - match intern_kind { - // Statics may point to mutable allocations. - // Even for immutable statics it would be ok to have mutable allocations behind - // raw pointers, e.g. for `static FOO: *const AtomicUsize = &AtomicUsize::new(42)`. - InternKind::Static(_) => {} - // Raw pointers in promoteds may only point to immutable things so we mark - // everything as immutable. - // It is UB to mutate through a raw pointer obtained via an immutable reference: - // Since all references and pointers inside a promoted must by their very definition - // be created from an immutable reference (and promotion also excludes interior - // mutability), mutating through them would be UB. - // There's no way we can check whether the user is using raw pointers correctly, - // so all we can do is mark this as immutable here. - InternKind::Promoted => { - // See const_eval::machine::MemoryExtra::can_access_statics for why - // immutability is so important. - alloc.mutability = Mutability::Not; - } - // If it's a constant, we should not have any "leftovers" as everything - // is tracked by const-checking. - // FIXME: downgrade this to a warning? It rejects some legitimate consts, - // such as `const CONST_RAW: *const Vec = &Vec::new() as *const _;`. - // - // NOTE: it looks likes this code path is only reachable when we try to intern - // something that cannot be promoted, which in constants means values that have - // drop glue, such as the example above. - InternKind::Constant => { - ecx.tcx.dcx().emit_err(UnsupportedUntypedPointer { span: ecx.tcx.span }); - // For better errors later, mark the allocation as immutable. - alloc.mutability = Mutability::Not; - } - } - let alloc = tcx.mk_const_alloc(alloc); - tcx.set_alloc_id_memory(alloc_id, alloc); - for &(_, prov) in alloc.inner().provenance().ptrs().iter() { - let alloc_id = prov.alloc_id(); - if leftover_allocations.insert(alloc_id) { - todo.push(alloc_id); - } - } - } else if ecx.memory.dead_alloc_map.contains_key(&alloc_id) { - // Codegen does not like dangling pointers, and generally `tcx` assumes that - // all allocations referenced anywhere actually exist. So, make sure we error here. - let reported = ecx.tcx.dcx().emit_err(DanglingPtrInFinal { span: ecx.tcx.span }); - return Err(reported); - } else if ecx.tcx.try_get_global_alloc(alloc_id).is_none() { - // We have hit an `AllocId` that is neither in local or global memory and isn't - // marked as dangling by local memory. That should be impossible. - span_bug!(ecx.tcx.span, "encountered unknown alloc id {:?}", alloc_id); - } - } Ok(()) } @@ -462,29 +215,18 @@ pub fn intern_const_alloc_for_constprop< ecx: &mut InterpCx<'mir, 'tcx, M>, alloc_id: AllocId, ) -> InterpResult<'tcx, ()> { - // Move allocation to `tcx`. - let Some((_, mut alloc)) = ecx.memory.alloc_map.remove(&alloc_id) else { - // Pointer not found in local memory map. It is either a pointer to the global - // map, or dangling. - if ecx.tcx.try_get_global_alloc(alloc_id).is_none() { - throw_ub!(DeadLocal) - } + if ecx.tcx.try_get_global_alloc(alloc_id).is_some() { // The constant is already in global memory. Do nothing. return Ok(()); - }; - - alloc.mutability = Mutability::Not; - - // We are not doing recursive interning, so we don't currently support provenance. - // (If this assertion ever triggers, we should just implement a - // proper recursive interning loop.) - assert!(alloc.provenance().ptrs().is_empty()); - - // Link the alloc id to the actual allocation - let alloc = ecx.tcx.mk_const_alloc(alloc); - ecx.tcx.set_alloc_id_memory(alloc_id, alloc); - - Ok(()) + } + // Move allocation to `tcx`. + intern_shallow(ecx, alloc_id, Mutability::Not, |_ecx, _| { + // We are not doing recursive interning, so we don't currently support provenance. + // (If this assertion ever triggers, we should just implement a + // proper recursive interning loop -- or just call `intern_const_alloc_recursive`. + panic!("`intern_const_alloc_for_constprop` called on allocation with nested provenance") + }) + .map_err(|()| err_ub!(DeadLocal).into()) } impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>> @@ -504,12 +246,16 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>> // `allocate` picks a fresh AllocId that we will associate with its data below. let dest = self.allocate(layout, MemoryKind::Stack)?; f(self, &dest.clone().into())?; - let mut alloc = - self.memory.alloc_map.remove(&dest.ptr().provenance.unwrap().alloc_id()).unwrap().1; - alloc.mutability = Mutability::Not; - let alloc = self.tcx.mk_const_alloc(alloc); let alloc_id = dest.ptr().provenance.unwrap().alloc_id(); // this was just allocated, it must have provenance - self.tcx.set_alloc_id_memory(alloc_id, alloc); + intern_shallow(self, alloc_id, Mutability::Not, |ecx, prov| { + // We are not doing recursive interning, so we don't currently support provenance. + // (If this assertion ever triggers, we should just implement a + // proper recursive interning loop -- or just call `intern_const_alloc_recursive`. + if !ecx.tcx.try_get_global_alloc(prov.alloc_id()).is_some() { + panic!("`intern_with_temp_alloc` with nested allocations"); + } + }) + .unwrap(); Ok(alloc_id) } } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 639b269ac257..b39efad61bb3 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -62,8 +62,8 @@ pub(super) struct MemPlace { impl MemPlace { /// Adjust the provenance of the main pointer (metadata is unaffected). - pub fn map_provenance(self, f: impl FnOnce(Option) -> Option) -> Self { - MemPlace { ptr: self.ptr.map_provenance(f), ..self } + pub fn map_provenance(self, f: impl FnOnce(Prov) -> Prov) -> Self { + MemPlace { ptr: self.ptr.map_provenance(|p| p.map(f)), ..self } } /// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space. @@ -128,7 +128,7 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> { } /// Adjust the provenance of the main pointer (metadata is unaffected). - pub fn map_provenance(self, f: impl FnOnce(Option) -> Option) -> Self { + pub fn map_provenance(self, f: impl FnOnce(Prov) -> Prov) -> Self { MPlaceTy { mplace: self.mplace.map_provenance(f), ..self } } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 8b44b87647da..b5cd32595201 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -9,12 +9,13 @@ use std::num::NonZeroUsize; use either::{Left, Right}; +use hir::def::DefKind; use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_middle::mir::interpret::{ - ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, ValidationErrorInfo, - ValidationErrorKind, ValidationErrorKind::*, + ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance, + ValidationErrorInfo, ValidationErrorKind, ValidationErrorKind::*, }; use rustc_middle::ty; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; @@ -123,15 +124,41 @@ pub enum PathElem { } /// Extra things to check for during validation of CTFE results. +#[derive(Copy, Clone)] pub enum CtfeValidationMode { - /// Regular validation, nothing special happening. - Regular, - /// Validation of a `const`. - /// `inner` says if this is an inner, indirect allocation (as opposed to the top-level const - /// allocation). Being an inner allocation makes a difference because the top-level allocation - /// of a `const` is copied for each use, but the inner allocations are implicitly shared. + /// Validation of a `static` + Static { mutbl: Mutability }, + /// Validation of a `const` (including promoteds). + /// `allow_immutable_unsafe_cell` says whether we allow `UnsafeCell` in immutable memory (which is the + /// case for the top-level allocation of a `const`, where this is fine because the allocation will be + /// copied at each use site). /// `allow_static_ptrs` says if pointers to statics are permitted (which is the case for promoteds in statics). - Const { inner: bool, allow_static_ptrs: bool }, + Const { allow_immutable_unsafe_cell: bool, allow_static_ptrs: bool }, +} + +impl CtfeValidationMode { + fn allow_immutable_unsafe_cell(self) -> bool { + match self { + CtfeValidationMode::Static { .. } => false, + CtfeValidationMode::Const { allow_immutable_unsafe_cell, .. } => { + allow_immutable_unsafe_cell + } + } + } + + fn allow_static_ptrs(self) -> bool { + match self { + CtfeValidationMode::Static { .. } => true, // statics can point to statics + CtfeValidationMode::Const { allow_static_ptrs, .. } => allow_static_ptrs, + } + } + + fn may_contain_mutable_ref(self) -> bool { + match self { + CtfeValidationMode::Static { mutbl } => mutbl == Mutability::Mut, + CtfeValidationMode::Const { .. } => false, + } + } } /// State for tracking recursive validation of references @@ -418,26 +445,52 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' } // Recursive checking if let Some(ref_tracking) = self.ref_tracking.as_deref_mut() { + // Determine whether this pointer expects to be pointing to something mutable. + let ptr_expected_mutbl = match ptr_kind { + PointerKind::Box => Mutability::Mut, + PointerKind::Ref => { + let tam = value.layout.ty.builtin_deref(false).unwrap(); + // ZST never require mutability. We do not take into account interior mutability + // here since we cannot know if there really is an `UnsafeCell` inside + // `Option` -- so we check that in the recursive descent behind this + // reference. + if size == Size::ZERO { Mutability::Not } else { tam.mutbl } + } + }; // Proceed recursively even for ZST, no reason to skip them! // `!` is a ZST and we want to validate it. if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr()) { // Let's see what kind of memory this points to. - let alloc_kind = self.ecx.tcx.try_get_global_alloc(alloc_id); + // `unwrap` since dangling pointers have already been handled. + let alloc_kind = self.ecx.tcx.try_get_global_alloc(alloc_id).unwrap(); match alloc_kind { - Some(GlobalAlloc::Static(did)) => { + GlobalAlloc::Static(did) => { // Special handling for pointers to statics (irrespective of their type). assert!(!self.ecx.tcx.is_thread_local_static(did)); assert!(self.ecx.tcx.is_static(did)); - if matches!( - self.ctfe_mode, - Some(CtfeValidationMode::Const { allow_static_ptrs: false, .. }) - ) { + if self.ctfe_mode.is_some_and(|c| !c.allow_static_ptrs()) { // See const_eval::machine::MemoryExtra::can_access_statics for why // this check is so important. // This check is reachable when the const just referenced the static, // but never read it (so we never entered `before_access_global`). throw_validation_failure!(self.path, PtrToStatic { ptr_kind }); } + // Mutability check. + if ptr_expected_mutbl == Mutability::Mut { + if matches!( + self.ecx.tcx.def_kind(did), + DefKind::Static(Mutability::Not) + ) && self + .ecx + .tcx + .type_of(did) + .no_bound_vars() + .expect("statics should not have generic parameters") + .is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all()) + { + throw_validation_failure!(self.path, MutableRefToImmutable); + } + } // We skip recursively checking other statics. These statics must be sound by // themselves, and the only way to get broken statics here is by using // unsafe code. @@ -450,18 +503,31 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // referring to statics). return Ok(()); } - Some(GlobalAlloc::Memory(alloc)) => { + GlobalAlloc::Memory(alloc) => { if alloc.inner().mutability == Mutability::Mut && matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. })) { - // This should be unreachable, but if someone manages to copy a pointer - // out of a `static`, then that pointer might point to mutable memory, - // and we would catch that here. - throw_validation_failure!(self.path, PtrToMut { ptr_kind }); + // This is impossible: this can only be some inner allocation of a + // `static mut` (everything else either hits the `GlobalAlloc::Static` + // case or is interned immutably). To get such a pointer we'd have to + // load it from a static, but such loads lead to a CTFE error. + span_bug!( + self.ecx.tcx.span, + "encountered reference to mutable memory inside a `const`" + ); + } + if ptr_expected_mutbl == Mutability::Mut + && alloc.inner().mutability == Mutability::Not + { + throw_validation_failure!(self.path, MutableRefToImmutable); + } + } + GlobalAlloc::Function(..) | GlobalAlloc::VTable(..) => { + // These are immutable, we better don't allow mutable pointers here. + if ptr_expected_mutbl == Mutability::Mut { + throw_validation_failure!(self.path, MutableRefToImmutable); } } - // Nothing to check for these. - None | Some(GlobalAlloc::Function(..) | GlobalAlloc::VTable(..)) => {} } } let path = &self.path; @@ -532,11 +598,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' Ok(true) } ty::Ref(_, ty, mutbl) => { - if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. })) + if self.ctfe_mode.is_some_and(|c| !c.may_contain_mutable_ref()) && *mutbl == Mutability::Mut { - // A mutable reference inside a const? That does not seem right (except if it is - // a ZST). let layout = self.ecx.layout_of(*ty)?; if !layout.is_zst() { throw_validation_failure!(self.path, MutableRefInConst); @@ -642,6 +706,19 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' ) } } + + fn in_mutable_memory(&self, op: &OpTy<'tcx, M::Provenance>) -> bool { + if let Some(mplace) = op.as_mplace_or_imm().left() { + if let Some(alloc_id) = mplace.ptr().provenance.and_then(|p| p.get_alloc_id()) { + if self.ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner().mutability + == Mutability::Mut + { + return true; + } + } + } + false + } } impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> @@ -705,10 +782,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> op: &OpTy<'tcx, M::Provenance>, _fields: NonZeroUsize, ) -> InterpResult<'tcx> { - // Special check preventing `UnsafeCell` inside unions in the inner part of constants. - if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. })) { - if !op.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.param_env) { - throw_validation_failure!(self.path, UnsafeCell); + // Special check for CTFE validation, preventing `UnsafeCell` inside unions in immutable memory. + if self.ctfe_mode.is_some_and(|c| !c.allow_immutable_unsafe_cell()) { + if !op.layout.is_zst() && !op.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.param_env) { + if !self.in_mutable_memory(op) { + throw_validation_failure!(self.path, UnsafeCellInImmutable); + } } } Ok(()) @@ -730,11 +809,14 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> } // Special check preventing `UnsafeCell` in the inner part of constants - if let Some(def) = op.layout.ty.ty_adt_def() { - if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. })) + if self.ctfe_mode.is_some_and(|c| !c.allow_immutable_unsafe_cell()) { + if !op.layout.is_zst() + && let Some(def) = op.layout.ty.ty_adt_def() && def.is_unsafe_cell() { - throw_validation_failure!(self.path, UnsafeCell); + if !self.in_mutable_memory(op) { + throw_validation_failure!(self.path, UnsafeCellInImmutable); + } } } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 327c91731bf5..39bc2b960e9f 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -466,7 +466,7 @@ impl<'tcx> NonConstOp<'tcx> for MutBorrow { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { match self.0 { - hir::BorrowKind::Raw => ccx.dcx().create_err(errors::UnallowedMutableRefsRaw { + hir::BorrowKind::Raw => ccx.tcx.dcx().create_err(errors::UnallowedMutableRaw { span, kind: ccx.const_kind(), teach: ccx.tcx.sess.teach(&error_code!(E0764)).then_some(()), @@ -491,10 +491,10 @@ impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let kind = ccx.const_kind(); match self.0 { - hir::BorrowKind::Raw => ccx.tcx.sess.create_feature_err( - errors::TransientMutBorrowErrRaw { span, kind }, - sym::const_mut_refs, - ), + hir::BorrowKind::Raw => ccx + .tcx + .sess + .create_feature_err(errors::TransientMutRawErr { span, kind }, sym::const_mut_refs), hir::BorrowKind::Ref => ccx.tcx.sess.create_feature_err( errors::TransientMutBorrowErr { span, kind }, sym::const_mut_refs, diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs index 4a3cfd50b441..36a315b8f307 100644 --- a/compiler/rustc_const_eval/src/util/caller_location.rs +++ b/compiler/rustc_const_eval/src/util/caller_location.rs @@ -27,6 +27,7 @@ fn alloc_caller_location<'mir, 'tcx>( // See https://github.com/rust-lang/rust/pull/89920#discussion_r730012398 ecx.allocate_str("", MemoryKind::CallerLocation, Mutability::Not).unwrap() }; + let file = file.map_provenance(CtfeProvenance::as_immutable); let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) }; let col = if loc_details.column { Scalar::from_u32(col) } else { Scalar::from_u32(0) }; diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index 556560945e94..5bc904e5930c 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -12,7 +12,9 @@ use rustc_trait_selection::traits::StructurallyNormalizeExt; #[derive(Copy, Clone, Debug)] pub enum AutoderefKind { + /// A true pointer type, such as `&T` and `*mut T`. Builtin, + /// A type which must dispatch to a `Deref` implementation. Overloaded, } @@ -83,6 +85,7 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { (AutoderefKind::Builtin, ty) } } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) { + // The overloaded deref check already normalizes the pointee type. (AutoderefKind::Overloaded, ty) } else { return None; diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 5b264f6f034c..57829d9d4184 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -254,7 +254,7 @@ fn compare_method_predicate_entailment<'tcx>( // checks. For the comparison to be valid, we need to // normalize the associated types in the impl/trait methods // first. However, because function types bind regions, just - // calling `normalize_associated_types_in` would have no effect on + // calling `FnCtxt::normalize` would have no effect on // any associated types appearing in the fn arguments or return // type. diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index bfa9dc42422c..3674a760cbf9 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -530,9 +530,13 @@ pub(super) fn type_of_opaque( Ok(ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id) { Node::Item(item) => match item.kind { ItemKind::OpaqueTy(OpaqueTy { - origin: hir::OpaqueTyOrigin::TyAlias { .. }, + origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false }, .. }) => opaque::find_opaque_ty_constraints_for_tait(tcx, def_id), + ItemKind::OpaqueTy(OpaqueTy { + origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true }, + .. + }) => opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(tcx, def_id), // Opaque types desugared from `impl Trait`. ItemKind::OpaqueTy(&OpaqueTy { origin: diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index 85093bc12b37..79cb384c5bde 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -23,6 +23,60 @@ pub fn test_opaque_hidden_types(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> res } +/// Checks "defining uses" of opaque `impl Trait` in associated types. +/// These can only be defined by associated items of the same trait. +#[instrument(skip(tcx), level = "debug")] +pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type( + tcx: TyCtxt<'_>, + def_id: LocalDefId, +) -> Ty<'_> { + let mut parent_def_id = def_id; + while tcx.def_kind(parent_def_id) == def::DefKind::OpaqueTy { + // Account for `type Alias = impl Trait;` (#116031) + parent_def_id = tcx.local_parent(parent_def_id); + } + let impl_def_id = tcx.local_parent(parent_def_id); + match tcx.def_kind(impl_def_id) { + DefKind::Impl { .. } => {} + other => bug!("invalid impl trait in assoc type parent: {other:?}"), + } + + let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] }; + + for &assoc_id in tcx.associated_item_def_ids(impl_def_id) { + let assoc = tcx.associated_item(assoc_id); + match assoc.kind { + ty::AssocKind::Const | ty::AssocKind::Fn => { + locator.check(assoc_id.expect_local(), ImplTraitSource::AssocTy) + } + // Associated types don't have bodies, so they can't constrain hidden types + ty::AssocKind::Type => {} + } + } + + if let Some(hidden) = locator.found { + // Only check against typeck if we didn't already error + if !hidden.ty.references_error() { + for concrete_type in locator.typeck_types { + if concrete_type.ty != tcx.erase_regions(hidden.ty) + && !(concrete_type, hidden).references_error() + { + hidden.report_mismatch(&concrete_type, def_id, tcx).emit(); + } + } + } + + hidden.ty + } else { + let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType { + span: tcx.def_span(def_id), + name: tcx.item_name(parent_def_id.to_def_id()), + what: "impl", + }); + Ty::new_error(tcx, reported) + } +} + /// Checks "defining uses" of opaque `impl Trait` types to ensure that they meet the restrictions /// laid for "higher-order pattern unification". /// This ensures that inference is tractable. @@ -128,9 +182,15 @@ struct TaitConstraintLocator<'tcx> { typeck_types: Vec>, } +#[derive(Debug)] +enum ImplTraitSource { + AssocTy, + TyAlias, +} + impl TaitConstraintLocator<'_> { #[instrument(skip(self), level = "debug")] - fn check(&mut self, item_def_id: LocalDefId) { + fn check(&mut self, item_def_id: LocalDefId, source: ImplTraitSource) { // Don't try to check items that cannot possibly constrain the type. if !self.tcx.has_typeck_results(item_def_id) { debug!("no constraint: no typeck results"); @@ -182,7 +242,13 @@ impl TaitConstraintLocator<'_> { continue; } constrained = true; - if !self.tcx.opaque_types_defined_by(item_def_id).contains(&self.def_id) { + let opaque_types_defined_by = match source { + ImplTraitSource::AssocTy => { + self.tcx.impl_trait_in_assoc_types_defined_by(item_def_id) + } + ImplTraitSource::TyAlias => self.tcx.opaque_types_defined_by(item_def_id), + }; + if !opaque_types_defined_by.contains(&self.def_id) { self.tcx.dcx().emit_err(TaitForwardCompat { span: hidden_type.span, item_span: self @@ -240,7 +306,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> { } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { if let hir::ExprKind::Closure(closure) = ex.kind { - self.check(closure.def_id); + self.check(closure.def_id, ImplTraitSource::TyAlias); } intravisit::walk_expr(self, ex); } @@ -248,7 +314,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> { trace!(?it.owner_id); // The opaque type itself or its children are not within its reveal scope. if it.owner_id.def_id != self.def_id { - self.check(it.owner_id.def_id); + self.check(it.owner_id.def_id, ImplTraitSource::TyAlias); intravisit::walk_item(self, it); } } @@ -256,13 +322,13 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> { trace!(?it.owner_id); // The opaque type itself or its children are not within its reveal scope. if it.owner_id.def_id != self.def_id { - self.check(it.owner_id.def_id); + self.check(it.owner_id.def_id, ImplTraitSource::TyAlias); intravisit::walk_impl_item(self, it); } } fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { trace!(?it.owner_id); - self.check(it.owner_id.def_id); + self.check(it.owner_id.def_id, ImplTraitSource::TyAlias); intravisit::walk_trait_item(self, it); } fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) { diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 9850892bd36a..b6dfc34d3ac1 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -44,7 +44,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty) || self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) || self.suggest_no_capture_closure(err, expected, expr_ty) - || self.suggest_boxing_when_appropriate(err, expr.span, expr.hir_id, expected, expr_ty) + || self.suggest_boxing_when_appropriate( + err, + expr.peel_blocks().span, + expr.hir_id, + expected, + expr_ty, + ) || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected) || self.suggest_copied_cloned_or_as_ref(err, expr, expr_ty, expected) || self.suggest_clone_for_ref(err, expr, expr_ty, expected) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 09dbfaa57d24..c6b9197d0e98 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -2,7 +2,7 @@ use crate::callee::{self, DeferredCallResolution}; use crate::errors::CtorIsPrivate; use crate::method::{self, MethodCallee, SelfSource}; use crate::rvalue_scopes; -use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, RawTy}; +use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan, StashKey}; @@ -373,14 +373,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub fn handle_raw_ty(&self, span: Span, ty: Ty<'tcx>) -> RawTy<'tcx> { - RawTy { raw: ty, normalized: self.normalize(span, ty) } - } - - pub fn to_ty(&self, ast_t: &hir::Ty<'tcx>) -> RawTy<'tcx> { + pub fn to_ty(&self, ast_t: &hir::Ty<'tcx>) -> LoweredTy<'tcx> { let t = self.astconv().ast_ty_to_ty(ast_t); self.register_wf_obligation(t.into(), ast_t.span, traits::WellFormed(None)); - self.handle_raw_ty(ast_t.span, t) + LoweredTy::from_raw(self, ast_t.span, t) } pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { @@ -396,7 +392,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty.normalized } - pub(super) fn user_args_for_adt(ty: RawTy<'tcx>) -> UserArgs<'tcx> { + pub(super) fn user_args_for_adt(ty: LoweredTy<'tcx>) -> UserArgs<'tcx> { match (ty.raw.kind(), ty.normalized.kind()) { (ty::Adt(_, args), _) => UserArgs { args, user_self_ty: None }, (_, ty::Adt(adt, args)) => UserArgs { @@ -801,7 +797,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir_id: hir::HirId, span: Span, args: Option<&'tcx [hir::Expr<'tcx>]>, - ) -> (Res, Option>, &'tcx [hir::PathSegment<'tcx>]) { + ) -> (Res, Option>, &'tcx [hir::PathSegment<'tcx>]) { debug!( "resolve_ty_and_res_fully_qualified_call: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span @@ -825,7 +821,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We manually call `register_wf_obligation` in the success path // below. let ty = self.astconv().ast_ty_to_ty_in_path(qself); - (self.handle_raw_ty(span, ty), qself, segment) + (LoweredTy::from_raw(self, span, ty), qself, segment) } QPath::LangItem(..) => { bug!("`resolve_ty_and_res_fully_qualified_call` called on `LangItem`") @@ -1074,7 +1070,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn instantiate_value_path( &self, segments: &'tcx [hir::PathSegment<'tcx>], - self_ty: Option>, + self_ty: Option>, res: Res, span: Span, hir_id: hir::HirId, @@ -1201,8 +1197,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { path_segs.last().is_some_and(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self); let (res, self_ctor_args) = if let Res::SelfCtor(impl_def_id) = res { - let ty = - self.handle_raw_ty(span, tcx.at(span).type_of(impl_def_id).instantiate_identity()); + let ty = LoweredTy::from_raw( + self, + span, + tcx.at(span).type_of(impl_def_id).instantiate_identity(), + ); match ty.normalized.ty_adt_def() { Some(adt_def) if adt_def.has_ctor() => { let (ctor_kind, ctor_def_id) = adt_def.non_enum_variant().ctor.unwrap(); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 136ed1a709e9..6a77450f075b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -6,7 +6,7 @@ use crate::method::MethodCallee; use crate::TupleArgumentsFlag::*; use crate::{errors, Expectation::*}; use crate::{ - struct_span_code_err, BreakableCtxt, Diverges, Expectation, FnCtxt, Needs, RawTy, + struct_span_code_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy, Needs, TupleArgumentsFlag, }; use itertools::Itertools; @@ -1792,12 +1792,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { qpath: &QPath<'tcx>, path_span: Span, hir_id: hir::HirId, - ) -> (Res, RawTy<'tcx>) { + ) -> (Res, LoweredTy<'tcx>) { match *qpath { QPath::Resolved(ref maybe_qself, path) => { let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself).raw); let ty = self.astconv().res_to_ty(self_ty, path, hir_id, true); - (path.res, self.handle_raw_ty(path_span, ty)) + (path.res, LoweredTy::from_raw(self, path_span, ty)) } QPath::TypeRelative(qself, segment) => { let ty = self.to_ty(qself); @@ -1808,7 +1808,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = result .map(|(ty, _, _)| ty) .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar)); - let ty = self.handle_raw_ty(path_span, ty); + let ty = LoweredTy::from_raw(self, path_span, ty); let result = result.map(|(_, kind, def_id)| (kind, def_id)); // Write back the new resolution. @@ -1818,7 +1818,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } QPath::LangItem(lang_item, span) => { let (res, ty) = self.resolve_lang_item_path(lang_item, span, hir_id); - (res, self.handle_raw_ty(path_span, ty)) + (res, LoweredTy::from_raw(self, path_span, ty)) } } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index f65e9b698ab2..18f547be2a71 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -353,14 +353,22 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { } } -/// Represents a user-provided type in the raw form (never normalized). +/// The `ty` representation of a user-provided type. Depending on the use-site +/// we want to either use the unnormalized or the normalized form of this type. /// /// This is a bridge between the interface of `AstConv`, which outputs a raw `Ty`, /// and the API in this module, which expect `Ty` to be fully normalized. #[derive(Clone, Copy, Debug)] -pub struct RawTy<'tcx> { +pub struct LoweredTy<'tcx> { + /// The unnormalized type provided by the user. pub raw: Ty<'tcx>, /// The normalized form of `raw`, stored here for efficiency. pub normalized: Ty<'tcx>, } + +impl<'tcx> LoweredTy<'tcx> { + pub fn from_raw(fcx: &FnCtxt<'_, 'tcx>, span: Span, raw: Ty<'tcx>) -> LoweredTy<'tcx> { + LoweredTy { raw, normalized: fcx.normalize(span, raw) } + } +} diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 80467ca9381e..bdd7c382903f 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -49,7 +49,7 @@ use crate::check::check_fn; use crate::coercion::DynamicCoerceMany; use crate::diverges::Diverges; use crate::expectation::Expectation; -use crate::fn_ctxt::RawTy; +use crate::fn_ctxt::LoweredTy; use crate::gather_locals::GatherLocalsVisitor; use rustc_data_structures::unord::UnordSet; use rustc_errors::{struct_span_code_err, ErrorGuaranteed}; diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 17eff54f7ae8..a446c7aa42bb 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -746,11 +746,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let (xform_self_ty, xform_ret_ty) = self.xform_self_ty(item, impl_ty, impl_args); debug!("xform_self_ty: {:?}, xform_ret_ty: {:?}", xform_self_ty, xform_ret_ty); - // We can't use normalize_associated_types_in as it will pollute the + // We can't use `FnCtxt::normalize` as it will pollute the // fcx's fulfillment context after this probe is over. + // // Note: we only normalize `xform_self_ty` here since the normalization // of the return type can lead to inference results that prohibit // valid candidates from being found, see issue #85671 + // // FIXME Postponing the normalization of the return type likely only hides a deeper bug, // which might be caused by the `param_env` itself. The clauses of the `param_env` // maybe shouldn't include `Param`s, but rather fresh variables or be canonicalized, diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 73fc0ee499e6..e31eeab4c4a4 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1,5 +1,5 @@ use crate::gather_locals::DeclOrigin; -use crate::{errors, FnCtxt, RawTy}; +use crate::{errors, FnCtxt, LoweredTy}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{ @@ -891,7 +891,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, pat: &Pat<'tcx>, qpath: &hir::QPath<'_>, - path_resolution: (Res, Option>, &'tcx [hir::PathSegment<'tcx>]), + path_resolution: (Res, Option>, &'tcx [hir::PathSegment<'tcx>]), expected: Ty<'tcx>, ti: TopInfo<'tcx>, ) -> Ty<'tcx> { diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index dfa3ced9dc14..12f8e42c78f9 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -284,7 +284,7 @@ impl BitSet { not_already } - fn last_set_in(&self, range: impl RangeBounds) -> Option { + pub fn last_set_in(&self, range: impl RangeBounds) -> Option { let (start, end) = inclusive_start_end(range, self.domain_size)?; let (start_word_index, _) = word_index_and_mask(start); let (end_word_index, end_mask) = word_index_and_mask(end); @@ -1299,7 +1299,7 @@ impl SparseBitSet { } impl SparseBitSet { - fn last_set_in(&self, range: impl RangeBounds) -> Option { + pub fn last_set_in(&self, range: impl RangeBounds) -> Option { let mut last_leq = None; for e in self.iter() { if range.contains(e) { diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index afb3c5c1e565..01cd3c57925b 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -294,8 +294,9 @@ impl Trait for X { ); } } - (ty::Alias(ty::Opaque, alias), _) | (_, ty::Alias(ty::Opaque, alias)) - if alias.def_id.is_local() + (_, ty::Alias(ty::Opaque, opaque_ty)) + | (ty::Alias(ty::Opaque, opaque_ty), _) => { + if opaque_ty.def_id.is_local() && matches!( tcx.def_kind(body_owner_def_id), DefKind::Fn @@ -303,21 +304,74 @@ impl Trait for X { | DefKind::Const | DefKind::AssocFn | DefKind::AssocConst - ) => - { - if tcx.is_type_alias_impl_trait(alias.def_id) { - if !tcx + ) + && tcx.is_type_alias_impl_trait(opaque_ty.def_id) + && !tcx .opaque_types_defined_by(body_owner_def_id.expect_local()) - .contains(&alias.def_id.expect_local()) - { - let sp = tcx - .def_ident_span(body_owner_def_id) - .unwrap_or_else(|| tcx.def_span(body_owner_def_id)); - diag.span_note( - sp, - "\ - this item must have the opaque type in its signature \ - in order to be able to register hidden types", + .contains(&opaque_ty.def_id.expect_local()) + { + let sp = tcx + .def_ident_span(body_owner_def_id) + .unwrap_or_else(|| tcx.def_span(body_owner_def_id)); + diag.span_note( + sp, + "this item must have the opaque type in its signature in order to \ + be able to register hidden types", + ); + } + // If two if arms can be coerced to a trait object, provide a structured + // suggestion. + let ObligationCauseCode::IfExpression(cause) = cause.code() else { + return; + }; + let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id) else { + return; + }; + let Some(then) = blk.expr else { + return; + }; + let hir::Node::Block(blk) = self.tcx.hir_node(cause.else_id) else { + return; + }; + let Some(else_) = blk.expr else { + return; + }; + let expected = match values.found.kind() { + ty::Alias(..) => values.expected, + _ => values.found, + }; + let preds = tcx.explicit_item_bounds(opaque_ty.def_id); + for (pred, _span) in preds.skip_binder() { + let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder() + else { + continue; + }; + if trait_predicate.polarity != ty::ImplPolarity::Positive { + continue; + } + let def_id = trait_predicate.def_id(); + let mut impl_def_ids = vec![]; + tcx.for_each_relevant_impl(def_id, expected, |did| { + impl_def_ids.push(did) + }); + if let [_] = &impl_def_ids[..] { + let trait_name = tcx.item_name(def_id); + diag.multipart_suggestion( + format!( + "`{expected}` implements `{trait_name}` so you can box \ + both arms and coerce to the trait object \ + `Box`", + ), + vec![ + (then.span.shrink_to_lo(), "Box::new(".to_string()), + ( + then.span.shrink_to_hi(), + format!(") as Box", tcx.def_path_str(def_id)), + ), + (else_.span.shrink_to_lo(), "Box::new(".to_string()), + (else_.span.shrink_to_hi(), ")".to_string()), + ], + MachineApplicable, ); } } @@ -330,6 +384,38 @@ impl Trait for X { ); } } + (ty::Adt(_, _), ty::Adt(def, args)) + if let ObligationCauseCode::IfExpression(cause) = cause.code() + && let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id) + && let Some(then) = blk.expr + && def.is_box() + && let boxed_ty = args.type_at(0) + && let ty::Dynamic(t, _, _) = boxed_ty.kind() + && let Some(def_id) = t.principal_def_id() + && let mut impl_def_ids = vec![] + && let _ = + tcx.for_each_relevant_impl(def_id, values.expected, |did| { + impl_def_ids.push(did) + }) + && let [_] = &impl_def_ids[..] => + { + // We have divergent if/else arms where the expected value is a type that + // implements the trait of the found boxed trait object. + diag.multipart_suggestion( + format!( + "`{}` implements `{}` so you can box it to coerce to the trait \ + object `{}`", + values.expected, + tcx.item_name(def_id), + values.found, + ), + vec![ + (then.span.shrink_to_lo(), "Box::new(".to_string()), + (then.span.shrink_to_hi(), ")".to_string()), + ], + MachineApplicable, + ); + } _ => {} } debug!( diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 1b4e9c286351..1c9b4e558f0a 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -416,14 +416,14 @@ pub enum ValidationErrorKind<'tcx> { PartialPointer, PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> }, PtrToStatic { ptr_kind: PointerKind }, - PtrToMut { ptr_kind: PointerKind }, MutableRefInConst, + MutableRefToImmutable, + UnsafeCellInImmutable, NullFnPtr, NeverVal, NullablePtrOutOfRange { range: WrappingRange, max_value: u128 }, PtrOutOfRange { range: WrappingRange, max_value: u128 }, OutOfRange { value: String, range: WrappingRange, max_value: u128 }, - UnsafeCell, UninhabitedVal { ty: Ty<'tcx> }, InvalidEnumTag { value: String }, UninhabitedEnumVariant, diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index 6893387736a5..dabf6297aa9c 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -277,6 +277,12 @@ impl From for Pointer { Pointer::new(alloc_id.into(), Size::ZERO) } } +impl From for Pointer { + #[inline(always)] + fn from(prov: CtfeProvenance) -> Self { + Pointer::new(prov, Size::ZERO) + } +} impl From> for Pointer> { #[inline(always)] diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index a9dc7f5d11a2..23daefd5a65b 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -343,6 +343,15 @@ rustc_queries! { } } + query impl_trait_in_assoc_types_defined_by( + key: LocalDefId + ) -> &'tcx ty::List { + desc { + |tcx| "computing the opaque types defined by `{}`", + tcx.def_path_str(key.to_def_id()) + } + } + /// Returns the list of bounds that can be used for /// `SelectionCandidate::ProjectionCandidate(_)` and /// `ProjectionTyCandidate::TraitDef`. diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index b6c3c34078f7..293fdb026b68 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -530,12 +530,12 @@ impl<'tcx> Instance<'tcx> { def_id: DefId, args: ty::GenericArgsRef<'tcx>, requested_kind: ty::ClosureKind, - ) -> Option> { + ) -> Instance<'tcx> { let actual_kind = args.as_closure().kind(); match needs_fn_once_adapter_shim(actual_kind, requested_kind) { Ok(true) => Instance::fn_once_adapter_instance(tcx, def_id, args), - _ => Some(Instance::new(def_id, args)), + _ => Instance::new(def_id, args), } } @@ -550,7 +550,7 @@ impl<'tcx> Instance<'tcx> { tcx: TyCtxt<'tcx>, closure_did: DefId, args: ty::GenericArgsRef<'tcx>, - ) -> Option> { + ) -> Instance<'tcx> { let fn_once = tcx.require_lang_item(LangItem::FnOnce, None); let call_once = tcx .associated_items(fn_once) @@ -564,14 +564,12 @@ impl<'tcx> Instance<'tcx> { let self_ty = Ty::new_closure(tcx, closure_did, args); - let sig = args.as_closure().sig(); - let sig = - tcx.try_normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), sig).ok()?; - assert_eq!(sig.inputs().len(), 1); - let args = tcx.mk_args_trait(self_ty, [sig.inputs()[0].into()]); + let tupled_inputs_ty = args.as_closure().sig().map_bound(|sig| sig.inputs()[0]); + let tupled_inputs_ty = tcx.instantiate_bound_regions_with_erased(tupled_inputs_ty); + let args = tcx.mk_args_trait(self_ty, [tupled_inputs_ty.into()]); - debug!(?self_ty, ?sig); - Some(Instance { def, args }) + debug!(?self_ty, args=?tupled_inputs_ty.tuple_fields()); + Instance { def, args } } pub fn try_resolve_item_for_coroutine( diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index a06f4c6ba12e..73052be30ae3 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -804,17 +804,12 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } } else { p!(print_def_path(did, args)); - p!(" upvar_tys=("); - if !args.as_coroutine().is_valid() { - p!("unavailable"); - } else { - self.comma_sep(args.as_coroutine().upvar_tys().iter())?; - } - p!(")"); - - if args.as_coroutine().is_valid() { - p!(" ", print(args.as_coroutine().witness())); - } + p!( + " upvar_tys=", + print(args.as_coroutine().tupled_upvars_ty()), + " witness=", + print(args.as_coroutine().witness()) + ); } p!("}}") @@ -868,19 +863,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } } else { p!(print_def_path(did, args)); - if !args.as_closure().is_valid() { - p!(" closure_args=(unavailable)"); - p!(write(" args={}", args.print_as_list())); - } else { - p!(" closure_kind_ty=", print(args.as_closure().kind_ty())); - p!( - " closure_sig_as_fn_ptr_ty=", - print(args.as_closure().sig_as_fn_ptr_ty()) - ); - p!(" upvar_tys=("); - self.comma_sep(args.as_closure().upvar_tys().iter())?; - p!(")"); - } + p!( + " closure_kind_ty=", + print(args.as_closure().kind_ty()), + " closure_sig_as_fn_ptr_ty=", + print(args.as_closure().sig_as_fn_ptr_ty()), + " upvar_tys=", + print(args.as_closure().tupled_upvars_ty()) + ); } p!("}}"); } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 8cf5fc8013f1..b089f4a9e78b 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -242,9 +242,15 @@ pub struct ClosureArgs<'tcx> { /// Struct returned by `split()`. pub struct ClosureArgsParts<'tcx> { + /// This is the args of the typeck root. pub parent_args: &'tcx [GenericArg<'tcx>], + /// Represents the maximum calling capability of the closure. pub closure_kind_ty: Ty<'tcx>, + /// Captures the closure's signature. This closure signature is "tupled", and + /// thus has a peculiar signature of `extern "rust-call" fn((Args, ...)) -> Ty`. pub closure_sig_as_fn_ptr_ty: Ty<'tcx>, + /// The upvars captured by the closure. Remains an inference variable + /// until the upvar analysis, which happens late in HIR typeck. pub tupled_upvars_ty: Ty<'tcx>, } @@ -277,15 +283,6 @@ impl<'tcx> ClosureArgs<'tcx> { } } - /// Returns `true` only if enough of the synthetic types are known to - /// allow using all of the methods on `ClosureArgs` without panicking. - /// - /// Used primarily by `ty::print::pretty` to be able to handle closure - /// types that haven't had their synthetic types substituted in. - pub fn is_valid(self) -> bool { - self.args.len() >= 3 && matches!(self.split().tupled_upvars_ty.kind(), Tuple(_)) - } - /// Returns the substitutions of the closure's parent. pub fn parent_args(self) -> &'tcx [GenericArg<'tcx>] { self.split().parent_args @@ -296,9 +293,9 @@ impl<'tcx> ClosureArgs<'tcx> { /// empty iterator is returned. #[inline] pub fn upvar_tys(self) -> &'tcx List> { - match self.tupled_upvars_ty().kind() { + match *self.tupled_upvars_ty().kind() { TyKind::Error(_) => ty::List::empty(), - TyKind::Tuple(..) => self.tupled_upvars_ty().tuple_fields(), + TyKind::Tuple(tys) => tys, TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"), ty => bug!("Unexpected representation of upvar types tuple {:?}", ty), } @@ -337,10 +334,9 @@ impl<'tcx> ClosureArgs<'tcx> { /// Extracts the signature from the closure. pub fn sig(self) -> ty::PolyFnSig<'tcx> { - let ty = self.sig_as_fn_ptr_ty(); - match ty.kind() { - ty::FnPtr(sig) => *sig, - _ => bug!("closure_sig_as_fn_ptr_ty is not a fn-ptr: {:?}", ty.kind()), + match *self.sig_as_fn_ptr_ty().kind() { + ty::FnPtr(sig) => sig, + ty => bug!("closure_sig_as_fn_ptr_ty is not a fn-ptr: {ty:?}"), } } @@ -356,11 +352,17 @@ pub struct CoroutineArgs<'tcx> { } pub struct CoroutineArgsParts<'tcx> { + /// This is the args of the typeck root. pub parent_args: &'tcx [GenericArg<'tcx>], pub resume_ty: Ty<'tcx>, pub yield_ty: Ty<'tcx>, pub return_ty: Ty<'tcx>, + /// The interior type of the coroutine. + /// Represents all types that are stored in locals + /// in the coroutine's body. pub witness: Ty<'tcx>, + /// The upvars captured by the closure. Remains an inference variable + /// until the upvar analysis, which happens late in HIR typeck. pub tupled_upvars_ty: Ty<'tcx>, } @@ -397,15 +399,6 @@ impl<'tcx> CoroutineArgs<'tcx> { } } - /// Returns `true` only if enough of the synthetic types are known to - /// allow using all of the methods on `CoroutineArgs` without panicking. - /// - /// Used primarily by `ty::print::pretty` to be able to handle coroutine - /// types that haven't had their synthetic types substituted in. - pub fn is_valid(self) -> bool { - self.args.len() >= 5 && matches!(self.split().tupled_upvars_ty.kind(), Tuple(_)) - } - /// Returns the substitutions of the coroutine's parent. pub fn parent_args(self) -> &'tcx [GenericArg<'tcx>] { self.split().parent_args @@ -425,9 +418,9 @@ impl<'tcx> CoroutineArgs<'tcx> { /// empty iterator is returned. #[inline] pub fn upvar_tys(self) -> &'tcx List> { - match self.tupled_upvars_ty().kind() { + match *self.tupled_upvars_ty().kind() { TyKind::Error(_) => ty::List::empty(), - TyKind::Tuple(..) => self.tupled_upvars_ty().tuple_fields(), + TyKind::Tuple(tys) => tys, TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"), ty => bug!("Unexpected representation of upvar types tuple {:?}", ty), } diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs index 5428333a1161..c669d3fd6230 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -20,6 +20,10 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { @call(mir_storage_dead, args) => { Ok(StatementKind::StorageDead(self.parse_local(args[0])?)) }, + @call(mir_assume, args) => { + let op = self.parse_operand(args[0])?; + Ok(StatementKind::Intrinsic(Box::new(NonDivergingIntrinsic::Assume(op)))) + }, @call(mir_deinit, args) => { Ok(StatementKind::Deinit(Box::new(self.parse_place(args[0])?))) }, diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index 6653b99b3f50..720515f262db 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -305,7 +305,10 @@ impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> { } impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { + /// There can be many more `MovePathIndex` than there are locals in a MIR body. + /// We use a chunked bitset to avoid paying too high a memory footprint. type Domain = MaybeReachable>; + const NAME: &'static str = "maybe_init"; fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { @@ -437,6 +440,8 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { + /// There can be many more `MovePathIndex` than there are locals in a MIR body. + /// We use a chunked bitset to avoid paying too high a memory footprint. type Domain = ChunkedBitSet; const NAME: &'static str = "maybe_uninit"; @@ -636,6 +641,8 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { } impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> { + /// There can be many more `InitIndex` than there are locals in a MIR body. + /// We use a chunked bitset to avoid paying too high a memory footprint. type Domain = ChunkedBitSet; const NAME: &'static str = "ever_init"; diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 04bae6ae2fe0..334fa9976f03 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -1,4 +1,4 @@ -use rustc_index::bit_set::{BitSet, ChunkedBitSet}; +use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{ self, CallReturnPlaces, Local, Location, Place, StatementKind, TerminatorEdges, @@ -26,14 +26,14 @@ use crate::{Analysis, AnalysisDomain, Backward, GenKill, GenKillAnalysis}; pub struct MaybeLiveLocals; impl<'tcx> AnalysisDomain<'tcx> for MaybeLiveLocals { - type Domain = ChunkedBitSet; + type Domain = BitSet; type Direction = Backward; const NAME: &'static str = "liveness"; fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { // bottom = not live - ChunkedBitSet::new_empty(body.local_decls.len()) + BitSet::new_empty(body.local_decls.len()) } fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { @@ -233,14 +233,14 @@ impl<'a> MaybeTransitiveLiveLocals<'a> { } impl<'a, 'tcx> AnalysisDomain<'tcx> for MaybeTransitiveLiveLocals<'a> { - type Domain = ChunkedBitSet; + type Domain = BitSet; type Direction = Backward; const NAME: &'static str = "transitive liveness"; fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { // bottom = not live - ChunkedBitSet::new_empty(body.local_decls.len()) + BitSet::new_empty(body.local_decls.len()) } fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { diff --git a/compiler/rustc_mir_dataflow/src/points.rs b/compiler/rustc_mir_dataflow/src/points.rs index ff17ce1fe079..bbfb37d2a826 100644 --- a/compiler/rustc_mir_dataflow/src/points.rs +++ b/compiler/rustc_mir_dataflow/src/points.rs @@ -1,5 +1,5 @@ use crate::framework::{visit_results, ResultsVisitable, ResultsVisitor}; -use rustc_index::bit_set::ChunkedBitSet; +use rustc_index::bit_set::BitSet; use rustc_index::interval::SparseIntervalMatrix; use rustc_index::Idx; use rustc_index::IndexVec; @@ -102,7 +102,7 @@ pub fn save_as_intervals<'tcx, N, R>( ) -> SparseIntervalMatrix where N: Idx, - R: ResultsVisitable<'tcx, FlowState = ChunkedBitSet>, + R: ResultsVisitable<'tcx, FlowState = BitSet>, { let values = SparseIntervalMatrix::new(elements.num_points()); let mut visitor = Visitor { elements, values }; @@ -124,7 +124,7 @@ impl<'mir, 'tcx, R, N> ResultsVisitor<'mir, 'tcx, R> for Visitor<'_, N> where N: Idx, { - type FlowState = ChunkedBitSet; + type FlowState = BitSet; fn visit_statement_after_primary_effect( &mut self, diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 08a5d70fb6fc..cbbf3548c07e 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -12,7 +12,7 @@ use crate::MoveDataParamEnv; use crate::{Analysis, JoinSemiLattice, ResultsCursor}; use rustc_ast::MetaItem; use rustc_hir::def_id::DefId; -use rustc_index::bit_set::ChunkedBitSet; +use rustc_index::bit_set::BitSet; use rustc_middle::mir::MirPass; use rustc_middle::mir::{self, Body, Local, Location}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -275,7 +275,7 @@ impl<'tcx> RustcPeekAt<'tcx> for MaybeLiveLocals { &self, tcx: TyCtxt<'tcx>, place: mir::Place<'tcx>, - flow_state: &ChunkedBitSet, + flow_state: &BitSet, call: PeekCall, ) { info!(?place, "peek_at"); diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index e87f68a09057..7a70ed5cb7f0 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -566,11 +566,6 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { cost: &CostChecker<'_, 'tcx>, depth: usize, ) { - let register_opportunity = |c: Condition| { - debug!(?bb, ?c.target, "register"); - self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target }) - }; - let term = self.body.basic_blocks[bb].terminator(); let place_to_flood = match term.kind { // We come from a target, so those are not possible. @@ -592,16 +587,8 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { // Flood the overwritten place, and progress through. TerminatorKind::Drop { place: destination, .. } | TerminatorKind::Call { destination, .. } => Some(destination), - // Treat as an `assume(cond == expected)`. - TerminatorKind::Assert { ref cond, expected, .. } => { - if let Some(place) = cond.place() - && let Some(conditions) = state.try_get(place.as_ref(), self.map) - { - let expected = if expected { ScalarInt::TRUE } else { ScalarInt::FALSE }; - conditions.iter_matches(expected).for_each(register_opportunity); - } - None - } + // Ignore, as this can be a no-op at codegen time. + TerminatorKind::Assert { .. } => None, }; // We can recurse through this terminator. diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs index ff309bd10ec8..c3a92911bbf0 100644 --- a/compiler/rustc_mir_transform/src/nrvo.rs +++ b/compiler/rustc_mir_transform/src/nrvo.rs @@ -1,7 +1,7 @@ //! See the docs for [`RenameReturnPlace`]. use rustc_hir::Mutability; -use rustc_index::bit_set::HybridBitSet; +use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, BasicBlock, Local, Location}; use rustc_middle::ty::TyCtxt; @@ -123,7 +123,7 @@ fn find_local_assigned_to_return_place( body: &mut mir::Body<'_>, ) -> Option { let mut block = start; - let mut seen = HybridBitSet::new_empty(body.basic_blocks.len()); + let mut seen = BitSet::new_empty(body.basic_blocks.len()); // Iterate as long as `block` has exactly one predecessor that we have not yet visited. while seen.insert(block) { diff --git a/compiler/rustc_mir_transform/src/remove_storage_markers.rs b/compiler/rustc_mir_transform/src/remove_storage_markers.rs index 795f5232ee30..f68e592db154 100644 --- a/compiler/rustc_mir_transform/src/remove_storage_markers.rs +++ b/compiler/rustc_mir_transform/src/remove_storage_markers.rs @@ -7,14 +7,10 @@ pub struct RemoveStorageMarkers; impl<'tcx> MirPass<'tcx> for RemoveStorageMarkers { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.mir_opt_level() > 0 + sess.mir_opt_level() > 0 && !sess.emit_lifetime_markers() } - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - if tcx.sess.emit_lifetime_markers() { - return; - } - + fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { trace!("Running RemoveStorageMarkers on {:?}", body.source); for data in body.basic_blocks.as_mut_preserves_cfg() { data.statements.retain(|statement| match statement.kind { diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 3e0906614145..8b991268819b 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -783,8 +783,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { def_id, args, ty::ClosureKind::FnOnce, - ) - .expect("failed to normalize and resolve closure during codegen"); + ); if should_codegen_locally(self.tcx, &instance) { self.output.push(create_fn_mono_item(self.tcx, instance, span)); } diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 10085f659b37..94a1fb33f99c 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -464,7 +464,10 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let def_id = def.0.internal(&mut *tables, tcx); let args_ref = args.internal(&mut *tables, tcx); let closure_kind = kind.internal(&mut *tables, tcx); - Instance::resolve_closure(tables.tcx, def_id, args_ref, closure_kind).stable(&mut *tables) + Some( + Instance::resolve_closure(tables.tcx, def_id, args_ref, closure_kind) + .stable(&mut *tables), + ) } fn eval_instance(&self, def: InstanceDef, const_ty: Ty) -> Result { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 6c39a38750ec..90a38b26f73c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1028,6 +1028,7 @@ symbols! { minnumf32, minnumf64, mips_target_feature, + mir_assume, mir_basic_block, mir_call, mir_cast_transmute, diff --git a/compiler/rustc_target/src/spec/base/dragonfly.rs b/compiler/rustc_target/src/spec/base/dragonfly.rs index de2be7817969..3c1846696f7a 100644 --- a/compiler/rustc_target/src/spec/base/dragonfly.rs +++ b/compiler/rustc_target/src/spec/base/dragonfly.rs @@ -8,6 +8,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, position_independent_executables: true, relro_level: RelroLevel::Full, + has_thread_local: true, default_dwarf_version: 2, ..Default::default() } diff --git a/compiler/rustc_target/src/spec/base/freebsd.rs b/compiler/rustc_target/src/spec/base/freebsd.rs index 80b3da8a752d..c772754aa8dd 100644 --- a/compiler/rustc_target/src/spec/base/freebsd.rs +++ b/compiler/rustc_target/src/spec/base/freebsd.rs @@ -9,6 +9,7 @@ pub fn opts() -> TargetOptions { crt_static_respected: true, position_independent_executables: true, relro_level: RelroLevel::Full, + has_thread_local: true, abi_return_struct_as_int: true, default_dwarf_version: 2, ..Default::default() diff --git a/compiler/rustc_target/src/spec/base/netbsd.rs b/compiler/rustc_target/src/spec/base/netbsd.rs index be94ea234658..495e3d10fbcc 100644 --- a/compiler/rustc_target/src/spec/base/netbsd.rs +++ b/compiler/rustc_target/src/spec/base/netbsd.rs @@ -9,6 +9,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, position_independent_executables: true, relro_level: RelroLevel::Full, + has_thread_local: true, use_ctors_section: true, default_dwarf_version: 2, ..Default::default() diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 5d74ebebdf3f..884bd23e8cce 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1597,6 +1597,7 @@ supported_targets! { ("x86_64-unikraft-linux-musl", x86_64_unikraft_linux_musl), ("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf), + ("riscv32im-risc0-zkvm-elf", riscv32im_risc0_zkvm_elf), ("riscv32im-unknown-none-elf", riscv32im_unknown_none_elf), ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf), ("riscv32imc-esp-espidf", riscv32imc_esp_espidf), diff --git a/compiler/rustc_target/src/spec/targets/riscv32im_risc0_zkvm_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32im_risc0_zkvm_elf.rs new file mode 100644 index 000000000000..bf819de41331 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv32im_risc0_zkvm_elf.rs @@ -0,0 +1,36 @@ +use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions}; + +pub fn target() -> Target { + Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(), + llvm_target: "riscv32".into(), + pointer_width: 32, + arch: "riscv32".into(), + + options: TargetOptions { + os: "zkvm".into(), + vendor: "risc0".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + cpu: "generic-rv32".into(), + + // Some crates (*cough* crossbeam) assume you have 64 bit + // atomics if the target name is not in a hardcoded list. + // Since zkvm is singlethreaded and all operations are + // atomic, I guess we can just say we support 64-bit + // atomics. + max_atomic_width: Some(64), + atomic_cas: true, + + features: "+m".into(), + executables: true, + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + singlethread: true, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs index 6d5728797d1e..9f91c02c1ab6 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs @@ -1,7 +1,7 @@ //! Computes a normalizes-to (projection) goal for inherent associated types, //! `#![feature(lazy_type_alias)]` and `#![feature(type_alias_impl_trait)]`. //! -//! Since a weak alias is not ambiguous, this just computes the `type_of` of +//! Since a weak alias is never ambiguous, this just computes the `type_of` of //! the alias and registers the where-clauses of the type alias. use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, QueryResult}; use rustc_middle::ty; diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 0e33e9cd790d..e31aaaa19698 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -3152,6 +3152,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ], Applicability::MachineApplicable, ); + } else { + // FIXME: we may suggest array::repeat instead + err.help("consider using `core::array::from_fn` to initialize the array"); + err.help("see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information"); } if self.tcx.sess.is_nightly_build() diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 138bc6129f7c..79f03242c587 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -232,32 +232,12 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( Ok::<_, NoSolution>(()) })?, - ty::Closure(_, args) => { - if !args.as_closure().is_valid() { - // By the time this code runs, all type variables ought to - // be fully resolved. - - tcx.dcx().span_delayed_bug( - span, - format!("upvar_tys for closure not found. Expected capture information for closure {ty}",), - ); - return Err(NoSolution); + ty::Closure(_, args) => rustc_data_structures::stack::ensure_sufficient_stack(|| { + for ty in args.as_closure().upvar_tys() { + dtorck_constraint_for_ty_inner(tcx, param_env, span, depth + 1, ty, constraints)?; } - - rustc_data_structures::stack::ensure_sufficient_stack(|| { - for ty in args.as_closure().upvar_tys() { - dtorck_constraint_for_ty_inner( - tcx, - param_env, - span, - depth + 1, - ty, - constraints, - )?; - } - Ok::<_, NoSolution>(()) - })? - } + Ok::<_, NoSolution>(()) + })?, ty::Coroutine(_, args) => { // rust-lang/rust#49918: types can be constructed, stored @@ -283,15 +263,6 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( // derived from lifetimes attached to the upvars and resume // argument, and we *do* incorporate those here. let args = args.as_coroutine(); - if !args.is_valid() { - // By the time this code runs, all type variables ought to - // be fully resolved. - tcx.dcx().span_delayed_bug( - span, - format!("upvar_tys for coroutine not found. Expected capture information for coroutine {ty}",), - ); - return Err(NoSolution); - } // While we conservatively assume that all coroutines require drop // to avoid query cycles during MIR building, we can check the actual diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 957de925dd37..fb09f094d37a 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -141,6 +141,13 @@ where infcx: &InferCtxt<'tcx>, span: Span, ) -> Result, ErrorGuaranteed> { + // In the new trait solver, query type ops are performed locally. This + // is because query type ops currently use the old canonicalizer, and + // that doesn't preserve things like opaques which have been registered + // during MIR typeck. Even after the old canonicalizer is gone, it's + // probably worthwhile just keeping this run-locally logic, since we + // probably don't gain much from caching here given the new solver does + // caching internally. if infcx.next_trait_solver() { return Ok(scrape_region_constraints( infcx, diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index e5e31f7caaa2..2d76cf994e43 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -265,7 +265,12 @@ fn resolve_associated_item<'tcx>( match *rcvr_args.type_at(0).kind() { ty::Closure(closure_def_id, args) => { let trait_closure_kind = tcx.fn_trait_kind_from_def_id(trait_id).unwrap(); - Instance::resolve_closure(tcx, closure_def_id, args, trait_closure_kind) + Some(Instance::resolve_closure( + tcx, + closure_def_id, + args, + trait_closure_kind, + )) } ty::FnDef(..) | ty::FnPtr(..) => Some(Instance { def: ty::InstanceDef::FnPtrShim(trait_item_id, rcvr_args.type_at(0)), diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 0bb833c66fa7..ef67317a601a 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -118,6 +118,69 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { } TaitInBodyFinder { collector: self }.visit_expr(body); } + + fn visit_opaque_ty(&mut self, alias_ty: &ty::AliasTy<'tcx>) { + if !self.seen.insert(alias_ty.def_id.expect_local()) { + return; + } + + // TAITs outside their defining scopes are ignored. + let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local()); + trace!(?origin); + match origin { + rustc_hir::OpaqueTyOrigin::FnReturn(_) | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {} + rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => { + if !in_assoc_ty { + if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) { + return; + } + } + } + } + + self.opaques.push(alias_ty.def_id.expect_local()); + + let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count; + // Only check that the parent generics of the TAIT/RPIT are unique. + // the args owned by the opaque are going to always be duplicate + // lifetime params for RPITs, and empty for TAITs. + match self + .tcx + .uses_unique_generic_params(&alias_ty.args[..parent_count], CheckRegions::FromFunction) + { + Ok(()) => { + // FIXME: implement higher kinded lifetime bounds on nested opaque types. They are not + // supported at all, so this is sound to do, but once we want to support them, you'll + // start seeing the error below. + + // Collect opaque types nested within the associated type bounds of this opaque type. + // We use identity args here, because we already know that the opaque type uses + // only generic parameters, and thus substituting would not give us more information. + for (pred, span) in self + .tcx + .explicit_item_bounds(alias_ty.def_id) + .instantiate_identity_iter_copied() + { + trace!(?pred); + self.visit_spanned(span, pred); + } + } + Err(NotUniqueParam::NotParam(arg)) => { + self.tcx.dcx().emit_err(NotParam { + arg, + span: self.span(), + opaque_span: self.tcx.def_span(alias_ty.def_id), + }); + } + Err(NotUniqueParam::DuplicateParam(arg)) => { + self.tcx.dcx().emit_err(DuplicateArg { + arg, + span: self.span(), + opaque_span: self.tcx.def_span(alias_ty.def_id), + }); + } + } + } } impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for OpaqueTypeCollector<'tcx> { @@ -134,67 +197,7 @@ impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { t.super_visit_with(self)?; match t.kind() { ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => { - if !self.seen.insert(alias_ty.def_id.expect_local()) { - return ControlFlow::Continue(()); - } - - // TAITs outside their defining scopes are ignored. - let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local()); - trace!(?origin); - match origin { - rustc_hir::OpaqueTyOrigin::FnReturn(_) - | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {} - rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => { - if !in_assoc_ty { - if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) { - return ControlFlow::Continue(()); - } - } - } - } - - self.opaques.push(alias_ty.def_id.expect_local()); - - let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count; - // Only check that the parent generics of the TAIT/RPIT are unique. - // the args owned by the opaque are going to always be duplicate - // lifetime params for RPITs, and empty for TAITs. - match self.tcx.uses_unique_generic_params( - &alias_ty.args[..parent_count], - CheckRegions::FromFunction, - ) { - Ok(()) => { - // FIXME: implement higher kinded lifetime bounds on nested opaque types. They are not - // supported at all, so this is sound to do, but once we want to support them, you'll - // start seeing the error below. - - // Collect opaque types nested within the associated type bounds of this opaque type. - // We use identity args here, because we already know that the opaque type uses - // only generic parameters, and thus substituting would not give us more information. - for (pred, span) in self - .tcx - .explicit_item_bounds(alias_ty.def_id) - .instantiate_identity_iter_copied() - { - trace!(?pred); - self.visit_spanned(span, pred); - } - } - Err(NotUniqueParam::NotParam(arg)) => { - self.tcx.dcx().emit_err(NotParam { - arg, - span: self.span(), - opaque_span: self.tcx.def_span(alias_ty.def_id), - }); - } - Err(NotUniqueParam::DuplicateParam(arg)) => { - self.tcx.dcx().emit_err(DuplicateArg { - arg, - span: self.span(), - opaque_span: self.tcx.def_span(alias_ty.def_id), - }); - } - } + self.visit_opaque_ty(alias_ty); } ty::Alias(ty::Weak, alias_ty) if alias_ty.def_id.is_local() => { self.tcx @@ -272,6 +275,91 @@ impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { } } +struct ImplTraitInAssocTypeCollector<'tcx>(OpaqueTypeCollector<'tcx>); + +impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for ImplTraitInAssocTypeCollector<'tcx> { + #[instrument(skip(self), ret, level = "trace")] + fn visit(&mut self, span: Span, value: impl TypeVisitable>) -> ControlFlow { + let old = self.0.span; + self.0.span = Some(span); + value.visit_with(self); + self.0.span = old; + + ControlFlow::Continue(()) + } +} + +impl<'tcx> TypeVisitor> for ImplTraitInAssocTypeCollector<'tcx> { + #[instrument(skip(self), ret, level = "trace")] + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + t.super_visit_with(self)?; + match t.kind() { + ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => { + self.0.visit_opaque_ty(alias_ty); + } + ty::Alias(ty::Projection, alias_ty) => { + // This avoids having to do normalization of `Self::AssocTy` by only + // supporting the case of a method defining opaque types from assoc types + // in the same impl block. + let parent_trait_ref = self + .0 + .parent_trait_ref() + .expect("impl trait in assoc type collector used on non-assoc item"); + // If the trait ref of the associated item and the impl differs, + // then we can't use the impl's identity substitutions below, so + // just skip. + if alias_ty.trait_ref(self.0.tcx) == parent_trait_ref { + let parent = self.0.parent().expect("we should have a parent here"); + + for &assoc in self.0.tcx.associated_items(parent).in_definition_order() { + trace!(?assoc); + if assoc.trait_item_def_id != Some(alias_ty.def_id) { + continue; + } + + // If the type is further specializable, then the type_of + // is not actually correct below. + if !assoc.defaultness(self.0.tcx).is_final() { + continue; + } + + let impl_args = alias_ty.args.rebase_onto( + self.0.tcx, + parent_trait_ref.def_id, + ty::GenericArgs::identity_for_item(self.0.tcx, parent), + ); + + if check_args_compatible(self.0.tcx, assoc, impl_args) { + return self + .0 + .tcx + .type_of(assoc.def_id) + .instantiate(self.0.tcx, impl_args) + .visit_with(self); + } else { + self.0.tcx.dcx().span_delayed_bug( + self.0.tcx.def_span(assoc.def_id), + "item had incorrect args", + ); + } + } + } + } + _ => trace!(kind=?t.kind()), + } + ControlFlow::Continue(()) + } +} + +fn impl_trait_in_assoc_types_defined_by<'tcx>( + tcx: TyCtxt<'tcx>, + item: LocalDefId, +) -> &'tcx ty::List { + let mut collector = ImplTraitInAssocTypeCollector(OpaqueTypeCollector::new(tcx, item)); + super::sig_types::walk_types(tcx, item, &mut collector); + tcx.mk_local_def_ids(&collector.0.opaques) +} + fn opaque_types_defined_by<'tcx>( tcx: TyCtxt<'tcx>, item: LocalDefId, @@ -321,5 +409,6 @@ fn opaque_types_defined_by<'tcx>( } pub(super) fn provide(providers: &mut Providers) { - *providers = Providers { opaque_types_defined_by, ..*providers }; + *providers = + Providers { opaque_types_defined_by, impl_trait_in_assoc_types_defined_by, ..*providers }; } diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 1663aa84921c..0b1429397559 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -3,7 +3,7 @@ #![stable(feature = "alloc_module", since = "1.28.0")] #[cfg(not(test))] -use core::intrinsics; +use core::hint; #[cfg(not(test))] use core::ptr::{self, NonNull}; @@ -208,7 +208,7 @@ impl Global { let new_size = new_layout.size(); // `realloc` probably checks for `new_size >= old_layout.size()` or something similar. - intrinsics::assume(new_size >= old_layout.size()); + hint::assert_unchecked(new_size >= old_layout.size()); let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size); let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; @@ -299,7 +299,7 @@ unsafe impl Allocator for Global { // SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller new_size if old_layout.align() == new_layout.align() => unsafe { // `realloc` probably checks for `new_size <= old_layout.size()` or something similar. - intrinsics::assume(new_size <= old_layout.size()); + hint::assert_unchecked(new_size <= old_layout.size()); let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size); let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 78629b39d34b..02ecbe22b3e1 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -129,6 +129,7 @@ #![feature(fmt_internals)] #![feature(fn_traits)] #![feature(hasher_prefixfree_extras)] +#![feature(hint_assert_unchecked)] #![feature(inline_const)] #![feature(inplace_iteration)] #![feature(iter_advance_by)] diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 45e822401646..94e6924f41a7 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -2,7 +2,7 @@ use core::alloc::LayoutError; use core::cmp; -use core::intrinsics; +use core::hint; use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties}; use core::ptr::{self, NonNull, Unique}; use core::slice; @@ -325,7 +325,7 @@ impl RawVec { } unsafe { // Inform the optimizer that the reservation has succeeded or wasn't needed - core::intrinsics::assume(!self.needs_to_grow(len, additional)); + hint::assert_unchecked(!self.needs_to_grow(len, additional)); } Ok(()) } @@ -363,7 +363,7 @@ impl RawVec { } unsafe { // Inform the optimizer that the reservation has succeeded or wasn't needed - core::intrinsics::assume(!self.needs_to_grow(len, additional)); + hint::assert_unchecked(!self.needs_to_grow(len, additional)); } Ok(()) } @@ -514,7 +514,7 @@ where debug_assert_eq!(old_layout.align(), new_layout.align()); unsafe { // The allocator checks for alignment equality - intrinsics::assume(old_layout.align() == new_layout.align()); + hint::assert_unchecked(old_layout.align() == new_layout.align()); alloc.grow(ptr, old_layout, new_layout) } } else { diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index f986df058467..7705c86001e6 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -252,6 +252,7 @@ use core::cell::Cell; use core::cmp::Ordering; use core::fmt; use core::hash::{Hash, Hasher}; +use core::hint; use core::intrinsics::abort; #[cfg(not(no_global_oom_handling))] use core::iter; @@ -1885,10 +1886,10 @@ impl Rc { // Initialize the RcBox let inner = mem_to_rcbox(ptr.as_non_null_ptr().as_ptr()); unsafe { - debug_assert_eq!(Layout::for_value(&*inner), layout); + debug_assert_eq!(Layout::for_value_raw(inner), layout); - ptr::write(&mut (*inner).strong, Cell::new(1)); - ptr::write(&mut (*inner).weak, Cell::new(1)); + ptr::addr_of_mut!((*inner).strong).write(Cell::new(1)); + ptr::addr_of_mut!((*inner).weak).write(Cell::new(1)); } Ok(inner) @@ -1902,7 +1903,7 @@ impl Rc { // Allocate for the `RcBox` using the given value. unsafe { Rc::::allocate_for_layout( - Layout::for_value(&*ptr), + Layout::for_value_raw(ptr), |layout| alloc.allocate(layout), |mem| mem.with_metadata_of(ptr as *const RcBox), ) @@ -1918,7 +1919,7 @@ impl Rc { // Copy value as bytes ptr::copy_nonoverlapping( &*src as *const T as *const u8, - &mut (*ptr).value as *mut _ as *mut u8, + ptr::addr_of_mut!((*ptr).value) as *mut u8, value_size, ); @@ -1952,7 +1953,11 @@ impl Rc<[T]> { unsafe fn copy_from_slice(v: &[T]) -> Rc<[T]> { unsafe { let ptr = Self::allocate_for_slice(v.len()); - ptr::copy_nonoverlapping(v.as_ptr(), &mut (*ptr).value as *mut [T] as *mut T, v.len()); + ptr::copy_nonoverlapping( + v.as_ptr(), + ptr::addr_of_mut!((*ptr).value) as *mut T, + v.len(), + ); Self::from_ptr(ptr) } } @@ -1987,10 +1992,10 @@ impl Rc<[T]> { let ptr = Self::allocate_for_slice(len); let mem = ptr as *mut _ as *mut u8; - let layout = Layout::for_value(&*ptr); + let layout = Layout::for_value_raw(ptr); // Pointer to first element - let elems = &mut (*ptr).value as *mut [T] as *mut T; + let elems = ptr::addr_of_mut!((*ptr).value) as *mut T; let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; @@ -2096,7 +2101,8 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Rc { self.inner().dec_weak(); if self.inner().weak() == 0 { - self.alloc.deallocate(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())); + self.alloc + .deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())); } } } @@ -2524,7 +2530,7 @@ impl From> for Rc<[T], A> { let (vec_ptr, len, cap, alloc) = v.into_raw_parts_with_alloc(); let rc_ptr = Self::allocate_for_slice_in(len, &alloc); - ptr::copy_nonoverlapping(vec_ptr, &mut (*rc_ptr).value as *mut [T] as *mut T, len); + ptr::copy_nonoverlapping(vec_ptr, ptr::addr_of_mut!((*rc_ptr).value) as *mut T, len); // Create a `Vec` with length 0, to deallocate the buffer // without dropping its contents or the allocator @@ -3268,7 +3274,7 @@ trait RcInnerPtr { // SAFETY: The reference count will never be zero when this is // called. unsafe { - core::intrinsics::assume(strong != 0); + hint::assert_unchecked(strong != 0); } let strong = strong.wrapping_add(1); @@ -3301,7 +3307,7 @@ trait RcInnerPtr { // SAFETY: The reference count will never be zero when this is // called. unsafe { - core::intrinsics::assume(weak != 0); + hint::assert_unchecked(weak != 0); } let weak = weak.wrapping_add(1); @@ -3514,7 +3520,7 @@ unsafe impl<#[may_dangle] T> Drop for UniqueRc { self.ptr.as_ref().dec_weak(); if self.ptr.as_ref().weak() == 0 { - Global.deallocate(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())); + Global.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())); } } } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index dc82c9c41111..48c8d9d113b0 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -1828,11 +1828,11 @@ impl Arc { mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner, ) -> *mut ArcInner { let inner = mem_to_arcinner(ptr.as_non_null_ptr().as_ptr()); - debug_assert_eq!(unsafe { Layout::for_value(&*inner) }, layout); + debug_assert_eq!(unsafe { Layout::for_value_raw(inner) }, layout); unsafe { - ptr::write(&mut (*inner).strong, atomic::AtomicUsize::new(1)); - ptr::write(&mut (*inner).weak, atomic::AtomicUsize::new(1)); + ptr::addr_of_mut!((*inner).strong).write(atomic::AtomicUsize::new(1)); + ptr::addr_of_mut!((*inner).weak).write(atomic::AtomicUsize::new(1)); } inner @@ -1847,7 +1847,7 @@ impl Arc { // Allocate for the `ArcInner` using the given value. unsafe { Arc::allocate_for_layout( - Layout::for_value(&*ptr), + Layout::for_value_raw(ptr), |layout| alloc.allocate(layout), |mem| mem.with_metadata_of(ptr as *const ArcInner), ) @@ -1863,7 +1863,7 @@ impl Arc { // Copy value as bytes ptr::copy_nonoverlapping( &*src as *const T as *const u8, - &mut (*ptr).data as *mut _ as *mut u8, + ptr::addr_of_mut!((*ptr).data) as *mut u8, value_size, ); @@ -1898,7 +1898,7 @@ impl Arc<[T]> { unsafe { let ptr = Self::allocate_for_slice(v.len()); - ptr::copy_nonoverlapping(v.as_ptr(), &mut (*ptr).data as *mut [T] as *mut T, v.len()); + ptr::copy_nonoverlapping(v.as_ptr(), ptr::addr_of_mut!((*ptr).data) as *mut T, v.len()); Self::from_ptr(ptr) } @@ -1934,10 +1934,10 @@ impl Arc<[T]> { let ptr = Self::allocate_for_slice(len); let mem = ptr as *mut _ as *mut u8; - let layout = Layout::for_value(&*ptr); + let layout = Layout::for_value_raw(ptr); // Pointer to first element - let elems = &mut (*ptr).data as *mut [T] as *mut T; + let elems = ptr::addr_of_mut!((*ptr).data) as *mut T; let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; @@ -3380,7 +3380,7 @@ impl From> for Arc<[T], A> { let (vec_ptr, len, cap, alloc) = v.into_raw_parts_with_alloc(); let rc_ptr = Self::allocate_for_slice_in(len, &alloc); - ptr::copy_nonoverlapping(vec_ptr, &mut (*rc_ptr).data as *mut [T] as *mut T, len); + ptr::copy_nonoverlapping(vec_ptr, ptr::addr_of_mut!((*rc_ptr).data) as *mut T, len); // Create a `Vec` with length 0, to deallocate the buffer // without dropping its contents or the allocator diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index c82e023fae1a..6ca0dca1e9d1 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1996,7 +1996,7 @@ impl Vec { } else { unsafe { self.len -= 1; - core::intrinsics::assume(self.len < self.capacity()); + core::hint::assert_unchecked(self.len < self.capacity()); Some(ptr::read(self.as_ptr().add(self.len()))) } } diff --git a/library/core/src/alloc/global.rs b/library/core/src/alloc/global.rs index c582111701a9..a1fff6707bd0 100644 --- a/library/core/src/alloc/global.rs +++ b/library/core/src/alloc/global.rs @@ -110,7 +110,7 @@ use crate::ptr; /// ```rust,ignore (unsound and has placeholders) /// drop(Box::new(42)); /// let number_of_heap_allocs = /* call private allocator API */; -/// unsafe { std::intrinsics::assume(number_of_heap_allocs > 0); } +/// unsafe { std::hint::assert_unchecked(number_of_heap_allocs > 0); } /// ``` /// /// Note that the optimizations mentioned above are not the only diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index 334e32b26b18..d348e31609d4 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -357,6 +357,8 @@ define!("mir_unwind_resume", define!("mir_storage_live", fn StorageLive(local: T)); define!("mir_storage_dead", fn StorageDead(local: T)); +#[cfg(not(bootstrap))] +define!("mir_assume", fn Assume(operand: bool)); define!("mir_deinit", fn Deinit(place: T)); define!("mir_checked", fn Checked(binop: T) -> (T, bool)); define!("mir_len", fn Len(place: T) -> usize); diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 1dc3f49df38b..ead8cbe0e2f4 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -132,6 +132,7 @@ #![feature(const_fmt_arguments_new)] #![feature(const_hash)] #![feature(const_heap)] +#![feature(const_hint_assert_unchecked)] #![feature(const_index_range_slice_index)] #![feature(const_int_unchecked_arith)] #![feature(const_intrinsic_forget)] diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 4d85addadf52..c2b646b9b3e4 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -3,6 +3,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::ascii; +use crate::hint; use crate::intrinsics; use crate::mem; use crate::ops::{Add, Mul, Sub}; diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index bda691b16d4a..640f1e3fa856 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1,6 +1,9 @@ //! Definitions of integer that is known not to equal zero. +use crate::cmp::Ordering; use crate::fmt; +use crate::hash::{Hash, Hasher}; +use crate::marker::StructuralPartialEq; use crate::ops::{BitOr, BitOrAssign, Div, Neg, Rem}; use crate::str::FromStr; @@ -31,13 +34,6 @@ pub trait ZeroablePrimitive: Sized + Copy + private::Sealed { type NonZero; } -#[unstable( - feature = "nonzero_internals", - reason = "implementation detail which may disappear or be replaced at any time", - issue = "none" -)] -pub(crate) type NonZero = ::NonZero; - macro_rules! impl_zeroable_primitive { ($NonZero:ident ( $primitive:ty )) => { #[unstable( @@ -71,6 +67,13 @@ impl_zeroable_primitive!(NonZeroI64(i64)); impl_zeroable_primitive!(NonZeroI128(i128)); impl_zeroable_primitive!(NonZeroIsize(isize)); +#[unstable( + feature = "nonzero_internals", + reason = "implementation detail which may disappear or be replaced at any time", + issue = "none" +)] +pub(crate) type NonZero = ::NonZero; + macro_rules! impl_nonzero_fmt { ( #[$stability: meta] ( $( $Trait: ident ),+ ) for $Ty: ident ) => { $( @@ -128,7 +131,7 @@ macro_rules! nonzero_integer { /// /// [null pointer optimization]: crate::option#representation #[$stability] - #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] + #[derive(Copy, Eq)] #[repr(transparent)] #[rustc_layout_scalar_valid_range_start(1)] #[rustc_nonnull_optimization_guaranteed] @@ -317,7 +320,7 @@ macro_rules! nonzero_integer { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn checked_mul(self, other: $Ty) -> Option<$Ty> { + pub const fn checked_mul(self, other: Self) -> Option { if let Some(result) = self.get().checked_mul(other.get()) { // SAFETY: // - `checked_mul` returns `None` on overflow @@ -326,7 +329,7 @@ macro_rules! nonzero_integer { // of the sides to be zero // // So the result cannot be zero. - Some(unsafe { $Ty::new_unchecked(result) }) + Some(unsafe { Self::new_unchecked(result) }) } else { None } @@ -356,7 +359,7 @@ macro_rules! nonzero_integer { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn saturating_mul(self, other: $Ty) -> $Ty { + pub const fn saturating_mul(self, other: Self) -> Self { // SAFETY: // - `saturating_mul` returns `u*::MAX`/`i*::MAX`/`i*::MIN` on overflow/underflow, // all of which are non-zero @@ -365,7 +368,7 @@ macro_rules! nonzero_integer { // of the sides to be zero // // So the result cannot be zero. - unsafe { $Ty::new_unchecked(self.get().saturating_mul(other.get())) } + unsafe { Self::new_unchecked(self.get().saturating_mul(other.get())) } } /// Multiplies two non-zero integers together, @@ -403,9 +406,9 @@ macro_rules! nonzero_integer { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const unsafe fn unchecked_mul(self, other: $Ty) -> $Ty { + pub const unsafe fn unchecked_mul(self, other: Self) -> Self { // SAFETY: The caller ensures there is no overflow. - unsafe { $Ty::new_unchecked(self.get().unchecked_mul(other.get())) } + unsafe { Self::new_unchecked(self.get().unchecked_mul(other.get())) } } /// Raises non-zero value to an integer power. @@ -433,7 +436,7 @@ macro_rules! nonzero_integer { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn checked_pow(self, other: u32) -> Option<$Ty> { + pub const fn checked_pow(self, other: u32) -> Option { if let Some(result) = self.get().checked_pow(other) { // SAFETY: // - `checked_pow` returns `None` on overflow/underflow @@ -442,7 +445,7 @@ macro_rules! nonzero_integer { // for base to be zero // // So the result cannot be zero. - Some(unsafe { $Ty::new_unchecked(result) }) + Some(unsafe { Self::new_unchecked(result) }) } else { None } @@ -481,7 +484,7 @@ macro_rules! nonzero_integer { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn saturating_pow(self, other: u32) -> $Ty { + pub const fn saturating_pow(self, other: u32) -> Self { // SAFETY: // - `saturating_pow` returns `u*::MAX`/`i*::MAX`/`i*::MIN` on overflow/underflow, // all of which are non-zero @@ -490,7 +493,97 @@ macro_rules! nonzero_integer { // for base to be zero // // So the result cannot be zero. - unsafe { $Ty::new_unchecked(self.get().saturating_pow(other)) } + unsafe { Self::new_unchecked(self.get().saturating_pow(other)) } + } + } + + #[$stability] + impl Clone for $Ty { + #[inline] + fn clone(&self) -> Self { + // SAFETY: The contained value is non-zero. + unsafe { Self(self.0) } + } + } + + #[$stability] + impl PartialEq for $Ty { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + + #[inline] + fn ne(&self, other: &Self) -> bool { + self.0 != other.0 + } + } + + #[unstable(feature = "structural_match", issue = "31434")] + impl StructuralPartialEq for $Ty {} + + #[$stability] + impl PartialOrd for $Ty { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } + + #[inline] + fn lt(&self, other: &Self) -> bool { + self.0 < other.0 + } + + #[inline] + fn le(&self, other: &Self) -> bool { + self.0 <= other.0 + } + + #[inline] + fn gt(&self, other: &Self) -> bool { + self.0 > other.0 + } + + #[inline] + fn ge(&self, other: &Self) -> bool { + self.0 >= other.0 + } + } + + #[$stability] + impl Ord for $Ty { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0) + } + + #[inline] + fn max(self, other: Self) -> Self { + // SAFETY: The maximum of two non-zero values is still non-zero. + unsafe { Self(self.0.max(other.0)) } + } + + #[inline] + fn min(self, other: Self) -> Self { + // SAFETY: The minimum of two non-zero values is still non-zero. + unsafe { Self(self.0.min(other.0)) } + } + + #[inline] + fn clamp(self, min: Self, max: Self) -> Self { + // SAFETY: A non-zero value clamped between two non-zero values is still non-zero. + unsafe { Self(self.0.clamp(min.0, max.0)) } + } + } + + #[$stability] + impl Hash for $Ty { + #[inline] + fn hash(&self, state: &mut H) + where + H: Hasher, + { + self.0.hash(state) } } @@ -508,29 +601,32 @@ macro_rules! nonzero_integer { #[stable(feature = "nonzero_bitor", since = "1.45.0")] impl BitOr for $Ty { type Output = Self; + #[inline] fn bitor(self, rhs: Self) -> Self::Output { // SAFETY: since `self` and `rhs` are both nonzero, the // result of the bitwise-or will be nonzero. - unsafe { $Ty::new_unchecked(self.get() | rhs.get()) } + unsafe { Self::new_unchecked(self.get() | rhs.get()) } } } #[stable(feature = "nonzero_bitor", since = "1.45.0")] impl BitOr<$Int> for $Ty { type Output = Self; + #[inline] fn bitor(self, rhs: $Int) -> Self::Output { // SAFETY: since `self` is nonzero, the result of the // bitwise-or will be nonzero regardless of the value of // `rhs`. - unsafe { $Ty::new_unchecked(self.get() | rhs) } + unsafe { Self::new_unchecked(self.get() | rhs) } } } #[stable(feature = "nonzero_bitor", since = "1.45.0")] impl BitOr<$Ty> for $Int { type Output = $Ty; + #[inline] fn bitor(self, rhs: $Ty) -> Self::Output { // SAFETY: since `rhs` is nonzero, the result of the @@ -603,6 +699,7 @@ macro_rules! nonzero_integer_signedness_dependent_impls { #[stable(feature = "nonzero_div", since = "1.51.0")] impl Div<$Ty> for $Int { type Output = $Int; + /// This operation rounds towards zero, /// truncating any fractional part of the exact result, and cannot panic. #[inline] @@ -616,6 +713,7 @@ macro_rules! nonzero_integer_signedness_dependent_impls { #[stable(feature = "nonzero_div", since = "1.51.0")] impl Rem<$Ty> for $Int { type Output = $Int; + /// This operation satisfies `n % d == n - (n / d) * d`, and cannot panic. #[inline] fn rem(self, other: $Ty) -> $Int { @@ -630,12 +728,12 @@ macro_rules! nonzero_integer_signedness_dependent_impls { ($Ty:ident signed $Int:ty) => { #[stable(feature = "signed_nonzero_neg", since = "1.71.0")] impl Neg for $Ty { - type Output = $Ty; + type Output = Self; #[inline] - fn neg(self) -> $Ty { + fn neg(self) -> Self { // SAFETY: negation of nonzero cannot yield zero values. - unsafe { $Ty::new_unchecked(self.get().neg()) } + unsafe { Self::new_unchecked(self.get().neg()) } } } @@ -703,7 +801,7 @@ macro_rules! nonzero_integer_signedness_dependent_methods { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn checked_add(self, other: $Int) -> Option<$Ty> { + pub const fn checked_add(self, other: $Int) -> Option { if let Some(result) = self.get().checked_add(other) { // SAFETY: // - `checked_add` returns `None` on overflow @@ -712,7 +810,7 @@ macro_rules! nonzero_integer_signedness_dependent_methods { // sides to be zero // // So the result cannot be zero. - Some(unsafe { $Ty::new_unchecked(result) }) + Some(unsafe { Self::new_unchecked(result) }) } else { None } @@ -742,7 +840,7 @@ macro_rules! nonzero_integer_signedness_dependent_methods { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn saturating_add(self, other: $Int) -> $Ty { + pub const fn saturating_add(self, other: $Int) -> Self { // SAFETY: // - `saturating_add` returns `u*::MAX` on overflow, which is non-zero // - `self` is non-zero @@ -750,7 +848,7 @@ macro_rules! nonzero_integer_signedness_dependent_methods { // sides to be zero // // So the result cannot be zero. - unsafe { $Ty::new_unchecked(self.get().saturating_add(other)) } + unsafe { Self::new_unchecked(self.get().saturating_add(other)) } } /// Adds an unsigned integer to a non-zero value, @@ -779,9 +877,9 @@ macro_rules! nonzero_integer_signedness_dependent_methods { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const unsafe fn unchecked_add(self, other: $Int) -> $Ty { + pub const unsafe fn unchecked_add(self, other: $Int) -> Self { // SAFETY: The caller ensures there is no overflow. - unsafe { $Ty::new_unchecked(self.get().unchecked_add(other)) } + unsafe { Self::new_unchecked(self.get().unchecked_add(other)) } } /// Returns the smallest power of two greater than or equal to n. @@ -812,11 +910,11 @@ macro_rules! nonzero_integer_signedness_dependent_methods { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn checked_next_power_of_two(self) -> Option<$Ty> { + pub const fn checked_next_power_of_two(self) -> Option { if let Some(nz) = self.get().checked_next_power_of_two() { // SAFETY: The next power of two is positive // and overflow is checked. - Some(unsafe { $Ty::new_unchecked(nz) }) + Some(unsafe { Self::new_unchecked(nz) }) } else { None } @@ -902,9 +1000,9 @@ macro_rules! nonzero_integer_signedness_dependent_methods { pub const fn midpoint(self, rhs: Self) -> Self { // SAFETY: The only way to get `0` with midpoint is to have two opposite or // near opposite numbers: (-5, 5), (0, 1), (0, 0) which is impossible because - // of the unsignedness of this number and also because $Ty is guaranteed to + // of the unsignedness of this number and also because `Self` is guaranteed to // never being 0. - unsafe { $Ty::new_unchecked(self.get().midpoint(rhs.get())) } + unsafe { Self::new_unchecked(self.get().midpoint(rhs.get())) } } /// Returns `true` if and only if `self == (1 << k)` for some `k`. @@ -1000,9 +1098,9 @@ macro_rules! nonzero_integer_signedness_dependent_methods { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn abs(self) -> $Ty { + pub const fn abs(self) -> Self { // SAFETY: This cannot overflow to zero. - unsafe { $Ty::new_unchecked(self.get().abs()) } + unsafe { Self::new_unchecked(self.get().abs()) } } /// Checked absolute value. @@ -1031,10 +1129,10 @@ macro_rules! nonzero_integer_signedness_dependent_methods { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn checked_abs(self) -> Option<$Ty> { + pub const fn checked_abs(self) -> Option { if let Some(nz) = self.get().checked_abs() { // SAFETY: absolute value of nonzero cannot yield zero values. - Some(unsafe { $Ty::new_unchecked(nz) }) + Some(unsafe { Self::new_unchecked(nz) }) } else { None } @@ -1066,11 +1164,11 @@ macro_rules! nonzero_integer_signedness_dependent_methods { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn overflowing_abs(self) -> ($Ty, bool) { + pub const fn overflowing_abs(self) -> (Self, bool) { let (nz, flag) = self.get().overflowing_abs(); ( // SAFETY: absolute value of nonzero cannot yield zero values. - unsafe { $Ty::new_unchecked(nz) }, + unsafe { Self::new_unchecked(nz) }, flag, ) } @@ -1105,9 +1203,9 @@ macro_rules! nonzero_integer_signedness_dependent_methods { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn saturating_abs(self) -> $Ty { + pub const fn saturating_abs(self) -> Self { // SAFETY: absolute value of nonzero cannot yield zero values. - unsafe { $Ty::new_unchecked(self.get().saturating_abs()) } + unsafe { Self::new_unchecked(self.get().saturating_abs()) } } /// Wrapping absolute value, see @@ -1138,9 +1236,9 @@ macro_rules! nonzero_integer_signedness_dependent_methods { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn wrapping_abs(self) -> $Ty { + pub const fn wrapping_abs(self) -> Self { // SAFETY: absolute value of nonzero cannot yield zero values. - unsafe { $Ty::new_unchecked(self.get().wrapping_abs()) } + unsafe { Self::new_unchecked(self.get().wrapping_abs()) } } /// Computes the absolute value of self @@ -1250,10 +1348,10 @@ macro_rules! nonzero_integer_signedness_dependent_methods { #[inline] #[stable(feature = "nonzero_negation_ops", since = "1.71.0")] #[rustc_const_stable(feature = "nonzero_negation_ops", since = "1.71.0")] - pub const fn checked_neg(self) -> Option<$Ty> { + pub const fn checked_neg(self) -> Option { if let Some(result) = self.get().checked_neg() { // SAFETY: negation of nonzero cannot yield zero values. - return Some(unsafe { $Ty::new_unchecked(result) }); + return Some(unsafe { Self::new_unchecked(result) }); } None } @@ -1282,10 +1380,10 @@ macro_rules! nonzero_integer_signedness_dependent_methods { #[inline] #[stable(feature = "nonzero_negation_ops", since = "1.71.0")] #[rustc_const_stable(feature = "nonzero_negation_ops", since = "1.71.0")] - pub const fn overflowing_neg(self) -> ($Ty, bool) { + pub const fn overflowing_neg(self) -> (Self, bool) { let (result, overflow) = self.get().overflowing_neg(); // SAFETY: negation of nonzero cannot yield zero values. - ((unsafe { $Ty::new_unchecked(result) }), overflow) + ((unsafe { Self::new_unchecked(result) }), overflow) } /// Saturating negation. Computes `-self`, @@ -1317,11 +1415,11 @@ macro_rules! nonzero_integer_signedness_dependent_methods { #[inline] #[stable(feature = "nonzero_negation_ops", since = "1.71.0")] #[rustc_const_stable(feature = "nonzero_negation_ops", since = "1.71.0")] - pub const fn saturating_neg(self) -> $Ty { + pub const fn saturating_neg(self) -> Self { if let Some(result) = self.checked_neg() { return result; } - $Ty::MAX + Self::MAX } /// Wrapping (modular) negation. Computes `-self`, wrapping around at the boundary @@ -1349,10 +1447,10 @@ macro_rules! nonzero_integer_signedness_dependent_methods { #[inline] #[stable(feature = "nonzero_negation_ops", since = "1.71.0")] #[rustc_const_stable(feature = "nonzero_negation_ops", since = "1.71.0")] - pub const fn wrapping_neg(self) -> $Ty { + pub const fn wrapping_neg(self) -> Self { let result = self.get().wrapping_neg(); // SAFETY: negation of nonzero cannot yield zero values. - unsafe { $Ty::new_unchecked(result) } + unsafe { Self::new_unchecked(result) } } }; } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index fdfb24330609..a217c2e259d2 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2452,8 +2452,8 @@ macro_rules! uint_impl { // SAFETY: the result is positive and fits in an integer with half as many bits. // Inform the optimizer about it. unsafe { - intrinsics::assume(0 < res); - intrinsics::assume(res < 1 << (Self::BITS / 2)); + hint::assert_unchecked(0 < res); + hint::assert_unchecked(res < 1 << (Self::BITS / 2)); } res diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 373b4aee47a0..fb9be396eab8 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -234,7 +234,7 @@ unsafe impl SliceIndex<[T]> for usize { // `self` is in bounds of `slice` so `self` cannot overflow an `isize`, // so the call to `add` is safe. unsafe { - crate::intrinsics::assume(self < slice.len()); + crate::hint::assert_unchecked(self < slice.len()); slice.as_ptr().add(self) } } diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index cd9e18d1a2de..1c65475b81d7 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -5,7 +5,7 @@ mod macros; use crate::cmp; use crate::fmt; -use crate::intrinsics::assume; +use crate::hint::assert_unchecked; use crate::iter::{ FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce, UncheckedIterator, }; diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index 95bcd123b824..fc6af45fb907 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -338,7 +338,7 @@ macro_rules! iterator { if predicate(x) { // SAFETY: we are guaranteed to be in bounds by the loop invariant: // when `i >= n`, `self.next()` returns `None` and the loop breaks. - unsafe { assume(i < n) }; + unsafe { assert_unchecked(i < n) }; return Some(i); } i += 1; @@ -361,7 +361,7 @@ macro_rules! iterator { if predicate(x) { // SAFETY: `i` must be lower than `n` since it starts at `n` // and is only decreasing. - unsafe { assume(i < n) }; + unsafe { assert_unchecked(i < n) }; return Some(i); } } diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 6d0bb924073c..27dda478848c 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -8,6 +8,7 @@ use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::fmt; +use crate::hint; use crate::intrinsics::exact_div; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZeroUsize; @@ -2799,7 +2800,7 @@ impl [T] { right = if cmp == Greater { mid } else { right }; if cmp == Equal { // SAFETY: same as the `get_unchecked` above - unsafe { crate::intrinsics::assume(mid < self.len()) }; + unsafe { hint::assert_unchecked(mid < self.len()) }; return Ok(mid); } @@ -2808,7 +2809,7 @@ impl [T] { // SAFETY: directly true from the overall invariant. // Note that this is `<=`, unlike the assume in the `Ok` path. - unsafe { crate::intrinsics::assume(left <= self.len()) }; + unsafe { hint::assert_unchecked(left <= self.len()) }; Err(left) } diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index 8fd64279ac5a..c44f23eea809 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -19,6 +19,9 @@ #[cfg(target_os = "android")] mod android; +#[cfg(target_os = "zkvm")] +mod zkvm; + use core::any::Any; use core::panic::PanicPayload; @@ -34,6 +37,8 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 { // Android has the ability to attach a message as part of the abort. #[cfg(target_os = "android")] android::android_set_abort_message(_payload); + #[cfg(target_os = "zkvm")] + zkvm::zkvm_set_abort_message(_payload); abort(); diff --git a/library/panic_abort/src/zkvm.rs b/library/panic_abort/src/zkvm.rs new file mode 100644 index 000000000000..a6a02abf1097 --- /dev/null +++ b/library/panic_abort/src/zkvm.rs @@ -0,0 +1,24 @@ +use alloc::string::String; +use core::panic::PanicPayload; + +// Forward the abort message to zkVM's sys_panic. This is implemented by RISC Zero's +// platform crate which exposes system calls specifically for the zkVM. +pub(crate) unsafe fn zkvm_set_abort_message(payload: &mut dyn PanicPayload) { + let payload = payload.get(); + let msg = match payload.downcast_ref::<&'static str>() { + Some(msg) => msg.as_bytes(), + None => match payload.downcast_ref::() { + Some(msg) => msg.as_bytes(), + None => &[], + }, + }; + if msg.is_empty() { + return; + } + + extern "C" { + fn sys_panic(msg_ptr: *const u8, len: usize) -> !; + } + + sys_panic(msg.as_ptr(), msg.len()); +} diff --git a/library/std/build.rs b/library/std/build.rs index 0f5068b59b7e..60c097db2f4b 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -35,6 +35,7 @@ fn main() { || target.contains("hurd") || target.contains("uefi") || target.contains("teeos") + || target.contains("zkvm") // See src/bootstrap/synthetic_targets.rs || env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok() { diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index bb786bd59dc8..a834b36697c0 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -56,7 +56,7 @@ #![deny(unsafe_op_in_unsafe_fn)] #![stable(feature = "alloc_module", since = "1.28.0")] -use core::intrinsics; +use core::hint; use core::ptr::NonNull; use core::sync::atomic::{AtomicPtr, Ordering}; use core::{mem, ptr}; @@ -172,7 +172,7 @@ impl System { let new_size = new_layout.size(); // `realloc` probably checks for `new_size >= old_layout.size()` or something similar. - intrinsics::assume(new_size >= old_layout.size()); + hint::assert_unchecked(new_size >= old_layout.size()); let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size); let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; @@ -264,7 +264,7 @@ unsafe impl Allocator for System { // SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller new_size if old_layout.align() == new_layout.align() => unsafe { // `realloc` probably checks for `new_size <= old_layout.size()` or something similar. - intrinsics::assume(new_size <= old_layout.size()); + hint::assert_unchecked(new_size <= old_layout.size()); let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size); let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 568645ddf731..751e988a99b3 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -325,6 +325,7 @@ #![feature(float_next_up_down)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] +#![feature(hint_assert_unchecked)] #![feature(ip)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array)] diff --git a/library/std/src/os/linux/process.rs b/library/std/src/os/linux/process.rs index 51af432d0568..2ba67a6dd1aa 100644 --- a/library/std/src/os/linux/process.rs +++ b/library/std/src/os/linux/process.rs @@ -149,8 +149,7 @@ pub trait CommandExt: Sealed { /// The pidfd can be retrieved from the child with [`pidfd`] or [`take_pidfd`]. /// /// A pidfd will only be created if it is possible to do so - /// in a guaranteed race-free manner (e.g. if the `clone3` system call - /// is supported). Otherwise, [`pidfd`] will return an error. + /// in a guaranteed race-free manner. Otherwise, [`pidfd`] will return an error. /// /// If a pidfd has been successfully created and not been taken from the `Child` /// then calls to `kill()`, `wait()` and `try_wait()` will use the pidfd diff --git a/library/std/src/sys/pal/common/alloc.rs b/library/std/src/sys/pal/common/alloc.rs index b7357460f393..8cf9ef680472 100644 --- a/library/std/src/sys/pal/common/alloc.rs +++ b/library/std/src/sys/pal/common/alloc.rs @@ -16,7 +16,7 @@ use crate::ptr; target_arch = "sparc", target_arch = "wasm32", target_arch = "hexagon", - all(target_arch = "riscv32", not(target_os = "espidf")), + all(target_arch = "riscv32", not(any(target_os = "espidf", target_os = "zkvm"))), all(target_arch = "xtensa", not(target_os = "espidf")), ))] pub const MIN_ALIGN: usize = 8; @@ -32,11 +32,11 @@ pub const MIN_ALIGN: usize = 8; target_arch = "wasm64", ))] pub const MIN_ALIGN: usize = 16; -// The allocator on the esp-idf platform guarantees 4 byte alignment. -#[cfg(any( - all(target_arch = "riscv32", target_os = "espidf"), +// The allocator on the esp-idf and zkvm platforms guarantee 4 byte alignment. +#[cfg(all(any( + all(target_arch = "riscv32", any(target_os = "espidf", target_os = "zkvm")), all(target_arch = "xtensa", target_os = "espidf"), -))] +)))] pub const MIN_ALIGN: usize = 4; pub unsafe fn realloc_fallback( diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index 66b2a4b88850..041b7c355822 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -55,6 +55,9 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "teeos")] { mod teeos; pub use self::teeos::*; + } else if #[cfg(target_os = "zkvm")] { + mod zkvm; + pub use self::zkvm::*; } else { mod unsupported; pub use self::unsupported::*; diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs index fac6d92439ee..df0fe2bb9d84 100644 --- a/library/std/src/sys/pal/unix/process/process_unix.rs +++ b/library/std/src/sys/pal/unix/process/process_unix.rs @@ -147,8 +147,7 @@ impl Command { #[cfg(not(target_os = "linux"))] let pidfd = -1; - // Safety: We obtained the pidfd from calling `clone3` with - // `CLONE_PIDFD` so it's valid an otherwise unowned. + // Safety: We obtained the pidfd (on Linux) using SOCK_SEQPACKET, so it's valid. let mut p = unsafe { Process::new(pid, pidfd) }; let mut bytes = [0; 8]; diff --git a/library/std/src/sys/pal/unix/process/process_unix/tests.rs b/library/std/src/sys/pal/unix/process/process_unix/tests.rs index 6e952ed7c42a..0a6c6ec19fc7 100644 --- a/library/std/src/sys/pal/unix/process/process_unix/tests.rs +++ b/library/std/src/sys/pal/unix/process/process_unix/tests.rs @@ -62,13 +62,14 @@ fn test_command_fork_no_unwind() { } #[test] -#[cfg(target_os = "linux")] +#[cfg(target_os = "linux")] // pidfds are a linux-specific concept fn test_command_pidfd() { use crate::assert_matches::assert_matches; use crate::os::fd::{AsRawFd, RawFd}; use crate::os::linux::process::{ChildExt, CommandExt}; use crate::process::Command; + // pidfds require the pidfd_open syscall let our_pid = crate::process::id(); let pidfd = unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) }; let pidfd_open_available = if pidfd >= 0 { @@ -81,7 +82,9 @@ fn test_command_pidfd() { // always exercise creation attempts let mut child = Command::new("false").create_pidfd(true).spawn().unwrap(); - // but only check if we know that the kernel supports pidfds + // but only check if we know that the kernel supports pidfds. + // We don't assert the precise value, since the standard library + // might have opened other file descriptors before our code runs. if pidfd_open_available { assert!(child.pidfd().is_ok()); } @@ -97,4 +100,17 @@ fn test_command_pidfd() { child.kill().expect("failed to kill child"); let status = child.wait().expect("error waiting on pidfd"); assert_eq!(status.signal(), Some(libc::SIGKILL)); + + let _ = Command::new("echo") + .create_pidfd(false) + .spawn() + .unwrap() + .pidfd() + .expect_err("pidfd should not have been created when create_pid(false) is set"); + + let _ = Command::new("echo") + .spawn() + .unwrap() + .pidfd() + .expect_err("pidfd should not have been created"); } diff --git a/library/std/src/sys/pal/unix/rand.rs b/library/std/src/sys/pal/unix/rand.rs index cf0fe0f47c53..1dba1ccf64e6 100644 --- a/library/std/src/sys/pal/unix/rand.rs +++ b/library/std/src/sys/pal/unix/rand.rs @@ -106,7 +106,18 @@ mod imp { // supported on the current kernel. // // Also fall back in case it is disabled by something like - // seccomp or inside of virtual machines. + // seccomp or inside of docker. + // + // If the `getrandom` syscall is not implemented in the current kernel version it should return an + // `ENOSYS` error. Docker also blocks the whole syscall inside unprivileged containers, and + // returns `EPERM` (instead of `ENOSYS`) when a program tries to invoke the syscall. Because of + // that we need to check for *both* `ENOSYS` and `EPERM`. + // + // Note that Docker's behavior is breaking other projects (notably glibc), so they're planning + // to update their filtering to return `ENOSYS` in a future release: + // + // https://github.com/moby/moby/issues/42680 + // GETRANDOM_UNAVAILABLE.store(true, Ordering::Relaxed); return false; } else if err == libc::EAGAIN { diff --git a/library/std/src/sys/pal/unix/thread_local_dtor.rs b/library/std/src/sys/pal/unix/thread_local_dtor.rs index 58f7ab84101a..7e19e6eb9ebb 100644 --- a/library/std/src/sys/pal/unix/thread_local_dtor.rs +++ b/library/std/src/sys/pal/unix/thread_local_dtor.rs @@ -17,7 +17,10 @@ target_os = "android", target_os = "fuchsia", target_os = "redox", - target_os = "hurd" + target_os = "hurd", + target_os = "freebsd", + target_os = "netbsd", + target_os = "dragonfly" ))] // FIXME: The Rust compiler currently omits weakly function definitions (i.e., // __cxa_thread_atexit_impl) and its metadata from LLVM IR. diff --git a/library/std/src/sys/pal/zkvm/abi.rs b/library/std/src/sys/pal/zkvm/abi.rs new file mode 100644 index 000000000000..53332d90e02c --- /dev/null +++ b/library/std/src/sys/pal/zkvm/abi.rs @@ -0,0 +1,55 @@ +//! ABI definitions for symbols exported by risc0-zkvm-platform. + +// Included here so we don't have to depend on risc0-zkvm-platform. +// +// FIXME: Should we move this to the "libc" crate? It seems like other +// architectures put a lot of this kind of stuff there. But there's +// currently no risc0 fork of the libc crate, so we'd either have to +// fork it or upstream it. + +#![allow(dead_code)] +pub const DIGEST_WORDS: usize = 8; + +/// Standard IO file descriptors for use with sys_read and sys_write. +pub mod fileno { + pub const STDIN: u32 = 0; + pub const STDOUT: u32 = 1; + pub const STDERR: u32 = 2; + pub const JOURNAL: u32 = 3; +} + +extern "C" { + // Wrappers around syscalls provided by risc0-zkvm-platform: + pub fn sys_halt(); + pub fn sys_output(output_id: u32, output_value: u32); + pub fn sys_sha_compress( + out_state: *mut [u32; DIGEST_WORDS], + in_state: *const [u32; DIGEST_WORDS], + block1_ptr: *const [u32; DIGEST_WORDS], + block2_ptr: *const [u32; DIGEST_WORDS], + ); + pub fn sys_sha_buffer( + out_state: *mut [u32; DIGEST_WORDS], + in_state: *const [u32; DIGEST_WORDS], + buf: *const u8, + count: u32, + ); + pub fn sys_rand(recv_buf: *mut u32, words: usize); + pub fn sys_panic(msg_ptr: *const u8, len: usize) -> !; + pub fn sys_log(msg_ptr: *const u8, len: usize); + pub fn sys_cycle_count() -> usize; + pub fn sys_read(fd: u32, recv_buf: *mut u8, nrequested: usize) -> usize; + pub fn sys_write(fd: u32, write_buf: *const u8, nbytes: usize); + pub fn sys_getenv( + recv_buf: *mut u32, + words: usize, + varname: *const u8, + varname_len: usize, + ) -> usize; + pub fn sys_argc() -> usize; + pub fn sys_argv(out_words: *mut u32, out_nwords: usize, arg_index: usize) -> usize; + + // Allocate memory from global HEAP. + pub fn sys_alloc_words(nwords: usize) -> *mut u32; + pub fn sys_alloc_aligned(nwords: usize, align: usize) -> *mut u8; +} diff --git a/library/std/src/sys/pal/zkvm/alloc.rs b/library/std/src/sys/pal/zkvm/alloc.rs new file mode 100644 index 000000000000..fd333f121515 --- /dev/null +++ b/library/std/src/sys/pal/zkvm/alloc.rs @@ -0,0 +1,15 @@ +use super::abi; +use crate::alloc::{GlobalAlloc, Layout, System}; + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + abi::sys_alloc_aligned(layout.size(), layout.align()) + } + + #[inline] + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) { + // this allocator never deallocates memory + } +} diff --git a/library/std/src/sys/pal/zkvm/args.rs b/library/std/src/sys/pal/zkvm/args.rs new file mode 100644 index 000000000000..7753cf63840e --- /dev/null +++ b/library/std/src/sys/pal/zkvm/args.rs @@ -0,0 +1,80 @@ +use super::{abi, WORD_SIZE}; +use crate::ffi::OsString; +use crate::fmt; +use crate::sys_common::FromInner; + +pub struct Args { + i_forward: usize, + i_back: usize, + count: usize, +} + +pub fn args() -> Args { + let count = unsafe { abi::sys_argc() }; + Args { i_forward: 0, i_back: 0, count } +} + +impl Args { + /// Use sys_argv to get the arg at the requested index. Does not check that i is less than argc + /// and will not return if the index is out of bounds. + fn argv(i: usize) -> OsString { + let arg_len = unsafe { abi::sys_argv(crate::ptr::null_mut(), 0, i) }; + + let arg_len_words = (arg_len + WORD_SIZE - 1) / WORD_SIZE; + let words = unsafe { abi::sys_alloc_words(arg_len_words) }; + + let arg_len2 = unsafe { abi::sys_argv(words, arg_len_words, i) }; + debug_assert_eq!(arg_len, arg_len2); + + // Convert to OsString. + // + // FIXME: We can probably get rid of the extra copy here if we + // reimplement "os_str" instead of just using the generic unix + // "os_str". + let arg_bytes: &[u8] = + unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, arg_len) }; + OsString::from_inner(super::os_str::Buf { inner: arg_bytes.to_vec() }) + } +} + +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().finish() + } +} + +impl Iterator for Args { + type Item = OsString; + + fn next(&mut self) -> Option { + if self.i_forward >= self.count - self.i_back { + None + } else { + let arg = Self::argv(self.i_forward); + self.i_forward += 1; + Some(arg) + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.count, Some(self.count)) + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + self.count + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option { + if self.i_back >= self.count - self.i_forward { + None + } else { + let arg = Self::argv(self.count - 1 - self.i_back); + self.i_back += 1; + Some(arg) + } + } +} diff --git a/library/std/src/sys/pal/zkvm/env.rs b/library/std/src/sys/pal/zkvm/env.rs new file mode 100644 index 000000000000..b85153642b1c --- /dev/null +++ b/library/std/src/sys/pal/zkvm/env.rs @@ -0,0 +1,9 @@ +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = ""; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".elf"; + pub const DLL_EXTENSION: &str = "elf"; + pub const EXE_SUFFIX: &str = ".elf"; + pub const EXE_EXTENSION: &str = "elf"; +} diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs new file mode 100644 index 000000000000..7f221dc4fd98 --- /dev/null +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -0,0 +1,93 @@ +//! System bindings for the risc0 zkvm platform +//! +//! This module contains the facade (aka platform-specific) implementations of +//! OS level functionality for zkvm. +//! +//! This is all super highly experimental and not actually intended for +//! wide/production use yet, it's still all in the experimental category. This +//! will likely change over time. + +const WORD_SIZE: usize = core::mem::size_of::(); + +pub mod alloc; +#[path = "../zkvm/args.rs"] +pub mod args; +#[path = "../unix/cmath.rs"] +pub mod cmath; +pub mod env; +#[path = "../unsupported/fs.rs"] +pub mod fs; +#[path = "../unsupported/io.rs"] +pub mod io; +#[path = "../unsupported/net.rs"] +pub mod net; +#[path = "../unsupported/once.rs"] +pub mod once; +pub mod os; +#[path = "../unix/os_str.rs"] +pub mod os_str; +#[path = "../unix/path.rs"] +pub mod path; +#[path = "../unsupported/pipe.rs"] +pub mod pipe; +#[path = "../unsupported/process.rs"] +pub mod process; +pub mod stdio; +pub mod thread_local_key; +#[path = "../unsupported/time.rs"] +pub mod time; + +#[path = "../unsupported/locks/mod.rs"] +pub mod locks; +#[path = "../unsupported/thread.rs"] +pub mod thread; + +#[path = "../unsupported/thread_parking.rs"] +pub mod thread_parking; + +mod abi; + +use crate::io as std_io; + +pub mod memchr { + pub use core::slice::memchr::{memchr, memrchr}; +} + +// SAFETY: must be called only once during runtime initialization. +// NOTE: this is not guaranteed to run, for example when Rust code is called externally. +pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} + +// SAFETY: must be called only once during runtime cleanup. +// NOTE: this is not guaranteed to run, for example when the program aborts. +pub unsafe fn cleanup() {} + +pub fn unsupported() -> std_io::Result { + Err(unsupported_err()) +} + +pub fn unsupported_err() -> std_io::Error { + std_io::const_io_error!( + std_io::ErrorKind::Unsupported, + "operation not supported on this platform", + ) +} + +pub fn is_interrupted(_code: i32) -> bool { + false +} + +pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind { + crate::io::ErrorKind::Uncategorized +} + +pub fn abort_internal() -> ! { + core::intrinsics::abort(); +} + +pub fn hashmap_random_keys() -> (u64, u64) { + let mut buf = [0u32; 4]; + unsafe { + abi::sys_rand(buf.as_mut_ptr(), 4); + }; + ((buf[0] as u64) << 32 + buf[1] as u64, (buf[2] as u64) << 32 + buf[3] as u64) +} diff --git a/library/std/src/sys/pal/zkvm/os.rs b/library/std/src/sys/pal/zkvm/os.rs new file mode 100644 index 000000000000..d8739ee38246 --- /dev/null +++ b/library/std/src/sys/pal/zkvm/os.rs @@ -0,0 +1,139 @@ +use super::{abi, unsupported, WORD_SIZE}; +use crate::error::Error as StdError; +use crate::ffi::{OsStr, OsString}; +use crate::fmt; +use crate::io; +use crate::marker::PhantomData; +use crate::path::{self, PathBuf}; +use crate::sys_common::FromInner; + +pub fn errno() -> i32 { + 0 +} + +pub fn error_string(_errno: i32) -> String { + "operation successful".to_string() +} + +pub fn getcwd() -> io::Result { + unsupported() +} + +pub fn chdir(_: &path::Path) -> io::Result<()> { + unsupported() +} + +pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); + +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { + panic!("unsupported") +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option { + self.0 + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + Err(JoinPathsError) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "not supported on this platform yet".fmt(f) + } +} + +impl StdError for JoinPathsError { + #[allow(deprecated)] + fn description(&self) -> &str { + "not supported on this platform yet" + } +} + +pub fn current_exe() -> io::Result { + unsupported() +} + +pub struct Env(!); + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.0 + } +} + +pub fn env() -> Env { + panic!("not supported on this platform") +} + +impl Env { + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self(inner) = self; + match *inner {} + } +} + +impl fmt::Debug for Env { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self(inner) = self; + match *inner {} + } +} + +pub fn getenv(varname: &OsStr) -> Option { + let varname = varname.as_encoded_bytes(); + let nbytes = + unsafe { abi::sys_getenv(crate::ptr::null_mut(), 0, varname.as_ptr(), varname.len()) }; + if nbytes == usize::MAX { + return None; + } + + let nwords = (nbytes + WORD_SIZE - 1) / WORD_SIZE; + let words = unsafe { abi::sys_alloc_words(nwords) }; + + let nbytes2 = unsafe { abi::sys_getenv(words, nwords, varname.as_ptr(), varname.len()) }; + debug_assert_eq!(nbytes, nbytes2); + + // Convert to OsString. + // + // FIXME: We can probably get rid of the extra copy here if we + // reimplement "os_str" instead of just using the generic unix + // "os_str". + let u8s: &[u8] = unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, nbytes) }; + Some(OsString::from_inner(super::os_str::Buf { inner: u8s.to_vec() })) +} + +pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { + Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) +} + +pub fn unsetenv(_: &OsStr) -> io::Result<()> { + Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) +} + +pub fn temp_dir() -> PathBuf { + panic!("no filesystem on this platform") +} + +pub fn home_dir() -> Option { + None +} + +pub fn exit(_code: i32) -> ! { + crate::intrinsics::abort() +} + +pub fn getpid() -> u32 { + panic!("no pids on this platform") +} diff --git a/library/std/src/sys/pal/zkvm/stdio.rs b/library/std/src/sys/pal/zkvm/stdio.rs new file mode 100644 index 000000000000..e771ed0de28d --- /dev/null +++ b/library/std/src/sys/pal/zkvm/stdio.rs @@ -0,0 +1,64 @@ +use super::{abi, abi::fileno}; +use crate::io; + +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; + +impl Stdin { + pub const fn new() -> Stdin { + Stdin + } +} + +impl io::Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + Ok(unsafe { abi::sys_read(fileno::STDIN, buf.as_mut_ptr(), buf.len()) }) + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + unsafe { abi::sys_write(fileno::STDOUT, buf.as_ptr(), buf.len()) } + + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub const fn new() -> Stderr { + Stderr + } +} + +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result { + unsafe { abi::sys_write(fileno::STDERR, buf.as_ptr(), buf.len()) } + + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; + +pub fn is_ebadf(_err: &io::Error) -> bool { + true +} + +pub fn panic_output() -> Option { + Some(Stderr::new()) +} diff --git a/library/std/src/sys/pal/zkvm/thread_local_key.rs b/library/std/src/sys/pal/zkvm/thread_local_key.rs new file mode 100644 index 000000000000..3ffe6247344e --- /dev/null +++ b/library/std/src/sys/pal/zkvm/thread_local_key.rs @@ -0,0 +1,23 @@ +use crate::alloc::{alloc, Layout}; + +pub type Key = usize; + +#[inline] +pub unsafe fn create(_dtor: Option) -> Key { + alloc(Layout::new::<*mut u8>()) as _ +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + let key: *mut *mut u8 = core::ptr::from_exposed_addr_mut(key); + *key = value; +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + let key: *mut *mut u8 = core::ptr::from_exposed_addr_mut(key); + *key +} + +#[inline] +pub unsafe fn destroy(_key: Key) {} diff --git a/library/test/src/console.rs b/library/test/src/console.rs index bbeb944e8b11..09aa3bfb6aac 100644 --- a/library/test/src/console.rs +++ b/library/test/src/console.rs @@ -323,7 +323,8 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Resu // Prevent the usage of `Instant` in some cases: // - It's currently not supported for wasm targets. // - We disable it for miri because it's not available when isolation is enabled. - let is_instant_supported = !cfg!(target_family = "wasm") && !cfg!(miri); + let is_instant_supported = + !cfg!(target_family = "wasm") && !cfg!(target_os = "zkvm") && !cfg!(miri); let start_time = is_instant_supported.then(Instant::now); run_tests(opts, tests, |x| on_test_event(&x, &mut st, &mut *out))?; diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 2e93796d9815..5969d6b772c3 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -540,7 +540,7 @@ pub fn run_test( // Emscripten can catch panics but other wasm targets cannot let ignore_because_no_process_support = desc.should_panic != ShouldPanic::No - && cfg!(target_family = "wasm") + && (cfg!(target_family = "wasm") || cfg!(target_os = "zkvm")) && !cfg!(target_os = "emscripten"); if force_ignore || desc.ignore || ignore_because_no_process_support { diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 30824f585228..1336abf6c7ab 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -90,6 +90,10 @@ const EXTRA_CHECK_CFGS: &[(Option, &str, Option<&[&'static str]>)] = &[ /* Extra values not defined in the built-in targets yet, but used in std */ (Some(Mode::Std), "target_env", Some(&["libnx"])), // (Some(Mode::Std), "target_os", Some(&[])), + // #[cfg(bootstrap)] zkvm + (Some(Mode::Std), "target_os", Some(&["zkvm"])), + // #[cfg(bootstrap)] risc0 + (Some(Mode::Std), "target_vendor", Some(&["risc0"])), (Some(Mode::Std), "target_arch", Some(&["spirv", "nvptx", "xtensa"])), /* Extra names used by dependencies */ // FIXME: Used by serde_json, but we should not be triggering on external dependencies. @@ -721,6 +725,11 @@ impl Build { if self.config.profiler_enabled(target) { features.push_str(" profiler"); } + // Generate memcpy, etc. FIXME: Remove this once compiler-builtins + // automatically detects this target. + if target.contains("zkvm") { + features.push_str(" compiler-builtins-mem"); + } features } diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 1c8e1d1e316b..1998b008dc81 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -47,6 +47,7 @@ - [mipsisa\*r6\*-unknown-linux-gnu\*](platform-support/mips-release-6.md) - [nvptx64-nvidia-cuda](platform-support/nvptx64-nvidia-cuda.md) - [powerpc64-ibm-aix](platform-support/aix.md) + - [riscv32im-risc0-zkvm-elf](platform-support/riscv32im-risc0-zkvm-elf.md) - [riscv32imac-unknown-xous-elf](platform-support/riscv32imac-unknown-xous-elf.md) - [riscv32*-unknown-none-elf](platform-support/riscv32imac-unknown-none-elf.md) - [sparc-unknown-none-elf](./platform-support/sparc-unknown-none-elf.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 9e5c1ed87489..f648a60b6c48 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -338,6 +338,7 @@ target | std | host | notes [`powerpc64-ibm-aix`](platform-support/aix.md) | ? | | 64-bit AIX (7.2 and newer) `riscv32gc-unknown-linux-gnu` | | | RISC-V Linux (kernel 5.4, glibc 2.33) `riscv32gc-unknown-linux-musl` | | | RISC-V Linux (kernel 5.4, musl + RISCV32 support patches) +[`riscv32im-risc0-zkvm-elf`](platform-support/riscv32im-risc0-zkvm-elf.md) | ? | | RISC Zero's zero-knowledge Virtual Machine (RV32IM ISA) [`riscv32imac-unknown-xous-elf`](platform-support/riscv32imac-unknown-xous-elf.md) | ? | | RISC-V Xous (RV32IMAC ISA) [`riscv32imc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF [`riscv32imac-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF diff --git a/src/doc/rustc/src/platform-support/riscv32im-risc0-zkvm-elf.md b/src/doc/rustc/src/platform-support/riscv32im-risc0-zkvm-elf.md new file mode 100644 index 000000000000..1fdd594012c2 --- /dev/null +++ b/src/doc/rustc/src/platform-support/riscv32im-risc0-zkvm-elf.md @@ -0,0 +1,86 @@ +# `riscv32im-risc0-zkvm-elf` + +**Tier: 3** + +RISC Zero's Zero Knowledge Virtual Machine (zkVM) implementing the RV32IM instruction set. + +## Target maintainers + +- Frank Laub, `frank@risczero.com`, https://github.com/flaub +- Jeremy Bruestle, `jeremy@risczero.com`, https://github.com/jbruestle +- Erik Kaneda, `erik@risczero.com`, https://github.com/SchmErik + +## Background + +This target is an execution environment to produce a proof of execution of +a RISC-V ELF binary and any output that the developer of the binary wishes to +display publicly. In order to do this, the target will execute the ELF to +generate a receipt containing the output of the computation along with a +cryptographic seal. This receipt can be verified to ensure the integrity of the +computation and its result. This target is implemented as software only; it has +no hardware implementation. + +We have a cargo extension called [cargo-risczero] that allow users to generate +project templates, install tools for improved user experience, build the binary +using a docker environment and test programs. + +## Requirements + +The target only supports cross compilation and no host tools. The target +supports `alloc` with a default allocator and has experimental support for +`std`. The target expects the binaries to be in ELF. + +The target's execution environment is single threaded, non-preemptive, and does +not support any privileged instructions, nor unaligned accesses. At the time of +writing the VM has 192 MB of memory and text/data, heap, and stack need to be +with in the address range `0x400` - `0x0C000000`. The binaries themselves expect +no operating system and can be thought of as running on bare-metal. The target +does not use `#[target_feature(...)]` or `-C target-feature=` values. + +Calling `extern "C"` on the target uses the C calling convention outlined in the +[RISC-V specification]. + +## Building for the zkVM + +Programs for the zkVM could be built by adding it to the `target` list in +`config.toml`. However, we recommend building programs in our starter template +generated by the [cargo-risczero] utility and the [risc0-build] crate. This +crate calls `rustc` with `-C "link-arg=-Ttext=` so that it maps the text in the +appropriate location as well as generating variables that represent the ELF and +a unique ID associated with the ELF. The starter template provides developers +with system calls that are useful to zero knowledge computing such as writing to +the public output, hashing using sha256, and multiply big integers. + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for this target. To compile for +this target, you will either need to build Rust with the target enabled (see +"Building the target" above). We do not recommend using `build-std` as we have +run into issues building core in the past on our starter template. An alternate +solution is to download the risc0 tool chain by running `cargo risczero install`. + +## Testing + +Note: the target is implemented as a software emulator called the zkVM and there +is no hardware implementation of the target. + +The most practical way to test the target program is to use our starter template +that can be generated by using the `cargo risczero new` command. The template +generates a sample "host" and "guest" code. The guest code compiled to the +target (which is RV32IM) whereas the "host" code is compiled to run on the +programmer's machine running either a Linux distribution or macOS. The host +program is responsible for running the guest binary on the zkVM and retrieving +its public output. + +The target currently does not support running the Rust test suite. + +## Cross-compilation toolchains and C code + +Compatible C code can be built for this target on any compiler that has a RV32IM +target. On clang and ld.lld linker, it can be generated using the +`-march=rv32im`, `-mabi=ilp32` with llvm features flag `features=+m` and llvm +target `riscv32-unknown-none`. + +[RISC-V specification]: https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf +[cargo-risczero]: https://docs.rs/cargo-risczero/latest/cargo_risczero/ +[risc0-build]: https://crates.io/crates/risc0-build diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index ca1870a3a645..a75d9bee3049 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -108,15 +108,13 @@ impl Res { Res::Primitive(_) => return Suggestion::Prefix("prim"), Res::Def(kind, _) => kind, }; - if kind == DefKind::Macro(MacroKind::Bang) { - return Suggestion::Macro; - } else if kind == DefKind::Fn || kind == DefKind::AssocFn { - return Suggestion::Function; - } else if kind == DefKind::Field { - return Suggestion::RemoveDisambiguator; - } let prefix = match kind { + DefKind::Fn | DefKind::AssocFn => return Suggestion::Function, + DefKind::Field => return Suggestion::RemoveDisambiguator, + DefKind::Macro(MacroKind::Bang) => return Suggestion::Macro, + + DefKind::Macro(MacroKind::Derive) => "derive", DefKind::Struct => "struct", DefKind::Enum => "enum", DefKind::Trait => "trait", @@ -126,7 +124,6 @@ impl Res { "const" } DefKind::Static(_) => "static", - DefKind::Macro(MacroKind::Derive) => "derive", // Now handle things that don't have a specific disambiguator _ => match kind .ns() @@ -283,20 +280,15 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { debug!("looking for enum variant {path_str}"); let mut split = path_str.rsplitn(3, "::"); - let variant_field_name = split - .next() - .map(|f| Symbol::intern(f)) - .expect("fold_item should ensure link is non-empty"); - let variant_name = - // we're not sure this is a variant at all, so use the full string - // If there's no second component, the link looks like `[path]`. - // So there's no partial res and we should say the whole link failed to resolve. - split.next().map(|f| Symbol::intern(f)).ok_or_else(no_res)?; - let path = split - .next() - // If there's no third component, we saw `[a::b]` before and it failed to resolve. - // So there's no partial res. - .ok_or_else(no_res)?; + let variant_field_name = Symbol::intern(split.next().unwrap()); + // We're not sure this is a variant at all, so use the full string. + // If there's no second component, the link looks like `[path]`. + // So there's no partial res and we should say the whole link failed to resolve. + let variant_name = Symbol::intern(split.next().ok_or_else(no_res)?); + + // If there's no third component, we saw `[a::b]` before and it failed to resolve. + // So there's no partial res. + let path = split.next().ok_or_else(no_res)?; let ty_res = self.resolve_path(&path, TypeNS, item_id, module_id).ok_or_else(no_res)?; match ty_res { @@ -447,41 +439,29 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } // Try looking for methods and associated items. - let mut split = path_str.rsplitn(2, "::"); - // NB: `split`'s first element is always defined, even if the delimiter was not present. - // NB: `item_str` could be empty when resolving in the root namespace (e.g. `::std`). - let item_str = split.next().unwrap(); - let item_name = Symbol::intern(item_str); - let path_root = split - .next() + // NB: `path_root` could be empty when resolving in the root namespace (e.g. `::std`). + let (path_root, item_str) = path_str.rsplit_once("::").ok_or_else(|| { // If there's no `::`, it's not an associated item. // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved. - .ok_or_else(|| { - debug!("found no `::`, assuming {item_name} was correctly not in scope"); - UnresolvedPath { - item_id, - module_id, - partial_res: None, - unresolved: item_str.into(), - } - })?; + debug!("found no `::`, assuming {path_str} was correctly not in scope"); + UnresolvedPath { item_id, module_id, partial_res: None, unresolved: path_str.into() } + })?; + let item_name = Symbol::intern(item_str); // FIXME(#83862): this arbitrarily gives precedence to primitives over modules to support // links to primitives when `#[rustc_doc_primitive]` is present. It should give an ambiguity // error instead and special case *only* modules with `#[rustc_doc_primitive]`, not all // primitives. - match resolve_primitive(&path_root, TypeNS) - .or_else(|| self.resolve_path(&path_root, TypeNS, item_id, module_id)) - .and_then(|ty_res| { - let candidates = self - .resolve_associated_item(ty_res, item_name, ns, module_id) + match resolve_primitive(path_root, TypeNS) + .or_else(|| self.resolve_path(path_root, TypeNS, item_id, module_id)) + .map(|ty_res| { + self.resolve_associated_item(ty_res, item_name, ns, module_id) .into_iter() .map(|(res, def_id)| (res, Some(def_id))) - .collect::>(); - if !candidates.is_empty() { Some(candidates) } else { None } + .collect::>() }) { - Some(r) => Ok(r), - None => { + Some(r) if !r.is_empty() => Ok(r), + _ => { if ns == Namespace::ValueNS { self.variant_field(path_str, item_id, module_id) .map(|(res, def_id)| vec![(res, Some(def_id))]) @@ -1263,7 +1243,7 @@ impl LinkCollector<'_, '_> { self.report_rawptr_assoc_feature_gate(diag.dox, &diag.link_range, diag.item); return None; } else { - candidates = vec![candidates[0]]; + candidates = vec![*candidate]; } } @@ -1271,8 +1251,10 @@ impl LinkCollector<'_, '_> { // and after removing duplicated kinds, only one remains, the `ambiguity_error` function // won't emit an error. So at this point, we can just take the first candidate as it was // the first retrieved and use it to generate the link. - if candidates.len() > 1 && !ambiguity_error(self.cx, &diag, &key.path_str, &candidates) { - candidates = vec![candidates[0]]; + if let [candidate, _candidate2, ..] = *candidates + && !ambiguity_error(self.cx, &diag, &key.path_str, &candidates) + { + candidates = vec![candidate]; } if let &[(res, def_id)] = candidates.as_slice() { @@ -1322,12 +1304,11 @@ impl LinkCollector<'_, '_> { let mut err = ResolutionFailure::NotResolved(err); for other_ns in [TypeNS, ValueNS, MacroNS] { if other_ns != expected_ns { - if let Ok(res) = - self.resolve(path_str, other_ns, item_id, module_id) - && !res.is_empty() + if let Ok(&[res, ..]) = + self.resolve(path_str, other_ns, item_id, module_id).as_deref() { err = ResolutionFailure::WrongNamespace { - res: full_res(self.cx.tcx, res[0]), + res: full_res(self.cx.tcx, res), expected_ns, }; break; @@ -1748,7 +1729,6 @@ fn report_diagnostic( lint.note(format!( "the link appears in this line:\n\n{line}\n\ {indicator: Option<(&str, &str)> { - let mut splitter = path.rsplitn(2, "::"); - splitter.next().and_then(|right| splitter.next().map(|left| (left, right))) - } // Check if _any_ parent of the path gets resolved. // If so, report it and say the first which failed; if not, say the first path segment didn't resolve. let mut name = path_str; 'outer: loop { - let Some((start, end)) = split(name) else { + // FIXME(jynelson): this might conflict with my `Self` fix in #76467 + let Some((start, end)) = name.rsplit_once("::") else { // avoid bug that marked [Quux::Z] as missing Z, not Quux if partial_res.is_none() { *unresolved = name.into(); @@ -1829,8 +1804,8 @@ fn resolution_failure( for ns in [TypeNS, ValueNS, MacroNS] { if let Ok(v_res) = collector.resolve(start, ns, item_id, module_id) { debug!("found partial_res={v_res:?}"); - if !v_res.is_empty() { - *partial_res = Some(full_res(tcx, v_res[0])); + if let Some(&res) = v_res.first() { + *partial_res = Some(full_res(tcx, res)); *unresolved = end.into(); break 'outer; } diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 9ac97236f19c..1ef8cf7de3cd 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -121,6 +121,7 @@ static TARGETS: &[&str] = &[ "powerpc64-unknown-linux-gnu", "powerpc64le-unknown-linux-gnu", "riscv32i-unknown-none-elf", + "riscv32im-risc0-zkvm-elf", "riscv32im-unknown-none-elf", "riscv32imc-unknown-none-elf", "riscv32imac-unknown-none-elf", diff --git a/src/tools/cargo b/src/tools/cargo index 1ae631085f01..7bb7b539558d 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 1ae631085f01c1a72d05df1ec81f3759a8360042 +Subproject commit 7bb7b539558dc88bea44cee4168b6269bf8177b0 diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 6bcc68ebf7cc..08fce334e03c 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -121,8 +121,9 @@ case $HOST_TARGET in MIRI_TEST_TARGET=aarch64-apple-darwin run_tests MIRI_TEST_TARGET=i686-pc-windows-gnu run_tests # Some targets are only partially supported. - MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc libc-fs atomic env align - MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc libc-fs atomic env align + # FIXME: freeBSD disabled due to https://github.com/rust-lang/miri/issues/3276 + #MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc libc-fs atomic env align + #MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc libc-fs atomic env align MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal hello integer vec panic/panic MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal no_std integer strings wasm MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std integer strings wasm diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index ceaabd38e418..62bef6d52d99 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -0011fac90d2846ea3c04506238ff6e4ed3ce0efe +f6ee4bf3847277d6d6e2007ff664f8ea0895b11b diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index eac315d04368..7740d383ee3f 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -830,7 +830,9 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' let new_prov = this.sb_reborrow(place, size, new_perm, new_tag, info)?; // Adjust place. - Ok(place.clone().map_provenance(|_| new_prov)) + // (If the closure gets called, that means the old provenance was `Some`, and hence the new + // one must also be `Some`.) + Ok(place.clone().map_provenance(|_| new_prov.unwrap())) } /// Retags an individual pointer, returning the retagged version. diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 573d13bf4a5d..0945a5292bb9 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -351,7 +351,9 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' let new_prov = this.tb_reborrow(place, reborrow_size, new_perm, new_tag)?; // Adjust place. - Ok(place.clone().map_provenance(|_| new_prov)) + // (If the closure gets called, that means the old provenance was `Some`, and hence the new + // one must also be `Some`.) + Ok(place.clone().map_provenance(|_| new_prov.unwrap())) } /// Retags an individual pointer, returning the retagged version. diff --git a/tests/assembly/targets/targets-elf.rs b/tests/assembly/targets/targets-elf.rs index 72a35f38ecad..6eec05e85ac7 100644 --- a/tests/assembly/targets/targets-elf.rs +++ b/tests/assembly/targets/targets-elf.rs @@ -402,6 +402,9 @@ // revisions: riscv32i_unknown_none_elf // [riscv32i_unknown_none_elf] compile-flags: --target riscv32i-unknown-none-elf // [riscv32i_unknown_none_elf] needs-llvm-components: riscv +// revisions: riscv32im_risc0_zkvm_elf +// [riscv32im_risc0_zkvm_elf] compile-flags: --target riscv32im-risc0-zkvm-elf +// [riscv32im_risc0_zkvm_elf] needs-llvm-components: riscv // revisions: riscv32im_unknown_none_elf // [riscv32im_unknown_none_elf] compile-flags: --target riscv32im-unknown-none-elf // [riscv32im_unknown_none_elf] needs-llvm-components: riscv diff --git a/tests/codegen/vec-reserve-extend.rs b/tests/codegen/vec-reserve-extend.rs index d95220104c22..395373ff4f10 100644 --- a/tests/codegen/vec-reserve-extend.rs +++ b/tests/codegen/vec-reserve-extend.rs @@ -1,4 +1,6 @@ // compile-flags: -O +// ignore-debug +// (with debug assertions turned on, `assert_unchecked` generates a real assertion) #![crate_type = "lib"] diff --git a/tests/codegen/vec_pop_push_noop.rs b/tests/codegen/vec_pop_push_noop.rs index 8bc7b68a816e..d9293f2b75de 100644 --- a/tests/codegen/vec_pop_push_noop.rs +++ b/tests/codegen/vec_pop_push_noop.rs @@ -1,4 +1,6 @@ // compile-flags: -O +// ignore-debug +// (with debug assertions turned on, `assert_unchecked` generates a real assertion) #![crate_type = "lib"] diff --git a/tests/mir-opt/building/custom/assume.assume_constant.built.after.mir b/tests/mir-opt/building/custom/assume.assume_constant.built.after.mir new file mode 100644 index 000000000000..8e70d0a1e9b1 --- /dev/null +++ b/tests/mir-opt/building/custom/assume.assume_constant.built.after.mir @@ -0,0 +1,10 @@ +// MIR for `assume_constant` after built + +fn assume_constant() -> () { + let mut _0: (); + + bb0: { + assume(const true); + return; + } +} diff --git a/tests/mir-opt/building/custom/assume.assume_local.built.after.mir b/tests/mir-opt/building/custom/assume.assume_local.built.after.mir new file mode 100644 index 000000000000..7ea1fcd30c2e --- /dev/null +++ b/tests/mir-opt/building/custom/assume.assume_local.built.after.mir @@ -0,0 +1,10 @@ +// MIR for `assume_local` after built + +fn assume_local(_1: bool) -> () { + let mut _0: (); + + bb0: { + assume(_1); + return; + } +} diff --git a/tests/mir-opt/building/custom/assume.assume_place.built.after.mir b/tests/mir-opt/building/custom/assume.assume_place.built.after.mir new file mode 100644 index 000000000000..ce914618d3dc --- /dev/null +++ b/tests/mir-opt/building/custom/assume.assume_place.built.after.mir @@ -0,0 +1,10 @@ +// MIR for `assume_place` after built + +fn assume_place(_1: (bool, u8)) -> () { + let mut _0: (); + + bb0: { + assume((_1.0: bool)); + return; + } +} diff --git a/tests/mir-opt/building/custom/assume.rs b/tests/mir-opt/building/custom/assume.rs new file mode 100644 index 000000000000..a477e12f0e03 --- /dev/null +++ b/tests/mir-opt/building/custom/assume.rs @@ -0,0 +1,44 @@ +// skip-filecheck +#![feature(custom_mir, core_intrinsics)] + +extern crate core; +use core::intrinsics::mir::*; + +// EMIT_MIR assume.assume_local.built.after.mir +#[custom_mir(dialect = "built")] +fn assume_local(x: bool) { + mir!( + { + Assume(x); + Return() + } + ) +} + +// EMIT_MIR assume.assume_place.built.after.mir +#[custom_mir(dialect = "built")] +fn assume_place(p: (bool, u8)) { + mir!( + { + Assume(p.0); + Return() + } + ) +} + +// EMIT_MIR assume.assume_constant.built.after.mir +#[custom_mir(dialect = "built")] +fn assume_constant() { + mir!( + { + Assume(true); + Return() + } + ) +} + +fn main() { + assume_local(true); + assume_place((true, 50)); + assume_constant(); +} diff --git a/tests/mir-opt/jump_threading.assume.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.assume.JumpThreading.panic-abort.diff new file mode 100644 index 000000000000..f1f0106fdbce --- /dev/null +++ b/tests/mir-opt/jump_threading.assume.JumpThreading.panic-abort.diff @@ -0,0 +1,39 @@ +- // MIR for `assume` before JumpThreading ++ // MIR for `assume` after JumpThreading + + fn assume(_1: u8, _2: bool) -> u8 { + let mut _0: u8; + + bb0: { + switchInt(_1) -> [7: bb1, otherwise: bb2]; + } + + bb1: { + assume(_2); +- goto -> bb3; ++ goto -> bb6; + } + + bb2: { + goto -> bb3; + } + + bb3: { + switchInt(_2) -> [0: bb4, otherwise: bb5]; + } + + bb4: { + _0 = const 4_u8; + return; + } + + bb5: { + _0 = const 5_u8; + return; ++ } ++ ++ bb6: { ++ goto -> bb5; + } + } + diff --git a/tests/mir-opt/jump_threading.assume.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.assume.JumpThreading.panic-unwind.diff new file mode 100644 index 000000000000..f1f0106fdbce --- /dev/null +++ b/tests/mir-opt/jump_threading.assume.JumpThreading.panic-unwind.diff @@ -0,0 +1,39 @@ +- // MIR for `assume` before JumpThreading ++ // MIR for `assume` after JumpThreading + + fn assume(_1: u8, _2: bool) -> u8 { + let mut _0: u8; + + bb0: { + switchInt(_1) -> [7: bb1, otherwise: bb2]; + } + + bb1: { + assume(_2); +- goto -> bb3; ++ goto -> bb6; + } + + bb2: { + goto -> bb3; + } + + bb3: { + switchInt(_2) -> [0: bb4, otherwise: bb5]; + } + + bb4: { + _0 = const 4_u8; + return; + } + + bb5: { + _0 = const 5_u8; + return; ++ } ++ ++ bb6: { ++ goto -> bb5; + } + } + diff --git a/tests/mir-opt/jump_threading.rs b/tests/mir-opt/jump_threading.rs index 7c2fa42828be..a66fe8b57e71 100644 --- a/tests/mir-opt/jump_threading.rs +++ b/tests/mir-opt/jump_threading.rs @@ -468,6 +468,52 @@ fn aggregate(x: u8) -> u8 { } } +/// Verify that we can leverage the existence of an `Assume` terminator. +#[custom_mir(dialect = "runtime", phase = "post-cleanup")] +fn assume(a: u8, b: bool) -> u8 { + // CHECK-LABEL: fn assume( + mir!( + { + // CHECK: bb0: { + // CHECK-NEXT: switchInt(_1) -> [7: bb1, otherwise: bb2] + match a { 7 => bb1, _ => bb2 } + } + bb1 = { + // CHECK: bb1: { + // CHECK-NEXT: assume(_2); + // CHECK-NEXT: goto -> bb6; + Assume(b); + Goto(bb3) + } + bb2 = { + // CHECK: bb2: { + // CHECK-NEXT: goto -> bb3; + Goto(bb3) + } + bb3 = { + // CHECK: bb3: { + // CHECK-NEXT: switchInt(_2) -> [0: bb4, otherwise: bb5]; + match b { false => bb4, _ => bb5 } + } + bb4 = { + // CHECK: bb4: { + // CHECK-NEXT: _0 = const 4_u8; + // CHECK-NEXT: return; + RET = 4; + Return() + } + bb5 = { + // CHECK: bb5: { + // CHECK-NEXT: _0 = const 5_u8; + // CHECK-NEXT: return; + RET = 5; + Return() + } + // CHECK: bb6: { + // CHECK-NEXT: goto -> bb5; + ) +} + fn main() { // CHECK-LABEL: fn main( too_complex(Ok(0)); @@ -481,6 +527,7 @@ fn main() { renumbered_bb(true); disappearing_bb(7); aggregate(7); + assume(7, false); } // EMIT_MIR jump_threading.too_complex.JumpThreading.diff @@ -494,3 +541,4 @@ fn main() { // EMIT_MIR jump_threading.renumbered_bb.JumpThreading.diff // EMIT_MIR jump_threading.disappearing_bb.JumpThreading.diff // EMIT_MIR jump_threading.aggregate.JumpThreading.diff +// EMIT_MIR jump_threading.assume.JumpThreading.diff diff --git a/tests/ui/array-slice-vec/repeat_empty_ok.stderr b/tests/ui/array-slice-vec/repeat_empty_ok.stderr index e8bac04ac450..bc3a68c905d4 100644 --- a/tests/ui/array-slice-vec/repeat_empty_ok.stderr +++ b/tests/ui/array-slice-vec/repeat_empty_ok.stderr @@ -5,6 +5,8 @@ LL | let headers = [Header{value: &[]}; 128]; | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>` | = note: the `Copy` trait is required because this value will be copied for each element of the array + = help: consider using `core::array::from_fn` to initialize the array + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information help: consider annotating `Header<'_>` with `#[derive(Copy)]` | LL + #[derive(Copy)] @@ -18,6 +20,8 @@ LL | let headers = [Header{value: &[0]}; 128]; | ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>` | = note: the `Copy` trait is required because this value will be copied for each element of the array + = help: consider using `core::array::from_fn` to initialize the array + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information help: consider annotating `Header<'_>` with `#[derive(Copy)]` | LL + #[derive(Copy)] diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 9d134dcfcfd8..814d47361977 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -179,7 +179,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_os = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `openbsd`, `psp`, `redox`, `solaris`, `solid_asp3`, `teeos`, `tvos`, `uefi`, `unknown`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous` + = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `openbsd`, `psp`, `redox`, `solaris`, `solid_asp3`, `teeos`, `tvos`, `uefi`, `unknown`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, `zkvm` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -208,7 +208,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_vendor = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `nintendo`, `nvidia`, `pc`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, `wrs` + = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -252,7 +252,7 @@ LL | #[cfg(target_os = "linuz")] // testing that we suggest `linux` | | | help: there is a expected value with a similar name: `"linux"` | - = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `openbsd`, `psp`, `redox`, `solaris`, `solid_asp3`, `teeos`, `tvos`, `uefi`, `unknown`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous` + = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `openbsd`, `psp`, `redox`, `solaris`, `solid_asp3`, `teeos`, `tvos`, `uefi`, `unknown`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, `zkvm` = note: see for more information about checking conditional configuration warning: 26 warnings emitted diff --git a/tests/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr b/tests/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr index 02d75ff1228d..b71cfc503339 100644 --- a/tests/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr +++ b/tests/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr @@ -9,7 +9,7 @@ LL | let c1 : () = c; | expected due to this | = note: expected unit type `()` - found closure `{mod1::f::{closure#0} closure_args=(unavailable) args=[T, ?8t, extern "rust-call" fn(()), ?7t]}` + found closure `{mod1::f::{closure#0} closure_kind_ty=?8t closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=?7t}` help: use parentheses to call this closure | LL | let c1 : () = c(); diff --git a/tests/ui/closures/print/closure-print-generic-verbose-2.stderr b/tests/ui/closures/print/closure-print-generic-verbose-2.stderr index a53676889898..88f4dc9b92ab 100644 --- a/tests/ui/closures/print/closure-print-generic-verbose-2.stderr +++ b/tests/ui/closures/print/closure-print-generic-verbose-2.stderr @@ -9,7 +9,7 @@ LL | let c1 : () = c; | expected due to this | = note: expected unit type `()` - found closure `{f::{closure#0} closure_args=(unavailable) args=[T, ?8t, extern "rust-call" fn(()), ?7t]}` + found closure `{f::{closure#0} closure_kind_ty=?8t closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=?7t}` help: use parentheses to call this closure | LL | let c1 : () = c(); diff --git a/tests/ui/closures/print/closure-print-verbose.stderr b/tests/ui/closures/print/closure-print-verbose.stderr index fca8f25792ac..204a86b63302 100644 --- a/tests/ui/closures/print/closure-print-verbose.stderr +++ b/tests/ui/closures/print/closure-print-verbose.stderr @@ -7,7 +7,7 @@ LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a }; | expected due to this | = note: expected fn pointer `fn(u8) -> u8` - found closure `{main::{closure#0} closure_args=(unavailable) args=[i8, extern "rust-call" fn((u8,)) -> u8, ?4t]}` + found closure `{main::{closure#0} closure_kind_ty=i8 closure_sig_as_fn_ptr_ty=extern "rust-call" fn((u8,)) -> u8 upvar_tys=?4t}` note: closures can only be coerced to `fn` types if they do not capture any variables --> $DIR/closure-print-verbose.rs:10:39 | diff --git a/tests/ui/command/command-create-pidfd.rs b/tests/ui/command/command-create-pidfd.rs deleted file mode 100644 index 4df443c66d65..000000000000 --- a/tests/ui/command/command-create-pidfd.rs +++ /dev/null @@ -1,56 +0,0 @@ -// run-pass -// only-linux - pidfds are a linux-specific concept - -#![feature(linux_pidfd)] -#![feature(rustc_private)] - -extern crate libc; - -use std::io::Error; -use std::os::linux::process::{ChildExt, CommandExt}; -use std::process::Command; - -fn has_clone3() -> bool { - let res = unsafe { libc::syscall(libc::SYS_clone3, 0, 0) }; - let err = (res == -1) - .then(|| Error::last_os_error()) - .expect("probe syscall should not succeed"); - - // If the `clone3` syscall is not implemented in the current kernel version it should return an - // `ENOSYS` error. Docker also blocks the whole syscall inside unprivileged containers, and - // returns `EPERM` (instead of `ENOSYS`) when a program tries to invoke the syscall. Because of - // that we need to check for *both* `ENOSYS` and `EPERM`. - // - // Note that Docker's behavior is breaking other projects (notably glibc), so they're planning - // to update their filtering to return `ENOSYS` in a future release: - // - // https://github.com/moby/moby/issues/42680 - // - err.raw_os_error() != Some(libc::ENOSYS) && err.raw_os_error() != Some(libc::EPERM) -} - -fn main() { - // pidfds require the clone3 syscall - if !has_clone3() { - return; - } - - // We don't assert the precise value, since the standard library - // might have opened other file descriptors before our code runs. - let _ = Command::new("echo") - .create_pidfd(true) - .spawn() - .unwrap() - .pidfd().expect("failed to obtain pidfd"); - - let _ = Command::new("echo") - .create_pidfd(false) - .spawn() - .unwrap() - .pidfd().expect_err("pidfd should not have been created when create_pid(false) is set"); - - let _ = Command::new("echo") - .spawn() - .unwrap() - .pidfd().expect_err("pidfd should not have been created"); -} diff --git a/tests/ui/const-generics/issues/issue-61336-2.stderr b/tests/ui/const-generics/issues/issue-61336-2.stderr index 0af6f87a68df..9064c2d0b94f 100644 --- a/tests/ui/const-generics/issues/issue-61336-2.stderr +++ b/tests/ui/const-generics/issues/issue-61336-2.stderr @@ -5,6 +5,8 @@ LL | [x; { N }] | ^ the trait `Copy` is not implemented for `T` | = note: the `Copy` trait is required because this value will be copied for each element of the array + = help: consider using `core::array::from_fn` to initialize the array + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information help: consider restricting type parameter `T` | LL | fn g(x: T) -> [T; N] { diff --git a/tests/ui/const-generics/issues/issue-61336.stderr b/tests/ui/const-generics/issues/issue-61336.stderr index 4132e511240a..9935d6c16891 100644 --- a/tests/ui/const-generics/issues/issue-61336.stderr +++ b/tests/ui/const-generics/issues/issue-61336.stderr @@ -5,6 +5,8 @@ LL | [x; N] | ^ the trait `Copy` is not implemented for `T` | = note: the `Copy` trait is required because this value will be copied for each element of the array + = help: consider using `core::array::from_fn` to initialize the array + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information help: consider restricting type parameter `T` | LL | fn g(x: T) -> [T; N] { diff --git a/tests/ui/consts/const-address-of-mut.rs b/tests/ui/consts/const-address-of-mut.rs index 3788088b810b..5f0c76d62855 100644 --- a/tests/ui/consts/const-address-of-mut.rs +++ b/tests/ui/consts/const-address-of-mut.rs @@ -1,14 +1,14 @@ #![feature(raw_ref_op)] -const A: () = { let mut x = 2; &raw mut x; }; //~ mutable reference +const A: () = { let mut x = 2; &raw mut x; }; //~ mutable pointer -static B: () = { let mut x = 2; &raw mut x; }; //~ mutable reference +static B: () = { let mut x = 2; &raw mut x; }; //~ mutable pointer -static mut C: () = { let mut x = 2; &raw mut x; }; //~ mutable reference +static mut C: () = { let mut x = 2; &raw mut x; }; //~ mutable pointer const fn foo() { let mut x = 0; - let y = &raw mut x; //~ mutable reference + let y = &raw mut x; //~ mutable pointer } fn main() {} diff --git a/tests/ui/consts/const-address-of-mut.stderr b/tests/ui/consts/const-address-of-mut.stderr index 2a69bb8be97d..1b371fcee984 100644 --- a/tests/ui/consts/const-address-of-mut.stderr +++ b/tests/ui/consts/const-address-of-mut.stderr @@ -1,4 +1,4 @@ -error[E0658]: raw mutable references are not allowed in constants +error[E0658]: raw mutable pointers are not allowed in constants --> $DIR/const-address-of-mut.rs:3:32 | LL | const A: () = { let mut x = 2; &raw mut x; }; @@ -8,7 +8,7 @@ LL | const A: () = { let mut x = 2; &raw mut x; }; = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: raw mutable references are not allowed in statics +error[E0658]: raw mutable pointers are not allowed in statics --> $DIR/const-address-of-mut.rs:5:33 | LL | static B: () = { let mut x = 2; &raw mut x; }; @@ -18,7 +18,7 @@ LL | static B: () = { let mut x = 2; &raw mut x; }; = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: raw mutable references are not allowed in statics +error[E0658]: raw mutable pointers are not allowed in statics --> $DIR/const-address-of-mut.rs:7:37 | LL | static mut C: () = { let mut x = 2; &raw mut x; }; @@ -28,7 +28,7 @@ LL | static mut C: () = { let mut x = 2; &raw mut x; }; = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: raw mutable references are not allowed in constant functions +error[E0658]: raw mutable pointers are not allowed in constant functions --> $DIR/const-address-of-mut.rs:11:13 | LL | let y = &raw mut x; diff --git a/tests/ui/consts/const-blocks/migrate-fail.stderr b/tests/ui/consts/const-blocks/migrate-fail.stderr index d1896f755d53..95fece0ae8ae 100644 --- a/tests/ui/consts/const-blocks/migrate-fail.stderr +++ b/tests/ui/consts/const-blocks/migrate-fail.stderr @@ -6,6 +6,8 @@ LL | let arr: [Option; 2] = [x; 2]; | = note: required for `Option` to implement `Copy` = note: the `Copy` trait is required because this value will be copied for each element of the array + = help: consider using `core::array::from_fn` to initialize the array + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information help: consider annotating `Bar` with `#[derive(Copy)]` | LL + #[derive(Copy)] @@ -20,6 +22,8 @@ LL | let arr: [Option; 2] = [x; 2]; | = note: required for `Option` to implement `Copy` = note: the `Copy` trait is required because this value will be copied for each element of the array + = help: consider using `core::array::from_fn` to initialize the array + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information help: consider annotating `Bar` with `#[derive(Copy)]` | LL + #[derive(Copy)] diff --git a/tests/ui/consts/const-blocks/nll-fail.stderr b/tests/ui/consts/const-blocks/nll-fail.stderr index 807c964a51d6..ed1dc78f77db 100644 --- a/tests/ui/consts/const-blocks/nll-fail.stderr +++ b/tests/ui/consts/const-blocks/nll-fail.stderr @@ -6,6 +6,8 @@ LL | let arr: [Option; 2] = [x; 2]; | = note: required for `Option` to implement `Copy` = note: the `Copy` trait is required because this value will be copied for each element of the array + = help: consider using `core::array::from_fn` to initialize the array + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information help: consider annotating `Bar` with `#[derive(Copy)]` | LL + #[derive(Copy)] @@ -20,6 +22,8 @@ LL | let arr: [Option; 2] = [x; 2]; | = note: required for `Option` to implement `Copy` = note: the `Copy` trait is required because this value will be copied for each element of the array + = help: consider using `core::array::from_fn` to initialize the array + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information help: consider annotating `Bar` with `#[derive(Copy)]` | LL + #[derive(Copy)] diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient.rs b/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient.rs index 0a8fc7bcaac5..6bc5faac532c 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient.rs +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient.rs @@ -5,6 +5,7 @@ use std::intrinsics; const FOO: &i32 = foo(); +const FOO_RAW: *const i32 = foo(); const fn foo() -> &'static i32 { let t = unsafe { @@ -15,5 +16,6 @@ const fn foo() -> &'static i32 { unsafe { &*t } } fn main() { - assert_eq!(*FOO, 20) + assert_eq!(*FOO, 20); + assert_eq!(unsafe { *FOO_RAW }, 20); } diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient_fail.rs b/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient_fail.rs deleted file mode 100644 index 9ae906bbb739..000000000000 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient_fail.rs +++ /dev/null @@ -1,18 +0,0 @@ -#![feature(core_intrinsics)] -#![feature(const_heap)] -#![feature(const_mut_refs)] -use std::intrinsics; - -const FOO: *const i32 = foo(); -//~^ ERROR unsupported untyped pointer in constant - -const fn foo() -> &'static i32 { - let t = unsafe { - let i = intrinsics::const_allocate(4, 4) as *mut i32; - *i = 20; - i - }; - unsafe { &*t } -} -fn main() { -} diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient_fail.stderr b/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient_fail.stderr deleted file mode 100644 index dfaecfbcd8f1..000000000000 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_nontransient_fail.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: unsupported untyped pointer in constant - --> $DIR/alloc_intrinsic_nontransient_fail.rs:6:1 - | -LL | const FOO: *const i32 = foo(); - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: memory only reachable via raw pointers is not supported - -error: aborting due to 1 previous error - diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.rs b/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.rs index 1354b3c33b31..105e8e38d84e 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.rs +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.rs @@ -4,6 +4,6 @@ use std::intrinsics; const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 }; -//~^ error: unsupported untyped pointer in constant +//~^ error: mutable pointer in final value of constant fn main() {} diff --git a/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.stderr b/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.stderr index 3903ccd6ade7..bd82e6781be1 100644 --- a/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.stderr +++ b/tests/ui/consts/const-eval/heap/alloc_intrinsic_untyped.stderr @@ -1,10 +1,8 @@ -error: unsupported untyped pointer in constant +error: encountered mutable pointer in final value of constant --> $DIR/alloc_intrinsic_untyped.rs:6:1 | LL | const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 }; | ^^^^^^^^^^^^^^^^^^^ - | - = note: memory only reachable via raw pointers is not supported error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs index a717a5f82921..6ede6f5339ca 100644 --- a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs +++ b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs @@ -5,7 +5,7 @@ use std::intrinsics; const _X: &'static u8 = unsafe { - //~^ error: dangling pointer in final constant + //~^ error: dangling pointer in final value of constant let ptr = intrinsics::const_allocate(4, 4); intrinsics::const_deallocate(ptr, 4, 4); &*ptr diff --git a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr index 5f4630f6f4bc..59f5f79a95f3 100644 --- a/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr +++ b/tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.stderr @@ -1,4 +1,4 @@ -error: encountered dangling pointer in final constant +error: encountered dangling pointer in final value of constant --> $DIR/dealloc_intrinsic_dangling.rs:7:1 | LL | const _X: &'static u8 = unsafe { diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs b/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs index a7d329f125be..93197d5bce49 100644 --- a/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs +++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final.rs @@ -44,6 +44,21 @@ static mut FOO3: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); // the enclosing scope rule. const BAR: NotAMutex<&i32> = NotAMutex(UnsafeCell::new(&42)); +struct SyncPtr { x : *const T } +unsafe impl Sync for SyncPtr {} + +// These pass the lifetime checks because of the "tail expression" / "outer scope" rule. +// (This relies on `SyncPtr` being a curly brace struct.) +// However, we intern the inner memory as read-only, so this must be rejected. +static RAW_MUT_CAST_S: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; +//~^ ERROR mutable references are not allowed +static RAW_MUT_COERCE_S: SyncPtr = SyncPtr { x: &mut 0 }; +//~^ ERROR mutable references are not allowed +const RAW_MUT_CAST_C: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; +//~^ ERROR mutable references are not allowed +const RAW_MUT_COERCE_C: SyncPtr = SyncPtr { x: &mut 0 }; +//~^ ERROR mutable references are not allowed + fn main() { println!("{}", unsafe { *A }); unsafe { *B = 4 } // Bad news diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr b/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr index 78c58b5ab092..59e6aa4011c2 100644 --- a/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr +++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr @@ -54,7 +54,31 @@ LL | static mut FOO3: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42)); | | creates a temporary value which is freed while still in use | using this value as a static requires that borrow lasts for `'static` -error: aborting due to 6 previous errors +error[E0764]: mutable references are not allowed in the final value of statics + --> $DIR/mut_ref_in_final.rs:53:53 + | +LL | static RAW_MUT_CAST_S: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; + | ^^^^^^^ + +error[E0764]: mutable references are not allowed in the final value of statics + --> $DIR/mut_ref_in_final.rs:55:54 + | +LL | static RAW_MUT_COERCE_S: SyncPtr = SyncPtr { x: &mut 0 }; + | ^^^^^^ + +error[E0764]: mutable references are not allowed in the final value of constants + --> $DIR/mut_ref_in_final.rs:57:52 + | +LL | const RAW_MUT_CAST_C: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; + | ^^^^^^^ + +error[E0764]: mutable references are not allowed in the final value of constants + --> $DIR/mut_ref_in_final.rs:59:53 + | +LL | const RAW_MUT_COERCE_C: SyncPtr = SyncPtr { x: &mut 0 }; + | ^^^^^^ + +error: aborting due to 10 previous errors Some errors have detailed explanations: E0716, E0764. For more information about an error, try `rustc --explain E0716`. diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.32bit.stderr b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.32bit.stderr index 33d4fec70166..87420a037514 100644 --- a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.32bit.stderr +++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.32bit.stderr @@ -1,16 +1,16 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/mut_ref_in_final_dynamic_check.rs:17:1 + --> $DIR/mut_ref_in_final_dynamic_check.rs:15:1 | LL | const A: Option<&mut i32> = helper(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered mutable reference in a `const` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered mutable reference in a `const` or `static` | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { 2a 00 00 00 │ *... } -error: encountered dangling pointer in final constant - --> $DIR/mut_ref_in_final_dynamic_check.rs:24:1 +error: encountered dangling pointer in final value of constant + --> $DIR/mut_ref_in_final_dynamic_check.rs:22:1 | LL | const B: Option<&mut i32> = helper2(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.64bit.stderr b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.64bit.stderr index 9eb2675856f3..fc68207512c0 100644 --- a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.64bit.stderr +++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.64bit.stderr @@ -1,16 +1,16 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/mut_ref_in_final_dynamic_check.rs:17:1 + --> $DIR/mut_ref_in_final_dynamic_check.rs:15:1 | LL | const A: Option<&mut i32> = helper(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered mutable reference in a `const` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0: encountered mutable reference in a `const` or `static` | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { 2a 00 00 00 00 00 00 00 │ *....... } -error: encountered dangling pointer in final constant - --> $DIR/mut_ref_in_final_dynamic_check.rs:24:1 +error: encountered dangling pointer in final value of constant + --> $DIR/mut_ref_in_final_dynamic_check.rs:22:1 | LL | const B: Option<&mut i32> = helper2(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs index 22e7a74e5fba..455b557b97c4 100644 --- a/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs +++ b/tests/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs @@ -12,8 +12,6 @@ const fn helper() -> Option<&'static mut i32> { unsafe { // Undefined behaviour (integer as pointer), who doesn't love tests like this. Some(&mut *(42 as *mut i32)) } } -// The error is an evaluation error and not a validation error, so the error is reported -// directly at the site where it occurs. const A: Option<&mut i32> = helper(); //~ ERROR it is undefined behavior to use this value //~^ encountered mutable reference in a `const` @@ -21,6 +19,6 @@ const fn helper2() -> Option<&'static mut i32> { unsafe { // Undefined behaviour (dangling pointer), who doesn't love tests like this. Some(&mut *(&mut 42 as *mut i32)) } } -const B: Option<&mut i32> = helper2(); //~ ERROR encountered dangling pointer in final constant +const B: Option<&mut i32> = helper2(); //~ ERROR encountered dangling pointer in final value of constant fn main() {} diff --git a/tests/ui/consts/const-points-to-static.32bit.stderr b/tests/ui/consts/const-points-to-static.32bit.stderr deleted file mode 100644 index 73fbf5e1837e..000000000000 --- a/tests/ui/consts/const-points-to-static.32bit.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/const-points-to-static.rs:6:1 - | -LL | const TEST: &u8 = &MY_STATIC; - | ^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: 4, align: 4) { - ╾ALLOC0╼ │ ╾──╼ - } - -warning: skipping const checks - | -help: skipping check that does not even have a feature gate - --> $DIR/const-points-to-static.rs:6:20 - | -LL | const TEST: &u8 = &MY_STATIC; - | ^^^^^^^^^ - -error: aborting due to 1 previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-points-to-static.64bit.stderr b/tests/ui/consts/const-points-to-static.64bit.stderr deleted file mode 100644 index 42088bbb2c4a..000000000000 --- a/tests/ui/consts/const-points-to-static.64bit.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/const-points-to-static.rs:6:1 - | -LL | const TEST: &u8 = &MY_STATIC; - | ^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: 8, align: 8) { - ╾ALLOC0╼ │ ╾──────╼ - } - -warning: skipping const checks - | -help: skipping check that does not even have a feature gate - --> $DIR/const-points-to-static.rs:6:20 - | -LL | const TEST: &u8 = &MY_STATIC; - | ^^^^^^^^^ - -error: aborting due to 1 previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-points-to-static.rs b/tests/ui/consts/const-points-to-static.rs deleted file mode 100644 index ca825a1f5ed6..000000000000 --- a/tests/ui/consts/const-points-to-static.rs +++ /dev/null @@ -1,13 +0,0 @@ -// compile-flags: -Zunleash-the-miri-inside-of-you -// stderr-per-bitwidth - -#![allow(dead_code)] - -const TEST: &u8 = &MY_STATIC; -//~^ ERROR it is undefined behavior to use this value -//~| encountered a reference pointing to a static variable - -static MY_STATIC: u8 = 4; - -fn main() { -} diff --git a/tests/ui/consts/dangling-alloc-id-ice.rs b/tests/ui/consts/dangling-alloc-id-ice.rs index d591bfc731ce..d9f458f3770a 100644 --- a/tests/ui/consts/dangling-alloc-id-ice.rs +++ b/tests/ui/consts/dangling-alloc-id-ice.rs @@ -6,7 +6,7 @@ union Foo<'a> { } const FOO: &() = { -//~^ ERROR encountered dangling pointer in final constant +//~^ ERROR encountered dangling pointer in final value of constant let y = (); unsafe { Foo { y: &y }.long_live_the_unit } }; diff --git a/tests/ui/consts/dangling-alloc-id-ice.stderr b/tests/ui/consts/dangling-alloc-id-ice.stderr index e14204c09e54..8322b18d692d 100644 --- a/tests/ui/consts/dangling-alloc-id-ice.stderr +++ b/tests/ui/consts/dangling-alloc-id-ice.stderr @@ -1,4 +1,4 @@ -error: encountered dangling pointer in final constant +error: encountered dangling pointer in final value of constant --> $DIR/dangling-alloc-id-ice.rs:8:1 | LL | const FOO: &() = { diff --git a/tests/ui/consts/dangling_raw_ptr.rs b/tests/ui/consts/dangling_raw_ptr.rs index ddd1fb1ba76e..0ab3643f1216 100644 --- a/tests/ui/consts/dangling_raw_ptr.rs +++ b/tests/ui/consts/dangling_raw_ptr.rs @@ -1,4 +1,4 @@ -const FOO: *const u32 = { //~ ERROR encountered dangling pointer in final constant +const FOO: *const u32 = { //~ ERROR encountered dangling pointer in final value of constant let x = 42; &x }; diff --git a/tests/ui/consts/dangling_raw_ptr.stderr b/tests/ui/consts/dangling_raw_ptr.stderr index 5a428355034a..28a58ae7be29 100644 --- a/tests/ui/consts/dangling_raw_ptr.stderr +++ b/tests/ui/consts/dangling_raw_ptr.stderr @@ -1,4 +1,4 @@ -error: encountered dangling pointer in final constant +error: encountered dangling pointer in final value of constant --> $DIR/dangling_raw_ptr.rs:1:1 | LL | const FOO: *const u32 = { diff --git a/tests/ui/consts/invalid-union.32bit.stderr b/tests/ui/consts/interior-mut-const-via-union.32bit.stderr similarity index 82% rename from tests/ui/consts/invalid-union.32bit.stderr rename to tests/ui/consts/interior-mut-const-via-union.32bit.stderr index 177e4f03e837..e9e9f1a4545d 100644 --- a/tests/ui/consts/invalid-union.32bit.stderr +++ b/tests/ui/consts/interior-mut-const-via-union.32bit.stderr @@ -1,8 +1,8 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/invalid-union.rs:35:1 + --> $DIR/interior-mut-const-via-union.rs:35:1 | LL | fn main() { - | ^^^^^^^^^ constructing invalid value at ..y..0: encountered `UnsafeCell` in a `const` + | ^^^^^^^^^ constructing invalid value at ..y..0: encountered `UnsafeCell` in read-only memory | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { @@ -10,13 +10,13 @@ LL | fn main() { } note: erroneous constant encountered - --> $DIR/invalid-union.rs:37:25 + --> $DIR/interior-mut-const-via-union.rs:37:25 | LL | let _: &'static _ = &C; | ^^ note: erroneous constant encountered - --> $DIR/invalid-union.rs:37:25 + --> $DIR/interior-mut-const-via-union.rs:37:25 | LL | let _: &'static _ = &C; | ^^ diff --git a/tests/ui/consts/invalid-union.64bit.stderr b/tests/ui/consts/interior-mut-const-via-union.64bit.stderr similarity index 82% rename from tests/ui/consts/invalid-union.64bit.stderr rename to tests/ui/consts/interior-mut-const-via-union.64bit.stderr index 09c648c3cda3..9cc98975ca9b 100644 --- a/tests/ui/consts/invalid-union.64bit.stderr +++ b/tests/ui/consts/interior-mut-const-via-union.64bit.stderr @@ -1,8 +1,8 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/invalid-union.rs:35:1 + --> $DIR/interior-mut-const-via-union.rs:35:1 | LL | fn main() { - | ^^^^^^^^^ constructing invalid value at ..y..0: encountered `UnsafeCell` in a `const` + | ^^^^^^^^^ constructing invalid value at ..y..0: encountered `UnsafeCell` in read-only memory | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { @@ -10,13 +10,13 @@ LL | fn main() { } note: erroneous constant encountered - --> $DIR/invalid-union.rs:37:25 + --> $DIR/interior-mut-const-via-union.rs:37:25 | LL | let _: &'static _ = &C; | ^^ note: erroneous constant encountered - --> $DIR/invalid-union.rs:37:25 + --> $DIR/interior-mut-const-via-union.rs:37:25 | LL | let _: &'static _ = &C; | ^^ diff --git a/tests/ui/consts/invalid-union.rs b/tests/ui/consts/interior-mut-const-via-union.rs similarity index 100% rename from tests/ui/consts/invalid-union.rs rename to tests/ui/consts/interior-mut-const-via-union.rs diff --git a/tests/ui/consts/min_const_fn/address_of.rs b/tests/ui/consts/min_const_fn/address_of.rs index 40d1882d7d2a..aa75423ca4d6 100644 --- a/tests/ui/consts/min_const_fn/address_of.rs +++ b/tests/ui/consts/min_const_fn/address_of.rs @@ -2,7 +2,7 @@ const fn mutable_address_of_in_const() { let mut a = 0; - let b = &raw mut a; //~ ERROR mutable reference + let b = &raw mut a; //~ ERROR mutable pointer } struct X; @@ -10,7 +10,7 @@ struct X; impl X { const fn inherent_mutable_address_of_in_const() { let mut a = 0; - let b = &raw mut a; //~ ERROR mutable reference + let b = &raw mut a; //~ ERROR mutable pointer } } diff --git a/tests/ui/consts/min_const_fn/address_of.stderr b/tests/ui/consts/min_const_fn/address_of.stderr index 4c23ba6cd519..143760c0943d 100644 --- a/tests/ui/consts/min_const_fn/address_of.stderr +++ b/tests/ui/consts/min_const_fn/address_of.stderr @@ -1,4 +1,4 @@ -error[E0658]: raw mutable references are not allowed in constant functions +error[E0658]: raw mutable pointers are not allowed in constant functions --> $DIR/address_of.rs:5:13 | LL | let b = &raw mut a; @@ -8,7 +8,7 @@ LL | let b = &raw mut a; = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: raw mutable references are not allowed in constant functions +error[E0658]: raw mutable pointers are not allowed in constant functions --> $DIR/address_of.rs:13:17 | LL | let b = &raw mut a; diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static.32bit.stderr b/tests/ui/consts/miri_unleashed/const_refers_to_static.32bit.stderr index ab33b0c00c90..5fe8e250df91 100644 --- a/tests/ui/consts/miri_unleashed/const_refers_to_static.32bit.stderr +++ b/tests/ui/consts/miri_unleashed/const_refers_to_static.32bit.stderr @@ -38,6 +38,17 @@ LL | const READ_IMMUT: &usize = { ╾ALLOC1╼ │ ╾──╼ } +error[E0080]: it is undefined behavior to use this value + --> $DIR/const_refers_to_static.rs:34:1 + | +LL | const REF_IMMUT: &u8 = &MY_STATIC; + | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾ALLOC2╼ │ ╾──╼ + } + warning: skipping const checks | help: skipping check that does not even have a feature gate @@ -75,7 +86,12 @@ help: skipping check that does not even have a feature gate | LL | &FOO | ^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static.rs:34:25 + | +LL | const REF_IMMUT: &u8 = &MY_STATIC; + | ^^^^^^^^^ -error: aborting due to 5 previous errors; 1 warning emitted +error: aborting due to 6 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static.64bit.stderr b/tests/ui/consts/miri_unleashed/const_refers_to_static.64bit.stderr index f1f58d9ca6d6..a80b07056a30 100644 --- a/tests/ui/consts/miri_unleashed/const_refers_to_static.64bit.stderr +++ b/tests/ui/consts/miri_unleashed/const_refers_to_static.64bit.stderr @@ -38,6 +38,17 @@ LL | const READ_IMMUT: &usize = { ╾ALLOC1╼ │ ╾──────╼ } +error[E0080]: it is undefined behavior to use this value + --> $DIR/const_refers_to_static.rs:34:1 + | +LL | const REF_IMMUT: &u8 = &MY_STATIC; + | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾ALLOC2╼ │ ╾──────╼ + } + warning: skipping const checks | help: skipping check that does not even have a feature gate @@ -75,7 +86,12 @@ help: skipping check that does not even have a feature gate | LL | &FOO | ^^^ +help: skipping check that does not even have a feature gate + --> $DIR/const_refers_to_static.rs:34:25 + | +LL | const REF_IMMUT: &u8 = &MY_STATIC; + | ^^^^^^^^^ -error: aborting due to 5 previous errors; 1 warning emitted +error: aborting due to 6 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static.rs b/tests/ui/consts/miri_unleashed/const_refers_to_static.rs index 7ed5a48d996c..df2563d8d7f2 100644 --- a/tests/ui/consts/miri_unleashed/const_refers_to_static.rs +++ b/tests/ui/consts/miri_unleashed/const_refers_to_static.rs @@ -30,4 +30,9 @@ const READ_IMMUT: &usize = { //~ ERROR it is undefined behavior to use this valu &FOO }; +static MY_STATIC: u8 = 4; +const REF_IMMUT: &u8 = &MY_STATIC; +//~^ ERROR it is undefined behavior to use this value +//~| encountered a reference pointing to a static variable + fn main() {} diff --git a/tests/ui/consts/miri_unleashed/mutable_references_err.32bit.stderr b/tests/ui/consts/miri_unleashed/mutable_references_err.32bit.stderr index c9da91a95977..2df80020fdc9 100644 --- a/tests/ui/consts/miri_unleashed/mutable_references_err.32bit.stderr +++ b/tests/ui/consts/miri_unleashed/mutable_references_err.32bit.stderr @@ -1,54 +1,199 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references_err.rs:15:1 +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:17:1 | LL | const MUH: Meh = Meh { - | ^^^^^^^^^^^^^^ constructing invalid value at .x.: encountered `UnsafeCell` in a `const` + | ^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:27:1 + | +LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0080]: it is undefined behavior to use this value + --> $DIR/mutable_references_err.rs:32:1 + | +LL | const SUBTLE: &mut i32 = unsafe { &mut FOO }; + | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference in a `const` or `static` | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { ╾ALLOC0╼ │ ╾──╼ } -error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references_err.rs:25:1 - | -LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; - | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ...x: encountered `UnsafeCell` in a `const` - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC1╼ ╾ALLOC2╼ │ ╾──╼╾──╼ - } - -error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references_err.rs:29:1 +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:35:1 | LL | const BLUNT: &mut i32 = &mut 42; - | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference in a `const` + | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0080]: it is undefined behavior to use this value + --> $DIR/mutable_references_err.rs:40:1 + | +LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - ╾ALLOC3╼ │ ╾──╼ + ╾ALLOC1╼ │ ╾──╼ } +error[E0080]: it is undefined behavior to use this value + --> $DIR/mutable_references_err.rs:47:1 + | +LL | const POINTS_TO_MUTABLE1: &i32 = unsafe { &MUTABLE }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ╾ALLOC2╼ │ ╾──╼ + } + +error[E0080]: evaluation of constant value failed + --> $DIR/mutable_references_err.rs:51:43 + | +LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF }; + | ^^^^^^^^^^^^^ constant accesses static + +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:55:1 + | +LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:57:1 + | +LL | const POINTS_TO_MUTABLE_INNER2: *const i32 = &mut 42 as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:59:1 + | +LL | const INTERIOR_MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:69:1 + | +LL | const RAW_SYNC: SyncPtr = SyncPtr { x: &AtomicI32::new(42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:71:1 + | +LL | const RAW_MUT_CAST: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:73:1 + | +LL | const RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + warning: skipping const checks | help: skipping check that does not even have a feature gate - --> $DIR/mutable_references_err.rs:16:8 + --> $DIR/mutable_references_err.rs:18:8 | LL | x: &UnsafeCell::new(42), | ^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references_err.rs:25:27 + --> $DIR/mutable_references_err.rs:27:27 | LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references_err.rs:29:25 + --> $DIR/mutable_references_err.rs:32:40 + | +LL | const SUBTLE: &mut i32 = unsafe { &mut FOO }; + | ^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:32:40 + | +LL | const SUBTLE: &mut i32 = unsafe { &mut FOO }; + | ^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:32:35 + | +LL | const SUBTLE: &mut i32 = unsafe { &mut FOO }; + | ^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:35:25 | LL | const BLUNT: &mut i32 = &mut 42; | ^^^^^^^ +help: skipping check for `const_mut_refs` feature + --> $DIR/mutable_references_err.rs:40:49 + | +LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check for `const_mut_refs` feature + --> $DIR/mutable_references_err.rs:40:49 + | +LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:40:49 + | +LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:47:44 + | +LL | const POINTS_TO_MUTABLE1: &i32 = unsafe { &MUTABLE }; + | ^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:47:44 + | +LL | const POINTS_TO_MUTABLE1: &i32 = unsafe { &MUTABLE }; + | ^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:50:36 + | +LL | static mut MUTABLE_REF: &mut i32 = &mut 42; + | ^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:51:45 + | +LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF }; + | ^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:51:45 + | +LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF }; + | ^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:55:45 + | +LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _; + | ^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:57:46 + | +LL | const POINTS_TO_MUTABLE_INNER2: *const i32 = &mut 42 as *const _; + | ^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:59:47 + | +LL | const INTERIOR_MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; + | ^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:69:51 + | +LL | const RAW_SYNC: SyncPtr = SyncPtr { x: &AtomicI32::new(42) }; + | ^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:71:50 + | +LL | const RAW_MUT_CAST: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; + | ^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:73:51 + | +LL | const RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; + | ^^^^^^ -error: aborting due to 3 previous errors; 1 warning emitted +error: aborting due to 13 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/miri_unleashed/mutable_references_err.64bit.stderr b/tests/ui/consts/miri_unleashed/mutable_references_err.64bit.stderr index 71be616b7ed7..3ff6811ea61f 100644 --- a/tests/ui/consts/miri_unleashed/mutable_references_err.64bit.stderr +++ b/tests/ui/consts/miri_unleashed/mutable_references_err.64bit.stderr @@ -1,54 +1,199 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references_err.rs:15:1 +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:17:1 | LL | const MUH: Meh = Meh { - | ^^^^^^^^^^^^^^ constructing invalid value at .x.: encountered `UnsafeCell` in a `const` + | ^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:27:1 + | +LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0080]: it is undefined behavior to use this value + --> $DIR/mutable_references_err.rs:32:1 + | +LL | const SUBTLE: &mut i32 = unsafe { &mut FOO }; + | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference in a `const` or `static` | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { ╾ALLOC0╼ │ ╾──────╼ } -error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references_err.rs:25:1 - | -LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; - | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ...x: encountered `UnsafeCell` in a `const` - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: 16, align: 8) { - ╾ALLOC1╼ ╾ALLOC2╼ │ ╾──────╼╾──────╼ - } - -error[E0080]: it is undefined behavior to use this value - --> $DIR/mutable_references_err.rs:29:1 +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:35:1 | LL | const BLUNT: &mut i32 = &mut 42; - | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference in a `const` + | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0080]: it is undefined behavior to use this value + --> $DIR/mutable_references_err.rs:40:1 + | +LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered mutable reference or box pointing to read-only memory | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾ALLOC3╼ │ ╾──────╼ + ╾ALLOC1╼ │ ╾──────╼ } +error[E0080]: it is undefined behavior to use this value + --> $DIR/mutable_references_err.rs:47:1 + | +LL | const POINTS_TO_MUTABLE1: &i32 = unsafe { &MUTABLE }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a reference pointing to a static variable in a constant + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 8) { + ╾ALLOC2╼ │ ╾──────╼ + } + +error[E0080]: evaluation of constant value failed + --> $DIR/mutable_references_err.rs:51:43 + | +LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF }; + | ^^^^^^^^^^^^^ constant accesses static + +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:55:1 + | +LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:57:1 + | +LL | const POINTS_TO_MUTABLE_INNER2: *const i32 = &mut 42 as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:59:1 + | +LL | const INTERIOR_MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:69:1 + | +LL | const RAW_SYNC: SyncPtr = SyncPtr { x: &AtomicI32::new(42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:71:1 + | +LL | const RAW_MUT_CAST: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of constant + --> $DIR/mutable_references_err.rs:73:1 + | +LL | const RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + warning: skipping const checks | help: skipping check that does not even have a feature gate - --> $DIR/mutable_references_err.rs:16:8 + --> $DIR/mutable_references_err.rs:18:8 | LL | x: &UnsafeCell::new(42), | ^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references_err.rs:25:27 + --> $DIR/mutable_references_err.rs:27:27 | LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/mutable_references_err.rs:29:25 + --> $DIR/mutable_references_err.rs:32:40 + | +LL | const SUBTLE: &mut i32 = unsafe { &mut FOO }; + | ^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:32:40 + | +LL | const SUBTLE: &mut i32 = unsafe { &mut FOO }; + | ^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:32:35 + | +LL | const SUBTLE: &mut i32 = unsafe { &mut FOO }; + | ^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:35:25 | LL | const BLUNT: &mut i32 = &mut 42; | ^^^^^^^ +help: skipping check for `const_mut_refs` feature + --> $DIR/mutable_references_err.rs:40:49 + | +LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check for `const_mut_refs` feature + --> $DIR/mutable_references_err.rs:40:49 + | +LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:40:49 + | +LL | static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:47:44 + | +LL | const POINTS_TO_MUTABLE1: &i32 = unsafe { &MUTABLE }; + | ^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:47:44 + | +LL | const POINTS_TO_MUTABLE1: &i32 = unsafe { &MUTABLE }; + | ^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:50:36 + | +LL | static mut MUTABLE_REF: &mut i32 = &mut 42; + | ^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:51:45 + | +LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF }; + | ^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:51:45 + | +LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF }; + | ^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:55:45 + | +LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _; + | ^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:57:46 + | +LL | const POINTS_TO_MUTABLE_INNER2: *const i32 = &mut 42 as *const _; + | ^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:59:47 + | +LL | const INTERIOR_MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; + | ^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:69:51 + | +LL | const RAW_SYNC: SyncPtr = SyncPtr { x: &AtomicI32::new(42) }; + | ^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:71:50 + | +LL | const RAW_MUT_CAST: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; + | ^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/mutable_references_err.rs:73:51 + | +LL | const RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; + | ^^^^^^ -error: aborting due to 3 previous errors; 1 warning emitted +error: aborting due to 13 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/miri_unleashed/mutable_references_err.rs b/tests/ui/consts/miri_unleashed/mutable_references_err.rs index 6399b122bb1f..83a460dadd0e 100644 --- a/tests/ui/consts/miri_unleashed/mutable_references_err.rs +++ b/tests/ui/consts/miri_unleashed/mutable_references_err.rs @@ -1,6 +1,8 @@ // stderr-per-bitwidth // compile-flags: -Zunleash-the-miri-inside-of-you +#![allow(invalid_reference_casting, static_mut_ref)] +use std::sync::atomic::*; use std::cell::UnsafeCell; // this test ensures that our mutability story is sound @@ -12,7 +14,7 @@ unsafe impl Sync for Meh {} // the following will never be ok! no interior mut behind consts, because // all allocs interned here will be marked immutable. -const MUH: Meh = Meh { //~ ERROR: it is undefined behavior to use this value +const MUH: Meh = Meh { //~ ERROR: mutable pointer in final value x: &UnsafeCell::new(42), }; @@ -23,11 +25,53 @@ unsafe impl Sync for Synced {} // Make sure we also catch this behind a type-erased `dyn Trait` reference. const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; -//~^ ERROR: it is undefined behavior to use this value +//~^ ERROR: mutable pointer in final value -// Make sure we also catch mutable references. -const BLUNT: &mut i32 = &mut 42; +// Make sure we also catch mutable references in values that shouldn't have them. +static mut FOO: i32 = 0; +const SUBTLE: &mut i32 = unsafe { &mut FOO }; //~^ ERROR: it is undefined behavior to use this value +//~| static +const BLUNT: &mut i32 = &mut 42; +//~^ ERROR: mutable pointer in final value + +// Check for mutable references to read-only memory. +static READONLY: i32 = 0; +static mut MUT_TO_READONLY: &mut i32 = unsafe { &mut *(&READONLY as *const _ as *mut _) }; +//~^ ERROR: it is undefined behavior to use this value +//~| pointing to read-only memory + +// Check for consts pointing to mutable memory. +// Currently it's not even possible to create such a const. +static mut MUTABLE: i32 = 42; +const POINTS_TO_MUTABLE1: &i32 = unsafe { &MUTABLE }; +//~^ ERROR: undefined behavior to use this value +//~| pointing to a static +static mut MUTABLE_REF: &mut i32 = &mut 42; +const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF }; +//~^ ERROR: evaluation of constant value failed +//~| accesses static + +const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _; +//~^ ERROR: mutable pointer in final value +const POINTS_TO_MUTABLE_INNER2: *const i32 = &mut 42 as *const _; +//~^ ERROR: mutable pointer in final value +const INTERIOR_MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; +//~^ ERROR: mutable pointer in final value + +struct SyncPtr { x : *const T } +unsafe impl Sync for SyncPtr {} + +// These pass the lifetime checks because of the "tail expression" / "outer scope" rule. +// (This relies on `SyncPtr` being a curly brace struct.) +// However, we intern the inner memory as read-only, so this must be rejected. +// (Also see `static-no-inner-mut` for similar tests on `static`.) +const RAW_SYNC: SyncPtr = SyncPtr { x: &AtomicI32::new(42) }; +//~^ ERROR mutable pointer in final value +const RAW_MUT_CAST: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; +//~^ ERROR mutable pointer in final value +const RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; +//~^ ERROR mutable pointer in final value fn main() { unsafe { diff --git a/tests/ui/consts/miri_unleashed/raw_mutable_const.rs b/tests/ui/consts/miri_unleashed/raw_mutable_const.rs deleted file mode 100644 index adb1f8bf3ec5..000000000000 --- a/tests/ui/consts/miri_unleashed/raw_mutable_const.rs +++ /dev/null @@ -1,8 +0,0 @@ -// compile-flags: -Zunleash-the-miri-inside-of-you - -use std::cell::UnsafeCell; - -const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; -//~^ ERROR: unsupported untyped pointer in constant - -fn main() {} diff --git a/tests/ui/consts/miri_unleashed/raw_mutable_const.stderr b/tests/ui/consts/miri_unleashed/raw_mutable_const.stderr deleted file mode 100644 index f4abaecac5e1..000000000000 --- a/tests/ui/consts/miri_unleashed/raw_mutable_const.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error: unsupported untyped pointer in constant - --> $DIR/raw_mutable_const.rs:5:1 - | -LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: memory only reachable via raw pointers is not supported - -warning: skipping const checks - | -help: skipping check that does not even have a feature gate - --> $DIR/raw_mutable_const.rs:5:38 - | -LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error; 1 warning emitted - diff --git a/tests/ui/consts/miri_unleashed/static-no-inner-mut.32bit.stderr b/tests/ui/consts/miri_unleashed/static-no-inner-mut.32bit.stderr new file mode 100644 index 000000000000..e8ed6742fab3 --- /dev/null +++ b/tests/ui/consts/miri_unleashed/static-no-inner-mut.32bit.stderr @@ -0,0 +1,82 @@ +error: encountered mutable pointer in final value of static + --> $DIR/static-no-inner-mut.rs:9:1 + | +LL | static REF: &AtomicI32 = &AtomicI32::new(42); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of static + --> $DIR/static-no-inner-mut.rs:10:1 + | +LL | static REFMUT: &mut i32 = &mut 0; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of static + --> $DIR/static-no-inner-mut.rs:13:1 + | +LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}}; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of static + --> $DIR/static-no-inner-mut.rs:14:1 + | +LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}}; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of static + --> $DIR/static-no-inner-mut.rs:29:1 + | +LL | static RAW_SYNC: SyncPtr = SyncPtr { x: &AtomicI32::new(42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of static + --> $DIR/static-no-inner-mut.rs:31:1 + | +LL | static RAW_MUT_CAST: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of static + --> $DIR/static-no-inner-mut.rs:33:1 + | +LL | static RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/static-no-inner-mut.rs:9:26 + | +LL | static REF: &AtomicI32 = &AtomicI32::new(42); + | ^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/static-no-inner-mut.rs:10:27 + | +LL | static REFMUT: &mut i32 = &mut 0; + | ^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/static-no-inner-mut.rs:13:56 + | +LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}}; + | ^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/static-no-inner-mut.rs:14:44 + | +LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}}; + | ^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/static-no-inner-mut.rs:29:52 + | +LL | static RAW_SYNC: SyncPtr = SyncPtr { x: &AtomicI32::new(42) }; + | ^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/static-no-inner-mut.rs:31:51 + | +LL | static RAW_MUT_CAST: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; + | ^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/static-no-inner-mut.rs:33:52 + | +LL | static RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; + | ^^^^^^ + +error: aborting due to 7 previous errors; 1 warning emitted + diff --git a/tests/ui/consts/miri_unleashed/static-no-inner-mut.64bit.stderr b/tests/ui/consts/miri_unleashed/static-no-inner-mut.64bit.stderr new file mode 100644 index 000000000000..e8ed6742fab3 --- /dev/null +++ b/tests/ui/consts/miri_unleashed/static-no-inner-mut.64bit.stderr @@ -0,0 +1,82 @@ +error: encountered mutable pointer in final value of static + --> $DIR/static-no-inner-mut.rs:9:1 + | +LL | static REF: &AtomicI32 = &AtomicI32::new(42); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of static + --> $DIR/static-no-inner-mut.rs:10:1 + | +LL | static REFMUT: &mut i32 = &mut 0; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of static + --> $DIR/static-no-inner-mut.rs:13:1 + | +LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}}; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of static + --> $DIR/static-no-inner-mut.rs:14:1 + | +LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}}; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of static + --> $DIR/static-no-inner-mut.rs:29:1 + | +LL | static RAW_SYNC: SyncPtr = SyncPtr { x: &AtomicI32::new(42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of static + --> $DIR/static-no-inner-mut.rs:31:1 + | +LL | static RAW_MUT_CAST: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered mutable pointer in final value of static + --> $DIR/static-no-inner-mut.rs:33:1 + | +LL | static RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/static-no-inner-mut.rs:9:26 + | +LL | static REF: &AtomicI32 = &AtomicI32::new(42); + | ^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/static-no-inner-mut.rs:10:27 + | +LL | static REFMUT: &mut i32 = &mut 0; + | ^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/static-no-inner-mut.rs:13:56 + | +LL | static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}}; + | ^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/static-no-inner-mut.rs:14:44 + | +LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}}; + | ^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/static-no-inner-mut.rs:29:52 + | +LL | static RAW_SYNC: SyncPtr = SyncPtr { x: &AtomicI32::new(42) }; + | ^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/static-no-inner-mut.rs:31:51 + | +LL | static RAW_MUT_CAST: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; + | ^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/static-no-inner-mut.rs:33:52 + | +LL | static RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; + | ^^^^^^ + +error: aborting due to 7 previous errors; 1 warning emitted + diff --git a/tests/ui/consts/miri_unleashed/static-no-inner-mut.rs b/tests/ui/consts/miri_unleashed/static-no-inner-mut.rs new file mode 100644 index 000000000000..a4033eb56834 --- /dev/null +++ b/tests/ui/consts/miri_unleashed/static-no-inner-mut.rs @@ -0,0 +1,36 @@ +// stderr-per-bitwidth +// compile-flags: -Zunleash-the-miri-inside-of-you +#![feature(const_refs_to_cell, const_mut_refs)] +// All "inner" allocations that come with a `static` are interned immutably. This means it is +// crucial that we do not accept any form of (interior) mutability there. + +use std::sync::atomic::*; + +static REF: &AtomicI32 = &AtomicI32::new(42); //~ERROR mutable pointer in final value +static REFMUT: &mut i32 = &mut 0; //~ERROR mutable pointer in final value + +// Different way of writing this that avoids promotion. +static REF2: &AtomicI32 = {let x = AtomicI32::new(42); &{x}}; //~ERROR mutable pointer in final value +static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}}; //~ERROR mutable pointer in final value + +// This one is obvious, since it is non-Sync. (It also suppresses the other errors, so it is +// commented out.) +// static RAW: *const AtomicI32 = &AtomicI32::new(42); + +struct SyncPtr { x : *const T } +unsafe impl Sync for SyncPtr {} + +// All of these pass the lifetime checks because of the "tail expression" / "outer scope" rule. +// (This relies on `SyncPtr` being a curly brace struct.) +// Then they get interned immutably, which is not great. +// `mut_ref_in_final.rs` and `std/cell.rs` ensure that we don't accept this even with the feature +// fate, but for unleashed Miri there's not really any way we can reject them: it's just +// non-dangling raw pointers. +static RAW_SYNC: SyncPtr = SyncPtr { x: &AtomicI32::new(42) }; +//~^ ERROR mutable pointer in final value +static RAW_MUT_CAST: SyncPtr = SyncPtr { x : &mut 42 as *mut _ as *const _ }; +//~^ ERROR mutable pointer in final value +static RAW_MUT_COERCE: SyncPtr = SyncPtr { x: &mut 0 }; +//~^ ERROR mutable pointer in final value + +fn main() {} diff --git a/tests/ui/consts/raw-ptr-const.rs b/tests/ui/consts/raw-ptr-const.rs index 541c5fd1ab13..24a77db9ffc5 100644 --- a/tests/ui/consts/raw-ptr-const.rs +++ b/tests/ui/consts/raw-ptr-const.rs @@ -1,8 +1,8 @@ +// check-pass + // This is a regression test for a `span_delayed_bug` during interning when a constant -// evaluates to a (non-dangling) raw pointer. For now this errors; potentially it -// could also be allowed. +// evaluates to a (non-dangling) raw pointer. const CONST_RAW: *const Vec = &Vec::new() as *const _; -//~^ ERROR unsupported untyped pointer in constant fn main() {} diff --git a/tests/ui/consts/raw-ptr-const.stderr b/tests/ui/consts/raw-ptr-const.stderr deleted file mode 100644 index 9aef78505c08..000000000000 --- a/tests/ui/consts/raw-ptr-const.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: unsupported untyped pointer in constant - --> $DIR/raw-ptr-const.rs:5:1 - | -LL | const CONST_RAW: *const Vec = &Vec::new() as *const _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: memory only reachable via raw pointers is not supported - -error: aborting due to 1 previous error - diff --git a/tests/ui/consts/raw-ptr-temp-const.rs b/tests/ui/consts/raw-ptr-temp-const.rs new file mode 100644 index 000000000000..deb51cd505c7 --- /dev/null +++ b/tests/ui/consts/raw-ptr-temp-const.rs @@ -0,0 +1,6 @@ +// A variant of raw-ptr-const that directly constructs a raw pointer. + +const CONST_RAW: *const Vec = std::ptr::addr_of!(Vec::new()); +//~^ ERROR cannot take address of a temporary + +fn main() {} diff --git a/tests/ui/consts/raw-ptr-temp-const.stderr b/tests/ui/consts/raw-ptr-temp-const.stderr new file mode 100644 index 000000000000..601671f71aec --- /dev/null +++ b/tests/ui/consts/raw-ptr-temp-const.stderr @@ -0,0 +1,9 @@ +error[E0745]: cannot take address of a temporary + --> $DIR/raw-ptr-temp-const.rs:3:55 + | +LL | const CONST_RAW: *const Vec = std::ptr::addr_of!(Vec::new()); + | ^^^^^^^^^^ temporary value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0745`. diff --git a/tests/ui/consts/refs-to-cell-in-final.rs b/tests/ui/consts/refs-to-cell-in-final.rs new file mode 100644 index 000000000000..6a849ff0df92 --- /dev/null +++ b/tests/ui/consts/refs-to-cell-in-final.rs @@ -0,0 +1,18 @@ +#![feature(const_refs_to_cell)] + +use std::cell::*; + +struct SyncPtr { x : *const T } +unsafe impl Sync for SyncPtr {} + +// These pass the lifetime checks because of the "tail expression" / "outer scope" rule. +// (This relies on `SyncPtr` being a curly brace struct.) +// However, we intern the inner memory as read-only. +// The resulting constant would pass all validation checks, so it is crucial that this gets rejected +// by static const checks! +static RAW_SYNC_S: SyncPtr> = SyncPtr { x: &Cell::new(42) }; +//~^ ERROR: cannot refer to interior mutable data +const RAW_SYNC_C: SyncPtr> = SyncPtr { x: &Cell::new(42) }; +//~^ ERROR: cannot refer to interior mutable data + +fn main() {} diff --git a/tests/ui/consts/refs-to-cell-in-final.stderr b/tests/ui/consts/refs-to-cell-in-final.stderr new file mode 100644 index 000000000000..fae16fa01251 --- /dev/null +++ b/tests/ui/consts/refs-to-cell-in-final.stderr @@ -0,0 +1,17 @@ +error[E0492]: statics cannot refer to interior mutable data + --> $DIR/refs-to-cell-in-final.rs:13:54 + | +LL | static RAW_SYNC_S: SyncPtr> = SyncPtr { x: &Cell::new(42) }; + | ^^^^^^^^^^^^^^ this borrow of an interior mutable value may end up in the final value + | + = help: to fix this, the value can be extracted to a separate `static` item and then referenced + +error[E0492]: constants cannot refer to interior mutable data + --> $DIR/refs-to-cell-in-final.rs:15:53 + | +LL | const RAW_SYNC_C: SyncPtr> = SyncPtr { x: &Cell::new(42) }; + | ^^^^^^^^^^^^^^ this borrow of an interior mutable value may end up in the final value + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0492`. diff --git a/tests/ui/consts/std/cell.stderr b/tests/ui/consts/std/cell.stderr index 937fa7db0c8c..873b797a466d 100644 --- a/tests/ui/consts/std/cell.stderr +++ b/tests/ui/consts/std/cell.stderr @@ -1,22 +1,22 @@ -error: encountered dangling pointer in final constant +error: encountered dangling pointer in final value of static --> $DIR/cell.rs:6:1 | LL | static FOO: Wrap<*mut u32> = Wrap(Cell::new(42).as_ptr()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: encountered dangling pointer in final constant +error: encountered dangling pointer in final value of constant --> $DIR/cell.rs:8:1 | LL | const FOO_CONST: Wrap<*mut u32> = Wrap(Cell::new(42).as_ptr()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: encountered dangling pointer in final constant +error: encountered dangling pointer in final value of constant --> $DIR/cell.rs:22:1 | LL | const FOO4_CONST: Wrap<*mut u32> = Wrap(FOO3_CONST.0.as_ptr()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: encountered dangling pointer in final constant +error: encountered dangling pointer in final value of constant --> $DIR/cell.rs:27:1 | LL | const FOO2: *mut u32 = Cell::new(42).as_ptr(); diff --git a/tests/ui/coroutine/print/coroutine-print-verbose-2.stderr b/tests/ui/coroutine/print/coroutine-print-verbose-2.stderr index e9c7a8ffcaab..165302ab1406 100644 --- a/tests/ui/coroutine/print/coroutine-print-verbose-2.stderr +++ b/tests/ui/coroutine/print/coroutine-print-verbose-2.stderr @@ -4,7 +4,7 @@ error: coroutine cannot be shared between threads safely LL | assert_sync(|| { | ^^^^^^^^^^^ coroutine is not `Sync` | - = help: within `{main::{closure#0} upvar_tys=() {main::{closure#0}}}`, the trait `Sync` is not implemented for `NotSync` + = help: within `{main::{closure#0} upvar_tys=() witness={main::{closure#0}}}`, the trait `Sync` is not implemented for `NotSync` note: coroutine is not `Sync` as this value is used across a yield --> $DIR/coroutine-print-verbose-2.rs:20:9 | @@ -24,7 +24,7 @@ error: coroutine cannot be sent between threads safely LL | assert_send(|| { | ^^^^^^^^^^^ coroutine is not `Send` | - = help: within `{main::{closure#1} upvar_tys=() {main::{closure#1}}}`, the trait `Send` is not implemented for `NotSend` + = help: within `{main::{closure#1} upvar_tys=() witness={main::{closure#1}}}`, the trait `Send` is not implemented for `NotSend` note: coroutine is not `Send` as this value is used across a yield --> $DIR/coroutine-print-verbose-2.rs:27:9 | diff --git a/tests/ui/coroutine/print/coroutine-print-verbose-3.stderr b/tests/ui/coroutine/print/coroutine-print-verbose-3.stderr index 100993bd33cf..e2973cde6d3c 100644 --- a/tests/ui/coroutine/print/coroutine-print-verbose-3.stderr +++ b/tests/ui/coroutine/print/coroutine-print-verbose-3.stderr @@ -12,7 +12,7 @@ LL | | }; | |_____^ expected `()`, found coroutine | = note: expected unit type `()` - found coroutine `{main::{closure#0} upvar_tys=(unavailable)}` + found coroutine `{main::{closure#0} upvar_tys=?4t witness=?6t}` error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/rpit/early_bound.rs b/tests/ui/impl-trait/rpit/early_bound.rs new file mode 100644 index 000000000000..03bd64d4d76d --- /dev/null +++ b/tests/ui/impl-trait/rpit/early_bound.rs @@ -0,0 +1,13 @@ +use std::convert::identity; + +fn test<'a: 'a>(n: bool) -> impl Sized + 'a { + //~^ ERROR concrete type differs from previous defining opaque type use + let true = n else { loop {} }; + let _ = || { + let _ = identity::<&'a ()>(test(false)); + //~^ ERROR hidden type for `impl Sized + 'a` captures lifetime that does not appear in bounds + }; + loop {} +} + +fn main() {} diff --git a/tests/ui/impl-trait/rpit/early_bound.stderr b/tests/ui/impl-trait/rpit/early_bound.stderr new file mode 100644 index 000000000000..815368f250e3 --- /dev/null +++ b/tests/ui/impl-trait/rpit/early_bound.stderr @@ -0,0 +1,26 @@ +error[E0700]: hidden type for `impl Sized + 'a` captures lifetime that does not appear in bounds + --> $DIR/early_bound.rs:7:17 + | +LL | fn test<'a: 'a>(n: bool) -> impl Sized + 'a { + | -- --------------- opaque type defined here + | | + | hidden type `&'a ()` captures the lifetime `'a` as defined here +... +LL | let _ = identity::<&'a ()>(test(false)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: concrete type differs from previous defining opaque type use + --> $DIR/early_bound.rs:3:29 + | +LL | fn test<'a: 'a>(n: bool) -> impl Sized + 'a { + | ^^^^^^^^^^^^^^^ expected `&()`, got `()` + | +note: previous use here + --> $DIR/early_bound.rs:7:36 + | +LL | let _ = identity::<&'a ()>(test(false)); + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/imports/ambiguous-2.rs b/tests/ui/imports/ambiguous-2.rs index 7b38f3006b10..2918feb05910 100644 --- a/tests/ui/imports/ambiguous-2.rs +++ b/tests/ui/imports/ambiguous-2.rs @@ -6,4 +6,5 @@ extern crate ambiguous_1; fn main() { ambiguous_1::id(); + //^ FIXME: `id` should be identified as an ambiguous item. } diff --git a/tests/ui/imports/ambiguous-4.rs b/tests/ui/imports/ambiguous-4.rs index 24ae33784c52..1e8f5be5a882 100644 --- a/tests/ui/imports/ambiguous-4.rs +++ b/tests/ui/imports/ambiguous-4.rs @@ -5,5 +5,5 @@ extern crate ambiguous_4_extern; fn main() { ambiguous_4_extern::id(); - // `warning_ambiguous` had been lost at metadata. + //^ FIXME: `id` should be identified as an ambiguous item. } diff --git a/tests/ui/imports/auxiliary/glob-conflict-cross-crate-2-extern.rs b/tests/ui/imports/auxiliary/glob-conflict-cross-crate-2-extern.rs new file mode 100644 index 000000000000..5dec6d469949 --- /dev/null +++ b/tests/ui/imports/auxiliary/glob-conflict-cross-crate-2-extern.rs @@ -0,0 +1,10 @@ +mod a { + pub type C = i8; +} + +mod b { + pub type C = i16; +} + +pub use a::*; +pub use b::*; diff --git a/tests/ui/imports/auxiliary/issue-114682-2-extern.rs b/tests/ui/imports/auxiliary/issue-114682-2-extern.rs new file mode 100644 index 000000000000..df2af7891668 --- /dev/null +++ b/tests/ui/imports/auxiliary/issue-114682-2-extern.rs @@ -0,0 +1,17 @@ +macro_rules! m { + () => { + pub fn max() {} + pub(crate) mod max {} + }; +} + +mod d { + m! {} +} + +mod e { + pub type max = i32; +} + +pub use self::d::*; +pub use self::e::*; diff --git a/tests/ui/imports/auxiliary/issue-114682-3-extern.rs b/tests/ui/imports/auxiliary/issue-114682-3-extern.rs new file mode 100644 index 000000000000..999b66342fe0 --- /dev/null +++ b/tests/ui/imports/auxiliary/issue-114682-3-extern.rs @@ -0,0 +1,16 @@ +mod gio { + pub trait SettingsExt { + fn abc(&self) {} + } + impl SettingsExt for T {} +} + +mod gtk { + pub trait SettingsExt { + fn efg(&self) {} + } + impl SettingsExt for T {} +} + +pub use gtk::*; +pub use gio::*; diff --git a/tests/ui/imports/auxiliary/issue-114682-4-extern.rs b/tests/ui/imports/auxiliary/issue-114682-4-extern.rs new file mode 100644 index 000000000000..86663f11b31e --- /dev/null +++ b/tests/ui/imports/auxiliary/issue-114682-4-extern.rs @@ -0,0 +1,10 @@ +mod a { + pub type Result = std::result::Result; +} + +mod b { + pub type Result = std::result::Result; +} + +pub use a::*; +pub use b::*; diff --git a/tests/ui/imports/auxiliary/issue-114682-5-extern-1.rs b/tests/ui/imports/auxiliary/issue-114682-5-extern-1.rs new file mode 100644 index 000000000000..ebf6493f9f71 --- /dev/null +++ b/tests/ui/imports/auxiliary/issue-114682-5-extern-1.rs @@ -0,0 +1 @@ +pub struct Url; diff --git a/tests/ui/imports/auxiliary/issue-114682-5-extern-2.rs b/tests/ui/imports/auxiliary/issue-114682-5-extern-2.rs new file mode 100644 index 000000000000..9dbefdd531be --- /dev/null +++ b/tests/ui/imports/auxiliary/issue-114682-5-extern-2.rs @@ -0,0 +1,13 @@ +// edition: 2018 +// aux-build: issue-114682-5-extern-1.rs +// compile-flags: --extern issue_114682_5_extern_1 + +pub mod p { + pub use crate::types::*; + pub use crate::*; +} +mod types { + pub mod issue_114682_5_extern_1 {} +} + +pub use issue_114682_5_extern_1; diff --git a/tests/ui/imports/auxiliary/issue-114682-6-extern.rs b/tests/ui/imports/auxiliary/issue-114682-6-extern.rs new file mode 100644 index 000000000000..caf3c4e35a0e --- /dev/null +++ b/tests/ui/imports/auxiliary/issue-114682-6-extern.rs @@ -0,0 +1,9 @@ +mod a { + pub fn log() {} +} +mod b { + pub fn log() {} +} + +pub use self::a::*; +pub use self::b::*; diff --git a/tests/ui/imports/extern-with-ambiguous-2.rs b/tests/ui/imports/extern-with-ambiguous-2.rs index 68c623c1c4a6..b7c9cccdb640 100644 --- a/tests/ui/imports/extern-with-ambiguous-2.rs +++ b/tests/ui/imports/extern-with-ambiguous-2.rs @@ -12,5 +12,7 @@ mod s { use s::*; use extern_with_ambiguous_2_extern::*; use error::*; +//^ FIXME: An ambiguity error should be thrown for `error`, +// as there is ambiguity present within `extern-with-ambiguous-2-extern.rs`. fn main() {} diff --git a/tests/ui/imports/extern-with-ambiguous-3.rs b/tests/ui/imports/extern-with-ambiguous-3.rs index 282c1d569b0c..44a9a2a00a45 100644 --- a/tests/ui/imports/extern-with-ambiguous-3.rs +++ b/tests/ui/imports/extern-with-ambiguous-3.rs @@ -13,5 +13,7 @@ mod s { use s::*; use extern_with_ambiguous_3_extern::*; use error::*; +//^ FIXME: An ambiguity error should be thrown for `error`, +// as there is ambiguity present within `extern-with-ambiguous-3-extern.rs`. fn main() {} diff --git a/tests/ui/imports/glob-conflict-cross-crate.rs b/tests/ui/imports/glob-conflict-cross-crate-1.rs similarity index 54% rename from tests/ui/imports/glob-conflict-cross-crate.rs rename to tests/ui/imports/glob-conflict-cross-crate-1.rs index d84c243f2139..832e6c888a64 100644 --- a/tests/ui/imports/glob-conflict-cross-crate.rs +++ b/tests/ui/imports/glob-conflict-cross-crate-1.rs @@ -4,5 +4,9 @@ extern crate glob_conflict; fn main() { glob_conflict::f(); //~ ERROR cannot find function `f` in crate `glob_conflict` + //^ FIXME: `glob_conflict::f` should raise an + // ambiguity error instead of a not found error. glob_conflict::glob::f(); //~ ERROR cannot find function `f` in module `glob_conflict::glob` + //^ FIXME: `glob_conflict::glob::f` should raise an + // ambiguity error instead of a not found error. } diff --git a/tests/ui/imports/glob-conflict-cross-crate.stderr b/tests/ui/imports/glob-conflict-cross-crate-1.stderr similarity index 82% rename from tests/ui/imports/glob-conflict-cross-crate.stderr rename to tests/ui/imports/glob-conflict-cross-crate-1.stderr index 0e3b4222fe44..758087107f39 100644 --- a/tests/ui/imports/glob-conflict-cross-crate.stderr +++ b/tests/ui/imports/glob-conflict-cross-crate-1.stderr @@ -1,11 +1,11 @@ error[E0425]: cannot find function `f` in crate `glob_conflict` - --> $DIR/glob-conflict-cross-crate.rs:6:20 + --> $DIR/glob-conflict-cross-crate-1.rs:6:20 | LL | glob_conflict::f(); | ^ not found in `glob_conflict` error[E0425]: cannot find function `f` in module `glob_conflict::glob` - --> $DIR/glob-conflict-cross-crate.rs:7:26 + --> $DIR/glob-conflict-cross-crate-1.rs:9:26 | LL | glob_conflict::glob::f(); | ^ not found in `glob_conflict::glob` diff --git a/tests/ui/imports/glob-conflict-cross-crate-2.rs b/tests/ui/imports/glob-conflict-cross-crate-2.rs new file mode 100644 index 000000000000..6ba71ad30ac5 --- /dev/null +++ b/tests/ui/imports/glob-conflict-cross-crate-2.rs @@ -0,0 +1,10 @@ +// aux-build:glob-conflict-cross-crate-2-extern.rs + +extern crate glob_conflict_cross_crate_2_extern; + +use glob_conflict_cross_crate_2_extern::*; + +fn main() { + let _a: C = 1; //~ ERROR cannot find type `C` in this scope + //^ FIXME: `C` should be identified as an ambiguous item. +} diff --git a/tests/ui/imports/glob-conflict-cross-crate-2.stderr b/tests/ui/imports/glob-conflict-cross-crate-2.stderr new file mode 100644 index 000000000000..aebb2d59d063 --- /dev/null +++ b/tests/ui/imports/glob-conflict-cross-crate-2.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `C` in this scope + --> $DIR/glob-conflict-cross-crate-2.rs:8:13 + | +LL | let _a: C = 1; + | ^ not found in this scope + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/imports/glob-conflict-cross-crate-3.rs b/tests/ui/imports/glob-conflict-cross-crate-3.rs new file mode 100644 index 000000000000..535d87d8ea28 --- /dev/null +++ b/tests/ui/imports/glob-conflict-cross-crate-3.rs @@ -0,0 +1,16 @@ +// check-pass +// aux-build:glob-conflict-cross-crate-2-extern.rs + +extern crate glob_conflict_cross_crate_2_extern; + +mod a { + pub type C = i32; +} + +use glob_conflict_cross_crate_2_extern::*; +use a::*; + +fn main() { + let _a: C = 1; + //^ FIXME: `C` should be identified as an ambiguous item. +} diff --git a/tests/ui/imports/issue-114682-1.rs b/tests/ui/imports/issue-114682-1.rs new file mode 100644 index 000000000000..88fe05e51444 --- /dev/null +++ b/tests/ui/imports/issue-114682-1.rs @@ -0,0 +1,25 @@ +// https://github.com/rust-lang/rust/pull/114682#discussion_r1420534109 + +#![feature(decl_macro)] + +macro_rules! mac { + () => { + pub macro A() { + println!("non import") + } + } +} + +mod m { + pub macro A() { + println!("import") + } +} + +pub use m::*; +mac!(); + +fn main() { + A!(); + //~^ ERROR `A` is ambiguous +} diff --git a/tests/ui/imports/issue-114682-1.stderr b/tests/ui/imports/issue-114682-1.stderr new file mode 100644 index 000000000000..85fb7f7919e4 --- /dev/null +++ b/tests/ui/imports/issue-114682-1.stderr @@ -0,0 +1,28 @@ +error[E0659]: `A` is ambiguous + --> $DIR/issue-114682-1.rs:23:5 + | +LL | A!(); + | ^ ambiguous name + | + = note: ambiguous because of a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution +note: `A` could refer to the macro defined here + --> $DIR/issue-114682-1.rs:7:9 + | +LL | / pub macro A() { +LL | | println!("non import") +LL | | } + | |_________^ +... +LL | mac!(); + | ------ in this macro invocation +note: `A` could also refer to the macro imported here + --> $DIR/issue-114682-1.rs:19:9 + | +LL | pub use m::*; + | ^^^^ + = help: consider adding an explicit import of `A` to disambiguate + = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0659`. diff --git a/tests/ui/imports/issue-114682-2.rs b/tests/ui/imports/issue-114682-2.rs new file mode 100644 index 000000000000..491105e62efe --- /dev/null +++ b/tests/ui/imports/issue-114682-2.rs @@ -0,0 +1,19 @@ +// aux-build: issue-114682-2-extern.rs +// https://github.com/rust-lang/rust/pull/114682#issuecomment-1879998900 + +extern crate issue_114682_2_extern; + +use issue_114682_2_extern::max; + +type A = issue_114682_2_extern::max; +//~^ ERROR: expected type, found function `issue_114682_2_extern::max` +// FIXME: +// The above error was emitted due to `(Mod(issue_114682_2_extern), Namespace(Type), Ident(max))` +// being identified as an ambiguous item. +// However, there are two points worth discussing: +// First, should this ambiguous item be omitted considering the maximum visibility +// of `issue_114682_2_extern::m::max` in the type namespace is only within the extern crate. +// Second, if we retain the ambiguous item of the extern crate, should it be treated +// as an ambiguous item within the local crate for the same reasoning? + +fn main() {} diff --git a/tests/ui/imports/issue-114682-2.stderr b/tests/ui/imports/issue-114682-2.stderr new file mode 100644 index 000000000000..972bcecb56bc --- /dev/null +++ b/tests/ui/imports/issue-114682-2.stderr @@ -0,0 +1,9 @@ +error[E0573]: expected type, found function `issue_114682_2_extern::max` + --> $DIR/issue-114682-2.rs:8:10 + | +LL | type A = issue_114682_2_extern::max; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not a type + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0573`. diff --git a/tests/ui/imports/issue-114682-3.rs b/tests/ui/imports/issue-114682-3.rs new file mode 100644 index 000000000000..0f658bfe1597 --- /dev/null +++ b/tests/ui/imports/issue-114682-3.rs @@ -0,0 +1,24 @@ +// check-pass +// aux-build: issue-114682-3-extern.rs +// https://github.com/rust-lang/rust/pull/114682#issuecomment-1880625909 + +extern crate issue_114682_3_extern; + +use issue_114682_3_extern::*; + +mod auto { + pub trait SettingsExt { + fn ext(&self) {} + } + + impl SettingsExt for T {} +} + +pub use self::auto::*; + +fn main() { + let a: u8 = 1; + a.ext(); + //^ FIXME: it should report `ext` not found because `SettingsExt` + // is an ambiguous item in `issue-114682-3-extern.rs`. +} diff --git a/tests/ui/imports/issue-114682-4.rs b/tests/ui/imports/issue-114682-4.rs new file mode 100644 index 000000000000..97615c104104 --- /dev/null +++ b/tests/ui/imports/issue-114682-4.rs @@ -0,0 +1,13 @@ +// check-pass +// aux-build: issue-114682-4-extern.rs +// https://github.com/rust-lang/rust/pull/114682#issuecomment-1880755441 + +extern crate issue_114682_4_extern; + +use issue_114682_4_extern::*; + +fn a() -> Result { // FIXME: `Result` should be identified as an ambiguous item. + Ok(1) +} + +fn main() {} diff --git a/tests/ui/imports/issue-114682-5.rs b/tests/ui/imports/issue-114682-5.rs new file mode 100644 index 000000000000..eb5ac10495be --- /dev/null +++ b/tests/ui/imports/issue-114682-5.rs @@ -0,0 +1,15 @@ +// check-pass +// edition: 2018 +// aux-build: issue-114682-5-extern-1.rs +// aux-build: issue-114682-5-extern-2.rs +// compile-flags: --extern issue_114682_5_extern_1 +// https://github.com/rust-lang/rust/pull/114682#issuecomment-1880755441 + +extern crate issue_114682_5_extern_2; + +use issue_114682_5_extern_2::p::*; +use issue_114682_5_extern_1::Url; +// FIXME: The `issue_114682_5_extern_1` should be considered an ambiguous item, +// as it has already been recognized as ambiguous in `issue_114682_5_extern_2`. + +fn main() {} diff --git a/tests/ui/imports/issue-114682-6.rs b/tests/ui/imports/issue-114682-6.rs new file mode 100644 index 000000000000..29a7d9e94264 --- /dev/null +++ b/tests/ui/imports/issue-114682-6.rs @@ -0,0 +1,13 @@ +// check-pass +// aux-build: issue-114682-6-extern.rs +// https://github.com/rust-lang/rust/pull/114682#issuecomment-1880755441 + +extern crate issue_114682_6_extern; + +use issue_114682_6_extern::*; + +fn main() { + let log = 2; + //^ `log` should be identified as an ambiguous item. + let _ = log; +} diff --git a/tests/ui/repeat-expr/repeat-to-run-dtor-twice.stderr b/tests/ui/repeat-expr/repeat-to-run-dtor-twice.stderr index 2e3ede46eca5..f3fe8c10c025 100644 --- a/tests/ui/repeat-expr/repeat-to-run-dtor-twice.stderr +++ b/tests/ui/repeat-expr/repeat-to-run-dtor-twice.stderr @@ -5,6 +5,8 @@ LL | let _ = [ a; 5 ]; | ^ the trait `Copy` is not implemented for `Foo` | = note: the `Copy` trait is required because this value will be copied for each element of the array + = help: consider using `core::array::from_fn` to initialize the array + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information help: consider annotating `Foo` with `#[derive(Copy)]` | LL + #[derive(Copy)] diff --git a/tests/ui/trait-bounds/issue-119530-sugg-from-fn.rs b/tests/ui/trait-bounds/issue-119530-sugg-from-fn.rs new file mode 100644 index 000000000000..cfe378f55b1e --- /dev/null +++ b/tests/ui/trait-bounds/issue-119530-sugg-from-fn.rs @@ -0,0 +1,5 @@ +fn foo() -> String { String::new() } + +fn main() { + let string_arr = [foo(); 64]; //~ ERROR the trait bound `String: Copy` is not satisfied +} diff --git a/tests/ui/trait-bounds/issue-119530-sugg-from-fn.stderr b/tests/ui/trait-bounds/issue-119530-sugg-from-fn.stderr new file mode 100644 index 000000000000..f394c4cf027e --- /dev/null +++ b/tests/ui/trait-bounds/issue-119530-sugg-from-fn.stderr @@ -0,0 +1,13 @@ +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/issue-119530-sugg-from-fn.rs:4:23 + | +LL | let string_arr = [foo(); 64]; + | ^^^^^ the trait `Copy` is not implemented for `String` + | + = note: the `Copy` trait is required because this value will be copied for each element of the array + = help: consider using `core::array::from_fn` to initialize the array + = help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html# for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs index e440dce5e514..4c881dd13308 100644 --- a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs +++ b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs @@ -1,9 +1,8 @@ -//! This test shows that we can even follow projections -//! into associated types of the same impl if they are -//! indirectly mentioned in a struct field. +//! This test shows that we do not treat opaque types +//! as defined by a method if the opaque type is +//! only indirectly mentioned in a struct field. #![feature(impl_trait_in_assoc_type)] -// check-pass struct Bar; @@ -16,6 +15,7 @@ impl Trait for Bar { type Assoc = impl std::fmt::Debug; fn foo() -> Foo { Foo { field: () } + //~^ ERROR: item constrains opaque type that is not in its signature } } diff --git a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr new file mode 100644 index 000000000000..5c53dfa3a750 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr @@ -0,0 +1,15 @@ +error: item constrains opaque type that is not in its signature + --> $DIR/hidden_behind_struct_field2.rs:17:22 + | +LL | Foo { field: () } + | ^^ + | + = note: this item must mention the opaque type in its signature in order to be able to register hidden types +note: this item must mention the opaque type in its signature in order to be able to register hidden types + --> $DIR/hidden_behind_struct_field2.rs:16:8 + | +LL | fn foo() -> Foo { + | ^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.rs b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.rs new file mode 100644 index 000000000000..a788563ab779 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.rs @@ -0,0 +1,38 @@ +//! Check that we cannot instantiate a hidden type in the body +//! of an assoc fn or const unless mentioned in the signature. + +#![feature(impl_trait_in_assoc_type)] + +trait Trait: Sized { + type Assoc; + fn foo(); + fn bar() -> Self::Assoc; +} + +impl Trait for () { + type Assoc = impl std::fmt::Debug; + fn foo() { + let x: Self::Assoc = 42; //~ ERROR: mismatched types + } + fn bar() -> Self::Assoc { + "" + } +} + +trait Trait2: Sized { + type Assoc; + const FOO: (); + fn bar() -> Self::Assoc; +} + +impl Trait2 for () { + type Assoc = impl std::fmt::Debug; + const FOO: () = { + let x: Self::Assoc = 42; //~ ERROR: mismatched types + }; + fn bar() -> Self::Assoc { + "" + } +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.stderr b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.stderr new file mode 100644 index 000000000000..1d7a97c53671 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.stderr @@ -0,0 +1,41 @@ +error[E0308]: mismatched types + --> $DIR/impl_trait_in_trait_defined_outside_trait.rs:15:30 + | +LL | type Assoc = impl std::fmt::Debug; + | -------------------- the expected opaque type +LL | fn foo() { +LL | let x: Self::Assoc = 42; + | ----------- ^^ expected opaque type, found integer + | | + | expected due to this + | + = note: expected opaque type `<() as Trait>::Assoc` + found type `{integer}` +note: this item must have the opaque type in its signature in order to be able to register hidden types + --> $DIR/impl_trait_in_trait_defined_outside_trait.rs:14:8 + | +LL | fn foo() { + | ^^^ + +error[E0308]: mismatched types + --> $DIR/impl_trait_in_trait_defined_outside_trait.rs:31:30 + | +LL | type Assoc = impl std::fmt::Debug; + | -------------------- the expected opaque type +LL | const FOO: () = { +LL | let x: Self::Assoc = 42; + | ----------- ^^ expected opaque type, found integer + | | + | expected due to this + | + = note: expected opaque type `<() as Trait2>::Assoc` + found type `{integer}` +note: this item must have the opaque type in its signature in order to be able to register hidden types + --> $DIR/impl_trait_in_trait_defined_outside_trait.rs:30:11 + | +LL | const FOO: () = { + | ^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.rs b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.rs new file mode 100644 index 000000000000..77cdca198dae --- /dev/null +++ b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.rs @@ -0,0 +1,22 @@ +//! Check that we cannot instantiate a hidden type from another assoc type. + +#![feature(impl_trait_in_assoc_type)] + +trait Trait: Sized { + type Assoc; + type Foo; + fn foo() -> Self::Assoc; +} + +impl Trait for () { + type Assoc = impl std::fmt::Debug; + type Foo = [(); { + let x: Self::Assoc = 42; //~ ERROR: mismatched types + 3 + }]; + fn foo() -> Self::Assoc { + "" + } +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.stderr b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.stderr new file mode 100644 index 000000000000..708c3f28d2d2 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.stderr @@ -0,0 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/impl_trait_in_trait_defined_outside_trait2.rs:14:30 + | +LL | type Assoc = impl std::fmt::Debug; + | -------------------- the expected opaque type +LL | type Foo = [(); { +LL | let x: Self::Assoc = 42; + | ----------- ^^ expected opaque type, found integer + | | + | expected due to this + | + = note: expected opaque type `<() as Trait>::Assoc` + found type `{integer}` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait3.rs b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait3.rs new file mode 100644 index 000000000000..dfcf73365337 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait3.rs @@ -0,0 +1,38 @@ +//! Check that non-defining assoc items can use the opaque type +//! opaquely. + +// check-pass + +#![feature(impl_trait_in_assoc_type)] + +trait Trait: Sized { + type Assoc; + fn foo(); + fn bar() -> Self::Assoc; +} + +impl Trait for () { + type Assoc = impl std::fmt::Debug; + fn foo() { + let x: Self::Assoc = Self::bar(); + } + fn bar() -> Self::Assoc { + "" + } +} + +trait Trait2: Sized { + type Assoc; + const FOO: (); + const BAR: Self::Assoc; +} + +impl Trait2 for () { + type Assoc = impl Copy; + const FOO: () = { + let x: Self::Assoc = Self::BAR; + }; + const BAR: Self::Assoc = ""; +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.rs b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.rs new file mode 100644 index 000000000000..baeba1d3de63 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.rs @@ -0,0 +1,17 @@ +#![feature(impl_trait_in_assoc_type)] + +trait Foo { + type Assoc<'a, 'b>; + fn bar<'a: 'a, 'b: 'b>(_: &'a ()) -> Self::Assoc<'a, 'b>; +} + +impl Foo for () { + type Assoc<'a, 'b> = impl Sized; + fn bar<'a: 'a, 'b: 'b>(x: &'a ()) -> Self::Assoc<'a, 'b> { + let closure = |x: &'a ()| -> Self::Assoc<'b, 'a> { x }; + //~^ ERROR `<() as Foo>::Assoc<'b, 'a>` captures lifetime that does not appear in bounds + x + } +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.stderr b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.stderr new file mode 100644 index 000000000000..a7d3e7f0be40 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.stderr @@ -0,0 +1,13 @@ +error[E0700]: hidden type for `<() as Foo>::Assoc<'b, 'a>` captures lifetime that does not appear in bounds + --> $DIR/in-assoc-ty-early-bound.rs:11:60 + | +LL | type Assoc<'a, 'b> = impl Sized; + | ---------- opaque type defined here +LL | fn bar<'a: 'a, 'b: 'b>(x: &'a ()) -> Self::Assoc<'a, 'b> { + | -- hidden type `&'a ()` captures the lifetime `'a` as defined here +LL | let closure = |x: &'a ()| -> Self::Assoc<'b, 'a> { x }; + | ^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs new file mode 100644 index 000000000000..7452000b65dc --- /dev/null +++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs @@ -0,0 +1,21 @@ +#![feature(impl_trait_in_assoc_type)] + +trait Foo { + type Assoc<'a>; + fn bar<'a: 'a>(); +} + +impl Foo for () { + type Assoc<'a> = impl Sized; //~ ERROR unconstrained opaque type + fn bar<'a: 'a>() + where + Self::Assoc<'a>:, + { + let _ = |x: &'a ()| { + let _: Self::Assoc<'a> = x; + //~^ ERROR `<() as Foo>::Assoc<'a>` captures lifetime that does not appear in bound + }; + } +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr new file mode 100644 index 000000000000..1274a8b60dea --- /dev/null +++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr @@ -0,0 +1,22 @@ +error[E0700]: hidden type for `<() as Foo>::Assoc<'a>` captures lifetime that does not appear in bounds + --> $DIR/in-assoc-ty-early-bound2.rs:15:20 + | +LL | type Assoc<'a> = impl Sized; + | ---------- opaque type defined here +LL | fn bar<'a: 'a>() + | -- hidden type `&'a ()` captures the lifetime `'a` as defined here +... +LL | let _: Self::Assoc<'a> = x; + | ^^^^^^^^^^^^^^^ + +error: unconstrained opaque type + --> $DIR/in-assoc-ty-early-bound2.rs:9:22 + | +LL | type Assoc<'a> = impl Sized; + | ^^^^^^^^^^ + | + = note: `Assoc` must be used in combination with a concrete type within the same impl + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.bad.stderr b/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.bad.stderr new file mode 100644 index 000000000000..4acc47eaef22 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.bad.stderr @@ -0,0 +1,23 @@ +error[E0308]: mismatched types + --> $DIR/itiat-allow-nested-closures.rs:21:22 + | +LL | type Assoc = impl Sized; + | ---------- the found opaque type +... +LL | let _: i32 = closure(); + | --- ^^^^^^^^^ expected `i32`, found opaque type + | | + | expected due to this + +error[E0308]: mismatched types + --> $DIR/itiat-allow-nested-closures.rs:22:9 + | +LL | fn bar() -> Self::Assoc { + | ----------- expected `()` because of return type +... +LL | 1i32 + | ^^^^ expected `()`, found `i32` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.rs b/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.rs new file mode 100644 index 000000000000..55994d6a3259 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.rs @@ -0,0 +1,26 @@ +#![feature(impl_trait_in_assoc_type)] + +// revisions: ok bad +// [ok] check-pass + +trait Foo { + type Assoc; + fn bar() -> Self::Assoc; +} + +impl Foo for () { + type Assoc = impl Sized; + fn bar() -> Self::Assoc { + let closure = || -> Self::Assoc { + #[cfg(ok)] + let x: Self::Assoc = 42_i32; + #[cfg(bad)] + let x: Self::Assoc = (); + x + }; + let _: i32 = closure(); //[bad]~ ERROR mismatched types + 1i32 //[bad]~ ERROR mismatched types + } +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.rs b/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.rs new file mode 100644 index 000000000000..8c9d780c1119 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.rs @@ -0,0 +1,20 @@ +#![feature(impl_trait_in_assoc_type)] + +trait Foo { + type Assoc; + fn bar() -> Self::Assoc; +} + +impl Foo for () { + type Assoc = impl Sized; + fn bar() -> Self::Assoc { + fn foo() -> <() as Foo>::Assoc { + let x: <() as Foo>::Assoc = 42_i32; //~ ERROR mismatched types + x + }; + let _: i32 = foo(); + 1i32 + } +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.stderr b/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.stderr new file mode 100644 index 000000000000..c177201431a6 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.stderr @@ -0,0 +1,22 @@ +error[E0308]: mismatched types + --> $DIR/itiat-forbid-nested-items.rs:12:41 + | +LL | type Assoc = impl Sized; + | ---------- the expected opaque type +... +LL | let x: <() as Foo>::Assoc = 42_i32; + | ------------------ ^^^^^^ expected opaque type, found `i32` + | | + | expected due to this + | + = note: expected opaque type `<() as Foo>::Assoc` + found type `i32` +note: this item must have the opaque type in its signature in order to be able to register hidden types + --> $DIR/itiat-forbid-nested-items.rs:11:12 + | +LL | fn foo() -> <() as Foo>::Assoc { + | ^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.fixed b/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.fixed new file mode 100644 index 000000000000..9ce46bc1a65b --- /dev/null +++ b/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.fixed @@ -0,0 +1,32 @@ +// run-rustfix +trait Trait {} +struct Struct; +impl Trait for Struct {} +fn foo() -> Box { + Box::new(Struct) +} +fn bar() -> impl Trait { + Struct +} +fn main() { + let _ = if true { + Box::new(Struct) + } else { + foo() //~ ERROR E0308 + }; + let _ = if true { + foo() + } else { + Box::new(Struct) //~ ERROR E0308 + }; + let _ = if true { + Box::new(Struct) as Box + } else { + Box::new(bar()) //~ ERROR E0308 + }; + let _ = if true { + Box::new(bar()) as Box + } else { + Box::new(Struct) //~ ERROR E0308 + }; +} diff --git a/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.rs b/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.rs new file mode 100644 index 000000000000..7f65a3bb59d3 --- /dev/null +++ b/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.rs @@ -0,0 +1,32 @@ +// run-rustfix +trait Trait {} +struct Struct; +impl Trait for Struct {} +fn foo() -> Box { + Box::new(Struct) +} +fn bar() -> impl Trait { + Struct +} +fn main() { + let _ = if true { + Struct + } else { + foo() //~ ERROR E0308 + }; + let _ = if true { + foo() + } else { + Struct //~ ERROR E0308 + }; + let _ = if true { + Struct + } else { + bar() //~ ERROR E0308 + }; + let _ = if true { + bar() + } else { + Struct //~ ERROR E0308 + }; +} diff --git a/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.stderr b/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.stderr new file mode 100644 index 000000000000..c58bf60e7d65 --- /dev/null +++ b/tests/ui/typeck/suggest-box-on-divergent-if-else-arms.stderr @@ -0,0 +1,94 @@ +error[E0308]: `if` and `else` have incompatible types + --> $DIR/suggest-box-on-divergent-if-else-arms.rs:15:9 + | +LL | let _ = if true { + | _____________- +LL | | Struct + | | ------ expected because of this +LL | | } else { +LL | | foo() + | | ^^^^^ expected `Struct`, found `Box` +LL | | }; + | |_____- `if` and `else` have incompatible types + | + = note: expected struct `Struct` + found struct `Box` +help: `Struct` implements `Trait` so you can box it to coerce to the trait object `Box` + | +LL | Box::new(Struct) + | +++++++++ + + +error[E0308]: `if` and `else` have incompatible types + --> $DIR/suggest-box-on-divergent-if-else-arms.rs:20:9 + | +LL | let _ = if true { + | _____________- +LL | | foo() + | | ----- expected because of this +LL | | } else { +LL | | Struct + | | ^^^^^^ expected `Box`, found `Struct` +LL | | }; + | |_____- `if` and `else` have incompatible types + | + = note: expected struct `Box` + found struct `Struct` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +help: store this in the heap by calling `Box::new` + | +LL | Box::new(Struct) + | +++++++++ + + +error[E0308]: `if` and `else` have incompatible types + --> $DIR/suggest-box-on-divergent-if-else-arms.rs:25:9 + | +LL | fn bar() -> impl Trait { + | ---------- the found opaque type +... +LL | let _ = if true { + | _____________- +LL | | Struct + | | ------ expected because of this +LL | | } else { +LL | | bar() + | | ^^^^^ expected `Struct`, found opaque type +LL | | }; + | |_____- `if` and `else` have incompatible types + | + = note: expected struct `Struct` + found opaque type `impl Trait` +help: `Struct` implements `Trait` so you can box both arms and coerce to the trait object `Box` + | +LL ~ Box::new(Struct) as Box +LL | } else { +LL ~ Box::new(bar()) + | + +error[E0308]: `if` and `else` have incompatible types + --> $DIR/suggest-box-on-divergent-if-else-arms.rs:30:9 + | +LL | fn bar() -> impl Trait { + | ---------- the expected opaque type +... +LL | let _ = if true { + | _____________- +LL | | bar() + | | ----- expected because of this +LL | | } else { +LL | | Struct + | | ^^^^^^ expected opaque type, found `Struct` +LL | | }; + | |_____- `if` and `else` have incompatible types + | + = note: expected opaque type `impl Trait` + found struct `Struct` +help: `Struct` implements `Trait` so you can box both arms and coerce to the trait object `Box` + | +LL ~ Box::new(bar()) as Box +LL | } else { +LL ~ Box::new(Struct) + | + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/weird-exprs.rs b/tests/ui/weird-exprs.rs index 6d40d6377c5d..748fe13c1e47 100644 --- a/tests/ui/weird-exprs.rs +++ b/tests/ui/weird-exprs.rs @@ -231,6 +231,31 @@ fn infcx() { let _cx: cx::cx::Cx = cx::cx::cx::cx::cx::Cx; } +fn return_already() -> impl std::fmt::Debug { + loop { + return !!!!!!! + break !!!!!!1111 + } +} + +fn fake_macros() -> impl std::fmt::Debug { + loop { + if! { + match! ( + break! { + return! { + 1337 + } + } + ) + + {} + } + + {} + } +} + pub fn main() { strange(); funny(); @@ -257,4 +282,6 @@ pub fn main() { semisemisemisemisemi(); useful_syntax(); infcx(); + return_already(); + fake_macros(); } diff --git a/triagebot.toml b/triagebot.toml index a9ddd1b99c72..36e58f0f9d8a 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -643,6 +643,7 @@ compiler-team = [ "@compiler-errors", "@petrochenkov", "@davidtwco", + "@estebank", "@oli-obk", "@wesleywiser", ] @@ -693,18 +694,21 @@ incremental = [ diagnostics = [ "@compiler-errors", "@davidtwco", + "@estebank", "@oli-obk", "@TaKO8Ki", ] parser = [ "@compiler-errors", "@davidtwco", + "@estebank", "@nnethercote", "@petrochenkov", ] lexer = [ "@nnethercote", "@petrochenkov", + "@estebank", ] arena = [ "@nnethercote", @@ -731,6 +735,7 @@ borrowck = [ ] ast_lowering = [ "@compiler-errors", + "@estebank", "@spastorino", ] fallback = [