From c5e5754c08296bb85d341716b9b1857fa2e06ddd Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Mon, 18 Jul 2022 20:35:06 +0000 Subject: [PATCH 01/23] C FFI support for functions with int args and returns (rebasing from current master) --- .gitignore | 2 + CONTRIBUTING.md | 7 +- Cargo.lock | 232 +++ Cargo.toml | 7 +- README.md | 2 +- .../slice-get-unchecked/Cargo.lock | 7 + .../slice-get-unchecked/Cargo.toml | 8 + .../slice-get-unchecked/src/main.rs | 12 + ci.sh | 4 +- miri | 93 +- rust-version | 2 +- src/bin/miri.rs | 13 + src/c_ffi_support.rs | 302 ++++ src/concurrency/range_object_map.rs | 4 +- src/diagnostics.rs | 4 +- src/eval.rs | 5 + src/helpers.rs | 8 +- src/lib.rs | 7 +- src/machine.rs | 36 +- src/operator.rs | 68 +- src/range_map.rs | 18 + src/shims/backtrace.rs | 5 +- src/shims/foreign_items.rs | 50 +- src/shims/intrinsics.rs | 1416 ----------------- src/shims/intrinsics/atomic.rs | 340 ++++ src/shims/intrinsics/mod.rs | 426 +++++ src/shims/intrinsics/simd.rs | 613 +++++++ src/shims/unix/foreign_items.rs | 16 +- src/shims/unix/freebsd/foreign_items.rs | 2 - src/shims/unix/fs.rs | 66 +- src/shims/unix/linux/foreign_items.rs | 2 - src/shims/unix/linux/sync.rs | 4 +- src/shims/unix/macos/foreign_items.rs | 2 - src/shims/windows/foreign_items.rs | 16 +- src/stacked_borrows/diagnostics.rs | 6 +- src/stacked_borrows/item.rs | 104 ++ .../mod.rs} | 348 ++-- src/stacked_borrows/stack.rs | 76 +- src/thread.rs | 4 + tests/compiletest.rs | 25 +- tests/external_C/test.c | 31 + tests/fail/alloc/deallocate-bad-alignment.rs | 2 +- tests/fail/alloc/deallocate-bad-size.rs | 2 +- tests/fail/alloc/deallocate-twice.rs | 2 +- tests/fail/alloc/global_system_mixup.rs | 8 +- tests/fail/alloc/no_global_allocator.stderr | 4 +- tests/fail/alloc/reallocate-bad-size.rs | 2 +- tests/fail/alloc/reallocate-change-alloc.rs | 2 +- tests/fail/alloc/reallocate-dangling.rs | 2 +- tests/fail/alloc/stack_free.rs | 4 +- tests/fail/backtrace/bad-backtrace-decl.rs | 2 +- tests/fail/backtrace/bad-backtrace-flags.rs | 2 +- tests/fail/backtrace/bad-backtrace-ptr.rs | 2 +- .../backtrace/bad-backtrace-resolve-flags.rs | 2 +- .../bad-backtrace-resolve-names-flags.rs | 2 +- .../backtrace/bad-backtrace-size-flags.rs | 2 +- tests/fail/box-cell-alias.rs | 4 +- tests/fail/box-cell-alias.stderr | 4 +- tests/fail/branchless-select-i128-pointer.rs | 2 +- tests/fail/breakpoint.rs | 2 +- .../libc_pthread_create_main_terminate.rs | 4 +- .../concurrency/libc_pthread_join_detached.rs | 2 +- .../concurrency/libc_pthread_join_joined.rs | 2 +- .../concurrency/libc_pthread_join_main.rs | 2 +- .../concurrency/libc_pthread_join_multiple.rs | 2 +- .../concurrency/libc_pthread_join_self.rs | 4 +- tests/fail/concurrency/thread-spawn.rs | 4 +- .../thread_local_static_dealloc.rs | 4 +- tests/fail/concurrency/too_few_args.rs | 2 +- tests/fail/concurrency/too_many_args.rs | 2 +- tests/fail/concurrency/unwind_top_of_stack.rs | 6 +- .../dangling_pointer_addr_of.rs | 4 +- .../dangling_pointer_deref.rs | 4 +- .../dangling_pointers/dangling_zst_deref.rs | 4 +- .../dangling_pointers/deref-invalid-ptr.rs | 4 +- .../deref-partially-dangling.rs | 2 +- tests/fail/dangling_pointers/dyn_size.rs | 4 +- .../maybe_null_pointer_deref_zst.rs | 4 +- .../maybe_null_pointer_write_zst.rs | 4 +- .../dangling_pointers/null_pointer_deref.rs | 2 +- .../null_pointer_deref_zst.rs | 4 +- .../dangling_pointers/null_pointer_write.rs | 2 +- .../null_pointer_write_zst.rs | 4 +- .../dangling_pointers/out_of_bounds_read1.rs | 2 +- .../dangling_pointers/out_of_bounds_read2.rs | 2 +- .../fail/dangling_pointers/stack_temporary.rs | 4 +- .../storage_dead_dangling.rs | 4 +- .../dangling_pointers/wild_pointer_deref.rs | 4 +- tests/fail/data_race/alloc_read_race.rs | 6 +- tests/fail/data_race/alloc_write_race.rs | 6 +- .../data_race/atomic_read_na_write_race1.rs | 6 +- .../data_race/atomic_read_na_write_race2.rs | 6 +- .../data_race/atomic_write_na_read_race1.rs | 6 +- .../data_race/atomic_write_na_read_race2.rs | 6 +- .../data_race/atomic_write_na_write_race1.rs | 6 +- .../data_race/atomic_write_na_write_race2.rs | 6 +- .../data_race/dangling_thread_async_race.rs | 7 +- tests/fail/data_race/dangling_thread_race.rs | 7 +- tests/fail/data_race/dealloc_read_race1.rs | 6 +- tests/fail/data_race/dealloc_read_race2.rs | 6 +- .../fail/data_race/dealloc_read_race_stack.rs | 6 +- tests/fail/data_race/dealloc_write_race1.rs | 6 +- tests/fail/data_race/dealloc_write_race2.rs | 6 +- .../data_race/dealloc_write_race_stack.rs | 6 +- .../data_race/enable_after_join_to_main.rs | 6 +- tests/fail/data_race/fence_after_load.rs | 6 +- tests/fail/data_race/read_write_race.rs | 6 +- tests/fail/data_race/read_write_race_stack.rs | 6 +- tests/fail/data_race/relax_acquire_race.rs | 6 +- tests/fail/data_race/release_seq_race.rs | 6 +- .../data_race/release_seq_race_same_thread.rs | 6 +- tests/fail/data_race/rmw_race.rs | 6 +- tests/fail/data_race/stack_pop_race.rs | 25 + tests/fail/data_race/stack_pop_race.stderr | 20 + tests/fail/data_race/write_write_race.rs | 6 +- .../fail/data_race/write_write_race_stack.rs | 6 +- tests/fail/environ-gets-deallocated.rs | 4 +- tests/fail/erroneous_const.rs | 6 +- tests/fail/erroneous_const2.rs | 12 +- tests/fail/extern_static.rs | 2 +- tests/fail/extern_static_in_const.rs | 2 +- tests/fail/external_C/function_not_in_SO.rs | 13 + .../fail/external_C/function_not_in_SO.stderr | 14 + tests/fail/fast_math_both.rs | 2 +- tests/fail/fast_math_first.rs | 2 +- tests/fail/fast_math_second.rs | 2 +- tests/fail/fs/close_stdout.rs | 6 +- tests/fail/fs/isolated_file.rs | 4 +- tests/fail/fs/isolated_stdin.rs | 4 +- tests/fail/fs/read_from_stdout.rs | 6 +- .../fs/unix_open_missing_required_mode.rs | 6 +- tests/fail/fs/write_to_stdin.rs | 4 +- tests/fail/function_calls/check_arg_abi.rs | 2 +- .../function_calls/check_arg_count_abort.rs | 2 +- .../check_arg_count_too_few_args.rs | 2 +- .../check_arg_count_too_many_args.rs | 2 +- .../fail/function_calls/check_callback_abi.rs | 2 +- .../exported_symbol_abi_mismatch.rs | 8 +- .../exported_symbol_bad_unwind1.rs | 4 +- .../exported_symbol_bad_unwind2.both.stderr | 4 +- ...orted_symbol_bad_unwind2.definition.stderr | 4 +- .../exported_symbol_bad_unwind2.rs | 8 +- .../exported_symbol_clashing.rs | 6 +- .../exported_symbol_shim_clashing.rs | 4 +- .../exported_symbol_wrong_arguments.rs | 2 +- .../exported_symbol_wrong_type.rs | 2 +- .../cast_box_int_to_fn_ptr.rs | 4 +- tests/fail/function_pointers/cast_fn_ptr1.rs | 2 +- tests/fail/function_pointers/cast_fn_ptr2.rs | 2 +- tests/fail/function_pointers/cast_fn_ptr3.rs | 2 +- tests/fail/function_pointers/cast_fn_ptr4.rs | 2 +- tests/fail/function_pointers/cast_fn_ptr5.rs | 2 +- .../function_pointers/cast_int_to_fn_ptr.rs | 4 +- tests/fail/function_pointers/deref_fn_ptr.rs | 2 +- .../fail/function_pointers/execute_memory.rs | 4 +- tests/fail/function_pointers/fn_ptr_offset.rs | 4 +- tests/fail/generator-pinned-moved.rs | 4 +- tests/fail/intrinsics/assume.rs | 2 +- tests/fail/intrinsics/copy_overflow.rs | 2 +- tests/fail/intrinsics/copy_overlapping.rs | 2 +- tests/fail/intrinsics/copy_unaligned.rs | 2 +- tests/fail/intrinsics/ctlz_nonzero.rs | 2 +- tests/fail/intrinsics/cttz_nonzero.rs | 2 +- tests/fail/intrinsics/div-by-zero.rs | 2 +- tests/fail/intrinsics/exact_div1.rs | 2 +- tests/fail/intrinsics/exact_div2.rs | 2 +- tests/fail/intrinsics/exact_div3.rs | 2 +- tests/fail/intrinsics/exact_div4.rs | 2 +- tests/fail/intrinsics/out_of_bounds_ptr_1.rs | 2 +- tests/fail/intrinsics/out_of_bounds_ptr_2.rs | 2 +- tests/fail/intrinsics/out_of_bounds_ptr_3.rs | 2 +- tests/fail/intrinsics/ptr_offset_0_plus_0.rs | 4 +- tests/fail/intrinsics/ptr_offset_from_oob.rs | 2 +- .../intrinsics/ptr_offset_int_plus_int.rs | 4 +- .../intrinsics/ptr_offset_int_plus_ptr.rs | 4 +- tests/fail/intrinsics/ptr_offset_overflow.rs | 2 +- .../fail/intrinsics/ptr_offset_ptr_plus_0.rs | 2 +- tests/fail/intrinsics/rem-by-zero.rs | 2 +- tests/fail/intrinsics/simd-div-by-zero.rs | 2 +- tests/fail/intrinsics/simd-div-overflow.rs | 2 +- tests/fail/intrinsics/simd-float-to-int.rs | 2 +- tests/fail/intrinsics/simd-gather.rs | 2 +- .../intrinsics/simd-reduce-invalid-bool.rs | 2 +- tests/fail/intrinsics/simd-rem-by-zero.rs | 2 +- tests/fail/intrinsics/simd-scatter.rs | 2 +- .../intrinsics/simd-select-bitmask-invalid.rs | 2 +- .../intrinsics/simd-select-invalid-bool.rs | 2 +- tests/fail/intrinsics/simd-shl-too-far.rs | 2 +- tests/fail/intrinsics/simd-shr-too-far.rs | 2 +- tests/fail/intrinsics/unchecked_add1.rs | 2 +- tests/fail/intrinsics/unchecked_add2.rs | 2 +- tests/fail/intrinsics/unchecked_div1.rs | 2 +- tests/fail/intrinsics/unchecked_mul1.rs | 2 +- tests/fail/intrinsics/unchecked_mul2.rs | 2 +- tests/fail/intrinsics/unchecked_sub1.rs | 2 +- tests/fail/intrinsics/unchecked_sub2.rs | 2 +- tests/fail/intrinsics/write_bytes_null.rs | 2 +- tests/fail/intrinsics/write_bytes_overflow.rs | 2 +- tests/fail/invalid_bool.rs | 4 +- tests/fail/invalid_char.rs | 4 +- tests/fail/invalid_enum_tag.rs | 4 +- tests/fail/invalid_int.rs | 4 +- tests/fail/issue-miri-1112.rs | 2 +- tests/fail/memleak.rs | 4 +- tests/fail/memleak_rc.rs | 6 +- tests/fail/memleak_rc.stderr | 11 + tests/fail/modifying_constants.rs | 4 +- tests/fail/never_say_never.rs | 4 +- tests/fail/never_transmute_humans.rs | 4 +- tests/fail/never_transmute_void.rs | 7 +- tests/fail/no_main.rs | 2 +- tests/fail/panic/bad_miri_start_panic.rs | 4 +- tests/fail/panic/double_panic.rs | 6 +- tests/fail/panic/panic_abort1.rs | 8 +- tests/fail/panic/panic_abort2.rs | 8 +- tests/fail/panic/panic_abort3.rs | 8 +- tests/fail/panic/panic_abort4.rs | 8 +- tests/fail/panic/unwind_panic_abort.rs | 4 +- tests/fail/pointer_partial_overwrite.rs | 4 +- tests/fail/provenance/provenance_transmute.rs | 4 +- tests/fail/provenance/ptr_int_unexposed.rs | 4 +- tests/fail/provenance/ptr_invalid.rs | 2 +- tests/fail/provenance/ptr_invalid_offset.rs | 4 +- .../fail/provenance/strict_provenance_cast.rs | 4 +- tests/fail/rc_as_ptr.rs | 4 +- tests/fail/reading_half_a_pointer.rs | 2 +- tests/fail/rustc-error.rs | 2 +- tests/fail/shim_arg_size.rs | 4 +- tests/fail/should-pass/cpp20_rwc_syncs.rs | 6 +- .../stacked_borrows/alias_through_mutation.rs | 2 +- tests/fail/stacked_borrows/aliasing_mut1.rs | 2 +- .../fail/stacked_borrows/aliasing_mut1.stderr | 4 +- tests/fail/stacked_borrows/aliasing_mut2.rs | 2 +- .../fail/stacked_borrows/aliasing_mut2.stderr | 4 +- tests/fail/stacked_borrows/aliasing_mut3.rs | 2 +- .../fail/stacked_borrows/aliasing_mut3.stderr | 4 +- tests/fail/stacked_borrows/aliasing_mut4.rs | 2 +- .../fail/stacked_borrows/aliasing_mut4.stderr | 4 +- .../box_exclusive_violation1.rs | 6 +- .../box_exclusive_violation1.stderr | 4 +- .../stacked_borrows/buggy_as_mut_slice.rs | 2 +- .../stacked_borrows/buggy_split_at_mut.rs | 2 +- .../stacked_borrows/buggy_split_at_mut.stderr | 4 +- .../deallocate_against_barrier1.rs | 2 +- .../deallocate_against_barrier1.stderr | 4 +- .../deallocate_against_barrier2.rs | 2 +- .../deallocate_against_barrier2.stderr | 4 +- tests/fail/stacked_borrows/exposed_only_ro.rs | 4 +- tests/fail/stacked_borrows/illegal_read1.rs | 2 +- tests/fail/stacked_borrows/illegal_read2.rs | 2 +- tests/fail/stacked_borrows/illegal_read3.rs | 2 +- tests/fail/stacked_borrows/illegal_read4.rs | 2 +- tests/fail/stacked_borrows/illegal_read5.rs | 4 +- tests/fail/stacked_borrows/illegal_read6.rs | 2 +- tests/fail/stacked_borrows/illegal_read7.rs | 2 +- .../fail/stacked_borrows/illegal_read7.stderr | 4 +- tests/fail/stacked_borrows/illegal_read8.rs | 2 +- .../illegal_read_despite_exposed1.rs | 4 +- .../illegal_read_despite_exposed2.rs | 4 +- tests/fail/stacked_borrows/illegal_write1.rs | 2 +- tests/fail/stacked_borrows/illegal_write2.rs | 2 +- tests/fail/stacked_borrows/illegal_write3.rs | 2 +- tests/fail/stacked_borrows/illegal_write4.rs | 2 +- tests/fail/stacked_borrows/illegal_write5.rs | 2 +- tests/fail/stacked_borrows/illegal_write6.rs | 2 +- .../stacked_borrows/illegal_write6.stderr | 4 +- .../illegal_write_despite_exposed1.rs | 5 +- tests/fail/stacked_borrows/interior_mut1.rs | 2 +- .../fail/stacked_borrows/interior_mut1.stderr | 4 +- tests/fail/stacked_borrows/interior_mut2.rs | 2 +- .../fail/stacked_borrows/interior_mut2.stderr | 4 +- .../invalidate_against_barrier1.rs | 2 +- .../invalidate_against_barrier1.stderr | 4 +- .../invalidate_against_barrier2.rs | 2 +- .../invalidate_against_barrier2.stderr | 4 +- .../fail/stacked_borrows/issue-miri-1050-1.rs | 4 +- .../stacked_borrows/issue-miri-1050-1.stderr | 4 +- .../fail/stacked_borrows/issue-miri-1050-2.rs | 4 +- .../stacked_borrows/issue-miri-1050-2.stderr | 4 +- .../fail/stacked_borrows/load_invalid_mut.rs | 4 +- .../stacked_borrows/load_invalid_mut.stderr | 4 +- .../fail/stacked_borrows/load_invalid_shr.rs | 4 +- .../stacked_borrows/load_invalid_shr.stderr | 4 +- .../mut_exclusive_violation1.rs | 2 +- .../mut_exclusive_violation2.rs | 2 +- .../fail/stacked_borrows/newtype_retagging.rs | 4 +- .../stacked_borrows/newtype_retagging.stderr | 4 +- tests/fail/stacked_borrows/outdated_local.rs | 2 +- .../fail/stacked_borrows/pass_invalid_mut.rs | 2 +- .../stacked_borrows/pass_invalid_mut.stderr | 4 +- .../fail/stacked_borrows/pass_invalid_shr.rs | 2 +- .../stacked_borrows/pass_invalid_shr.stderr | 4 +- .../fail/stacked_borrows/pointer_smuggling.rs | 2 +- tests/fail/stacked_borrows/raw_tracking.rs | 2 +- .../stacked_borrows/return_invalid_mut.rs | 2 +- .../stacked_borrows/return_invalid_mut.stderr | 4 +- .../return_invalid_mut_option.rs | 2 +- .../return_invalid_mut_option.stderr | 4 +- .../return_invalid_mut_tuple.rs | 2 +- .../return_invalid_mut_tuple.stderr | 4 +- .../stacked_borrows/return_invalid_shr.rs | 2 +- .../stacked_borrows/return_invalid_shr.stderr | 4 +- .../return_invalid_shr_option.rs | 2 +- .../return_invalid_shr_option.stderr | 4 +- .../return_invalid_shr_tuple.rs | 2 +- .../return_invalid_shr_tuple.stderr | 4 +- .../shared_rw_borrows_are_weak1.rs | 2 +- .../shared_rw_borrows_are_weak1.stderr | 4 +- .../shared_rw_borrows_are_weak2.rs | 4 +- .../stacked_borrows/shr_frozen_violation1.rs | 2 +- .../static_memory_modification.rs | 2 +- .../stacked_borrows/transmute-is-no-escape.rs | 2 +- tests/fail/stacked_borrows/unescaped_local.rs | 4 +- .../fail/stacked_borrows/unescaped_static.rs | 2 +- tests/fail/stacked_borrows/vtable.rs | 2 +- tests/fail/stacked_borrows/zst_slice.rs | 4 +- tests/fail/stacked_borrows/zst_slice.stderr | 4 +- tests/fail/static_memory_modification1.rs | 4 +- tests/fail/static_memory_modification2.rs | 4 +- tests/fail/static_memory_modification3.rs | 4 +- .../sync/libc_pthread_cond_double_destroy.rs | 4 +- .../libc_pthread_condattr_double_destroy.rs | 4 +- .../sync/libc_pthread_mutex_NULL_deadlock.rs | 4 +- .../fail/sync/libc_pthread_mutex_deadlock.rs | 2 +- .../libc_pthread_mutex_default_deadlock.rs | 4 +- .../sync/libc_pthread_mutex_destroy_locked.rs | 4 +- .../sync/libc_pthread_mutex_double_destroy.rs | 4 +- .../libc_pthread_mutex_normal_deadlock.rs | 4 +- ...bc_pthread_mutex_normal_unlock_unlocked.rs | 4 +- .../sync/libc_pthread_mutex_wrong_owner.rs | 2 +- .../libc_pthread_mutexattr_double_destroy.rs | 4 +- ...libc_pthread_rwlock_destroy_read_locked.rs | 4 +- ...ibc_pthread_rwlock_destroy_write_locked.rs | 4 +- .../libc_pthread_rwlock_double_destroy.rs | 4 +- ...wlock_read_write_deadlock_single_thread.rs | 2 +- .../libc_pthread_rwlock_read_wrong_owner.rs | 2 +- .../libc_pthread_rwlock_unlock_unlocked.rs | 4 +- ...libc_pthread_rwlock_write_read_deadlock.rs | 2 +- ...wlock_write_read_deadlock_single_thread.rs | 2 +- ...ibc_pthread_rwlock_write_write_deadlock.rs | 2 +- ...lock_write_write_deadlock_single_thread.rs | 2 +- .../libc_pthread_rwlock_write_wrong_owner.rs | 2 +- tests/fail/transmute-pair-uninit.rs | 2 +- tests/fail/type-too-large.rs | 4 +- tests/fail/unaligned_pointers/alignment.rs | 2 +- .../unaligned_pointers/atomic_unaligned.rs | 4 +- .../fail/unaligned_pointers/dyn_alignment.rs | 4 +- .../intptrcast_alignment_check.rs | 4 +- .../unaligned_pointers/reference_to_packed.rs | 4 +- .../fail/unaligned_pointers/unaligned_ptr1.rs | 4 +- .../fail/unaligned_pointers/unaligned_ptr2.rs | 4 +- .../fail/unaligned_pointers/unaligned_ptr3.rs | 4 +- .../fail/unaligned_pointers/unaligned_ptr4.rs | 4 +- .../unaligned_ptr_addr_of.rs | 4 +- .../unaligned_pointers/unaligned_ptr_zst.rs | 4 +- tests/fail/uninit_buffer.rs | 2 +- tests/fail/uninit_buffer.stderr | 6 +- tests/fail/uninit_byte_read.rs | 4 +- tests/fail/uninit_raw_ptr.rs | 2 +- tests/fail/unreachable.rs | 2 +- tests/fail/unsized-local.rs | 23 + tests/fail/unsized-local.stderr | 14 + tests/fail/unsupported_foreign_function.rs | 2 +- .../fail/unsupported_foreign_function.stderr | 4 +- tests/fail/unsupported_signal.rs | 4 +- tests/fail/unsupported_signal.stderr | 4 +- tests/fail/validity/cast_fn_ptr1.rs | 4 +- tests/fail/validity/cast_fn_ptr2.rs | 4 +- tests/fail/validity/dangling_ref1.rs | 4 +- tests/fail/validity/dangling_ref2.rs | 4 +- tests/fail/validity/dangling_ref3.rs | 4 +- tests/fail/validity/invalid_bool.rs | 2 +- tests/fail/validity/invalid_bool_uninit.rs | 2 +- tests/fail/validity/invalid_char.rs | 2 +- tests/fail/validity/invalid_char_uninit.rs | 2 +- tests/fail/validity/invalid_enum_tag.rs | 2 +- .../invalid_enum_tag_256variants_uninit.rs | 4 +- tests/fail/validity/invalid_fnptr_null.rs | 2 +- tests/fail/validity/invalid_fnptr_uninit.rs | 2 +- tests/fail/validity/nonzero.rs | 4 +- .../validity/ptr_integer_array_transmute.rs | 2 +- tests/fail/validity/ref_to_uninhabited1.rs | 2 +- tests/fail/validity/ref_to_uninhabited2.rs | 2 +- tests/fail/validity/transmute_through_ptr.rs | 2 +- tests/fail/validity/uninit_float.rs | 6 +- tests/fail/validity/uninit_float.stderr | 4 +- tests/fail/validity/uninit_integer.rs | 2 +- tests/fail/validity/uninit_integer_signed.rs | 6 - .../validity/uninit_integer_signed.stderr | 15 - tests/fail/weak_memory/racing_mixed_size.rs | 4 +- .../weak_memory/racing_mixed_size_read.rs | 4 +- tests/fail/zst1.rs | 2 +- tests/fail/zst2.rs | 4 +- tests/fail/zst3.rs | 4 +- tests/panic/external_C/incorrect_SO_file.rs | 16 + .../panic/external_C/incorrect_SO_file.stderr | 15 + tests/panic/panic/panic1.rs | 4 +- .../panic/unsupported_foreign_function.rs | 2 +- .../panic/unsupported_foreign_function.stderr | 2 +- tests/panic/panic/unsupported_syscall.rs | 6 +- tests/pass/0weak_memory_consistency.rs | 57 +- tests/pass/adjacent-allocs.rs | 2 +- tests/pass/align.rs | 2 +- tests/pass/align_offset_symbolic.rs | 2 +- ...atomic-compare-exchange-weak-never-fail.rs | 2 +- tests/pass/atomic.rs | 100 +- tests/pass/backtrace/backtrace-api-v0.rs | 2 +- tests/pass/backtrace/backtrace-api-v1.rs | 2 +- .../pass/backtrace/backtrace-global-alloc.rs | 2 +- tests/pass/backtrace/backtrace-std.rs | 2 +- tests/pass/btreemap.rs | 2 +- tests/pass/calloc.rs | 2 +- tests/pass/concurrency/channels.rs | 4 +- .../concurrency/concurrent_caller_location.rs | 2 +- tests/pass/concurrency/data_race.rs | 4 +- .../concurrency/disable_data_race_detector.rs | 4 +- tests/pass/concurrency/issue1643.rs | 2 +- tests/pass/concurrency/libc_pthread_cond.rs | 6 +- tests/pass/concurrency/linux-futex.rs | 4 +- tests/pass/concurrency/mutex_leak.rs | 2 +- tests/pass/concurrency/simple.rs | 4 +- tests/pass/concurrency/spin_loop.rs | 2 +- .../pass/concurrency/spin_loops_nopreempt.rs | 4 +- tests/pass/concurrency/sync.rs | 9 +- tests/pass/concurrency/sync_nopreempt.rs | 4 +- tests/pass/concurrency/thread_locals.rs | 4 +- tests/pass/concurrency/tls_lib_drop.rs | 2 +- .../concurrency/tls_pthread_drop_order.rs | 2 +- tests/pass/current_dir.rs | 2 +- tests/pass/current_dir_with_isolation.rs | 6 +- tests/pass/disable-alignment-check.rs | 2 +- tests/pass/dyn-traits.rs | 12 +- tests/pass/enum_discriminant_ptr_value.rs | 2 +- tests/pass/env-exclude.rs | 2 +- tests/pass/env-forward.rs | 2 +- tests/pass/env-without-isolation.rs | 2 +- tests/pass/external_C/int_c_tests.rs | 43 + tests/pass/external_C/print_from_c.rs | 15 + tests/pass/external_C/print_from_c.stdout | 2 + tests/pass/fs.rs | 4 +- tests/pass/fs_with_isolation.rs | 6 +- .../pass/function_calls/disable_abi_check.rs | 2 +- tests/pass/getpid.rs | 2 +- tests/pass/hide_stdout.rs | 2 +- tests/pass/integer-ops.rs | 2 +- tests/pass/intptrcast.rs | 2 +- tests/pass/intrinsics.rs | 2 +- tests/pass/issues/issue-miri-1925.rs | 2 +- tests/pass/issues/issue-miri-2068-2.rs | 2 +- tests/pass/libc.rs | 39 +- .../pass/linux-getrandom-without-isolation.rs | 4 +- tests/pass/linux-getrandom.rs | 2 +- tests/pass/malloc.rs | 2 +- tests/pass/memleak_ignored.rs | 2 +- tests/pass/move-uninit-primval.rs | 2 +- tests/pass/no_std.rs | 2 +- tests/pass/observed_local_mut.rs | 2 +- tests/pass/overflow_checks_off.rs | 2 +- tests/pass/panic/catch_panic.rs | 2 +- tests/pass/panic/concurrent-panic.rs | 4 +- tests/pass/portable-simd.rs | 2 +- tests/pass/ptr_int_casts.rs | 2 +- tests/pass/ptr_int_from_exposed.rs | 2 +- tests/pass/ptr_offset.rs | 2 +- tests/pass/rc.rs | 2 +- tests/pass/slices.rs | 2 +- tests/pass/stacked-borrows/int-to-ptr.rs | 2 +- .../stacked-borrows/interior_mutability.rs | 2 +- tests/pass/stacked-borrows/stacked-borrows.rs | 2 +- tests/pass/strings.rs | 2 +- tests/pass/threadleak_ignored.rs | 4 +- tests/pass/time.rs | 2 +- tests/pass/track-alloc-1.rs | 4 +- tests/pass/transmute_fat.rs | 3 +- tests/pass/uninit_number_ignored.rs | 2 +- tests/pass/union-overwrite.rs | 2 - tests/pass/union.rs | 3 +- tests/pass/unsized-tuple-impls.rs | 13 - tests/pass/unsized.rs | 36 + tests/pass/vec.rs | 2 +- tests/pass/vecdeque.rs | 2 +- tests/pass/weak_memory/extra_cpp.rs | 4 +- tests/pass/weak_memory/extra_cpp_unsafe.rs | 4 +- tests/pass/weak_memory/weak.rs | 69 +- tests/pass/without-validation.rs | 2 +- tests/pass/wtf8.rs | 2 +- tests/pass/zst.rs | 2 +- triagebot.toml | 11 + ui_test/Cargo.lock | 194 +++ ui_test/Cargo.toml | 2 + ui_test/README.md | 45 +- ui_test/src/comments.rs | 175 -- ui_test/src/comments/tests.rs | 22 - ui_test/src/lib.rs | 101 +- ui_test/src/parser.rs | 327 ++++ ui_test/src/parser/tests.rs | 85 + ui_test/src/rustc_stderr.rs | 7 +- ui_test/src/tests.rs | 64 +- 498 files changed, 4503 insertions(+), 2876 deletions(-) create mode 100644 bench-cargo-miri/slice-get-unchecked/Cargo.lock create mode 100644 bench-cargo-miri/slice-get-unchecked/Cargo.toml create mode 100644 bench-cargo-miri/slice-get-unchecked/src/main.rs create mode 100644 src/c_ffi_support.rs delete mode 100644 src/shims/intrinsics.rs create mode 100644 src/shims/intrinsics/atomic.rs create mode 100644 src/shims/intrinsics/mod.rs create mode 100644 src/shims/intrinsics/simd.rs create mode 100644 src/stacked_borrows/item.rs rename src/{stacked_borrows.rs => stacked_borrows/mod.rs} (80%) create mode 100644 tests/external_C/test.c create mode 100644 tests/fail/data_race/stack_pop_race.rs create mode 100644 tests/fail/data_race/stack_pop_race.stderr create mode 100644 tests/fail/external_C/function_not_in_SO.rs create mode 100644 tests/fail/external_C/function_not_in_SO.stderr create mode 100644 tests/fail/memleak_rc.stderr create mode 100644 tests/fail/unsized-local.rs create mode 100644 tests/fail/unsized-local.stderr delete mode 100644 tests/fail/validity/uninit_integer_signed.rs delete mode 100644 tests/fail/validity/uninit_integer_signed.stderr create mode 100644 tests/panic/external_C/incorrect_SO_file.rs create mode 100644 tests/panic/external_C/incorrect_SO_file.stderr create mode 100644 tests/pass/external_C/int_c_tests.rs create mode 100644 tests/pass/external_C/print_from_c.rs create mode 100644 tests/pass/external_C/print_from_c.stdout delete mode 100644 tests/pass/unsized-tuple-impls.rs create mode 100644 tests/pass/unsized.rs create mode 100644 triagebot.toml delete mode 100644 ui_test/src/comments.rs delete mode 100644 ui_test/src/comments/tests.rs create mode 100644 ui_test/src/parser.rs create mode 100644 ui_test/src/parser/tests.rs diff --git a/.gitignore b/.gitignore index dcefbc62c7..68b7fd40e8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,11 @@ target /doc tex/*/out *.dot +*.out *.rs.bk .vscode *.mm_profdata perf.data perf.data.old flamegraph.svg +tests/external_C/libtestlib.so diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bde1921eb8..a57ef09e7d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,11 +28,6 @@ install that exact version of rustc as a toolchain: This will set up a rustup toolchain called `miri` and set it as an override for the current directory. -If you want to also have `clippy` installed, you need to run this: -``` -./rustup-toolchain "" -c clippy -``` - [`rustup-toolchain-install-master`]: https://github.com/kennytm/rustup-toolchain-install-master ## Building and testing Miri @@ -131,6 +126,8 @@ development version of Miri using and then you can use it as if it was installed by `rustup`. Make sure you use the same toolchain when calling `cargo miri` that you used when installing Miri! +Usually this means you have to write `cargo +miri miri ...` to select the `miri` +toolchain that was installed by `./rustup-toolchain`. There's a test for the cargo wrapper in the `test-cargo-miri` directory; run `./run-test.py` in there to execute it. Like `./miri test`, this respects the diff --git a/Cargo.lock b/Cargo.lock index 880fa271d9..8ce82d9df8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,27 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "abort_on_panic" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955f37ac58af2416bac687c8ab66a4ccba282229bd7422a28d2281a5e66a6116" + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "0.7.18" @@ -37,18 +58,66 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "color-eyre" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ebf286c900a6d5867aeff75cfee3192857bb7f24b547d4f0df2ed6baa812c90" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + [[package]] name = "colored" version = "2.0.0" @@ -158,6 +227,16 @@ dependencies = [ "termcolor", ] +[[package]] +name = "eyre" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "getrandom" version = "0.2.3" @@ -169,6 +248,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -184,6 +269,12 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "instant" version = "0.1.12" @@ -211,6 +302,36 @@ version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" +[[package]] +name = "libffi" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e08093a2ddeee94bd0c830a53d895ff91f1f3bb0f9b3c8c6b00739cdf76bc1d" +dependencies = [ + "abort_on_panic", + "libc", + "libffi-sys", +] + +[[package]] +name = "libffi-sys" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab4106b7f09d7b87d021334d5618fac1dfcfb824d4c5fe111ff0074dfd242e15" +dependencies = [ + "cc", +] + +[[package]] +name = "libloading" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "lock_api" version = "0.4.5" @@ -267,6 +388,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +dependencies = [ + "adler", +] + [[package]] name = "miri" version = "0.1.0" @@ -276,6 +406,8 @@ dependencies = [ "getrandom", "lazy_static", "libc", + "libffi", + "libloading", "log", "measureme", "rand", @@ -286,6 +418,21 @@ dependencies = [ "ui_test", ] +[[package]] +name = "object" +version = "0.28.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + [[package]] name = "output_vt100" version = "0.1.3" @@ -295,6 +442,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "owo-colors" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "decf7381921fea4dcb2549c5667eda59b3ec297ab7e2b5fc33eac69d2e7da87b" + [[package]] name = "parking_lot" version = "0.11.2" @@ -329,6 +482,12 @@ dependencies = [ "libc", ] +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + [[package]] name = "ppv-lite86" version = "0.2.15" @@ -431,6 +590,12 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -501,6 +666,15 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + [[package]] name = "shell-escape" version = "0.1.5" @@ -533,10 +707,62 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tracing" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a713421342a5a666b7577783721d3117f1b69a393df803ee17bb73b1e122a59" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + [[package]] name = "ui_test" version = "0.1.0" dependencies = [ + "color-eyre", "colored", "crossbeam", "lazy_static", @@ -553,6 +779,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index af73a7af31..e89c79f1b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,8 @@ doctest = false # and no doc tests [dependencies] getrandom = { version = "0.2", features = ["std"] } env_logger = "0.9" +libffi = "3.0.0" +libloading = "0.7" log = "0.4" shell-escape = "0.1.4" rand = "0.8" @@ -54,6 +56,7 @@ harness = false [features] default = ["stack-cache"] -# Will be enabled on CI via `--all-features`. -expensive-debug-assertions = [] stack-cache = [] + +[profile.dev] +opt-level = 2 # because it's too slow otherwise diff --git a/README.md b/README.md index 52934a6cd4..7679537e7f 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ in your program, and cannot run all programs: not support networking. System API support varies between targets; if you run on Windows it is a good idea to use `--target x86_64-unknown-linux-gnu` to get better support. -* Weak memory emulation may [produce weak behaivours](https://github.com/rust-lang/miri/issues/2301) +* Weak memory emulation may [produce weak behaviours](https://github.com/rust-lang/miri/issues/2301) unobservable by compiled programs running on real hardware when `SeqCst` fences are used, and it cannot produce all behaviors possibly observable on real hardware. diff --git a/bench-cargo-miri/slice-get-unchecked/Cargo.lock b/bench-cargo-miri/slice-get-unchecked/Cargo.lock new file mode 100644 index 0000000000..a375afaed3 --- /dev/null +++ b/bench-cargo-miri/slice-get-unchecked/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "slice-get-unchecked" +version = "0.1.0" diff --git a/bench-cargo-miri/slice-get-unchecked/Cargo.toml b/bench-cargo-miri/slice-get-unchecked/Cargo.toml new file mode 100644 index 0000000000..1ac2276866 --- /dev/null +++ b/bench-cargo-miri/slice-get-unchecked/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "slice-get-unchecked" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/bench-cargo-miri/slice-get-unchecked/src/main.rs b/bench-cargo-miri/slice-get-unchecked/src/main.rs new file mode 100644 index 0000000000..a72083bd9d --- /dev/null +++ b/bench-cargo-miri/slice-get-unchecked/src/main.rs @@ -0,0 +1,12 @@ +//! This is a stripped-down version of the code pattern that causes runtime blowup when printing +//! backtraces in a failed test under cargo miri test with -Zmiri-disable-isolation. +//! See https://github.com/rust-lang/miri/issues/2273 + +fn main() { + let x = vec![0u8; 4096]; + let mut i = 0; + while i < x.len() { + let _element = unsafe { *x.get_unchecked(i) }; + i += 1; + } +} diff --git a/ci.sh b/ci.sh index e7c6ed833c..8c63a57a4f 100755 --- a/ci.sh +++ b/ci.sh @@ -5,11 +5,11 @@ set -x # Determine configuration export RUSTFLAGS="-D warnings" export CARGO_INCREMENTAL=0 -export CARGO_EXTRA_FLAGS="--all-features" # in particular, expensive-debug-assertions +export CARGO_EXTRA_FLAGS="--all-features" # Prepare echo "Build and install miri" -CARGO_EXTRA_FLAGS="" ./miri install # implicitly locked -- and the *installed* Miri does *not* get the expensive-debug-assertions feature +./miri install # implicitly locked ./miri build --all-targets --locked # the build that all the `./miri test` below will use echo diff --git a/miri b/miri index 2debf70c16..5b95c00925 100755 --- a/miri +++ b/miri @@ -6,7 +6,8 @@ USAGE=$(cat <<"EOF" ./miri install : Installs the miri driver and cargo-miri. are passed to `cargo install`. Sets up the rpath such that the installed binary should work in any -working directory. +working directory. However, the rustup toolchain when invoking `cargo miri` +needs to be the same one used for `./miri install`. ./miri build : Just build miri. are passed to `cargo build`. @@ -22,15 +23,11 @@ to the final `cargo test` invocation. Build miri, set up a sysroot and then run the driver with the given . (Also respects MIRIFLAGS environment variable.) -The commands above also exist in a "-debug" variant (e.g. "./miri run-debug -") which uses debug builds instead of release builds, for faster build -times and slower execution times. - ./miri fmt : Format all sources and tests. are passed to `rustfmt`. ./miri clippy : -Format all sources and tests. are passed to `cargo clippy`. +Runs clippy on all sources. are passed to `cargo clippy`. ./miri many-seeds : Runs over and over again with different seeds for Miri. The MIRIFLAGS @@ -56,6 +53,11 @@ EOF MIRIDIR=$(python3 -c 'import os, sys; print(os.path.dirname(os.path.realpath(sys.argv[1])))' "$0") TOOLCHAIN=$(cd "$MIRIDIR"; rustup show active-toolchain | head -n 1 | cut -d ' ' -f 1) +# if $CC is not set, use gcc as default +if [[ -z $CC ]]; then + CC=cc +fi + # Determine command. COMMAND="$1" [ $# -gt 0 ] && shift @@ -99,40 +101,21 @@ fi # Prepare flags for cargo and rustc. CARGO="cargo +$TOOLCHAIN" -if [ -z "$CARGO_INCREMENTAL" ]; then - # Default CARGO_INCREMENTAL to 1. - export CARGO_INCREMENTAL=1 -fi if [ -z "$CARGO_TARGET_DIR" ]; then # Share target dir between `miri` and `cargo-miri`. export CARGO_TARGET_DIR="$MIRIDIR/target" fi # We set the rpath so that Miri finds the private rustc libraries it needs. -# We enable debug-assertions to get tracing. -# We enable line-only debuginfo for backtraces. -export RUSTFLAGS="-C link-args=-Wl,-rpath,$LIBDIR -C debug-assertions -C debuginfo=1 $RUSTFLAGS" -# Determine flags passed to all cargo invocations. -# This is a bit more annoying that one would hope due to -# . -case "$COMMAND" in -*-debug) - CARGO_INSTALL_FLAGS="--target $TARGET --debug $CARGO_EXTRA_FLAGS" - CARGO_BUILD_FLAGS="--target $TARGET $CARGO_EXTRA_FLAGS" - ;; -*) - CARGO_INSTALL_FLAGS="--target $TARGET $CARGO_EXTRA_FLAGS" - CARGO_BUILD_FLAGS="--target $TARGET --release $CARGO_EXTRA_FLAGS" - ;; -esac +export RUSTFLAGS="-C link-args=-Wl,-rpath,$LIBDIR $RUSTFLAGS" ## Helper functions # Build a sysroot and set MIRI_SYSROOT to use it. Arguments are passed to `cargo miri setup`. build_sysroot() { # Build once, for the user to see. - $CARGO run $CARGO_BUILD_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml -- miri setup "$@" + $CARGO run $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml -- miri setup "$@" # Call again, to just set env var. - export MIRI_SYSROOT="$($CARGO run $CARGO_BUILD_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml -q -- miri setup --print-sysroot "$@")" + export MIRI_SYSROOT="$($CARGO run $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml -q -- miri setup --print-sysroot "$@")" } # Prepare and set MIRI_SYSROOT. Respects `MIRI_TEST_TARGET` and takes into account @@ -154,37 +137,41 @@ find_sysroot() { # Run command. case "$COMMAND" in -install|install-debug) - # "--locked" to respect the Cargo.lock file if it exists, - # "--offline" to avoid querying the registry (for yanked packages). - $CARGO install $CARGO_INSTALL_FLAGS --path "$MIRIDIR" --force --locked --offline "$@" - $CARGO install $CARGO_INSTALL_FLAGS --path "$MIRIDIR"/cargo-miri --force --locked --offline "$@" +install) + # "--locked" to respect the Cargo.lock file if it exists. + $CARGO install $CARGO_EXTRA_FLAGS --path "$MIRIDIR" --force --locked "$@" + $CARGO install $CARGO_EXTRA_FLAGS --path "$MIRIDIR"/cargo-miri --force --locked "$@" ;; -check|check-debug) +check) # Check, and let caller control flags. - $CARGO check $CARGO_BUILD_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml --all-targets "$@" - $CARGO check $CARGO_BUILD_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml "$@" + $CARGO check $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml --all-targets "$@" + $CARGO check $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml "$@" ;; -build|build-debug) +build) # Build, and let caller control flags. - $CARGO build $CARGO_BUILD_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml "$@" - $CARGO build $CARGO_BUILD_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml "$@" + $CARGO build $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml "$@" + $CARGO build $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml "$@" ;; -test|test-debug|bless|bless-debug) +test|bless) # First build and get a sysroot. - $CARGO build $CARGO_BUILD_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml + $CARGO build $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml find_sysroot - case "$COMMAND" in - bless|bless-debug) + # if we're on linux + if [[ $OSTYPE == 'linux'* ]]; then + # rebuild the C test shared object file if it's not there + if [ ! -f "tests/external_C/libtestlib.so" ]; then + $CC -shared -o tests/external_C/libtestlib.so tests/external_C/test.c + fi + fi + if [ "$COMMAND" = "bless" ]; then export MIRI_BLESS="Gesundheit" - ;; - esac + fi # Then test, and let caller control flags. # Only in root project and ui_test as `cargo-miri` has no tests. - $CARGO test $CARGO_BUILD_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml "$@" - $CARGO test $CARGO_BUILD_FLAGS --manifest-path "$MIRIDIR"/ui_test/Cargo.toml "$@" + $CARGO test $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml "$@" + $CARGO test $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/ui_test/Cargo.toml "$@" ;; -run|run-debug) +run) # Scan for "--target" to overwrite the "MIRI_TEST_TARGET" env var so # that we set the MIRI_SYSROOT up the right way. FOUND_TARGET_OPT=0 @@ -202,19 +189,19 @@ run|run-debug) MIRIFLAGS="$MIRIFLAGS --target $MIRI_TEST_TARGET" fi # First build and get a sysroot. - $CARGO build $CARGO_BUILD_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml + $CARGO build $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml find_sysroot # Then run the actual command. - exec $CARGO run $CARGO_BUILD_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml -- --sysroot "$MIRI_SYSROOT" $MIRIFLAGS "$@" + exec $CARGO run $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml -- --sysroot "$MIRI_SYSROOT" $MIRIFLAGS "$@" ;; fmt) find "$MIRIDIR" -not \( -name target -prune \) -name '*.rs' \ | xargs rustfmt +$TOOLCHAIN --edition=2021 --config-path "$MIRIDIR/rustfmt.toml" "$@" ;; clippy) - $CARGO clippy $CARGO_BUILD_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml --all-targets "$@" - $CARGO clippy $CARGO_BUILD_FLAGS --manifest-path "$MIRIDIR"/ui_test/Cargo.toml --all-targets "$@" - $CARGO clippy $CARGO_BUILD_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml "$@" + $CARGO clippy $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml --all-targets "$@" + $CARGO clippy $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/ui_test/Cargo.toml --all-targets "$@" + $CARGO clippy $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml "$@" ;; *) if [ -n "$COMMAND" ]; then diff --git a/rust-version b/rust-version index 123c4a5b5b..362bcc35ea 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -f342bea9d19f14616c6559312552e6d0ee529cfd +880416180b0a9ee1141c07d4d17667edb77daebd diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 4f00e4be18..5e99483a92 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -487,6 +487,19 @@ fn main() { "full" => BacktraceStyle::Full, _ => panic!("-Zmiri-backtrace may only be 0, 1, or full"), }; + } else if let Some(param) = arg.strip_prefix("-Zmiri-external_c_so_file=") { + let filename = param.to_string(); + if std::path::Path::new(&filename).exists() { + if let Some(other_filename) = miri_config.external_c_so_file { + panic!( + "-Zmiri-external_so_file external SO file is already set to {}", + other_filename.display() + ); + } + miri_config.external_c_so_file = Some(filename.into()); + } else { + panic!("-Zmiri-external_c_so_file path {} does not exist", filename); + } } else { // Forward to rustc. rustc_args.push(arg); diff --git a/src/c_ffi_support.rs b/src/c_ffi_support.rs new file mode 100644 index 0000000000..e0b7cad83d --- /dev/null +++ b/src/c_ffi_support.rs @@ -0,0 +1,302 @@ +use libffi::{high::call::*, low::CodePtr}; + +use rustc_ast::{IntTy, UintTy}; +use rustc_hir::{self as hir, FnRetTy, Node, Ty}; +use rustc_span::Symbol; +use rustc_target::abi::HasDataLayout; + +use crate::*; + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} + +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { + /// Extract the scalar value from the result of reading a scalar from the machine, + /// and convert it to a `CArg`. + fn extract_scalar( + k: ScalarMaybeUninit, + arg_type: &Ty<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, CArg> { + if let &hir::Ty { + hir_id: _, + kind: + hir::TyKind::Path(hir::QPath::Resolved( + _, + hir::Path { span: _, res: hir::def::Res::PrimTy(prim_type), .. }, + .., + )), + .. + } = arg_type + { + // If the primitive provided can be converted to a type matching the hir type pattern + // then create a `CArg` of this primitive value with the corresponding `CArg` constructor. + match prim_type { + // the ints + hir::PrimTy::Int(IntTy::I8) => { + return Ok(CArg::Int8(k.to_i8()?)); + } + hir::PrimTy::Int(IntTy::I16) => { + return Ok(CArg::Int16(k.to_i16()?)); + } + hir::PrimTy::Int(IntTy::I32) => { + return Ok(CArg::Int32(k.to_i32()?)); + } + hir::PrimTy::Int(IntTy::I64) => { + return Ok(CArg::Int64(k.to_i64()?)); + } + hir::PrimTy::Int(IntTy::Isize) => { + return Ok(CArg::ISize(k.to_machine_isize(cx)?.try_into().unwrap())); + } + // the uints + hir::PrimTy::Uint(UintTy::U8) => { + return Ok(CArg::UInt8(k.to_u8()?)); + } + hir::PrimTy::Uint(UintTy::U16) => { + return Ok(CArg::UInt16(k.to_u16()?)); + } + hir::PrimTy::Uint(UintTy::U32) => { + return Ok(CArg::UInt32(k.to_u32()?)); + } + hir::PrimTy::Uint(UintTy::U64) => { + return Ok(CArg::UInt64(k.to_u64()?)); + } + hir::PrimTy::Uint(UintTy::Usize) => { + return Ok(CArg::USize(k.to_machine_usize(cx)?.try_into().unwrap())); + } + _ => {} + } + } + // If no primitives were returned then we have an unsupported type. + throw_unsup_format!("Unsupported scalar argument type to external C function: {:?}", k); + } + + /// Call external C function and + /// store output, depending on return type in the function signature. + fn call_external_c_and_store_return<'a>( + &mut self, + external_fct_defn: ExternalCFuncDeclRep<'tcx>, + dest: &PlaceTy<'tcx, Tag>, + ptr: CodePtr, + libffi_args: Vec>, + ) -> InterpResult<'tcx, ()> { + let this = self.eval_context_mut(); + + // Unsafe because of the call to external C code. + // Because this is calling a C function it is not necessarily sound, + // but there is no way around this and we've checked as much as we can. + unsafe { + // If the return type of a function is a primitive integer type pattern in hir, + // then call the function (`ptr`) with arguments `libffi_args`, store the return value as the specified + // primitive integer type, and then write this value out to the miri memory as an integer. + if let hir::FnRetTy::Return(&hir::Ty { + hir_id: _, + kind: + hir::TyKind::Path(hir::QPath::Resolved( + _, + hir::Path { span: _, res: hir::def::Res::PrimTy(prim_type), .. }, + .., + )), + .. + }) = external_fct_defn.output_type + { + match prim_type { + // ints + hir::PrimTy::Int(IntTy::I8) => { + let x = call::(ptr, libffi_args.as_slice()); + this.write_int(x, dest)?; + return Ok(()); + } + hir::PrimTy::Int(IntTy::I16) => { + let x = call::(ptr, libffi_args.as_slice()); + this.write_int(x, dest)?; + return Ok(()); + } + hir::PrimTy::Int(IntTy::I32) => { + let x = call::(ptr, libffi_args.as_slice()); + this.write_int(x, dest)?; + return Ok(()); + } + hir::PrimTy::Int(IntTy::I64) => { + let x = call::(ptr, libffi_args.as_slice()); + this.write_int(x, dest)?; + return Ok(()); + } + hir::PrimTy::Int(IntTy::Isize) => { + let x = call::(ptr, libffi_args.as_slice()); + // `isize` doesn't `impl Into`, so convert manually. + // Convert to `i64` since this covers both 32- and 64-bit machines. + this.write_int(i64::try_from(x).unwrap(), dest)?; + return Ok(()); + } + // uints + hir::PrimTy::Uint(UintTy::U8) => { + let x = call::(ptr, libffi_args.as_slice()); + this.write_int(x, dest)?; + return Ok(()); + } + hir::PrimTy::Uint(UintTy::U16) => { + let x = call::(ptr, libffi_args.as_slice()); + this.write_int(x, dest)?; + return Ok(()); + } + hir::PrimTy::Uint(UintTy::U32) => { + let x = call::(ptr, libffi_args.as_slice()); + this.write_int(x, dest)?; + return Ok(()); + } + hir::PrimTy::Uint(UintTy::U64) => { + let x = call::(ptr, libffi_args.as_slice()); + this.write_int(x, dest)?; + return Ok(()); + } + hir::PrimTy::Uint(UintTy::Usize) => { + let x = call::(ptr, libffi_args.as_slice()); + // `usize` doesn't `impl Into`, so convert manually. + // Convert to `u64` since this covers both 32- and 64-bit machines. + this.write_int(u64::try_from(x).unwrap(), dest)?; + return Ok(()); + } + _ => {} + } + } + if matches!(external_fct_defn.output_type, hir::FnRetTy::DefaultReturn(_)) { + call::<()>(ptr, libffi_args.as_slice()); + return Ok(()); + } + // TODO ellen! deal with all the other return types + throw_unsup_format!("UNSUPPORTED RETURN TYPE -- NOT VOID"); + } + } + + /// Call specified external C function, with supplied arguments. + /// Need to convert all the arguments from their hir representations to + /// a form compatible with C (through `libffi` call). + /// Then, convert return from the C call into a corresponding form that + /// can be stored in MIRI internal memory. + fn call_and_add_external_c_fct_to_context( + &mut self, + external_fct_defn: ExternalCFuncDeclRep<'tcx>, + dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Tag>], + ) -> InterpResult<'tcx, bool> { + let this = self.eval_context_mut(); + let link_name = external_fct_defn.link_name; + use std::ops::Deref; + let lib = this.machine.external_c_so_lib.as_ref().unwrap(); + + // Load the C function from the library. + // Because this is getting the C function from the shared object file + // it is not necessarily a sound operation, but there is no way around + // this and we've checked as much as we can. + let func: libloading::Symbol<'_, unsafe extern "C" fn()> = unsafe { + match lib.get(link_name.as_str().as_bytes()) { + Ok(x) => x, + Err(_) => { + // Shared object file does not export this function -- try the shims next. + return Ok(false); + } + } + }; + + // Get the function arguments, and convert them to `libffi`-compatible form. + assert!(args.len() == external_fct_defn.inputs_types.len()); + let mut libffi_args = Vec::::with_capacity(args.len()); + for (cur_arg, arg_type) in args.iter().zip(external_fct_defn.inputs_types.iter()) { + libffi_args.push(Self::extract_scalar(this.read_scalar(cur_arg)?, arg_type, this)?); + } + + // Convert them to `libffi::high::Arg` type. + let libffi_args = libffi_args + .iter() + .map(|cur_arg| cur_arg.arg_downcast()) + .collect::>>(); + + // Code pointer to C function. + let ptr = CodePtr(*func.deref() as *mut _); + // Call the functio and store output, depending on return type in the function signature. + self.call_external_c_and_store_return(external_fct_defn, dest, ptr, libffi_args)?; + Ok(true) + } +} + +#[derive(Debug)] +/// Signature of an external C function. +pub struct ExternalCFuncDeclRep<'hir> { + /// Function name. + pub link_name: Symbol, + /// Argument types. + pub inputs_types: &'hir [Ty<'hir>], + /// Return type. + pub output_type: &'hir FnRetTy<'hir>, +} + +#[derive(Debug, Clone)] +/// Enum of supported arguments to external C functions. +pub enum CArg { + /// Invalid argument (unsupported type). + Invalid, + /// 8-bit signed integer. + Int8(i8), + /// 16-bit signed integer. + Int16(i16), + /// 32-bit signed integer. + Int32(i32), + /// 64-bit signed integer. + Int64(i64), + /// isize. + ISize(isize), + /// 8-bit unsigned integer. + UInt8(u8), + /// 16-bit unsigned integer. + UInt16(u16), + /// 32-bit unsigned integer. + UInt32(u32), + /// 64-bit unsigned integer. + UInt64(u64), + /// usize. + USize(usize), +} + +impl<'a> CArg { + /// Convert a `CArg` to a `libffi` argument type. + pub fn arg_downcast(&'a self) -> libffi::high::Arg<'a> { + match self { + CArg::Int8(i) => arg(i), + CArg::Int16(i) => arg(i), + CArg::Int32(i) => arg(i), + CArg::Int64(i) => arg(i), + CArg::ISize(i) => arg(i), + CArg::UInt8(i) => arg(i), + CArg::UInt16(i) => arg(i), + CArg::UInt32(i) => arg(i), + CArg::UInt64(i) => arg(i), + CArg::USize(i) => arg(i), + _ => { + // should be unreachable + panic!( + "Trying to call external function with unsupported type: should have already bailed" + ); + } + } + } +} + +impl<'hir> ExternalCFuncDeclRep<'hir> { + /// Convert `hir` node representation of a foreign function signature + /// to the signature we're storing. + pub fn from_hir_node(node: &Node<'hir>, link_name: Symbol) -> Option { + match node { + // Calls to foreign functions: + // `kind`: `Fn(&'hir FnDecl<'hir>, &'hir [Ident], &'hir Generics<'hir>)`. + // We care about the inputs (arguments) and output (return type). + Node::ForeignItem(&hir::ForeignItem { + ident: _, + kind: hir::ForeignItemKind::Fn(&hir::FnDecl { inputs, ref output, .. }, ..), + def_id: _, + span: _, + vis_span: _, + }) => Some(Self { link_name, inputs_types: inputs, output_type: output }), + _ => None, /* Not a call to a foreign function */ + } + } +} diff --git a/src/concurrency/range_object_map.rs b/src/concurrency/range_object_map.rs index 2bb3280302..50d3f8c9b2 100644 --- a/src/concurrency/range_object_map.rs +++ b/src/concurrency/range_object_map.rs @@ -117,11 +117,11 @@ impl RangeObjectMap { self.v.insert(pos, Elem { range, data }); // If we aren't the first element, then our start must be greater than the preivous element's end if pos > 0 { - debug_assert!(self.v[pos - 1].range.end() <= range.start); + assert!(self.v[pos - 1].range.end() <= range.start); } // If we aren't the last element, then our end must be smaller than next element's start if pos < self.v.len() - 1 { - debug_assert!(range.end() <= self.v[pos + 1].range.start); + assert!(range.end() <= self.v[pos + 1].range.start); } } diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 289c46a5d2..4dfa7bd07c 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -291,8 +291,8 @@ pub fn report_error<'tcx, 'mir>( match e.kind() { UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some((alloc_id, access)))) => { eprintln!( - "Uninitialized read occurred at {alloc_id:?}{range:?}, in this allocation:", - range = alloc_range(access.uninit_offset, access.uninit_size), + "Uninitialized memory occurred at {alloc_id:?}{range:?}, in this allocation:", + range = access.uninit, ); eprintln!("{:?}", ecx.dump_alloc(*alloc_id)); } diff --git a/src/eval.rs b/src/eval.rs index d75b4f5fa6..ca19dd796a 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -2,6 +2,7 @@ use std::ffi::OsStr; use std::iter; +use std::path::PathBuf; use log::info; @@ -126,6 +127,9 @@ pub struct MiriConfig { pub report_progress: Option, /// Whether Stacked Borrows retagging should recurse into fields of datatypes. pub retag_fields: bool, + /// The location of a shared object file to load when calling external C functions + /// TODO! consider allowing users to specify paths to multiple SO files, or to a directory + pub external_c_so_file: Option, } impl Default for MiriConfig { @@ -157,6 +161,7 @@ impl Default for MiriConfig { preemption_rate: 0.01, // 1% report_progress: None, retag_fields: false, + external_c_so_file: None, } } } diff --git a/src/helpers.rs b/src/helpers.rs index 4b2604afa2..b650e2ddf2 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -195,9 +195,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// Test if this pointer equals 0. fn ptr_is_null(&self, ptr: Pointer>) -> InterpResult<'tcx, bool> { - let this = self.eval_context_ref(); - let null = Scalar::null_ptr(this); - this.ptr_eq(Scalar::from_maybe_pointer(ptr, this), null) + Ok(ptr.addr().bytes() == 0) } /// Get the `Place` for a local @@ -261,7 +259,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let mir = this.load_mir(f.def, None)?; let dest = match dest { Some(dest) => *dest, - None => MPlaceTy::dangling(this.layout_of(mir.return_ty())?).into(), + None => MPlaceTy::fake_alloc_zst(this.layout_of(mir.return_ty())?).into(), }; this.push_stack_frame(f, mir, &dest, stack_pop)?; @@ -633,7 +631,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Ensure that the access is within bounds. assert!(op_place.layout.size >= offset + layout.size); - let value_place = op_place.offset(offset, MemPlaceMeta::None, layout, this)?; + let value_place = op_place.offset(offset, layout, this)?; Ok(value_place) } diff --git a/src/lib.rs b/src/lib.rs index f3e6e0eef7..315648be6c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,7 @@ extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; +mod c_ffi_support; mod concurrency; mod diagnostics; mod eval; @@ -69,6 +70,8 @@ pub use crate::shims::time::EvalContextExt as _; pub use crate::shims::tls::{EvalContextExt as _, TlsData}; pub use crate::shims::EvalContextExt as _; +pub use crate::c_ffi_support::EvalContextExt as CFFIEvalContextExt; + pub use crate::concurrency::data_race::{ AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as DataRaceEvalContextExt, @@ -90,8 +93,8 @@ pub use crate::mono_hash_map::MonoHashMap; pub use crate::operator::EvalContextExt as OperatorEvalContextExt; pub use crate::range_map::RangeMap; pub use crate::stacked_borrows::{ - stack::Stack, CallId, EvalContextExt as StackedBorEvalContextExt, Item, Permission, SbTag, - SbTagExtra, Stacks, + CallId, EvalContextExt as StackedBorEvalContextExt, Item, Permission, SbTag, SbTagExtra, Stack, + Stacks, }; pub use crate::sync::{CondvarId, EvalContextExt as SyncEvalContextExt, MutexId, RwLockId}; pub use crate::thread::{ diff --git a/src/machine.rs b/src/machine.rs index 029c32cad9..8404e29b53 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -5,7 +5,6 @@ use std::borrow::Cow; use std::cell::RefCell; use std::collections::HashSet; use std::fmt; -use std::num::NonZeroU64; use std::time::Instant; use rand::rngs::StdRng; @@ -43,7 +42,7 @@ pub const NUM_CPUS: u64 = 1; /// Extra data stored with each stack frame pub struct FrameData<'tcx> { /// Extra data for Stacked Borrows. - pub call_id: stacked_borrows::CallId, + pub stacked_borrows: Option, /// If this is Some(), then this is a special "catch unwind" frame (the frame of `try_fn` /// called by `try`). When this frame is popped during unwinding a panic, @@ -59,9 +58,9 @@ pub struct FrameData<'tcx> { impl<'tcx> std::fmt::Debug for FrameData<'tcx> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // Omitting `timing`, it does not support `Debug`. - let FrameData { call_id, catch_unwind, timing: _ } = self; + let FrameData { stacked_borrows, catch_unwind, timing: _ } = self; f.debug_struct("FrameData") - .field("call_id", call_id) + .field("stacked_borrows", stacked_borrows) .field("catch_unwind", catch_unwind) .finish() } @@ -169,7 +168,7 @@ impl Provenance for Tag { write!(f, "{:?}", sb)?; } Tag::Wildcard => { - write!(f, "[Wildcard]")?; + write!(f, "[wildcard]")?; } } @@ -339,6 +338,9 @@ pub struct Evaluator<'mir, 'tcx> { pub(crate) report_progress: Option, /// The number of blocks that passed since the last progress report. pub(crate) since_progress_report: u32, + + /// Handle of the optional C shared object file + pub external_c_so_lib: Option, } impl<'mir, 'tcx> Evaluator<'mir, 'tcx> { @@ -398,6 +400,14 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> { preemption_rate: config.preemption_rate, report_progress: config.report_progress, since_progress_report: 0, + external_c_so_lib: config.external_c_so_file.as_ref().map(|lib_file_path| { + // Note: it is the user's responsibility to provide a correct SO file + // and if this condition is met, then this library loading is a safe operation. + unsafe { + libloading::Library::new(lib_file_path) + .expect("Failed to read specified shared object file") + } + }), } } @@ -542,7 +552,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { } #[inline(always)] - fn check_binop_checks_overflow(ecx: &MiriEvalContext<'mir, 'tcx>) -> bool { + fn checked_binop_checks_overflow(ecx: &MiriEvalContext<'mir, 'tcx>) -> bool { ecx.tcx.sess.overflow_checks() } @@ -788,6 +798,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { range, machine.stacked_borrows.as_ref().unwrap(), machine.current_span(), + &machine.threads, )?; } if let Some(weak_memory) = &alloc_extra.weak_memory { @@ -819,6 +830,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { range, machine.stacked_borrows.as_ref().unwrap(), machine.current_span(), + &machine.threads, )?; } if let Some(weak_memory) = &alloc_extra.weak_memory { @@ -852,6 +864,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { tag, range, machine.stacked_borrows.as_ref().unwrap(), + &machine.threads, ) } else { Ok(()) @@ -888,11 +901,12 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { }; let stacked_borrows = ecx.machine.stacked_borrows.as_ref(); - let call_id = stacked_borrows.map_or(NonZeroU64::new(1).unwrap(), |stacked_borrows| { - stacked_borrows.borrow_mut().new_call() - }); - let extra = FrameData { call_id, catch_unwind: None, timing }; + let extra = FrameData { + stacked_borrows: stacked_borrows.map(|sb| sb.borrow_mut().new_frame()), + catch_unwind: None, + timing, + }; Ok(frame.with_extra(extra)) } @@ -936,7 +950,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { ) -> InterpResult<'tcx, StackPopJump> { let timing = frame.extra.timing.take(); if let Some(stacked_borrows) = &ecx.machine.stacked_borrows { - stacked_borrows.borrow_mut().end_call(frame.extra.call_id); + stacked_borrows.borrow_mut().end_call(&frame.extra); } let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding); if let Some(profiler) = ecx.machine.profiler.as_ref() { diff --git a/src/operator.rs b/src/operator.rs index 33c97e7d31..2c77830a6d 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -1,6 +1,7 @@ use log::trace; use rustc_middle::{mir, ty::Ty}; +use rustc_target::abi::Size; use crate::*; @@ -11,8 +12,6 @@ pub trait EvalContextExt<'tcx> { left: &ImmTy<'tcx, Tag>, right: &ImmTy<'tcx, Tag>, ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)>; - - fn ptr_eq(&self, left: Scalar, right: Scalar) -> InterpResult<'tcx, bool>; } impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> { @@ -27,23 +26,8 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> { trace!("ptr_op: {:?} {:?} {:?}", *left, bin_op, *right); Ok(match bin_op { - Eq | Ne => { - // This supports fat pointers. - #[rustfmt::skip] - let eq = match (**left, **right) { - (Immediate::Scalar(left), Immediate::Scalar(right)) => { - self.ptr_eq(left.check_init()?, right.check_init()?)? - } - (Immediate::ScalarPair(left1, left2), Immediate::ScalarPair(right1, right2)) => { - self.ptr_eq(left1.check_init()?, right1.check_init()?)? - && self.ptr_eq(left2.check_init()?, right2.check_init()?)? - } - _ => bug!("Type system should not allow comparing Scalar with ScalarPair"), - }; - (Scalar::from_bool(if bin_op == Eq { eq } else { !eq }), false, self.tcx.types.bool) - } - - Lt | Le | Gt | Ge => { + Eq | Ne | Lt | Le | Gt | Ge => { + assert_eq!(left.layout.abi, right.layout.abi); // types an differ, e.g. fn ptrs with different `for` let size = self.pointer_size(); // Just compare the bits. ScalarPairs are compared lexicographically. // We thus always compare pairs and simply fill scalars up with 0. @@ -51,42 +35,58 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> { Immediate::Scalar(l) => (l.check_init()?.to_bits(size)?, 0), Immediate::ScalarPair(l1, l2) => (l1.check_init()?.to_bits(size)?, l2.check_init()?.to_bits(size)?), + Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)), }; let right = match **right { Immediate::Scalar(r) => (r.check_init()?.to_bits(size)?, 0), Immediate::ScalarPair(r1, r2) => (r1.check_init()?.to_bits(size)?, r2.check_init()?.to_bits(size)?), + Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)), }; let res = match bin_op { + Eq => left == right, + Ne => left != right, Lt => left < right, Le => left <= right, Gt => left > right, Ge => left >= right, - _ => bug!("We already established it has to be one of these operators."), + _ => bug!(), }; (Scalar::from_bool(res), false, self.tcx.types.bool) } Offset => { + assert!(left.layout.ty.is_unsafe_ptr()); + let ptr = self.scalar_to_ptr(left.to_scalar()?)?; + let offset = right.to_scalar()?.to_machine_isize(self)?; + let pointee_ty = left.layout.ty.builtin_deref(true).expect("Offset called on non-ptr type").ty; - let ptr = self.ptr_offset_inbounds( - self.scalar_to_ptr(left.to_scalar()?)?, - pointee_ty, - right.to_scalar()?.to_machine_isize(self)?, - )?; + let ptr = self.ptr_offset_inbounds(ptr, pointee_ty, offset)?; (Scalar::from_maybe_pointer(ptr, self), false, left.layout.ty) } - _ => bug!("Invalid operator on pointers: {:?}", bin_op), - }) - } + // Some more operations are possible with atomics. + // The return value always has the provenance of the *left* operand. + Add | Sub | BitOr | BitAnd | BitXor => { + assert!(left.layout.ty.is_unsafe_ptr()); + assert!(right.layout.ty.is_unsafe_ptr()); + let ptr = self.scalar_to_ptr(left.to_scalar()?)?; + // We do the actual operation with usize-typed scalars. + let left = ImmTy::from_uint(ptr.addr().bytes(), self.machine.layouts.usize); + let right = ImmTy::from_uint( + right.to_scalar()?.to_machine_usize(self)?, + self.machine.layouts.usize, + ); + let (result, overflowing, _ty) = + self.overflowing_binary_op(bin_op, &left, &right)?; + // Construct a new pointer with the provenance of `ptr` (the LHS). + let result_ptr = + Pointer::new(ptr.provenance, Size::from_bytes(result.to_machine_usize(self)?)); + (Scalar::from_maybe_pointer(result_ptr, self), overflowing, left.layout.ty) + } - fn ptr_eq(&self, left: Scalar, right: Scalar) -> InterpResult<'tcx, bool> { - let size = self.pointer_size(); - // Just compare the integers. - let left = left.to_bits(size)?; - let right = right.to_bits(size)?; - Ok(left == right) + _ => span_bug!(self.cur_span(), "Invalid operator on pointers: {:?}", bin_op), + }) } } diff --git a/src/range_map.rs b/src/range_map.rs index 474ad9dccc..c77ea63b08 100644 --- a/src/range_map.rs +++ b/src/range_map.rs @@ -77,6 +77,10 @@ impl RangeMap { }; // The first offset that is not included any more. let end = offset + len; + assert!( + end <= self.v.last().unwrap().range.end, + "iterating beyond the bounds of this RangeMap" + ); slice .iter() .take_while(move |elem| elem.range.start < end) @@ -279,4 +283,18 @@ mod tests { assert_eq!(map.v.len(), 5); assert_eq!(to_vec(&map, 10, 10), vec![23, 42, 23, 23, 23, 19, 19, 19, 19, 19]); } + + #[test] + #[should_panic] + fn out_of_range_iter_mut() { + let mut map = RangeMap::::new(Size::from_bytes(20), -1); + let _ = map.iter_mut(Size::from_bytes(11), Size::from_bytes(11)); + } + + #[test] + #[should_panic] + fn out_of_range_iter() { + let map = RangeMap::::new(Size::from_bytes(20), -1); + let _ = map.iter(Size::from_bytes(11), Size::from_bytes(11)); + } } diff --git a/src/shims/backtrace.rs b/src/shims/backtrace.rs index 339aa4d57e..5b39f2a48a 100644 --- a/src/shims/backtrace.rs +++ b/src/shims/backtrace.rs @@ -48,7 +48,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let mut span = frame.current_span(); // Match the behavior of runtime backtrace spans // by using a non-macro span in our backtrace. See `FunctionCx::debug_loc`. - if span.from_expansion() && !tcx.sess.opts.debugging_opts.debug_macros { + if span.from_expansion() && !tcx.sess.opts.unstable_opts.debug_macros { span = rustc_span::hygiene::walk_chain(span, frame.body.span.ctxt()) } data.push((frame.instance, span.lo())); @@ -104,8 +104,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx for (i, ptr) in ptrs.into_iter().enumerate() { let offset = ptr_layout.size * i.try_into().unwrap(); - let op_place = - buf_place.offset(offset, MemPlaceMeta::None, ptr_layout, this)?; + let op_place = buf_place.offset(offset, ptr_layout, this)?; this.write_pointer(ptr, &op_place.into())?; } diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index cd4fedad0f..e9ba806062 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -22,6 +22,7 @@ use rustc_target::{ }; use super::backtrace::EvalContextExt as _; +use crate::c_ffi_support::ExternalCFuncDeclRep; use crate::helpers::{convert::Truncate, target_os_is_unix}; use crate::*; @@ -31,8 +32,10 @@ pub enum EmulateByNameResult<'mir, 'tcx> { NeedsJumping, /// Jumping has already been taken care of. AlreadyJumped, - /// A MIR body has been found for the function + /// A MIR body has been found for the function. MirBody(&'mir mir::Body<'tcx>, ty::Instance<'tcx>), + /// Executed an external C call. + ExecutedExternalCCall, /// The item is not supported. NotSupported, } @@ -297,9 +300,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Some(p) => p, }; - // Second: functions that return. - match this.emulate_foreign_item_by_name(link_name, abi, args, dest, ret)? { - EmulateByNameResult::NeedsJumping => { + // Second: functions that return immediately. + match this.emulate_foreign_item_by_name(def_id, link_name, abi, args, dest)? { + EmulateByNameResult::NeedsJumping | EmulateByNameResult::ExecutedExternalCCall => { trace!("{:?}", this.dump_place(**dest)); this.go_to_block(ret); } @@ -310,7 +313,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return Ok(Some(body)); } - this.handle_unsupported(format!("can't call foreign function: {}", link_name))?; + this.handle_unsupported( + format!("can't call foreign function: {}; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile", + link_name))?; return Ok(None); } } @@ -351,14 +356,40 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// Emulates calling a foreign item using its name. fn emulate_foreign_item_by_name( &mut self, + def_id: DefId, link_name: Symbol, abi: Abi, args: &[OpTy<'tcx, Tag>], dest: &PlaceTy<'tcx, Tag>, - ret: mir::BasicBlock, ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> { let this = self.eval_context_mut(); + let tcx = this.tcx.tcx; + + // First deal with any external C functions in linked .so file + // (if any SO file is specified). + if this.machine.external_c_so_lib.as_ref().is_some() { + if let Some(local_id) = def_id.as_local() { + if let Some(extern_c_fct_rep) = ExternalCFuncDeclRep::from_hir_node( + &tcx.hir().get(tcx.hir().local_def_id_to_hir_id(local_id)), + link_name, + ) { + // An Ok(false) here means that the function being called was not exported + // by the specified SO file; we should continue and check if it corresponds to + // a provided shim. + + // TODO ellen! For now, continue to try the shims if there was an error in calling + // the external function. + // As discussed: https://github.com/rust-lang/miri/pull/2363#discussion_r922392879 + if let Ok(true) = + this.call_and_add_external_c_fct_to_context(extern_c_fct_rep, dest, args) + { + return Ok(EmulateByNameResult::ExecutedExternalCCall); + } + } + } + } + // Here we dispatch all the shims for foreign functions. If you have a platform specific // shim, add it to the corresponding submodule. match link_name.as_str() { @@ -702,14 +733,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Platform-specific shims _ => match this.tcx.sess.target.os.as_ref() { - target if target_os_is_unix(target) => return shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret), - "windows" => return shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret), + target if target_os_is_unix(target) => return shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest), + "windows" => return shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest), target => throw_unsup_format!("the target `{}` is not supported", target), } }; - // We only fall through to here if we did *not* hit the `_` arm above, - // i.e., if we actually emulated the function. + // i.e., if we actually emulated the function with one of the shims. Ok(EmulateByNameResult::NeedsJumping) } diff --git a/src/shims/intrinsics.rs b/src/shims/intrinsics.rs deleted file mode 100644 index 652f5c94cb..0000000000 --- a/src/shims/intrinsics.rs +++ /dev/null @@ -1,1416 +0,0 @@ -use std::iter; - -use log::trace; - -use rustc_apfloat::{Float, Round}; -use rustc_middle::ty::layout::{HasParamEnv, IntegerExt, LayoutOf}; -use rustc_middle::{mir, mir::BinOp, ty, ty::FloatTy}; -use rustc_target::abi::{Align, Endian, HasDataLayout, Integer, Size}; - -use crate::*; -use helpers::check_arg_count; - -pub enum AtomicOp { - MirOp(mir::BinOp, bool), - Max, - Min, -} - -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { - fn call_intrinsic( - &mut self, - instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, - ret: Option, - _unwind: StackPopUnwind, - ) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - - if this.emulate_intrinsic(instance, args, dest, ret)? { - return Ok(()); - } - - // All supported intrinsics have a return place. - let intrinsic_name = this.tcx.item_name(instance.def_id()); - let intrinsic_name = intrinsic_name.as_str(); - let ret = match ret { - None => throw_unsup_format!("unimplemented (diverging) intrinsic: `{intrinsic_name}`"), - Some(p) => p, - }; - - // Then handle terminating intrinsics. - match intrinsic_name { - // Miri overwriting CTFE intrinsics. - "ptr_guaranteed_eq" => { - let [left, right] = check_arg_count(args)?; - let left = this.read_immediate(left)?; - let right = this.read_immediate(right)?; - this.binop_ignore_overflow(mir::BinOp::Eq, &left, &right, dest)?; - } - "ptr_guaranteed_ne" => { - let [left, right] = check_arg_count(args)?; - let left = this.read_immediate(left)?; - let right = this.read_immediate(right)?; - this.binop_ignore_overflow(mir::BinOp::Ne, &left, &right, dest)?; - } - "const_allocate" => { - // For now, for compatibility with the run-time implementation of this, we just return null. - // See . - this.write_null(dest)?; - } - "const_deallocate" => { - // complete NOP - } - - // Raw memory accesses - "volatile_load" => { - let [place] = check_arg_count(args)?; - let place = this.deref_operand(place)?; - this.copy_op(&place.into(), dest)?; - } - "volatile_store" => { - let [place, dest] = check_arg_count(args)?; - let place = this.deref_operand(place)?; - this.copy_op(dest, &place.into())?; - } - - "write_bytes" | "volatile_set_memory" => { - let [ptr, val_byte, count] = check_arg_count(args)?; - let ty = instance.substs.type_at(0); - let ty_layout = this.layout_of(ty)?; - let val_byte = this.read_scalar(val_byte)?.to_u8()?; - let ptr = this.read_pointer(ptr)?; - let count = this.read_scalar(count)?.to_machine_usize(this)?; - // `checked_mul` enforces a too small bound (the correct one would probably be machine_isize_max), - // but no actual allocation can be big enough for the difference to be noticeable. - let byte_count = ty_layout.size.checked_mul(count, this).ok_or_else(|| { - err_ub_format!("overflow computing total size of `{intrinsic_name}`") - })?; - this.write_bytes_ptr( - ptr, - iter::repeat(val_byte).take(byte_count.bytes() as usize), - )?; - } - - // Floating-point operations - "fabsf32" => { - let [f] = check_arg_count(args)?; - let f = this.read_scalar(f)?.to_f32()?; - // Can be implemented in soft-floats. - this.write_scalar(Scalar::from_f32(f.abs()), dest)?; - } - "fabsf64" => { - let [f] = check_arg_count(args)?; - let f = this.read_scalar(f)?.to_f64()?; - // Can be implemented in soft-floats. - this.write_scalar(Scalar::from_f64(f.abs()), dest)?; - } - #[rustfmt::skip] - | "sinf32" - | "cosf32" - | "sqrtf32" - | "expf32" - | "exp2f32" - | "logf32" - | "log10f32" - | "log2f32" - | "floorf32" - | "ceilf32" - | "truncf32" - | "roundf32" - => { - let [f] = check_arg_count(args)?; - // FIXME: Using host floats. - let f = f32::from_bits(this.read_scalar(f)?.to_u32()?); - let f = match intrinsic_name { - "sinf32" => f.sin(), - "cosf32" => f.cos(), - "sqrtf32" => f.sqrt(), - "expf32" => f.exp(), - "exp2f32" => f.exp2(), - "logf32" => f.ln(), - "log10f32" => f.log10(), - "log2f32" => f.log2(), - "floorf32" => f.floor(), - "ceilf32" => f.ceil(), - "truncf32" => f.trunc(), - "roundf32" => f.round(), - _ => bug!(), - }; - this.write_scalar(Scalar::from_u32(f.to_bits()), dest)?; - } - - #[rustfmt::skip] - | "sinf64" - | "cosf64" - | "sqrtf64" - | "expf64" - | "exp2f64" - | "logf64" - | "log10f64" - | "log2f64" - | "floorf64" - | "ceilf64" - | "truncf64" - | "roundf64" - => { - let [f] = check_arg_count(args)?; - // FIXME: Using host floats. - let f = f64::from_bits(this.read_scalar(f)?.to_u64()?); - let f = match intrinsic_name { - "sinf64" => f.sin(), - "cosf64" => f.cos(), - "sqrtf64" => f.sqrt(), - "expf64" => f.exp(), - "exp2f64" => f.exp2(), - "logf64" => f.ln(), - "log10f64" => f.log10(), - "log2f64" => f.log2(), - "floorf64" => f.floor(), - "ceilf64" => f.ceil(), - "truncf64" => f.trunc(), - "roundf64" => f.round(), - _ => bug!(), - }; - this.write_scalar(Scalar::from_u64(f.to_bits()), dest)?; - } - - #[rustfmt::skip] - | "fadd_fast" - | "fsub_fast" - | "fmul_fast" - | "fdiv_fast" - | "frem_fast" - => { - let [a, b] = check_arg_count(args)?; - let a = this.read_immediate(a)?; - let b = this.read_immediate(b)?; - let op = match intrinsic_name { - "fadd_fast" => mir::BinOp::Add, - "fsub_fast" => mir::BinOp::Sub, - "fmul_fast" => mir::BinOp::Mul, - "fdiv_fast" => mir::BinOp::Div, - "frem_fast" => mir::BinOp::Rem, - _ => bug!(), - }; - let float_finite = |x: ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> { - Ok(match x.layout.ty.kind() { - ty::Float(FloatTy::F32) => x.to_scalar()?.to_f32()?.is_finite(), - ty::Float(FloatTy::F64) => x.to_scalar()?.to_f64()?.is_finite(), - _ => bug!( - "`{intrinsic_name}` called with non-float input type {ty:?}", - ty = x.layout.ty, - ), - }) - }; - match (float_finite(a)?, float_finite(b)?) { - (false, false) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as both parameters", - ), - (false, _) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as first parameter", - ), - (_, false) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as second parameter", - ), - _ => {} - } - this.binop_ignore_overflow(op, &a, &b, dest)?; - } - - #[rustfmt::skip] - | "minnumf32" - | "maxnumf32" - | "copysignf32" - => { - let [a, b] = check_arg_count(args)?; - let a = this.read_scalar(a)?.to_f32()?; - let b = this.read_scalar(b)?.to_f32()?; - let res = match intrinsic_name { - "minnumf32" => a.min(b), - "maxnumf32" => a.max(b), - "copysignf32" => a.copy_sign(b), - _ => bug!(), - }; - this.write_scalar(Scalar::from_f32(res), dest)?; - } - - #[rustfmt::skip] - | "minnumf64" - | "maxnumf64" - | "copysignf64" - => { - let [a, b] = check_arg_count(args)?; - let a = this.read_scalar(a)?.to_f64()?; - let b = this.read_scalar(b)?.to_f64()?; - let res = match intrinsic_name { - "minnumf64" => a.min(b), - "maxnumf64" => a.max(b), - "copysignf64" => a.copy_sign(b), - _ => bug!(), - }; - this.write_scalar(Scalar::from_f64(res), dest)?; - } - - "powf32" => { - let [f, f2] = check_arg_count(args)?; - // FIXME: Using host floats. - let f = f32::from_bits(this.read_scalar(f)?.to_u32()?); - let f2 = f32::from_bits(this.read_scalar(f2)?.to_u32()?); - this.write_scalar(Scalar::from_u32(f.powf(f2).to_bits()), dest)?; - } - - "powf64" => { - let [f, f2] = check_arg_count(args)?; - // FIXME: Using host floats. - let f = f64::from_bits(this.read_scalar(f)?.to_u64()?); - let f2 = f64::from_bits(this.read_scalar(f2)?.to_u64()?); - this.write_scalar(Scalar::from_u64(f.powf(f2).to_bits()), dest)?; - } - - "fmaf32" => { - let [a, b, c] = check_arg_count(args)?; - let a = this.read_scalar(a)?.to_f32()?; - let b = this.read_scalar(b)?.to_f32()?; - let c = this.read_scalar(c)?.to_f32()?; - let res = a.mul_add(b, c).value; - this.write_scalar(Scalar::from_f32(res), dest)?; - } - - "fmaf64" => { - let [a, b, c] = check_arg_count(args)?; - let a = this.read_scalar(a)?.to_f64()?; - let b = this.read_scalar(b)?.to_f64()?; - let c = this.read_scalar(c)?.to_f64()?; - let res = a.mul_add(b, c).value; - this.write_scalar(Scalar::from_f64(res), dest)?; - } - - "powif32" => { - let [f, i] = check_arg_count(args)?; - // FIXME: Using host floats. - let f = f32::from_bits(this.read_scalar(f)?.to_u32()?); - let i = this.read_scalar(i)?.to_i32()?; - this.write_scalar(Scalar::from_u32(f.powi(i).to_bits()), dest)?; - } - - "powif64" => { - let [f, i] = check_arg_count(args)?; - // FIXME: Using host floats. - let f = f64::from_bits(this.read_scalar(f)?.to_u64()?); - let i = this.read_scalar(i)?.to_i32()?; - this.write_scalar(Scalar::from_u64(f.powi(i).to_bits()), dest)?; - } - - "float_to_int_unchecked" => { - let [val] = check_arg_count(args)?; - let val = this.read_immediate(val)?; - - let res = match val.layout.ty.kind() { - ty::Float(FloatTy::F32) => - this.float_to_int_unchecked(val.to_scalar()?.to_f32()?, dest.layout.ty)?, - ty::Float(FloatTy::F64) => - this.float_to_int_unchecked(val.to_scalar()?.to_f64()?, dest.layout.ty)?, - _ => - bug!( - "`float_to_int_unchecked` called with non-float input type {:?}", - val.layout.ty - ), - }; - - this.write_scalar(res, dest)?; - } - - // SIMD operations - #[rustfmt::skip] - | "simd_neg" - | "simd_fabs" - | "simd_ceil" - | "simd_floor" - | "simd_round" - | "simd_trunc" - | "simd_fsqrt" => { - let [op] = check_arg_count(args)?; - let (op, op_len) = this.operand_to_simd(op)?; - let (dest, dest_len) = this.place_to_simd(dest)?; - - assert_eq!(dest_len, op_len); - - #[derive(Copy, Clone)] - enum HostFloatOp { - Ceil, - Floor, - Round, - Trunc, - Sqrt, - } - #[derive(Copy, Clone)] - enum Op { - MirOp(mir::UnOp), - Abs, - HostOp(HostFloatOp), - } - let which = match intrinsic_name { - "simd_neg" => Op::MirOp(mir::UnOp::Neg), - "simd_fabs" => Op::Abs, - "simd_ceil" => Op::HostOp(HostFloatOp::Ceil), - "simd_floor" => Op::HostOp(HostFloatOp::Floor), - "simd_round" => Op::HostOp(HostFloatOp::Round), - "simd_trunc" => Op::HostOp(HostFloatOp::Trunc), - "simd_fsqrt" => Op::HostOp(HostFloatOp::Sqrt), - _ => unreachable!(), - }; - - for i in 0..dest_len { - let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; - let dest = this.mplace_index(&dest, i)?; - let val = match which { - Op::MirOp(mir_op) => this.unary_op(mir_op, &op)?.to_scalar()?, - Op::Abs => { - // Works for f32 and f64. - let ty::Float(float_ty) = op.layout.ty.kind() else { - bug!("{} operand is not a float", intrinsic_name) - }; - let op = op.to_scalar()?; - match float_ty { - FloatTy::F32 => Scalar::from_f32(op.to_f32()?.abs()), - FloatTy::F64 => Scalar::from_f64(op.to_f64()?.abs()), - } - } - Op::HostOp(host_op) => { - let ty::Float(float_ty) = op.layout.ty.kind() else { - bug!("{} operand is not a float", intrinsic_name) - }; - // FIXME using host floats - match float_ty { - FloatTy::F32 => { - let f = f32::from_bits(op.to_scalar()?.to_u32()?); - let res = match host_op { - HostFloatOp::Ceil => f.ceil(), - HostFloatOp::Floor => f.floor(), - HostFloatOp::Round => f.round(), - HostFloatOp::Trunc => f.trunc(), - HostFloatOp::Sqrt => f.sqrt(), - }; - Scalar::from_u32(res.to_bits()) - } - FloatTy::F64 => { - let f = f64::from_bits(op.to_scalar()?.to_u64()?); - let res = match host_op { - HostFloatOp::Ceil => f.ceil(), - HostFloatOp::Floor => f.floor(), - HostFloatOp::Round => f.round(), - HostFloatOp::Trunc => f.trunc(), - HostFloatOp::Sqrt => f.sqrt(), - }; - Scalar::from_u64(res.to_bits()) - } - } - - } - }; - this.write_scalar(val, &dest.into())?; - } - } - #[rustfmt::skip] - | "simd_add" - | "simd_sub" - | "simd_mul" - | "simd_div" - | "simd_rem" - | "simd_shl" - | "simd_shr" - | "simd_and" - | "simd_or" - | "simd_xor" - | "simd_eq" - | "simd_ne" - | "simd_lt" - | "simd_le" - | "simd_gt" - | "simd_ge" - | "simd_fmax" - | "simd_fmin" - | "simd_saturating_add" - | "simd_saturating_sub" - | "simd_arith_offset" => { - use mir::BinOp; - - let [left, right] = check_arg_count(args)?; - let (left, left_len) = this.operand_to_simd(left)?; - let (right, right_len) = this.operand_to_simd(right)?; - let (dest, dest_len) = this.place_to_simd(dest)?; - - assert_eq!(dest_len, left_len); - assert_eq!(dest_len, right_len); - - enum Op { - MirOp(BinOp), - SaturatingOp(BinOp), - FMax, - FMin, - WrappingOffset, - } - let which = match intrinsic_name { - "simd_add" => Op::MirOp(BinOp::Add), - "simd_sub" => Op::MirOp(BinOp::Sub), - "simd_mul" => Op::MirOp(BinOp::Mul), - "simd_div" => Op::MirOp(BinOp::Div), - "simd_rem" => Op::MirOp(BinOp::Rem), - "simd_shl" => Op::MirOp(BinOp::Shl), - "simd_shr" => Op::MirOp(BinOp::Shr), - "simd_and" => Op::MirOp(BinOp::BitAnd), - "simd_or" => Op::MirOp(BinOp::BitOr), - "simd_xor" => Op::MirOp(BinOp::BitXor), - "simd_eq" => Op::MirOp(BinOp::Eq), - "simd_ne" => Op::MirOp(BinOp::Ne), - "simd_lt" => Op::MirOp(BinOp::Lt), - "simd_le" => Op::MirOp(BinOp::Le), - "simd_gt" => Op::MirOp(BinOp::Gt), - "simd_ge" => Op::MirOp(BinOp::Ge), - "simd_fmax" => Op::FMax, - "simd_fmin" => Op::FMin, - "simd_saturating_add" => Op::SaturatingOp(BinOp::Add), - "simd_saturating_sub" => Op::SaturatingOp(BinOp::Sub), - "simd_arith_offset" => Op::WrappingOffset, - _ => unreachable!(), - }; - - for i in 0..dest_len { - let left = this.read_immediate(&this.mplace_index(&left, i)?.into())?; - let right = this.read_immediate(&this.mplace_index(&right, i)?.into())?; - let dest = this.mplace_index(&dest, i)?; - let val = match which { - Op::MirOp(mir_op) => { - let (val, overflowed, ty) = this.overflowing_binary_op(mir_op, &left, &right)?; - if matches!(mir_op, BinOp::Shl | BinOp::Shr) { - // Shifts have extra UB as SIMD operations that the MIR binop does not have. - // See . - if overflowed { - let r_val = right.to_scalar()?.to_bits(right.layout.size)?; - throw_ub_format!("overflowing shift by {r_val} in `{intrinsic_name}` in SIMD lane {i}"); - } - } - if matches!(mir_op, BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge) { - // Special handling for boolean-returning operations - assert_eq!(ty, this.tcx.types.bool); - let val = val.to_bool().unwrap(); - bool_to_simd_element(val, dest.layout.size) - } else { - assert_ne!(ty, this.tcx.types.bool); - assert_eq!(ty, dest.layout.ty); - val - } - } - Op::SaturatingOp(mir_op) => { - this.saturating_arith(mir_op, &left, &right)? - } - Op::WrappingOffset => { - let ptr = this.scalar_to_ptr(left.to_scalar()?)?; - let offset_count = right.to_scalar()?.to_machine_isize(this)?; - let pointee_ty = left.layout.ty.builtin_deref(true).unwrap().ty; - - let pointee_size = i64::try_from(this.layout_of(pointee_ty)?.size.bytes()).unwrap(); - let offset_bytes = offset_count.wrapping_mul(pointee_size); - let offset_ptr = ptr.wrapping_signed_offset(offset_bytes, this); - Scalar::from_maybe_pointer(offset_ptr, this) - } - Op::FMax => { - fmax_op(&left, &right)? - } - Op::FMin => { - fmin_op(&left, &right)? - } - }; - this.write_scalar(val, &dest.into())?; - } - } - "simd_fma" => { - let [a, b, c] = check_arg_count(args)?; - let (a, a_len) = this.operand_to_simd(a)?; - let (b, b_len) = this.operand_to_simd(b)?; - let (c, c_len) = this.operand_to_simd(c)?; - let (dest, dest_len) = this.place_to_simd(dest)?; - - assert_eq!(dest_len, a_len); - assert_eq!(dest_len, b_len); - assert_eq!(dest_len, c_len); - - for i in 0..dest_len { - let a = this.read_immediate(&this.mplace_index(&a, i)?.into())?.to_scalar()?; - let b = this.read_immediate(&this.mplace_index(&b, i)?.into())?.to_scalar()?; - let c = this.read_immediate(&this.mplace_index(&c, i)?.into())?.to_scalar()?; - let dest = this.mplace_index(&dest, i)?; - - // Works for f32 and f64. - let ty::Float(float_ty) = dest.layout.ty.kind() else { - bug!("{} operand is not a float", intrinsic_name) - }; - let val = match float_ty { - FloatTy::F32 => - Scalar::from_f32(a.to_f32()?.mul_add(b.to_f32()?, c.to_f32()?).value), - FloatTy::F64 => - Scalar::from_f64(a.to_f64()?.mul_add(b.to_f64()?, c.to_f64()?).value), - }; - this.write_scalar(val, &dest.into())?; - } - } - #[rustfmt::skip] - | "simd_reduce_and" - | "simd_reduce_or" - | "simd_reduce_xor" - | "simd_reduce_any" - | "simd_reduce_all" - | "simd_reduce_max" - | "simd_reduce_min" => { - use mir::BinOp; - - let [op] = check_arg_count(args)?; - let (op, op_len) = this.operand_to_simd(op)?; - - let imm_from_bool = - |b| ImmTy::from_scalar(Scalar::from_bool(b), this.machine.layouts.bool); - - enum Op { - MirOp(BinOp), - MirOpBool(BinOp), - Max, - Min, - } - let which = match intrinsic_name { - "simd_reduce_and" => Op::MirOp(BinOp::BitAnd), - "simd_reduce_or" => Op::MirOp(BinOp::BitOr), - "simd_reduce_xor" => Op::MirOp(BinOp::BitXor), - "simd_reduce_any" => Op::MirOpBool(BinOp::BitOr), - "simd_reduce_all" => Op::MirOpBool(BinOp::BitAnd), - "simd_reduce_max" => Op::Max, - "simd_reduce_min" => Op::Min, - _ => unreachable!(), - }; - - // Initialize with first lane, then proceed with the rest. - let mut res = this.read_immediate(&this.mplace_index(&op, 0)?.into())?; - if matches!(which, Op::MirOpBool(_)) { - // Convert to `bool` scalar. - res = imm_from_bool(simd_element_to_bool(res)?); - } - for i in 1..op_len { - let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; - res = match which { - Op::MirOp(mir_op) => { - this.binary_op(mir_op, &res, &op)? - } - Op::MirOpBool(mir_op) => { - let op = imm_from_bool(simd_element_to_bool(op)?); - this.binary_op(mir_op, &res, &op)? - } - Op::Max => { - if matches!(res.layout.ty.kind(), ty::Float(_)) { - ImmTy::from_scalar(fmax_op(&res, &op)?, res.layout) - } else { - // Just boring integers, so NaNs to worry about - if this.binary_op(BinOp::Ge, &res, &op)?.to_scalar()?.to_bool()? { - res - } else { - op - } - } - } - Op::Min => { - if matches!(res.layout.ty.kind(), ty::Float(_)) { - ImmTy::from_scalar(fmin_op(&res, &op)?, res.layout) - } else { - // Just boring integers, so NaNs to worry about - if this.binary_op(BinOp::Le, &res, &op)?.to_scalar()?.to_bool()? { - res - } else { - op - } - } - } - }; - } - this.write_immediate(*res, dest)?; - } - #[rustfmt::skip] - | "simd_reduce_add_ordered" - | "simd_reduce_mul_ordered" => { - use mir::BinOp; - - let [op, init] = check_arg_count(args)?; - let (op, op_len) = this.operand_to_simd(op)?; - let init = this.read_immediate(init)?; - - let mir_op = match intrinsic_name { - "simd_reduce_add_ordered" => BinOp::Add, - "simd_reduce_mul_ordered" => BinOp::Mul, - _ => unreachable!(), - }; - - let mut res = init; - for i in 0..op_len { - let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; - res = this.binary_op(mir_op, &res, &op)?; - } - this.write_immediate(*res, dest)?; - } - "simd_select" => { - let [mask, yes, no] = check_arg_count(args)?; - let (mask, mask_len) = this.operand_to_simd(mask)?; - let (yes, yes_len) = this.operand_to_simd(yes)?; - let (no, no_len) = this.operand_to_simd(no)?; - let (dest, dest_len) = this.place_to_simd(dest)?; - - assert_eq!(dest_len, mask_len); - assert_eq!(dest_len, yes_len); - assert_eq!(dest_len, no_len); - - for i in 0..dest_len { - let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?; - let yes = this.read_immediate(&this.mplace_index(&yes, i)?.into())?; - let no = this.read_immediate(&this.mplace_index(&no, i)?.into())?; - let dest = this.mplace_index(&dest, i)?; - - let val = if simd_element_to_bool(mask)? { yes } else { no }; - this.write_immediate(*val, &dest.into())?; - } - } - "simd_select_bitmask" => { - let [mask, yes, no] = check_arg_count(args)?; - let (yes, yes_len) = this.operand_to_simd(yes)?; - let (no, no_len) = this.operand_to_simd(no)?; - let (dest, dest_len) = this.place_to_simd(dest)?; - let bitmask_len = dest_len.max(8); - - assert!(mask.layout.ty.is_integral()); - assert!(bitmask_len <= 64); - assert_eq!(bitmask_len, mask.layout.size.bits()); - assert_eq!(dest_len, yes_len); - assert_eq!(dest_len, no_len); - - let mask: u64 = this - .read_scalar(mask)? - .check_init()? - .to_bits(mask.layout.size)? - .try_into() - .unwrap(); - for i in 0..dest_len { - let mask = - mask & (1 << simd_bitmask_index(i, dest_len, this.data_layout().endian)); - let yes = this.read_immediate(&this.mplace_index(&yes, i)?.into())?; - let no = this.read_immediate(&this.mplace_index(&no, i)?.into())?; - let dest = this.mplace_index(&dest, i)?; - - let val = if mask != 0 { yes } else { no }; - this.write_immediate(*val, &dest.into())?; - } - for i in dest_len..bitmask_len { - // If the mask is "padded", ensure that padding is all-zero. - let mask = mask & (1 << i); - if mask != 0 { - throw_ub_format!( - "a SIMD bitmask less than 8 bits long must be filled with 0s for the remaining bits" - ); - } - } - } - #[rustfmt::skip] - "simd_cast" | "simd_as" => { - let [op] = check_arg_count(args)?; - let (op, op_len) = this.operand_to_simd(op)?; - let (dest, dest_len) = this.place_to_simd(dest)?; - - assert_eq!(dest_len, op_len); - - let safe_cast = intrinsic_name == "simd_as"; - - for i in 0..dest_len { - let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; - let dest = this.mplace_index(&dest, i)?; - - let val = match (op.layout.ty.kind(), dest.layout.ty.kind()) { - // Int-to-(int|float): always safe - (ty::Int(_) | ty::Uint(_), ty::Int(_) | ty::Uint(_) | ty::Float(_)) => - this.misc_cast(&op, dest.layout.ty)?, - // Float-to-float: always safe - (ty::Float(_), ty::Float(_)) => - this.misc_cast(&op, dest.layout.ty)?, - // Float-to-int in safe mode - (ty::Float(_), ty::Int(_) | ty::Uint(_)) if safe_cast => - this.misc_cast(&op, dest.layout.ty)?, - // Float-to-int in unchecked mode - (ty::Float(FloatTy::F32), ty::Int(_) | ty::Uint(_)) if !safe_cast => - this.float_to_int_unchecked(op.to_scalar()?.to_f32()?, dest.layout.ty)?.into(), - (ty::Float(FloatTy::F64), ty::Int(_) | ty::Uint(_)) if !safe_cast => - this.float_to_int_unchecked(op.to_scalar()?.to_f64()?, dest.layout.ty)?.into(), - _ => - throw_unsup_format!( - "Unsupported SIMD cast from element type {from_ty} to {to_ty}", - from_ty = op.layout.ty, - to_ty = dest.layout.ty, - ), - }; - this.write_immediate(val, &dest.into())?; - } - } - "simd_shuffle" => { - let [left, right, index] = check_arg_count(args)?; - let (left, left_len) = this.operand_to_simd(left)?; - let (right, right_len) = this.operand_to_simd(right)?; - let (dest, dest_len) = this.place_to_simd(dest)?; - - // `index` is an array, not a SIMD type - let ty::Array(_, index_len) = index.layout.ty.kind() else { - bug!("simd_shuffle index argument has non-array type {}", index.layout.ty) - }; - let index_len = index_len.eval_usize(*this.tcx, this.param_env()); - - assert_eq!(left_len, right_len); - assert_eq!(index_len, dest_len); - - for i in 0..dest_len { - let src_index: u64 = this - .read_immediate(&this.operand_index(index, i)?)? - .to_scalar()? - .to_u32()? - .into(); - let dest = this.mplace_index(&dest, i)?; - - let val = if src_index < left_len { - this.read_immediate(&this.mplace_index(&left, src_index)?.into())? - } else if src_index < left_len.checked_add(right_len).unwrap() { - this.read_immediate( - &this.mplace_index(&right, src_index - left_len)?.into(), - )? - } else { - bug!( - "simd_shuffle index {} is out of bounds for 2 vectors of size {}", - src_index, - left_len - ); - }; - this.write_immediate(*val, &dest.into())?; - } - } - "simd_gather" => { - let [passthru, ptrs, mask] = check_arg_count(args)?; - let (passthru, passthru_len) = this.operand_to_simd(passthru)?; - let (ptrs, ptrs_len) = this.operand_to_simd(ptrs)?; - let (mask, mask_len) = this.operand_to_simd(mask)?; - let (dest, dest_len) = this.place_to_simd(dest)?; - - assert_eq!(dest_len, passthru_len); - assert_eq!(dest_len, ptrs_len); - assert_eq!(dest_len, mask_len); - - for i in 0..dest_len { - let passthru = this.read_immediate(&this.mplace_index(&passthru, i)?.into())?; - let ptr = this.read_immediate(&this.mplace_index(&ptrs, i)?.into())?; - let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?; - let dest = this.mplace_index(&dest, i)?; - - let val = if simd_element_to_bool(mask)? { - let place = this.deref_operand(&ptr.into())?; - this.read_immediate(&place.into())? - } else { - passthru - }; - this.write_immediate(*val, &dest.into())?; - } - } - "simd_scatter" => { - let [value, ptrs, mask] = check_arg_count(args)?; - let (value, value_len) = this.operand_to_simd(value)?; - let (ptrs, ptrs_len) = this.operand_to_simd(ptrs)?; - let (mask, mask_len) = this.operand_to_simd(mask)?; - - assert_eq!(ptrs_len, value_len); - assert_eq!(ptrs_len, mask_len); - - for i in 0..ptrs_len { - let value = this.read_immediate(&this.mplace_index(&value, i)?.into())?; - let ptr = this.read_immediate(&this.mplace_index(&ptrs, i)?.into())?; - let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?; - - if simd_element_to_bool(mask)? { - let place = this.deref_operand(&ptr.into())?; - this.write_immediate(*value, &place.into())?; - } - } - } - "simd_bitmask" => { - let [op] = check_arg_count(args)?; - let (op, op_len) = this.operand_to_simd(op)?; - let bitmask_len = op_len.max(8); - - assert!(dest.layout.ty.is_integral()); - assert!(bitmask_len <= 64); - assert_eq!(bitmask_len, dest.layout.size.bits()); - - let mut res = 0u64; - for i in 0..op_len { - let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; - if simd_element_to_bool(op)? { - res |= 1 << simd_bitmask_index(i, op_len, this.data_layout().endian); - } - } - this.write_int(res, dest)?; - } - - // Atomic operations - "atomic_load_seqcst" => this.atomic_load(args, dest, AtomicReadOrd::SeqCst)?, - "atomic_load_relaxed" => this.atomic_load(args, dest, AtomicReadOrd::Relaxed)?, - "atomic_load_acquire" => this.atomic_load(args, dest, AtomicReadOrd::Acquire)?, - - "atomic_store_seqcst" => this.atomic_store(args, AtomicWriteOrd::SeqCst)?, - "atomic_store_relaxed" => this.atomic_store(args, AtomicWriteOrd::Relaxed)?, - "atomic_store_release" => this.atomic_store(args, AtomicWriteOrd::Release)?, - - "atomic_fence_acquire" => this.atomic_fence(args, AtomicFenceOrd::Acquire)?, - "atomic_fence_release" => this.atomic_fence(args, AtomicFenceOrd::Release)?, - "atomic_fence_acqrel" => this.atomic_fence(args, AtomicFenceOrd::AcqRel)?, - "atomic_fence_seqcst" => this.atomic_fence(args, AtomicFenceOrd::SeqCst)?, - - "atomic_singlethreadfence_acquire" => - this.compiler_fence(args, AtomicFenceOrd::Acquire)?, - "atomic_singlethreadfence_release" => - this.compiler_fence(args, AtomicFenceOrd::Release)?, - "atomic_singlethreadfence_acqrel" => - this.compiler_fence(args, AtomicFenceOrd::AcqRel)?, - "atomic_singlethreadfence_seqcst" => - this.compiler_fence(args, AtomicFenceOrd::SeqCst)?, - - "atomic_xchg_seqcst" => this.atomic_exchange(args, dest, AtomicRwOrd::SeqCst)?, - "atomic_xchg_acquire" => this.atomic_exchange(args, dest, AtomicRwOrd::Acquire)?, - "atomic_xchg_release" => this.atomic_exchange(args, dest, AtomicRwOrd::Release)?, - "atomic_xchg_acqrel" => this.atomic_exchange(args, dest, AtomicRwOrd::AcqRel)?, - "atomic_xchg_relaxed" => this.atomic_exchange(args, dest, AtomicRwOrd::Relaxed)?, - - #[rustfmt::skip] - "atomic_cxchg_seqcst_seqcst" => - this.atomic_compare_exchange(args, dest, AtomicRwOrd::SeqCst, AtomicReadOrd::SeqCst)?, - #[rustfmt::skip] - "atomic_cxchg_acquire_acquire" => - this.atomic_compare_exchange(args, dest, AtomicRwOrd::Acquire, AtomicReadOrd::Acquire)?, - #[rustfmt::skip] - "atomic_cxchg_release_relaxed" => - this.atomic_compare_exchange(args, dest, AtomicRwOrd::Release, AtomicReadOrd::Relaxed)?, - #[rustfmt::skip] - "atomic_cxchg_acqrel_acquire" => - this.atomic_compare_exchange(args, dest, AtomicRwOrd::AcqRel, AtomicReadOrd::Acquire)?, - #[rustfmt::skip] - "atomic_cxchg_relaxed_relaxed" => - this.atomic_compare_exchange(args, dest, AtomicRwOrd::Relaxed, AtomicReadOrd::Relaxed)?, - #[rustfmt::skip] - "atomic_cxchg_acquire_relaxed" => - this.atomic_compare_exchange(args, dest, AtomicRwOrd::Acquire, AtomicReadOrd::Relaxed)?, - #[rustfmt::skip] - "atomic_cxchg_acqrel_relaxed" => - this.atomic_compare_exchange(args, dest, AtomicRwOrd::AcqRel, AtomicReadOrd::Relaxed)?, - #[rustfmt::skip] - "atomic_cxchg_seqcst_relaxed" => - this.atomic_compare_exchange(args, dest, AtomicRwOrd::SeqCst, AtomicReadOrd::Relaxed)?, - #[rustfmt::skip] - "atomic_cxchg_seqcst_acquire" => - this.atomic_compare_exchange(args, dest, AtomicRwOrd::SeqCst, AtomicReadOrd::Acquire)?, - - #[rustfmt::skip] - "atomic_cxchgweak_seqcst_seqcst" => - this.atomic_compare_exchange_weak(args, dest, AtomicRwOrd::SeqCst, AtomicReadOrd::SeqCst)?, - #[rustfmt::skip] - "atomic_cxchgweak_acquire_acquire" => - this.atomic_compare_exchange_weak(args, dest, AtomicRwOrd::Acquire, AtomicReadOrd::Acquire)?, - #[rustfmt::skip] - "atomic_cxchgweak_release_relaxed" => - this.atomic_compare_exchange_weak(args, dest, AtomicRwOrd::Release, AtomicReadOrd::Relaxed)?, - #[rustfmt::skip] - "atomic_cxchgweak_acqrel_acquire" => - this.atomic_compare_exchange_weak(args, dest, AtomicRwOrd::AcqRel, AtomicReadOrd::Acquire)?, - #[rustfmt::skip] - "atomic_cxchgweak_relaxed_relaxed" => - this.atomic_compare_exchange_weak(args, dest, AtomicRwOrd::Relaxed, AtomicReadOrd::Relaxed)?, - #[rustfmt::skip] - "atomic_cxchgweak_acquire_relaxed" => - this.atomic_compare_exchange_weak(args, dest, AtomicRwOrd::Acquire, AtomicReadOrd::Relaxed)?, - #[rustfmt::skip] - "atomic_cxchgweak_acqrel_relaxed" => - this.atomic_compare_exchange_weak(args, dest, AtomicRwOrd::AcqRel, AtomicReadOrd::Relaxed)?, - #[rustfmt::skip] - "atomic_cxchgweak_seqcst_relaxed" => - this.atomic_compare_exchange_weak(args, dest, AtomicRwOrd::SeqCst, AtomicReadOrd::Relaxed)?, - #[rustfmt::skip] - "atomic_cxchgweak_seqcst_acquire" => - this.atomic_compare_exchange_weak(args, dest, AtomicRwOrd::SeqCst, AtomicReadOrd::Acquire)?, - - #[rustfmt::skip] - "atomic_or_seqcst" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOrd::SeqCst)?, - #[rustfmt::skip] - "atomic_or_acquire" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOrd::Acquire)?, - #[rustfmt::skip] - "atomic_or_release" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOrd::Release)?, - #[rustfmt::skip] - "atomic_or_acqrel" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOrd::AcqRel)?, - #[rustfmt::skip] - "atomic_or_relaxed" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), AtomicRwOrd::Relaxed)?, - #[rustfmt::skip] - "atomic_xor_seqcst" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOrd::SeqCst)?, - #[rustfmt::skip] - "atomic_xor_acquire" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOrd::Acquire)?, - #[rustfmt::skip] - "atomic_xor_release" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOrd::Release)?, - #[rustfmt::skip] - "atomic_xor_acqrel" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOrd::AcqRel)?, - #[rustfmt::skip] - "atomic_xor_relaxed" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), AtomicRwOrd::Relaxed)?, - #[rustfmt::skip] - "atomic_and_seqcst" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOrd::SeqCst)?, - #[rustfmt::skip] - "atomic_and_acquire" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOrd::Acquire)?, - #[rustfmt::skip] - "atomic_and_release" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOrd::Release)?, - #[rustfmt::skip] - "atomic_and_acqrel" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOrd::AcqRel)?, - #[rustfmt::skip] - "atomic_and_relaxed" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), AtomicRwOrd::Relaxed)?, - #[rustfmt::skip] - "atomic_nand_seqcst" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOrd::SeqCst)?, - #[rustfmt::skip] - "atomic_nand_acquire" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOrd::Acquire)?, - #[rustfmt::skip] - "atomic_nand_release" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOrd::Release)?, - #[rustfmt::skip] - "atomic_nand_acqrel" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOrd::AcqRel)?, - #[rustfmt::skip] - "atomic_nand_relaxed" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), AtomicRwOrd::Relaxed)?, - #[rustfmt::skip] - "atomic_xadd_seqcst" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOrd::SeqCst)?, - #[rustfmt::skip] - "atomic_xadd_acquire" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOrd::Acquire)?, - #[rustfmt::skip] - "atomic_xadd_release" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOrd::Release)?, - #[rustfmt::skip] - "atomic_xadd_acqrel" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOrd::AcqRel)?, - #[rustfmt::skip] - "atomic_xadd_relaxed" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), AtomicRwOrd::Relaxed)?, - #[rustfmt::skip] - "atomic_xsub_seqcst" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOrd::SeqCst)?, - #[rustfmt::skip] - "atomic_xsub_acquire" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOrd::Acquire)?, - #[rustfmt::skip] - "atomic_xsub_release" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOrd::Release)?, - #[rustfmt::skip] - "atomic_xsub_acqrel" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOrd::AcqRel)?, - #[rustfmt::skip] - "atomic_xsub_relaxed" => - this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), AtomicRwOrd::Relaxed)?, - "atomic_min_seqcst" => - this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOrd::SeqCst)?, - "atomic_min_acquire" => - this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOrd::Acquire)?, - "atomic_min_release" => - this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOrd::Release)?, - "atomic_min_acqrel" => - this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOrd::AcqRel)?, - "atomic_min_relaxed" => - this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOrd::Relaxed)?, - "atomic_max_seqcst" => - this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOrd::SeqCst)?, - "atomic_max_acquire" => - this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOrd::Acquire)?, - "atomic_max_release" => - this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOrd::Release)?, - "atomic_max_acqrel" => - this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOrd::AcqRel)?, - "atomic_max_relaxed" => - this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOrd::Relaxed)?, - "atomic_umin_seqcst" => - this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOrd::SeqCst)?, - "atomic_umin_acquire" => - this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOrd::Acquire)?, - "atomic_umin_release" => - this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOrd::Release)?, - "atomic_umin_acqrel" => - this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOrd::AcqRel)?, - "atomic_umin_relaxed" => - this.atomic_op(args, dest, AtomicOp::Min, AtomicRwOrd::Relaxed)?, - "atomic_umax_seqcst" => - this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOrd::SeqCst)?, - "atomic_umax_acquire" => - this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOrd::Acquire)?, - "atomic_umax_release" => - this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOrd::Release)?, - "atomic_umax_acqrel" => - this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOrd::AcqRel)?, - "atomic_umax_relaxed" => - this.atomic_op(args, dest, AtomicOp::Max, AtomicRwOrd::Relaxed)?, - - // Other - "exact_div" => { - let [num, denom] = check_arg_count(args)?; - this.exact_div(&this.read_immediate(num)?, &this.read_immediate(denom)?, dest)?; - } - - "try" => return this.handle_try(args, dest, ret), - - "breakpoint" => { - let [] = check_arg_count(args)?; - // normally this would raise a SIGTRAP, which aborts if no debugger is connected - throw_machine_stop!(TerminationInfo::Abort("Trace/breakpoint trap".to_string())) - } - - name => throw_unsup_format!("unimplemented intrinsic: `{name}`"), - } - - trace!("{:?}", this.dump_place(**dest)); - this.go_to_block(ret); - Ok(()) - } - - fn atomic_load( - &mut self, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, - atomic: AtomicReadOrd, - ) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - - let [place] = check_arg_count(args)?; - let place = this.deref_operand(place)?; - - // make sure it fits into a scalar; otherwise it cannot be atomic - let val = this.read_scalar_atomic(&place, atomic)?; - - // Check alignment requirements. Atomics must always be aligned to their size, - // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must - // be 8-aligned). - let align = Align::from_bytes(place.layout.size.bytes()).unwrap(); - this.check_ptr_access_align( - place.ptr, - place.layout.size, - align, - CheckInAllocMsg::MemoryAccessTest, - )?; - // Perform regular access. - this.write_scalar(val, dest)?; - Ok(()) - } - - fn atomic_store( - &mut self, - args: &[OpTy<'tcx, Tag>], - atomic: AtomicWriteOrd, - ) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - - let [place, val] = check_arg_count(args)?; - let place = this.deref_operand(place)?; - let val = this.read_scalar(val)?; // make sure it fits into a scalar; otherwise it cannot be atomic - - // Check alignment requirements. Atomics must always be aligned to their size, - // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must - // be 8-aligned). - let align = Align::from_bytes(place.layout.size.bytes()).unwrap(); - this.check_ptr_access_align( - place.ptr, - place.layout.size, - align, - CheckInAllocMsg::MemoryAccessTest, - )?; - - // Perform atomic store - this.write_scalar_atomic(val, &place, atomic)?; - Ok(()) - } - - fn compiler_fence( - &mut self, - args: &[OpTy<'tcx, Tag>], - atomic: AtomicFenceOrd, - ) -> InterpResult<'tcx> { - let [] = check_arg_count(args)?; - let _ = atomic; - //FIXME: compiler fences are currently ignored - Ok(()) - } - - fn atomic_fence( - &mut self, - args: &[OpTy<'tcx, Tag>], - atomic: AtomicFenceOrd, - ) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - let [] = check_arg_count(args)?; - this.validate_atomic_fence(atomic)?; - Ok(()) - } - - fn atomic_op( - &mut self, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, - atomic_op: AtomicOp, - atomic: AtomicRwOrd, - ) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - - let [place, rhs] = check_arg_count(args)?; - let place = this.deref_operand(place)?; - - if !place.layout.ty.is_integral() { - bug!("Atomic arithmetic operations only work on integer types"); - } - let rhs = this.read_immediate(rhs)?; - - // Check alignment requirements. Atomics must always be aligned to their size, - // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must - // be 8-aligned). - let align = Align::from_bytes(place.layout.size.bytes()).unwrap(); - this.check_ptr_access_align( - place.ptr, - place.layout.size, - align, - CheckInAllocMsg::MemoryAccessTest, - )?; - - match atomic_op { - AtomicOp::Min => { - let old = this.atomic_min_max_scalar(&place, rhs, true, atomic)?; - this.write_immediate(*old, dest)?; // old value is returned - Ok(()) - } - AtomicOp::Max => { - let old = this.atomic_min_max_scalar(&place, rhs, false, atomic)?; - this.write_immediate(*old, dest)?; // old value is returned - Ok(()) - } - AtomicOp::MirOp(op, neg) => { - let old = this.atomic_op_immediate(&place, &rhs, op, neg, atomic)?; - this.write_immediate(*old, dest)?; // old value is returned - Ok(()) - } - } - } - - fn atomic_exchange( - &mut self, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, - atomic: AtomicRwOrd, - ) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - - let [place, new] = check_arg_count(args)?; - let place = this.deref_operand(place)?; - let new = this.read_scalar(new)?; - - // Check alignment requirements. Atomics must always be aligned to their size, - // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must - // be 8-aligned). - let align = Align::from_bytes(place.layout.size.bytes()).unwrap(); - this.check_ptr_access_align( - place.ptr, - place.layout.size, - align, - CheckInAllocMsg::MemoryAccessTest, - )?; - - let old = this.atomic_exchange_scalar(&place, new, atomic)?; - this.write_scalar(old, dest)?; // old value is returned - Ok(()) - } - - fn atomic_compare_exchange_impl( - &mut self, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, - success: AtomicRwOrd, - fail: AtomicReadOrd, - can_fail_spuriously: bool, - ) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - - let [place, expect_old, new] = check_arg_count(args)?; - let place = this.deref_operand(place)?; - let expect_old = this.read_immediate(expect_old)?; // read as immediate for the sake of `binary_op()` - let new = this.read_scalar(new)?; - - // Check alignment requirements. Atomics must always be aligned to their size, - // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must - // be 8-aligned). - let align = Align::from_bytes(place.layout.size.bytes()).unwrap(); - this.check_ptr_access_align( - place.ptr, - place.layout.size, - align, - CheckInAllocMsg::MemoryAccessTest, - )?; - - let old = this.atomic_compare_exchange_scalar( - &place, - &expect_old, - new, - success, - fail, - can_fail_spuriously, - )?; - - // Return old value. - this.write_immediate(old, dest)?; - Ok(()) - } - - fn atomic_compare_exchange( - &mut self, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, - success: AtomicRwOrd, - fail: AtomicReadOrd, - ) -> InterpResult<'tcx> { - self.atomic_compare_exchange_impl(args, dest, success, fail, false) - } - - fn atomic_compare_exchange_weak( - &mut self, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, - success: AtomicRwOrd, - fail: AtomicReadOrd, - ) -> InterpResult<'tcx> { - self.atomic_compare_exchange_impl(args, dest, success, fail, true) - } - - fn float_to_int_unchecked( - &self, - f: F, - dest_ty: ty::Ty<'tcx>, - ) -> InterpResult<'tcx, Scalar> - where - F: Float + Into>, - { - let this = self.eval_context_ref(); - - // Step 1: cut off the fractional part of `f`. The result of this is - // guaranteed to be precisely representable in IEEE floats. - let f = f.round_to_integral(Round::TowardZero).value; - - // Step 2: Cast the truncated float to the target integer type and see if we lose any information in this step. - Ok(match dest_ty.kind() { - // Unsigned - ty::Uint(t) => { - let size = Integer::from_uint_ty(this, *t).size(); - let res = f.to_u128(size.bits_usize()); - if res.status.is_empty() { - // No status flags means there was no further rounding or other loss of precision. - Scalar::from_uint(res.value, size) - } else { - // `f` was not representable in this integer type. - throw_ub_format!( - "`float_to_int_unchecked` intrinsic called on {f} which cannot be represented in target type `{dest_ty:?}`", - ); - } - } - // Signed - ty::Int(t) => { - let size = Integer::from_int_ty(this, *t).size(); - let res = f.to_i128(size.bits_usize()); - if res.status.is_empty() { - // No status flags means there was no further rounding or other loss of precision. - Scalar::from_int(res.value, size) - } else { - // `f` was not representable in this integer type. - throw_ub_format!( - "`float_to_int_unchecked` intrinsic called on {f} which cannot be represented in target type `{dest_ty:?}`", - ); - } - } - // Nothing else - _ => bug!("`float_to_int_unchecked` called with non-int output type {dest_ty:?}"), - }) - } -} - -fn fmax_op<'tcx>( - left: &ImmTy<'tcx, Tag>, - right: &ImmTy<'tcx, Tag>, -) -> InterpResult<'tcx, Scalar> { - assert_eq!(left.layout.ty, right.layout.ty); - let ty::Float(float_ty) = left.layout.ty.kind() else { - bug!("fmax operand is not a float") - }; - let left = left.to_scalar()?; - let right = right.to_scalar()?; - Ok(match float_ty { - FloatTy::F32 => Scalar::from_f32(left.to_f32()?.max(right.to_f32()?)), - FloatTy::F64 => Scalar::from_f64(left.to_f64()?.max(right.to_f64()?)), - }) -} - -fn fmin_op<'tcx>( - left: &ImmTy<'tcx, Tag>, - right: &ImmTy<'tcx, Tag>, -) -> InterpResult<'tcx, Scalar> { - assert_eq!(left.layout.ty, right.layout.ty); - let ty::Float(float_ty) = left.layout.ty.kind() else { - bug!("fmin operand is not a float") - }; - let left = left.to_scalar()?; - let right = right.to_scalar()?; - Ok(match float_ty { - FloatTy::F32 => Scalar::from_f32(left.to_f32()?.min(right.to_f32()?)), - FloatTy::F64 => Scalar::from_f64(left.to_f64()?.min(right.to_f64()?)), - }) -} - -fn bool_to_simd_element(b: bool, size: Size) -> Scalar { - // SIMD uses all-1 as pattern for "true" - let val = if b { -1 } else { 0 }; - Scalar::from_int(val, size) -} - -fn simd_element_to_bool(elem: ImmTy<'_, Tag>) -> InterpResult<'_, bool> { - let val = elem.to_scalar()?.to_int(elem.layout.size)?; - Ok(match val { - 0 => false, - -1 => true, - _ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"), - }) -} - -fn simd_bitmask_index(idx: u64, vec_len: u64, endianess: Endian) -> u64 { - assert!(idx < vec_len); - match endianess { - Endian::Little => idx, - Endian::Big => vec_len - 1 - idx, // reverse order of bits - } -} diff --git a/src/shims/intrinsics/atomic.rs b/src/shims/intrinsics/atomic.rs new file mode 100644 index 0000000000..78e13a498c --- /dev/null +++ b/src/shims/intrinsics/atomic.rs @@ -0,0 +1,340 @@ +use rustc_middle::{mir, mir::BinOp, ty}; +use rustc_target::abi::Align; + +use crate::*; +use helpers::check_arg_count; + +pub enum AtomicOp { + /// The `bool` indicates whether the result of the operation should be negated + /// (must be a boolean-typed operation). + MirOp(mir::BinOp, bool), + Max, + Min, +} + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { + /// Calls the atomic intrinsic `intrinsic`; the `atomic_` prefix has already been removed. + fn emulate_atomic_intrinsic( + &mut self, + intrinsic_name: &str, + args: &[OpTy<'tcx, Tag>], + dest: &PlaceTy<'tcx, Tag>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let intrinsic_structure: Vec<_> = intrinsic_name.split('_').collect(); + + fn read_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicReadOrd> { + Ok(match ord { + "seqcst" => AtomicReadOrd::SeqCst, + "acquire" => AtomicReadOrd::Acquire, + "relaxed" => AtomicReadOrd::Relaxed, + _ => throw_unsup_format!("unsupported read ordering `{ord}`"), + }) + } + + fn write_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicWriteOrd> { + Ok(match ord { + "seqcst" => AtomicWriteOrd::SeqCst, + "release" => AtomicWriteOrd::Release, + "relaxed" => AtomicWriteOrd::Relaxed, + _ => throw_unsup_format!("unsupported write ordering `{ord}`"), + }) + } + + fn rw_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicRwOrd> { + Ok(match ord { + "seqcst" => AtomicRwOrd::SeqCst, + "acqrel" => AtomicRwOrd::AcqRel, + "acquire" => AtomicRwOrd::Acquire, + "release" => AtomicRwOrd::Release, + "relaxed" => AtomicRwOrd::Relaxed, + _ => throw_unsup_format!("unsupported read-write ordering `{ord}`"), + }) + } + + fn fence_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicFenceOrd> { + Ok(match ord { + "seqcst" => AtomicFenceOrd::SeqCst, + "acqrel" => AtomicFenceOrd::AcqRel, + "acquire" => AtomicFenceOrd::Acquire, + "release" => AtomicFenceOrd::Release, + _ => throw_unsup_format!("unsupported fence ordering `{ord}`"), + }) + } + + match &*intrinsic_structure { + ["load", ord] => this.atomic_load(args, dest, read_ord(ord)?)?, + ["store", ord] => this.atomic_store(args, write_ord(ord)?)?, + + ["fence", ord] => this.atomic_fence(args, fence_ord(ord)?)?, + ["singlethreadfence", ord] => this.compiler_fence(args, fence_ord(ord)?)?, + + ["xchg", ord] => this.atomic_exchange(args, dest, rw_ord(ord)?)?, + ["cxchg", ord1, ord2] => + this.atomic_compare_exchange(args, dest, rw_ord(ord1)?, read_ord(ord2)?)?, + ["cxchgweak", ord1, ord2] => + this.atomic_compare_exchange_weak(args, dest, rw_ord(ord1)?, read_ord(ord2)?)?, + + ["or", ord] => + this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), rw_ord(ord)?)?, + ["xor", ord] => + this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), rw_ord(ord)?)?, + ["and", ord] => + this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), rw_ord(ord)?)?, + ["nand", ord] => + this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), rw_ord(ord)?)?, + ["xadd", ord] => + this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), rw_ord(ord)?)?, + ["xsub", ord] => + this.atomic_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), rw_ord(ord)?)?, + ["min", ord] => { + // Later we will use the type to indicate signed vs unsigned, + // so make sure it matches the intrinsic name. + assert!(matches!(args[1].layout.ty.kind(), ty::Int(_))); + this.atomic_op(args, dest, AtomicOp::Min, rw_ord(ord)?)?; + } + ["umin", ord] => { + // Later we will use the type to indicate signed vs unsigned, + // so make sure it matches the intrinsic name. + assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_))); + this.atomic_op(args, dest, AtomicOp::Min, rw_ord(ord)?)?; + } + ["max", ord] => { + // Later we will use the type to indicate signed vs unsigned, + // so make sure it matches the intrinsic name. + assert!(matches!(args[1].layout.ty.kind(), ty::Int(_))); + this.atomic_op(args, dest, AtomicOp::Max, rw_ord(ord)?)?; + } + ["umax", ord] => { + // Later we will use the type to indicate signed vs unsigned, + // so make sure it matches the intrinsic name. + assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_))); + this.atomic_op(args, dest, AtomicOp::Max, rw_ord(ord)?)?; + } + + _ => throw_unsup_format!("unimplemented intrinsic: `atomic_{intrinsic_name}`"), + } + Ok(()) + } + + fn atomic_load( + &mut self, + args: &[OpTy<'tcx, Tag>], + dest: &PlaceTy<'tcx, Tag>, + atomic: AtomicReadOrd, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let [place] = check_arg_count(args)?; + let place = this.deref_operand(place)?; + + // make sure it fits into a scalar; otherwise it cannot be atomic + let val = this.read_scalar_atomic(&place, atomic)?; + + // Check alignment requirements. Atomics must always be aligned to their size, + // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must + // be 8-aligned). + let align = Align::from_bytes(place.layout.size.bytes()).unwrap(); + this.check_ptr_access_align( + place.ptr, + place.layout.size, + align, + CheckInAllocMsg::MemoryAccessTest, + )?; + // Perform regular access. + this.write_scalar(val, dest)?; + Ok(()) + } + + fn atomic_store( + &mut self, + args: &[OpTy<'tcx, Tag>], + atomic: AtomicWriteOrd, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let [place, val] = check_arg_count(args)?; + let place = this.deref_operand(place)?; + let val = this.read_scalar(val)?; // make sure it fits into a scalar; otherwise it cannot be atomic + + // Check alignment requirements. Atomics must always be aligned to their size, + // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must + // be 8-aligned). + let align = Align::from_bytes(place.layout.size.bytes()).unwrap(); + this.check_ptr_access_align( + place.ptr, + place.layout.size, + align, + CheckInAllocMsg::MemoryAccessTest, + )?; + + // Perform atomic store + this.write_scalar_atomic(val, &place, atomic)?; + Ok(()) + } + + fn compiler_fence( + &mut self, + args: &[OpTy<'tcx, Tag>], + atomic: AtomicFenceOrd, + ) -> InterpResult<'tcx> { + let [] = check_arg_count(args)?; + let _ = atomic; + //FIXME: compiler fences are currently ignored + Ok(()) + } + + fn atomic_fence( + &mut self, + args: &[OpTy<'tcx, Tag>], + atomic: AtomicFenceOrd, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + let [] = check_arg_count(args)?; + this.validate_atomic_fence(atomic)?; + Ok(()) + } + + fn atomic_op( + &mut self, + args: &[OpTy<'tcx, Tag>], + dest: &PlaceTy<'tcx, Tag>, + atomic_op: AtomicOp, + atomic: AtomicRwOrd, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let [place, rhs] = check_arg_count(args)?; + let place = this.deref_operand(place)?; + let rhs = this.read_immediate(rhs)?; + + if !place.layout.ty.is_integral() && !place.layout.ty.is_unsafe_ptr() { + span_bug!( + this.cur_span(), + "atomic arithmetic operations only work on integer and raw pointer types", + ); + } + if rhs.layout.ty != place.layout.ty { + span_bug!(this.cur_span(), "atomic arithmetic operation type mismatch"); + } + + // Check alignment requirements. Atomics must always be aligned to their size, + // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must + // be 8-aligned). + let align = Align::from_bytes(place.layout.size.bytes()).unwrap(); + this.check_ptr_access_align( + place.ptr, + place.layout.size, + align, + CheckInAllocMsg::MemoryAccessTest, + )?; + + match atomic_op { + AtomicOp::Min => { + let old = this.atomic_min_max_scalar(&place, rhs, true, atomic)?; + this.write_immediate(*old, dest)?; // old value is returned + Ok(()) + } + AtomicOp::Max => { + let old = this.atomic_min_max_scalar(&place, rhs, false, atomic)?; + this.write_immediate(*old, dest)?; // old value is returned + Ok(()) + } + AtomicOp::MirOp(op, neg) => { + let old = this.atomic_op_immediate(&place, &rhs, op, neg, atomic)?; + this.write_immediate(*old, dest)?; // old value is returned + Ok(()) + } + } + } + + fn atomic_exchange( + &mut self, + args: &[OpTy<'tcx, Tag>], + dest: &PlaceTy<'tcx, Tag>, + atomic: AtomicRwOrd, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let [place, new] = check_arg_count(args)?; + let place = this.deref_operand(place)?; + let new = this.read_scalar(new)?; + + // Check alignment requirements. Atomics must always be aligned to their size, + // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must + // be 8-aligned). + let align = Align::from_bytes(place.layout.size.bytes()).unwrap(); + this.check_ptr_access_align( + place.ptr, + place.layout.size, + align, + CheckInAllocMsg::MemoryAccessTest, + )?; + + let old = this.atomic_exchange_scalar(&place, new, atomic)?; + this.write_scalar(old, dest)?; // old value is returned + Ok(()) + } + + fn atomic_compare_exchange_impl( + &mut self, + args: &[OpTy<'tcx, Tag>], + dest: &PlaceTy<'tcx, Tag>, + success: AtomicRwOrd, + fail: AtomicReadOrd, + can_fail_spuriously: bool, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let [place, expect_old, new] = check_arg_count(args)?; + let place = this.deref_operand(place)?; + let expect_old = this.read_immediate(expect_old)?; // read as immediate for the sake of `binary_op()` + let new = this.read_scalar(new)?; + + // Check alignment requirements. Atomics must always be aligned to their size, + // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must + // be 8-aligned). + let align = Align::from_bytes(place.layout.size.bytes()).unwrap(); + this.check_ptr_access_align( + place.ptr, + place.layout.size, + align, + CheckInAllocMsg::MemoryAccessTest, + )?; + + let old = this.atomic_compare_exchange_scalar( + &place, + &expect_old, + new, + success, + fail, + can_fail_spuriously, + )?; + + // Return old value. + this.write_immediate(old, dest)?; + Ok(()) + } + + fn atomic_compare_exchange( + &mut self, + args: &[OpTy<'tcx, Tag>], + dest: &PlaceTy<'tcx, Tag>, + success: AtomicRwOrd, + fail: AtomicReadOrd, + ) -> InterpResult<'tcx> { + self.atomic_compare_exchange_impl(args, dest, success, fail, false) + } + + fn atomic_compare_exchange_weak( + &mut self, + args: &[OpTy<'tcx, Tag>], + dest: &PlaceTy<'tcx, Tag>, + success: AtomicRwOrd, + fail: AtomicReadOrd, + ) -> InterpResult<'tcx> { + self.atomic_compare_exchange_impl(args, dest, success, fail, true) + } +} diff --git a/src/shims/intrinsics/mod.rs b/src/shims/intrinsics/mod.rs new file mode 100644 index 0000000000..9ffa40f333 --- /dev/null +++ b/src/shims/intrinsics/mod.rs @@ -0,0 +1,426 @@ +mod atomic; +mod simd; + +use std::iter; + +use log::trace; + +use rustc_apfloat::{Float, Round}; +use rustc_middle::ty::layout::{IntegerExt, LayoutOf}; +use rustc_middle::{mir, ty, ty::FloatTy}; +use rustc_target::abi::Integer; + +use crate::*; +use atomic::EvalContextExt as _; +use helpers::check_arg_count; +use simd::EvalContextExt as _; + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { + fn call_intrinsic( + &mut self, + instance: ty::Instance<'tcx>, + args: &[OpTy<'tcx, Tag>], + dest: &PlaceTy<'tcx, Tag>, + ret: Option, + _unwind: StackPopUnwind, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + // See if the core engine can handle this intrinsic. + if this.emulate_intrinsic(instance, args, dest, ret)? { + return Ok(()); + } + + // All remaining supported intrinsics have a return place. + let intrinsic_name = this.tcx.item_name(instance.def_id()); + let intrinsic_name = intrinsic_name.as_str(); + let ret = match ret { + None => throw_unsup_format!("unimplemented (diverging) intrinsic: `{intrinsic_name}`"), + Some(p) => p, + }; + + // Some intrinsics are special and need the "ret". + match intrinsic_name { + "try" => return this.handle_try(args, dest, ret), + _ => {} + } + + // The rest jumps to `ret` immediately. + this.emulate_intrinsic_by_name(intrinsic_name, args, dest)?; + + trace!("{:?}", this.dump_place(**dest)); + this.go_to_block(ret); + Ok(()) + } + + /// Emulates a Miri-supported intrinsic (not supported by the core engine). + fn emulate_intrinsic_by_name( + &mut self, + intrinsic_name: &str, + args: &[OpTy<'tcx, Tag>], + dest: &PlaceTy<'tcx, Tag>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + if let Some(name) = intrinsic_name.strip_prefix("atomic_") { + return this.emulate_atomic_intrinsic(name, args, dest); + } + if let Some(name) = intrinsic_name.strip_prefix("simd_") { + return this.emulate_simd_intrinsic(name, args, dest); + } + + match intrinsic_name { + // Miri overwriting CTFE intrinsics. + "ptr_guaranteed_eq" => { + let [left, right] = check_arg_count(args)?; + let left = this.read_immediate(left)?; + let right = this.read_immediate(right)?; + this.binop_ignore_overflow(mir::BinOp::Eq, &left, &right, dest)?; + } + "ptr_guaranteed_ne" => { + let [left, right] = check_arg_count(args)?; + let left = this.read_immediate(left)?; + let right = this.read_immediate(right)?; + this.binop_ignore_overflow(mir::BinOp::Ne, &left, &right, dest)?; + } + "const_allocate" => { + // For now, for compatibility with the run-time implementation of this, we just return null. + // See . + this.write_null(dest)?; + } + "const_deallocate" => { + // complete NOP + } + + // Raw memory accesses + "volatile_load" => { + let [place] = check_arg_count(args)?; + let place = this.deref_operand(place)?; + this.copy_op(&place.into(), dest, /*allow_transmute*/ false)?; + } + "volatile_store" => { + let [place, dest] = check_arg_count(args)?; + let place = this.deref_operand(place)?; + this.copy_op(dest, &place.into(), /*allow_transmute*/ false)?; + } + + "write_bytes" | "volatile_set_memory" => { + let [ptr, val_byte, count] = check_arg_count(args)?; + let ty = ptr.layout.ty.builtin_deref(true).unwrap().ty; + let ty_layout = this.layout_of(ty)?; + let val_byte = this.read_scalar(val_byte)?.to_u8()?; + let ptr = this.read_pointer(ptr)?; + let count = this.read_scalar(count)?.to_machine_usize(this)?; + // `checked_mul` enforces a too small bound (the correct one would probably be machine_isize_max), + // but no actual allocation can be big enough for the difference to be noticeable. + let byte_count = ty_layout.size.checked_mul(count, this).ok_or_else(|| { + err_ub_format!("overflow computing total size of `{intrinsic_name}`") + })?; + this.write_bytes_ptr( + ptr, + iter::repeat(val_byte).take(byte_count.bytes() as usize), + )?; + } + + // Floating-point operations + "fabsf32" => { + let [f] = check_arg_count(args)?; + let f = this.read_scalar(f)?.to_f32()?; + // Can be implemented in soft-floats. + this.write_scalar(Scalar::from_f32(f.abs()), dest)?; + } + "fabsf64" => { + let [f] = check_arg_count(args)?; + let f = this.read_scalar(f)?.to_f64()?; + // Can be implemented in soft-floats. + this.write_scalar(Scalar::from_f64(f.abs()), dest)?; + } + #[rustfmt::skip] + | "sinf32" + | "cosf32" + | "sqrtf32" + | "expf32" + | "exp2f32" + | "logf32" + | "log10f32" + | "log2f32" + | "floorf32" + | "ceilf32" + | "truncf32" + | "roundf32" + => { + let [f] = check_arg_count(args)?; + // FIXME: Using host floats. + let f = f32::from_bits(this.read_scalar(f)?.to_u32()?); + let f = match intrinsic_name { + "sinf32" => f.sin(), + "cosf32" => f.cos(), + "sqrtf32" => f.sqrt(), + "expf32" => f.exp(), + "exp2f32" => f.exp2(), + "logf32" => f.ln(), + "log10f32" => f.log10(), + "log2f32" => f.log2(), + "floorf32" => f.floor(), + "ceilf32" => f.ceil(), + "truncf32" => f.trunc(), + "roundf32" => f.round(), + _ => bug!(), + }; + this.write_scalar(Scalar::from_u32(f.to_bits()), dest)?; + } + + #[rustfmt::skip] + | "sinf64" + | "cosf64" + | "sqrtf64" + | "expf64" + | "exp2f64" + | "logf64" + | "log10f64" + | "log2f64" + | "floorf64" + | "ceilf64" + | "truncf64" + | "roundf64" + => { + let [f] = check_arg_count(args)?; + // FIXME: Using host floats. + let f = f64::from_bits(this.read_scalar(f)?.to_u64()?); + let f = match intrinsic_name { + "sinf64" => f.sin(), + "cosf64" => f.cos(), + "sqrtf64" => f.sqrt(), + "expf64" => f.exp(), + "exp2f64" => f.exp2(), + "logf64" => f.ln(), + "log10f64" => f.log10(), + "log2f64" => f.log2(), + "floorf64" => f.floor(), + "ceilf64" => f.ceil(), + "truncf64" => f.trunc(), + "roundf64" => f.round(), + _ => bug!(), + }; + this.write_scalar(Scalar::from_u64(f.to_bits()), dest)?; + } + + #[rustfmt::skip] + | "fadd_fast" + | "fsub_fast" + | "fmul_fast" + | "fdiv_fast" + | "frem_fast" + => { + let [a, b] = check_arg_count(args)?; + let a = this.read_immediate(a)?; + let b = this.read_immediate(b)?; + let op = match intrinsic_name { + "fadd_fast" => mir::BinOp::Add, + "fsub_fast" => mir::BinOp::Sub, + "fmul_fast" => mir::BinOp::Mul, + "fdiv_fast" => mir::BinOp::Div, + "frem_fast" => mir::BinOp::Rem, + _ => bug!(), + }; + let float_finite = |x: ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> { + Ok(match x.layout.ty.kind() { + ty::Float(FloatTy::F32) => x.to_scalar()?.to_f32()?.is_finite(), + ty::Float(FloatTy::F64) => x.to_scalar()?.to_f64()?.is_finite(), + _ => bug!( + "`{intrinsic_name}` called with non-float input type {ty:?}", + ty = x.layout.ty, + ), + }) + }; + match (float_finite(a)?, float_finite(b)?) { + (false, false) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as both parameters", + ), + (false, _) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as first parameter", + ), + (_, false) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as second parameter", + ), + _ => {} + } + this.binop_ignore_overflow(op, &a, &b, dest)?; + } + + #[rustfmt::skip] + | "minnumf32" + | "maxnumf32" + | "copysignf32" + => { + let [a, b] = check_arg_count(args)?; + let a = this.read_scalar(a)?.to_f32()?; + let b = this.read_scalar(b)?.to_f32()?; + let res = match intrinsic_name { + "minnumf32" => a.min(b), + "maxnumf32" => a.max(b), + "copysignf32" => a.copy_sign(b), + _ => bug!(), + }; + this.write_scalar(Scalar::from_f32(res), dest)?; + } + + #[rustfmt::skip] + | "minnumf64" + | "maxnumf64" + | "copysignf64" + => { + let [a, b] = check_arg_count(args)?; + let a = this.read_scalar(a)?.to_f64()?; + let b = this.read_scalar(b)?.to_f64()?; + let res = match intrinsic_name { + "minnumf64" => a.min(b), + "maxnumf64" => a.max(b), + "copysignf64" => a.copy_sign(b), + _ => bug!(), + }; + this.write_scalar(Scalar::from_f64(res), dest)?; + } + + "powf32" => { + let [f, f2] = check_arg_count(args)?; + // FIXME: Using host floats. + let f = f32::from_bits(this.read_scalar(f)?.to_u32()?); + let f2 = f32::from_bits(this.read_scalar(f2)?.to_u32()?); + this.write_scalar(Scalar::from_u32(f.powf(f2).to_bits()), dest)?; + } + + "powf64" => { + let [f, f2] = check_arg_count(args)?; + // FIXME: Using host floats. + let f = f64::from_bits(this.read_scalar(f)?.to_u64()?); + let f2 = f64::from_bits(this.read_scalar(f2)?.to_u64()?); + this.write_scalar(Scalar::from_u64(f.powf(f2).to_bits()), dest)?; + } + + "fmaf32" => { + let [a, b, c] = check_arg_count(args)?; + let a = this.read_scalar(a)?.to_f32()?; + let b = this.read_scalar(b)?.to_f32()?; + let c = this.read_scalar(c)?.to_f32()?; + let res = a.mul_add(b, c).value; + this.write_scalar(Scalar::from_f32(res), dest)?; + } + + "fmaf64" => { + let [a, b, c] = check_arg_count(args)?; + let a = this.read_scalar(a)?.to_f64()?; + let b = this.read_scalar(b)?.to_f64()?; + let c = this.read_scalar(c)?.to_f64()?; + let res = a.mul_add(b, c).value; + this.write_scalar(Scalar::from_f64(res), dest)?; + } + + "powif32" => { + let [f, i] = check_arg_count(args)?; + // FIXME: Using host floats. + let f = f32::from_bits(this.read_scalar(f)?.to_u32()?); + let i = this.read_scalar(i)?.to_i32()?; + this.write_scalar(Scalar::from_u32(f.powi(i).to_bits()), dest)?; + } + + "powif64" => { + let [f, i] = check_arg_count(args)?; + // FIXME: Using host floats. + let f = f64::from_bits(this.read_scalar(f)?.to_u64()?); + let i = this.read_scalar(i)?.to_i32()?; + this.write_scalar(Scalar::from_u64(f.powi(i).to_bits()), dest)?; + } + + "float_to_int_unchecked" => { + let [val] = check_arg_count(args)?; + let val = this.read_immediate(val)?; + + let res = match val.layout.ty.kind() { + ty::Float(FloatTy::F32) => + this.float_to_int_unchecked(val.to_scalar()?.to_f32()?, dest.layout.ty)?, + ty::Float(FloatTy::F64) => + this.float_to_int_unchecked(val.to_scalar()?.to_f64()?, dest.layout.ty)?, + _ => + span_bug!( + this.cur_span(), + "`float_to_int_unchecked` called with non-float input type {:?}", + val.layout.ty + ), + }; + + this.write_scalar(res, dest)?; + } + + // Other + "exact_div" => { + let [num, denom] = check_arg_count(args)?; + this.exact_div(&this.read_immediate(num)?, &this.read_immediate(denom)?, dest)?; + } + + "breakpoint" => { + let [] = check_arg_count(args)?; + // normally this would raise a SIGTRAP, which aborts if no debugger is connected + throw_machine_stop!(TerminationInfo::Abort("Trace/breakpoint trap".to_string())) + } + + name => throw_unsup_format!("unimplemented intrinsic: `{name}`"), + } + + Ok(()) + } + + fn float_to_int_unchecked( + &self, + f: F, + dest_ty: ty::Ty<'tcx>, + ) -> InterpResult<'tcx, Scalar> + where + F: Float + Into>, + { + let this = self.eval_context_ref(); + + // Step 1: cut off the fractional part of `f`. The result of this is + // guaranteed to be precisely representable in IEEE floats. + let f = f.round_to_integral(Round::TowardZero).value; + + // Step 2: Cast the truncated float to the target integer type and see if we lose any information in this step. + Ok(match dest_ty.kind() { + // Unsigned + ty::Uint(t) => { + let size = Integer::from_uint_ty(this, *t).size(); + let res = f.to_u128(size.bits_usize()); + if res.status.is_empty() { + // No status flags means there was no further rounding or other loss of precision. + Scalar::from_uint(res.value, size) + } else { + // `f` was not representable in this integer type. + throw_ub_format!( + "`float_to_int_unchecked` intrinsic called on {f} which cannot be represented in target type `{dest_ty:?}`", + ); + } + } + // Signed + ty::Int(t) => { + let size = Integer::from_int_ty(this, *t).size(); + let res = f.to_i128(size.bits_usize()); + if res.status.is_empty() { + // No status flags means there was no further rounding or other loss of precision. + Scalar::from_int(res.value, size) + } else { + // `f` was not representable in this integer type. + throw_ub_format!( + "`float_to_int_unchecked` intrinsic called on {f} which cannot be represented in target type `{dest_ty:?}`", + ); + } + } + // Nothing else + _ => + span_bug!( + this.cur_span(), + "`float_to_int_unchecked` called with non-int output type {dest_ty:?}" + ), + }) + } +} diff --git a/src/shims/intrinsics/simd.rs b/src/shims/intrinsics/simd.rs new file mode 100644 index 0000000000..fe5250ed08 --- /dev/null +++ b/src/shims/intrinsics/simd.rs @@ -0,0 +1,613 @@ +use rustc_apfloat::Float; +use rustc_middle::ty::layout::{HasParamEnv, LayoutOf}; +use rustc_middle::{mir, ty, ty::FloatTy}; +use rustc_target::abi::{Endian, HasDataLayout, Size}; + +use crate::*; +use helpers::check_arg_count; + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { + /// Calls the simd intrinsic `intrinsic`; the `simd_` prefix has already been removed. + fn emulate_simd_intrinsic( + &mut self, + intrinsic_name: &str, + args: &[OpTy<'tcx, Tag>], + dest: &PlaceTy<'tcx, Tag>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + match intrinsic_name { + #[rustfmt::skip] + | "neg" + | "fabs" + | "ceil" + | "floor" + | "round" + | "trunc" + | "fsqrt" => { + let [op] = check_arg_count(args)?; + let (op, op_len) = this.operand_to_simd(op)?; + let (dest, dest_len) = this.place_to_simd(dest)?; + + assert_eq!(dest_len, op_len); + + #[derive(Copy, Clone)] + enum HostFloatOp { + Ceil, + Floor, + Round, + Trunc, + Sqrt, + } + #[derive(Copy, Clone)] + enum Op { + MirOp(mir::UnOp), + Abs, + HostOp(HostFloatOp), + } + let which = match intrinsic_name { + "neg" => Op::MirOp(mir::UnOp::Neg), + "fabs" => Op::Abs, + "ceil" => Op::HostOp(HostFloatOp::Ceil), + "floor" => Op::HostOp(HostFloatOp::Floor), + "round" => Op::HostOp(HostFloatOp::Round), + "trunc" => Op::HostOp(HostFloatOp::Trunc), + "fsqrt" => Op::HostOp(HostFloatOp::Sqrt), + _ => unreachable!(), + }; + + for i in 0..dest_len { + let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; + let dest = this.mplace_index(&dest, i)?; + let val = match which { + Op::MirOp(mir_op) => this.unary_op(mir_op, &op)?.to_scalar()?, + Op::Abs => { + // Works for f32 and f64. + let ty::Float(float_ty) = op.layout.ty.kind() else { + span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name) + }; + let op = op.to_scalar()?; + match float_ty { + FloatTy::F32 => Scalar::from_f32(op.to_f32()?.abs()), + FloatTy::F64 => Scalar::from_f64(op.to_f64()?.abs()), + } + } + Op::HostOp(host_op) => { + let ty::Float(float_ty) = op.layout.ty.kind() else { + span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name) + }; + // FIXME using host floats + match float_ty { + FloatTy::F32 => { + let f = f32::from_bits(op.to_scalar()?.to_u32()?); + let res = match host_op { + HostFloatOp::Ceil => f.ceil(), + HostFloatOp::Floor => f.floor(), + HostFloatOp::Round => f.round(), + HostFloatOp::Trunc => f.trunc(), + HostFloatOp::Sqrt => f.sqrt(), + }; + Scalar::from_u32(res.to_bits()) + } + FloatTy::F64 => { + let f = f64::from_bits(op.to_scalar()?.to_u64()?); + let res = match host_op { + HostFloatOp::Ceil => f.ceil(), + HostFloatOp::Floor => f.floor(), + HostFloatOp::Round => f.round(), + HostFloatOp::Trunc => f.trunc(), + HostFloatOp::Sqrt => f.sqrt(), + }; + Scalar::from_u64(res.to_bits()) + } + } + + } + }; + this.write_scalar(val, &dest.into())?; + } + } + #[rustfmt::skip] + | "add" + | "sub" + | "mul" + | "div" + | "rem" + | "shl" + | "shr" + | "and" + | "or" + | "xor" + | "eq" + | "ne" + | "lt" + | "le" + | "gt" + | "ge" + | "fmax" + | "fmin" + | "saturating_add" + | "saturating_sub" + | "arith_offset" => { + use mir::BinOp; + + let [left, right] = check_arg_count(args)?; + let (left, left_len) = this.operand_to_simd(left)?; + let (right, right_len) = this.operand_to_simd(right)?; + let (dest, dest_len) = this.place_to_simd(dest)?; + + assert_eq!(dest_len, left_len); + assert_eq!(dest_len, right_len); + + enum Op { + MirOp(BinOp), + SaturatingOp(BinOp), + FMax, + FMin, + WrappingOffset, + } + let which = match intrinsic_name { + "add" => Op::MirOp(BinOp::Add), + "sub" => Op::MirOp(BinOp::Sub), + "mul" => Op::MirOp(BinOp::Mul), + "div" => Op::MirOp(BinOp::Div), + "rem" => Op::MirOp(BinOp::Rem), + "shl" => Op::MirOp(BinOp::Shl), + "shr" => Op::MirOp(BinOp::Shr), + "and" => Op::MirOp(BinOp::BitAnd), + "or" => Op::MirOp(BinOp::BitOr), + "xor" => Op::MirOp(BinOp::BitXor), + "eq" => Op::MirOp(BinOp::Eq), + "ne" => Op::MirOp(BinOp::Ne), + "lt" => Op::MirOp(BinOp::Lt), + "le" => Op::MirOp(BinOp::Le), + "gt" => Op::MirOp(BinOp::Gt), + "ge" => Op::MirOp(BinOp::Ge), + "fmax" => Op::FMax, + "fmin" => Op::FMin, + "saturating_add" => Op::SaturatingOp(BinOp::Add), + "saturating_sub" => Op::SaturatingOp(BinOp::Sub), + "arith_offset" => Op::WrappingOffset, + _ => unreachable!(), + }; + + for i in 0..dest_len { + let left = this.read_immediate(&this.mplace_index(&left, i)?.into())?; + let right = this.read_immediate(&this.mplace_index(&right, i)?.into())?; + let dest = this.mplace_index(&dest, i)?; + let val = match which { + Op::MirOp(mir_op) => { + let (val, overflowed, ty) = this.overflowing_binary_op(mir_op, &left, &right)?; + if matches!(mir_op, BinOp::Shl | BinOp::Shr) { + // Shifts have extra UB as SIMD operations that the MIR binop does not have. + // See . + if overflowed { + let r_val = right.to_scalar()?.to_bits(right.layout.size)?; + throw_ub_format!("overflowing shift by {r_val} in `simd_{intrinsic_name}` in SIMD lane {i}"); + } + } + if matches!(mir_op, BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge) { + // Special handling for boolean-returning operations + assert_eq!(ty, this.tcx.types.bool); + let val = val.to_bool().unwrap(); + bool_to_simd_element(val, dest.layout.size) + } else { + assert_ne!(ty, this.tcx.types.bool); + assert_eq!(ty, dest.layout.ty); + val + } + } + Op::SaturatingOp(mir_op) => { + this.saturating_arith(mir_op, &left, &right)? + } + Op::WrappingOffset => { + let ptr = this.scalar_to_ptr(left.to_scalar()?)?; + let offset_count = right.to_scalar()?.to_machine_isize(this)?; + let pointee_ty = left.layout.ty.builtin_deref(true).unwrap().ty; + + let pointee_size = i64::try_from(this.layout_of(pointee_ty)?.size.bytes()).unwrap(); + let offset_bytes = offset_count.wrapping_mul(pointee_size); + let offset_ptr = ptr.wrapping_signed_offset(offset_bytes, this); + Scalar::from_maybe_pointer(offset_ptr, this) + } + Op::FMax => { + fmax_op(&left, &right)? + } + Op::FMin => { + fmin_op(&left, &right)? + } + }; + this.write_scalar(val, &dest.into())?; + } + } + "fma" => { + let [a, b, c] = check_arg_count(args)?; + let (a, a_len) = this.operand_to_simd(a)?; + let (b, b_len) = this.operand_to_simd(b)?; + let (c, c_len) = this.operand_to_simd(c)?; + let (dest, dest_len) = this.place_to_simd(dest)?; + + assert_eq!(dest_len, a_len); + assert_eq!(dest_len, b_len); + assert_eq!(dest_len, c_len); + + for i in 0..dest_len { + let a = this.read_immediate(&this.mplace_index(&a, i)?.into())?.to_scalar()?; + let b = this.read_immediate(&this.mplace_index(&b, i)?.into())?.to_scalar()?; + let c = this.read_immediate(&this.mplace_index(&c, i)?.into())?.to_scalar()?; + let dest = this.mplace_index(&dest, i)?; + + // Works for f32 and f64. + let ty::Float(float_ty) = dest.layout.ty.kind() else { + span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name) + }; + let val = match float_ty { + FloatTy::F32 => + Scalar::from_f32(a.to_f32()?.mul_add(b.to_f32()?, c.to_f32()?).value), + FloatTy::F64 => + Scalar::from_f64(a.to_f64()?.mul_add(b.to_f64()?, c.to_f64()?).value), + }; + this.write_scalar(val, &dest.into())?; + } + } + #[rustfmt::skip] + | "reduce_and" + | "reduce_or" + | "reduce_xor" + | "reduce_any" + | "reduce_all" + | "reduce_max" + | "reduce_min" => { + use mir::BinOp; + + let [op] = check_arg_count(args)?; + let (op, op_len) = this.operand_to_simd(op)?; + + let imm_from_bool = + |b| ImmTy::from_scalar(Scalar::from_bool(b), this.machine.layouts.bool); + + enum Op { + MirOp(BinOp), + MirOpBool(BinOp), + Max, + Min, + } + let which = match intrinsic_name { + "reduce_and" => Op::MirOp(BinOp::BitAnd), + "reduce_or" => Op::MirOp(BinOp::BitOr), + "reduce_xor" => Op::MirOp(BinOp::BitXor), + "reduce_any" => Op::MirOpBool(BinOp::BitOr), + "reduce_all" => Op::MirOpBool(BinOp::BitAnd), + "reduce_max" => Op::Max, + "reduce_min" => Op::Min, + _ => unreachable!(), + }; + + // Initialize with first lane, then proceed with the rest. + let mut res = this.read_immediate(&this.mplace_index(&op, 0)?.into())?; + if matches!(which, Op::MirOpBool(_)) { + // Convert to `bool` scalar. + res = imm_from_bool(simd_element_to_bool(res)?); + } + for i in 1..op_len { + let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; + res = match which { + Op::MirOp(mir_op) => { + this.binary_op(mir_op, &res, &op)? + } + Op::MirOpBool(mir_op) => { + let op = imm_from_bool(simd_element_to_bool(op)?); + this.binary_op(mir_op, &res, &op)? + } + Op::Max => { + if matches!(res.layout.ty.kind(), ty::Float(_)) { + ImmTy::from_scalar(fmax_op(&res, &op)?, res.layout) + } else { + // Just boring integers, so NaNs to worry about + if this.binary_op(BinOp::Ge, &res, &op)?.to_scalar()?.to_bool()? { + res + } else { + op + } + } + } + Op::Min => { + if matches!(res.layout.ty.kind(), ty::Float(_)) { + ImmTy::from_scalar(fmin_op(&res, &op)?, res.layout) + } else { + // Just boring integers, so NaNs to worry about + if this.binary_op(BinOp::Le, &res, &op)?.to_scalar()?.to_bool()? { + res + } else { + op + } + } + } + }; + } + this.write_immediate(*res, dest)?; + } + #[rustfmt::skip] + | "reduce_add_ordered" + | "reduce_mul_ordered" => { + use mir::BinOp; + + let [op, init] = check_arg_count(args)?; + let (op, op_len) = this.operand_to_simd(op)?; + let init = this.read_immediate(init)?; + + let mir_op = match intrinsic_name { + "reduce_add_ordered" => BinOp::Add, + "reduce_mul_ordered" => BinOp::Mul, + _ => unreachable!(), + }; + + let mut res = init; + for i in 0..op_len { + let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; + res = this.binary_op(mir_op, &res, &op)?; + } + this.write_immediate(*res, dest)?; + } + "select" => { + let [mask, yes, no] = check_arg_count(args)?; + let (mask, mask_len) = this.operand_to_simd(mask)?; + let (yes, yes_len) = this.operand_to_simd(yes)?; + let (no, no_len) = this.operand_to_simd(no)?; + let (dest, dest_len) = this.place_to_simd(dest)?; + + assert_eq!(dest_len, mask_len); + assert_eq!(dest_len, yes_len); + assert_eq!(dest_len, no_len); + + for i in 0..dest_len { + let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?; + let yes = this.read_immediate(&this.mplace_index(&yes, i)?.into())?; + let no = this.read_immediate(&this.mplace_index(&no, i)?.into())?; + let dest = this.mplace_index(&dest, i)?; + + let val = if simd_element_to_bool(mask)? { yes } else { no }; + this.write_immediate(*val, &dest.into())?; + } + } + "select_bitmask" => { + let [mask, yes, no] = check_arg_count(args)?; + let (yes, yes_len) = this.operand_to_simd(yes)?; + let (no, no_len) = this.operand_to_simd(no)?; + let (dest, dest_len) = this.place_to_simd(dest)?; + let bitmask_len = dest_len.max(8); + + assert!(mask.layout.ty.is_integral()); + assert!(bitmask_len <= 64); + assert_eq!(bitmask_len, mask.layout.size.bits()); + assert_eq!(dest_len, yes_len); + assert_eq!(dest_len, no_len); + + let mask: u64 = this + .read_scalar(mask)? + .check_init()? + .to_bits(mask.layout.size)? + .try_into() + .unwrap(); + for i in 0..dest_len { + let mask = + mask & (1 << simd_bitmask_index(i, dest_len, this.data_layout().endian)); + let yes = this.read_immediate(&this.mplace_index(&yes, i)?.into())?; + let no = this.read_immediate(&this.mplace_index(&no, i)?.into())?; + let dest = this.mplace_index(&dest, i)?; + + let val = if mask != 0 { yes } else { no }; + this.write_immediate(*val, &dest.into())?; + } + for i in dest_len..bitmask_len { + // If the mask is "padded", ensure that padding is all-zero. + let mask = mask & (1 << i); + if mask != 0 { + throw_ub_format!( + "a SIMD bitmask less than 8 bits long must be filled with 0s for the remaining bits" + ); + } + } + } + #[rustfmt::skip] + "cast" | "as" => { + let [op] = check_arg_count(args)?; + let (op, op_len) = this.operand_to_simd(op)?; + let (dest, dest_len) = this.place_to_simd(dest)?; + + assert_eq!(dest_len, op_len); + + let safe_cast = intrinsic_name == "as"; + + for i in 0..dest_len { + let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; + let dest = this.mplace_index(&dest, i)?; + + let val = match (op.layout.ty.kind(), dest.layout.ty.kind()) { + // Int-to-(int|float): always safe + (ty::Int(_) | ty::Uint(_), ty::Int(_) | ty::Uint(_) | ty::Float(_)) => + this.misc_cast(&op, dest.layout.ty)?, + // Float-to-float: always safe + (ty::Float(_), ty::Float(_)) => + this.misc_cast(&op, dest.layout.ty)?, + // Float-to-int in safe mode + (ty::Float(_), ty::Int(_) | ty::Uint(_)) if safe_cast => + this.misc_cast(&op, dest.layout.ty)?, + // Float-to-int in unchecked mode + (ty::Float(FloatTy::F32), ty::Int(_) | ty::Uint(_)) if !safe_cast => + this.float_to_int_unchecked(op.to_scalar()?.to_f32()?, dest.layout.ty)?.into(), + (ty::Float(FloatTy::F64), ty::Int(_) | ty::Uint(_)) if !safe_cast => + this.float_to_int_unchecked(op.to_scalar()?.to_f64()?, dest.layout.ty)?.into(), + _ => + throw_unsup_format!( + "Unsupported SIMD cast from element type {from_ty} to {to_ty}", + from_ty = op.layout.ty, + to_ty = dest.layout.ty, + ), + }; + this.write_immediate(val, &dest.into())?; + } + } + "shuffle" => { + let [left, right, index] = check_arg_count(args)?; + let (left, left_len) = this.operand_to_simd(left)?; + let (right, right_len) = this.operand_to_simd(right)?; + let (dest, dest_len) = this.place_to_simd(dest)?; + + // `index` is an array, not a SIMD type + let ty::Array(_, index_len) = index.layout.ty.kind() else { + span_bug!(this.cur_span(), "simd_shuffle index argument has non-array type {}", index.layout.ty) + }; + let index_len = index_len.eval_usize(*this.tcx, this.param_env()); + + assert_eq!(left_len, right_len); + assert_eq!(index_len, dest_len); + + for i in 0..dest_len { + let src_index: u64 = this + .read_immediate(&this.operand_index(index, i)?)? + .to_scalar()? + .to_u32()? + .into(); + let dest = this.mplace_index(&dest, i)?; + + let val = if src_index < left_len { + this.read_immediate(&this.mplace_index(&left, src_index)?.into())? + } else if src_index < left_len.checked_add(right_len).unwrap() { + this.read_immediate( + &this.mplace_index(&right, src_index - left_len)?.into(), + )? + } else { + span_bug!( + this.cur_span(), + "simd_shuffle index {src_index} is out of bounds for 2 vectors of size {left_len}", + ); + }; + this.write_immediate(*val, &dest.into())?; + } + } + "gather" => { + let [passthru, ptrs, mask] = check_arg_count(args)?; + let (passthru, passthru_len) = this.operand_to_simd(passthru)?; + let (ptrs, ptrs_len) = this.operand_to_simd(ptrs)?; + let (mask, mask_len) = this.operand_to_simd(mask)?; + let (dest, dest_len) = this.place_to_simd(dest)?; + + assert_eq!(dest_len, passthru_len); + assert_eq!(dest_len, ptrs_len); + assert_eq!(dest_len, mask_len); + + for i in 0..dest_len { + let passthru = this.read_immediate(&this.mplace_index(&passthru, i)?.into())?; + let ptr = this.read_immediate(&this.mplace_index(&ptrs, i)?.into())?; + let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?; + let dest = this.mplace_index(&dest, i)?; + + let val = if simd_element_to_bool(mask)? { + let place = this.deref_operand(&ptr.into())?; + this.read_immediate(&place.into())? + } else { + passthru + }; + this.write_immediate(*val, &dest.into())?; + } + } + "scatter" => { + let [value, ptrs, mask] = check_arg_count(args)?; + let (value, value_len) = this.operand_to_simd(value)?; + let (ptrs, ptrs_len) = this.operand_to_simd(ptrs)?; + let (mask, mask_len) = this.operand_to_simd(mask)?; + + assert_eq!(ptrs_len, value_len); + assert_eq!(ptrs_len, mask_len); + + for i in 0..ptrs_len { + let value = this.read_immediate(&this.mplace_index(&value, i)?.into())?; + let ptr = this.read_immediate(&this.mplace_index(&ptrs, i)?.into())?; + let mask = this.read_immediate(&this.mplace_index(&mask, i)?.into())?; + + if simd_element_to_bool(mask)? { + let place = this.deref_operand(&ptr.into())?; + this.write_immediate(*value, &place.into())?; + } + } + } + "bitmask" => { + let [op] = check_arg_count(args)?; + let (op, op_len) = this.operand_to_simd(op)?; + let bitmask_len = op_len.max(8); + + assert!(dest.layout.ty.is_integral()); + assert!(bitmask_len <= 64); + assert_eq!(bitmask_len, dest.layout.size.bits()); + + let mut res = 0u64; + for i in 0..op_len { + let op = this.read_immediate(&this.mplace_index(&op, i)?.into())?; + if simd_element_to_bool(op)? { + res |= 1 << simd_bitmask_index(i, op_len, this.data_layout().endian); + } + } + this.write_int(res, dest)?; + } + + name => throw_unsup_format!("unimplemented intrinsic: `simd_{name}`"), + } + Ok(()) + } +} + +fn bool_to_simd_element(b: bool, size: Size) -> Scalar { + // SIMD uses all-1 as pattern for "true" + let val = if b { -1 } else { 0 }; + Scalar::from_int(val, size) +} + +fn simd_element_to_bool(elem: ImmTy<'_, Tag>) -> InterpResult<'_, bool> { + let val = elem.to_scalar()?.to_int(elem.layout.size)?; + Ok(match val { + 0 => false, + -1 => true, + _ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"), + }) +} + +fn simd_bitmask_index(idx: u64, vec_len: u64, endianess: Endian) -> u64 { + assert!(idx < vec_len); + match endianess { + Endian::Little => idx, + Endian::Big => vec_len - 1 - idx, // reverse order of bits + } +} + +fn fmax_op<'tcx>( + left: &ImmTy<'tcx, Tag>, + right: &ImmTy<'tcx, Tag>, +) -> InterpResult<'tcx, Scalar> { + assert_eq!(left.layout.ty, right.layout.ty); + let ty::Float(float_ty) = left.layout.ty.kind() else { + bug!("fmax operand is not a float") + }; + let left = left.to_scalar()?; + let right = right.to_scalar()?; + Ok(match float_ty { + FloatTy::F32 => Scalar::from_f32(left.to_f32()?.max(right.to_f32()?)), + FloatTy::F64 => Scalar::from_f64(left.to_f64()?.max(right.to_f64()?)), + }) +} + +fn fmin_op<'tcx>( + left: &ImmTy<'tcx, Tag>, + right: &ImmTy<'tcx, Tag>, +) -> InterpResult<'tcx, Scalar> { + assert_eq!(left.layout.ty, right.layout.ty); + let ty::Float(float_ty) = left.layout.ty.kind() else { + bug!("fmin operand is not a float") + }; + let left = left.to_scalar()?; + let right = right.to_scalar()?; + Ok(match float_ty { + FloatTy::F32 => Scalar::from_f32(left.to_f32()?.min(right.to_f32()?)), + FloatTy::F64 => Scalar::from_f64(left.to_f64()?.min(right.to_f64()?)), + }) +} diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index 44147433c0..cf34b4baec 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -2,7 +2,6 @@ use std::ffi::OsStr; use log::trace; -use rustc_middle::mir; use rustc_middle::ty::layout::LayoutOf; use rustc_span::Symbol; use rustc_target::abi::{Align, Size}; @@ -22,7 +21,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx abi: Abi, args: &[OpTy<'tcx, Tag>], dest: &PlaceTy<'tcx, Tag>, - ret: mir::BasicBlock, ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> { let this = self.eval_context_mut(); @@ -440,12 +438,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Miscellaneous "isatty" => { let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - this.read_scalar(fd)?.to_i32()?; - // "returns 1 if fd is an open file descriptor referring to a terminal; otherwise 0 is returned, and errno is set to indicate the error" - // FIXME: we just say nothing is a terminal. - let enotty = this.eval_libc("ENOTTY")?; - this.set_last_error(enotty)?; - this.write_null(dest)?; + let result = this.isatty(fd)?; + this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_atfork" => { let [prepare, parent, child] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -537,9 +531,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Platform-specific shims _ => { match this.tcx.sess.target.os.as_ref() { - "linux" => return shims::unix::linux::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret), - "macos" => return shims::unix::macos::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret), - "freebsd" => return shims::unix::freebsd::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret), + "linux" => return shims::unix::linux::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest), + "macos" => return shims::unix::macos::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest), + "freebsd" => return shims::unix::freebsd::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest), _ => unreachable!(), } } diff --git a/src/shims/unix/freebsd/foreign_items.rs b/src/shims/unix/freebsd/foreign_items.rs index 66e339cc4a..2350e5a12c 100644 --- a/src/shims/unix/freebsd/foreign_items.rs +++ b/src/shims/unix/freebsd/foreign_items.rs @@ -1,4 +1,3 @@ -use rustc_middle::mir; use rustc_span::Symbol; use rustc_target::spec::abi::Abi; @@ -14,7 +13,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx abi: Abi, args: &[OpTy<'tcx, Tag>], dest: &PlaceTy<'tcx, Tag>, - _ret: mir::BasicBlock, ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> { let this = self.eval_context_mut(); match link_name.as_str() { diff --git a/src/shims/unix/fs.rs b/src/shims/unix/fs.rs index c688455710..1420279247 100644 --- a/src/shims/unix/fs.rs +++ b/src/shims/unix/fs.rs @@ -50,6 +50,9 @@ trait FileDescriptor: std::fmt::Debug { ) -> InterpResult<'tcx, io::Result>; fn dup(&mut self) -> io::Result>; + + #[cfg(unix)] + fn as_unix_host_fd(&self) -> Option; } impl FileDescriptor for FileHandle { @@ -114,6 +117,12 @@ impl FileDescriptor for FileHandle { let duplicated = self.file.try_clone()?; Ok(Box::new(FileHandle { file: duplicated, writable: self.writable })) } + + #[cfg(unix)] + fn as_unix_host_fd(&self) -> Option { + use std::os::unix::io::AsRawFd; + Some(self.file.as_raw_fd()) + } } impl FileDescriptor for io::Stdin { @@ -159,6 +168,11 @@ impl FileDescriptor for io::Stdin { fn dup(&mut self) -> io::Result> { Ok(Box::new(io::stdin())) } + + #[cfg(unix)] + fn as_unix_host_fd(&self) -> Option { + Some(libc::STDIN_FILENO) + } } impl FileDescriptor for io::Stdout { @@ -209,6 +223,11 @@ impl FileDescriptor for io::Stdout { fn dup(&mut self) -> io::Result> { Ok(Box::new(io::stdout())) } + + #[cfg(unix)] + fn as_unix_host_fd(&self) -> Option { + Some(libc::STDOUT_FILENO) + } } impl FileDescriptor for io::Stderr { @@ -252,6 +271,11 @@ impl FileDescriptor for io::Stderr { fn dup(&mut self) -> io::Result> { Ok(Box::new(io::stderr())) } + + #[cfg(unix)] + fn as_unix_host_fd(&self) -> Option { + Some(libc::STDERR_FILENO) + } } #[derive(Debug)] @@ -297,6 +321,11 @@ impl FileDescriptor for DummyOutput { fn dup<'tcx>(&mut self) -> io::Result> { Ok(Box::new(DummyOutput)) } + + #[cfg(unix)] + fn as_unix_host_fd(&self) -> Option { + None + } } #[derive(Debug)] @@ -1639,6 +1668,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let result = std::fs::read_link(pathname); match result { Ok(resolved) => { + // 'readlink' truncates the resolved path if the provided buffer is not large + // enough, and does *not* add a null terminator. That means we cannot use the usual + // `write_path_to_c_str` and have to re-implement parts of it ourselves. let resolved = this.convert_path_separator( Cow::Borrowed(resolved.as_ref()), crate::shims::os_str::PathConversion::HostToTarget, @@ -1648,8 +1680,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx if path_bytes.len() > bufsize { path_bytes = &path_bytes[..bufsize] } - // 'readlink' truncates the resolved path if - // the provided buffer is not large enough. this.write_bytes_ptr(buf, path_bytes.iter().copied())?; Ok(path_bytes.len().try_into().unwrap()) } @@ -1659,6 +1689,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } } + + #[cfg_attr(not(unix), allow(unused))] + fn isatty(&mut self, miri_fd: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + #[cfg(unix)] + if matches!(this.machine.isolated_op, IsolatedOp::Allow) { + let miri_fd = this.read_scalar(miri_fd)?.to_i32()?; + if let Some(host_fd) = + this.machine.file_handler.handles.get(&miri_fd).and_then(|fd| fd.as_unix_host_fd()) + { + // "returns 1 if fd is an open file descriptor referring to a terminal; + // otherwise 0 is returned, and errno is set to indicate the error" + // SAFETY: isatty has no preconditions + let is_tty = unsafe { libc::isatty(host_fd) }; + if is_tty == 0 { + let errno = std::io::Error::last_os_error() + .raw_os_error() + .map(Scalar::from_i32) + .unwrap(); + this.set_last_error(errno)?; + } + return Ok(is_tty); + } + } + // We are attemping to use a Unix interface on a non-Unix platform, or we are on a Unix + // platform and the passed file descriptor is not open, or isolation is enabled + // FIXME: It should be possible to emulate this at least on Windows by using + // GetConsoleMode. + let enotty = this.eval_libc("ENOTTY")?; + this.set_last_error(enotty)?; + Ok(0) + } } /// Extracts the number of seconds and nanoseconds elapsed between `time` and the unix epoch when diff --git a/src/shims/unix/linux/foreign_items.rs b/src/shims/unix/linux/foreign_items.rs index bbdb1b8a31..efd4e2a8c0 100644 --- a/src/shims/unix/linux/foreign_items.rs +++ b/src/shims/unix/linux/foreign_items.rs @@ -1,4 +1,3 @@ -use rustc_middle::mir; use rustc_span::Symbol; use rustc_target::spec::abi::Abi; @@ -17,7 +16,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx abi: Abi, args: &[OpTy<'tcx, Tag>], dest: &PlaceTy<'tcx, Tag>, - _ret: mir::BasicBlock, ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> { let this = self.eval_context_mut(); diff --git a/src/shims/unix/linux/sync.rs b/src/shims/unix/linux/sync.rs index a0e35c730c..a81fdb5e99 100644 --- a/src/shims/unix/linux/sync.rs +++ b/src/shims/unix/linux/sync.rs @@ -169,7 +169,7 @@ pub fn futex<'tcx>( // // Thankfully, preemptions cannot happen inside a Miri shim, so we do not need to // do anything special to guarantee fence-load-comparison atomicity. - this.atomic_fence(&[], AtomicFenceOrd::SeqCst)?; + this.validate_atomic_fence(AtomicFenceOrd::SeqCst)?; // Read an `i32` through the pointer, regardless of any wrapper types. // It's not uncommon for `addr` to be passed as another type than `*mut i32`, such as `*const AtomicI32`. let futex_val = this @@ -240,7 +240,7 @@ pub fn futex<'tcx>( // Together with the SeqCst fence in futex_wait, this makes sure that futex_wait // will see the latest value on addr which could be changed by our caller // before doing the syscall. - this.atomic_fence(&[], AtomicFenceOrd::SeqCst)?; + this.validate_atomic_fence(AtomicFenceOrd::SeqCst)?; let mut n = 0; for _ in 0..val { if let Some(thread) = this.futex_wake(addr_usize, bitset) { diff --git a/src/shims/unix/macos/foreign_items.rs b/src/shims/unix/macos/foreign_items.rs index 089a082fa3..58dd40cda3 100644 --- a/src/shims/unix/macos/foreign_items.rs +++ b/src/shims/unix/macos/foreign_items.rs @@ -1,4 +1,3 @@ -use rustc_middle::mir; use rustc_span::Symbol; use rustc_target::spec::abi::Abi; @@ -15,7 +14,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx abi: Abi, args: &[OpTy<'tcx, Tag>], dest: &PlaceTy<'tcx, Tag>, - _ret: mir::BasicBlock, ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> { let this = self.eval_context_mut(); diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index 0bb082dba9..6563434241 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -1,6 +1,5 @@ use std::iter; -use rustc_middle::mir; use rustc_span::Symbol; use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; @@ -18,7 +17,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx abi: Abi, args: &[OpTy<'tcx, Tag>], dest: &PlaceTy<'tcx, Tag>, - _ret: mir::BasicBlock, ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> { let this = self.eval_context_mut(); @@ -152,23 +150,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx .collect(); // Set page size. - let page_size = system_info.offset( - field_offsets[2], - MemPlaceMeta::None, - dword_layout, - &this.tcx, - )?; + let page_size = system_info.offset(field_offsets[2], dword_layout, &this.tcx)?; this.write_scalar( Scalar::from_int(PAGE_SIZE, dword_layout.size), &page_size.into(), )?; // Set number of processors. - let num_cpus = system_info.offset( - field_offsets[6], - MemPlaceMeta::None, - dword_layout, - &this.tcx, - )?; + let num_cpus = system_info.offset(field_offsets[6], dword_layout, &this.tcx)?; this.write_scalar(Scalar::from_int(NUM_CPUS, dword_layout.size), &num_cpus.into())?; } diff --git a/src/stacked_borrows/diagnostics.rs b/src/stacked_borrows/diagnostics.rs index d787865c4e..133164f390 100644 --- a/src/stacked_borrows/diagnostics.rs +++ b/src/stacked_borrows/diagnostics.rs @@ -140,8 +140,8 @@ impl AllocHistory { stack: &Stack, ) -> InterpError<'tcx> { let action = format!( - "trying to reborrow {derived_from:?} for {new_perm:?} permission at {alloc_id:?}[{offset:#x}]", - new_perm = new.perm, + "trying to reborrow from {derived_from:?} for {new_perm:?} permission at {alloc_id:?}[{offset:#x}]", + new_perm = new.perm(), offset = error_offset.bytes(), ); err_sb_ub( @@ -185,7 +185,7 @@ fn error_cause(stack: &Stack, tag: SbTagExtra) -> &'static str { if let SbTagExtra::Concrete(tag) = tag { if (0..stack.len()) .map(|i| stack.get(i).unwrap()) - .any(|item| item.tag == tag && item.perm != Permission::Disabled) + .any(|item| item.tag() == tag && item.perm() != Permission::Disabled) { ", but that tag only grants SharedReadOnly permission for this location" } else { diff --git a/src/stacked_borrows/item.rs b/src/stacked_borrows/item.rs new file mode 100644 index 0000000000..ad1b9b075b --- /dev/null +++ b/src/stacked_borrows/item.rs @@ -0,0 +1,104 @@ +use crate::stacked_borrows::SbTag; +use std::fmt; +use std::num::NonZeroU64; + +/// An item in the per-location borrow stack. +#[derive(Copy, Clone, Hash, PartialEq, Eq)] +pub struct Item(u64); + +// An Item contains 3 bitfields: +// * Bits 0-61 store an SbTag +// * Bits 61-63 store a Permission +// * Bit 64 stores a flag which indicates if we have a protector +const TAG_MASK: u64 = u64::MAX >> 3; +const PERM_MASK: u64 = 0x3 << 61; +const PROTECTED_MASK: u64 = 0x1 << 63; + +const PERM_SHIFT: u64 = 61; +const PROTECTED_SHIFT: u64 = 63; + +impl Item { + pub fn new(tag: SbTag, perm: Permission, protected: bool) -> Self { + assert!(tag.0.get() <= TAG_MASK); + let packed_tag = tag.0.get(); + let packed_perm = perm.to_bits() << PERM_SHIFT; + let packed_protected = (protected as u64) << PROTECTED_SHIFT; + + let new = Self(packed_tag | packed_perm | packed_protected); + + debug_assert!(new.tag() == tag); + debug_assert!(new.perm() == perm); + debug_assert!(new.protected() == protected); + + new + } + + /// The pointers the permission is granted to. + pub fn tag(self) -> SbTag { + SbTag(NonZeroU64::new(self.0 & TAG_MASK).unwrap()) + } + + /// The permission this item grants. + pub fn perm(self) -> Permission { + Permission::from_bits((self.0 & PERM_MASK) >> PERM_SHIFT) + } + + /// Whether or not there is a protector for this tag + pub fn protected(self) -> bool { + self.0 & PROTECTED_MASK > 0 + } + + /// Set the Permission stored in this Item + pub fn set_permission(&mut self, perm: Permission) { + // Clear the current set permission + self.0 &= !PERM_MASK; + // Write Permission::Disabled to the Permission bits + self.0 |= perm.to_bits() << PERM_SHIFT; + } +} + +impl fmt::Debug for Item { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "[{:?} for {:?}]", self.perm(), self.tag()) + } +} + +/// Indicates which permission is granted (by this item to some pointers) +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum Permission { + /// Grants unique mutable access. + Unique, + /// Grants shared mutable access. + SharedReadWrite, + /// Grants shared read-only access. + SharedReadOnly, + /// Grants no access, but separates two groups of SharedReadWrite so they are not + /// all considered mutually compatible. + Disabled, +} + +impl Permission { + const UNIQUE: u64 = 0; + const SHARED_READ_WRITE: u64 = 1; + const SHARED_READ_ONLY: u64 = 2; + const DISABLED: u64 = 3; + + fn to_bits(self) -> u64 { + match self { + Permission::Unique => Self::UNIQUE, + Permission::SharedReadWrite => Self::SHARED_READ_WRITE, + Permission::SharedReadOnly => Self::SHARED_READ_ONLY, + Permission::Disabled => Self::DISABLED, + } + } + + fn from_bits(perm: u64) -> Self { + match perm { + Self::UNIQUE => Permission::Unique, + Self::SHARED_READ_WRITE => Permission::SharedReadWrite, + Self::SHARED_READ_ONLY => Permission::SharedReadOnly, + Self::DISABLED => Permission::Disabled, + _ => unreachable!(), + } + } +} diff --git a/src/stacked_borrows.rs b/src/stacked_borrows/mod.rs similarity index 80% rename from src/stacked_borrows.rs rename to src/stacked_borrows/mod.rs index 9969fbdbcd..9040d03632 100644 --- a/src/stacked_borrows.rs +++ b/src/stacked_borrows/mod.rs @@ -16,6 +16,7 @@ use rustc_middle::ty::{ }; use rustc_span::DUMMY_SP; use rustc_target::abi::Size; +use smallvec::SmallVec; use std::collections::HashSet; use crate::*; @@ -23,8 +24,10 @@ use crate::*; pub mod diagnostics; use diagnostics::{AllocHistory, TagHistory}; -pub mod stack; -use stack::Stack; +mod item; +pub use item::{Item, Permission}; +mod stack; +pub use stack::Stack; pub type CallId = NonZeroU64; @@ -78,40 +81,20 @@ impl SbTagExtra { } } -/// Indicates which permission is granted (by this item to some pointers) -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub enum Permission { - /// Grants unique mutable access. - Unique, - /// Grants shared mutable access. - SharedReadWrite, - /// Grants shared read-only access. - SharedReadOnly, - /// Grants no access, but separates two groups of SharedReadWrite so they are not - /// all considered mutually compatible. - Disabled, -} - -/// An item in the per-location borrow stack. -#[derive(Copy, Clone, Hash, PartialEq, Eq)] -pub struct Item { - /// The permission this item grants. - perm: Permission, - /// The pointers the permission is granted to. - tag: SbTag, - /// An optional protector, ensuring the item cannot get popped until `CallId` is over. - protector: Option, -} - -impl fmt::Debug for Item { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "[{:?} for {:?}", self.perm, self.tag)?; - if let Some(call) = self.protector { - write!(f, " (call {})", call)?; - } - write!(f, "]")?; - Ok(()) - } +#[derive(Debug)] +pub struct FrameExtra { + /// The ID of the call this frame corresponds to. + call_id: CallId, + + /// If this frame is protecting any tags, they are listed here. We use this list to do + /// incremental updates of the global list of protected tags stored in the + /// `stacked_borrows::GlobalState` upon function return, and if we attempt to pop a protected + /// tag, to identify which call is responsible for protecting the tag. + /// See `Stack::item_popped` for more explanation. + /// + /// This will contain one tag per reference passed to the function, so + /// a size of 2 is enough for the vast majority of functions. + protected_tags: SmallVec<[SbTag; 2]>, } /// Extra per-allocation state. @@ -136,8 +119,12 @@ pub struct GlobalStateInner { base_ptr_tags: FxHashMap, /// Next unused call ID (for protectors). next_call_id: CallId, - /// Those call IDs corresponding to functions that are still running. - active_calls: FxHashSet, + /// All currently protected tags. + /// An item is protected if its tag is in this set, *and* it has the "protected" bit set. + /// We add tags to this when they are created with a protector in `reborrow`, and + /// we remove tags from this when the call which is protecting them returns, in + /// `GlobalStateInner::end_call`. See `Stack::item_popped` for more details. + protected_tags: FxHashSet, /// The pointer ids to trace tracked_pointer_tags: HashSet, /// The call ids to trace @@ -201,7 +188,7 @@ impl GlobalStateInner { next_ptr_tag: SbTag(NonZeroU64::new(1).unwrap()), base_ptr_tags: FxHashMap::default(), next_call_id: NonZeroU64::new(1).unwrap(), - active_calls: FxHashSet::default(), + protected_tags: FxHashSet::default(), tracked_pointer_tags, tracked_call_ids, retag_fields, @@ -215,23 +202,25 @@ impl GlobalStateInner { id } - pub fn new_call(&mut self) -> CallId { - let id = self.next_call_id; - trace!("new_call: Assigning ID {}", id); - if self.tracked_call_ids.contains(&id) { - register_diagnostic(NonHaltingDiagnostic::CreatedCallId(id)); + pub fn new_frame(&mut self) -> FrameExtra { + let call_id = self.next_call_id; + trace!("new_frame: Assigning call ID {}", call_id); + if self.tracked_call_ids.contains(&call_id) { + register_diagnostic(NonHaltingDiagnostic::CreatedCallId(call_id)); } - assert!(self.active_calls.insert(id)); - self.next_call_id = NonZeroU64::new(id.get() + 1).unwrap(); - id - } - - pub fn end_call(&mut self, id: CallId) { - assert!(self.active_calls.remove(&id)); + self.next_call_id = NonZeroU64::new(call_id.get() + 1).unwrap(); + FrameExtra { call_id, protected_tags: SmallVec::new() } } - fn is_active(&self, id: CallId) -> bool { - self.active_calls.contains(&id) + pub fn end_call(&mut self, frame: &machine::FrameData<'_>) { + for tag in &frame + .stacked_borrows + .as_ref() + .expect("we should have Stacked Borrows data") + .protected_tags + { + self.protected_tags.remove(tag); + } } pub fn base_ptr_tag(&mut self, id: AllocId) -> SbTag { @@ -287,7 +276,7 @@ impl<'tcx> Stack { /// Find the first write-incompatible item above the given one -- /// i.e, find the height to which the stack will be truncated when writing to `granting`. fn find_first_write_incompatible(&self, granting: usize) -> usize { - let perm = self.get(granting).unwrap().perm; + let perm = self.get(granting).unwrap().perm(); match perm { Permission::SharedReadOnly => bug!("Cannot use SharedReadOnly for writing"), Permission::Disabled => bug!("Cannot use Disabled for anything"), @@ -299,7 +288,7 @@ impl<'tcx> Stack { // The SharedReadWrite *just* above us are compatible, to skip those. let mut idx = granting + 1; while let Some(item) = self.get(idx) { - if item.perm == Permission::SharedReadWrite { + if item.perm() == Permission::SharedReadWrite { // Go on. idx += 1; } else { @@ -325,32 +314,90 @@ impl<'tcx> Stack { provoking_access: Option<(SbTagExtra, AllocRange, Size, AccessKind)>, // just for debug printing and error messages global: &GlobalStateInner, alloc_history: &mut AllocHistory, + threads: &ThreadManager<'_, 'tcx>, ) -> InterpResult<'tcx> { - if global.tracked_pointer_tags.contains(&item.tag) { - register_diagnostic(NonHaltingDiagnostic::PoppedPointerTag( - *item, - provoking_access.map(|(tag, _alloc_range, _size, access)| (tag, access)), - )); + if !global.tracked_pointer_tags.is_empty() { + check_tracked(item, &provoking_access, global); + + #[inline(never)] // cold path + fn check_tracked( + item: &Item, + provoking_access: &Option<(SbTagExtra, AllocRange, Size, AccessKind)>, + global: &GlobalStateInner, + ) { + if global.tracked_pointer_tags.contains(&item.tag()) { + register_diagnostic(NonHaltingDiagnostic::PoppedPointerTag( + *item, + provoking_access.map(|(tag, _alloc_range, _size, access)| (tag, access)), + )); + } + } } - if let Some(call) = item.protector { - if global.is_active(call) { + if !item.protected() { + return Ok(()); + } + + // We store tags twice, once in global.protected_tags and once in each call frame. + // We do this because consulting a single global set in this function is faster + // than attempting to search all call frames in the program for the `FrameExtra` + // (if any) which is protecting the popped tag. + // + // This duplication trades off making `end_call` slower to make this function faster. This + // trade-off is profitable in practice for a combination of two reasons. + // 1. A single protected tag can (and does in some programs) protect thousands of `Item`s. + // Therefore, adding overhead to in function call/return is profitable even if it only + // saves a little work in this function. + // 2. Most frames protect only one or two tags. So this duplicative global turns a search + // which ends up about linear in the number of protected tags in the program into a + // constant time check (and a slow linear, because the tags in the frames aren't contiguous). + if global.protected_tags.contains(&item.tag()) { + return Err(protector_error(item, &provoking_access, alloc_history, threads)); + + #[inline(never)] // cold path + fn protector_error<'tcx>( + item: &Item, + provoking_access: &Option<(SbTagExtra, AllocRange, Size, AccessKind)>, + alloc_history: &mut AllocHistory, + threads: &ThreadManager<'_, 'tcx>, + ) -> InterpErrorInfo<'tcx> { + // This path is cold because it is fatal to the program. So here it is fine to do the + // more expensive search to figure out which call is responsible for protecting this + // tag. + let call_id = threads + .all_stacks() + .flatten() + .map(|frame| { + frame + .extra + .stacked_borrows + .as_ref() + .expect("we should have Stacked Borrows data") + }) + .find(|frame| frame.protected_tags.contains(&item.tag())) + .map(|frame| frame.call_id) + .unwrap(); // FIXME: Surely we should find something, but a panic seems wrong here? if let Some((tag, _alloc_range, _offset, _access)) = provoking_access { - Err(err_sb_ub( + err_sb_ub( format!( - "not granting access to tag {:?} because incompatible item is protected: {:?}", - tag, item + "not granting access to tag {:?} because incompatible item {:?} is protected by call {:?}", + tag, item, call_id ), None, - tag.and_then(|tag| alloc_history.get_logs_relevant_to(tag, Some(item.tag))), - ))? + tag.and_then(|tag| { + alloc_history.get_logs_relevant_to(tag, Some(item.tag())) + }), + ) } else { - Err(err_sb_ub( - format!("deallocating while item is protected: {:?}", item), + err_sb_ub( + format!( + "deallocating while item {:?} is protected by call {:?}", + item, call_id + ), None, None, - ))? - } + ) + }.into() } } Ok(()) @@ -369,6 +416,7 @@ impl<'tcx> Stack { current_span: &mut CurrentSpan<'_, '_, 'tcx>, alloc_history: &mut AllocHistory, exposed_tags: &FxHashSet, + threads: &ThreadManager<'_, 'tcx>, ) -> InterpResult<'tcx> { // Two main steps: Find granting item, remove incompatible items above. @@ -399,8 +447,9 @@ impl<'tcx> Stack { Some((tag, alloc_range, offset, access)), global, alloc_history, + threads, )?; - alloc_history.log_invalidation(item.tag, alloc_range, current_span); + alloc_history.log_invalidation(item.tag(), alloc_range, current_span); Ok(()) })?; } else { @@ -425,8 +474,9 @@ impl<'tcx> Stack { Some((tag, alloc_range, offset, access)), global, alloc_history, + threads, )?; - alloc_history.log_invalidation(item.tag, alloc_range, current_span); + alloc_history.log_invalidation(item.tag(), alloc_range, current_span); Ok(()) })?; } @@ -439,9 +489,9 @@ impl<'tcx> Stack { for i in 0..self.len() { let item = self.get(i).unwrap(); // Skip disabled items, they cannot be matched anyway. - if !matches!(item.perm, Permission::Disabled) { + if !matches!(item.perm(), Permission::Disabled) { // We are looking for a strict upper bound, so add 1 to this tag. - max = cmp::max(item.tag.0.checked_add(1).unwrap(), max); + max = cmp::max(item.tag().0.checked_add(1).unwrap(), max); } } if let Some(unk) = self.unknown_bottom() { @@ -467,6 +517,7 @@ impl<'tcx> Stack { global: &GlobalStateInner, alloc_history: &mut AllocHistory, exposed_tags: &FxHashSet, + threads: &ThreadManager<'_, 'tcx>, ) -> InterpResult<'tcx> { // Step 1: Make sure there is a granting item. self.find_granting(AccessKind::Write, tag, exposed_tags).map_err(|_| { @@ -482,7 +533,7 @@ impl<'tcx> Stack { // Step 2: Consider all items removed. This checks for protectors. for idx in (0..self.len()).rev() { let item = self.get(idx).unwrap(); - Stack::item_popped(&item, None, global, alloc_history)?; + Stack::item_popped(&item, None, global, alloc_history, threads)?; } Ok(()) } @@ -502,10 +553,11 @@ impl<'tcx> Stack { current_span: &mut CurrentSpan<'_, '_, 'tcx>, alloc_history: &mut AllocHistory, exposed_tags: &FxHashSet, + threads: &ThreadManager<'_, 'tcx>, ) -> InterpResult<'tcx> { // Figure out which access `perm` corresponds to. let access = - if new.perm.grants(AccessKind::Write) { AccessKind::Write } else { AccessKind::Read }; + if new.perm().grants(AccessKind::Write) { AccessKind::Write } else { AccessKind::Read }; // Now we figure out which item grants our parent (`derived_from`) this kind of access. // We use that to determine where to put the new item. @@ -517,7 +569,7 @@ impl<'tcx> Stack { // Compute where to put the new item. // Either way, we ensure that we insert the new item in a way such that between // `derived_from` and the new one, there are only items *compatible with* `derived_from`. - let new_idx = if new.perm == Permission::SharedReadWrite { + let new_idx = if new.perm() == Permission::SharedReadWrite { assert!( access == AccessKind::Write, "this case only makes sense for stack-like accesses" @@ -550,6 +602,7 @@ impl<'tcx> Stack { current_span, alloc_history, exposed_tags, + threads, )?; // We insert "as far up as possible": We know only compatible items are remaining @@ -559,16 +612,9 @@ impl<'tcx> Stack { self.len() }; - // Put the new item there. As an optimization, deduplicate if it is equal to one of its new neighbors. - // `new_idx` might be 0 if we just cleared the entire stack. - if self.get(new_idx) == Some(new) || (new_idx > 0 && self.get(new_idx - 1).unwrap() == new) - { - // Optimization applies, done. - trace!("reborrow: avoiding adding redundant item {:?}", new); - } else { - trace!("reborrow: adding item {:?}", new); - self.insert(new_idx, new); - } + // Put the new item there. + trace!("reborrow: adding item {:?}", new); + self.insert(new_idx, new); Ok(()) } } @@ -578,7 +624,7 @@ impl<'tcx> Stack { impl<'tcx> Stacks { /// Creates new stack with initial tag. fn new(size: Size, perm: Permission, tag: SbTag) -> Self { - let item = Item { perm, tag, protector: None }; + let item = Item::new(tag, perm, false); let stack = Stack::new(item); Stacks { @@ -644,6 +690,7 @@ impl Stacks { range: AllocRange, state: &GlobalState, mut current_span: CurrentSpan<'_, '_, 'tcx>, + threads: &ThreadManager<'_, 'tcx>, ) -> InterpResult<'tcx> { trace!( "read access with tag {:?}: {:?}, size {}", @@ -661,6 +708,7 @@ impl Stacks { &mut current_span, history, exposed_tags, + threads, ) }) } @@ -673,6 +721,7 @@ impl Stacks { range: AllocRange, state: &GlobalState, mut current_span: CurrentSpan<'_, '_, 'tcx>, + threads: &ThreadManager<'_, 'tcx>, ) -> InterpResult<'tcx> { trace!( "write access with tag {:?}: {:?}, size {}", @@ -690,6 +739,7 @@ impl Stacks { &mut current_span, history, exposed_tags, + threads, ) }) } @@ -701,11 +751,12 @@ impl Stacks { tag: SbTagExtra, range: AllocRange, state: &GlobalState, + threads: &ThreadManager<'_, 'tcx>, ) -> InterpResult<'tcx> { trace!("deallocation with tag {:?}: {:?}, size {}", tag, alloc_id, range.size.bytes()); let state = state.borrow(); self.for_each(range, |offset, stack, history, exposed_tags| { - stack.dealloc(tag, (alloc_id, range, offset), &state, history, exposed_tags) + stack.dealloc(tag, (alloc_id, range, offset), &state, history, exposed_tags, threads) })?; Ok(()) } @@ -808,7 +859,6 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx }); } - let protector = if protect { Some(this.frame().extra.call_id) } else { None }; trace!( "reborrow: {} reference {:?} derived from {:?} (pointee {}): {:?}, size {}", kind, @@ -819,6 +869,13 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx size.bytes() ); + if protect { + this.frame_mut().extra.stacked_borrows.as_mut().unwrap().protected_tags.push(new_tag); + this.machine.stacked_borrows.as_mut().unwrap().get_mut().protected_tags.insert(new_tag); + } + // FIXME: can't hold the current span handle across the borrows of self above + let current_span = &mut this.machine.current_span(); + // Update the stacks. // Make sure that raw pointers and mutable shared references are reborrowed "weak": // There could be existing unique pointers reborrowed from them that should remain valid! @@ -855,15 +912,16 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } else { Permission::SharedReadWrite }; - let protector = if frozen { - protector + let protected = if frozen { + protect } else { // We do not protect inside UnsafeCell. // This fixes https://github.com/rust-lang/rust/issues/55005. - None + false }; - let item = Item { perm, tag: new_tag, protector }; + let item = Item::new(new_tag, perm, protected); let mut global = this.machine.stacked_borrows.as_ref().unwrap().borrow_mut(); + let threads = &this.machine.threads; stacked_borrows.for_each(range, |offset, stack, history, exposed_tags| { stack.grant( orig_tag, @@ -873,6 +931,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx current_span, history, exposed_tags, + threads, ) }) })?; @@ -888,9 +947,10 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx .as_mut() .expect("we should have Stacked Borrows data") .borrow_mut(); - let item = Item { perm, tag: new_tag, protector }; + let item = Item::new(new_tag, perm, protect); let range = alloc_range(base_offset, size); let mut global = machine.stacked_borrows.as_ref().unwrap().borrow_mut(); + let threads = &machine.threads; let current_span = &mut machine.current_span(); // `get_alloc_extra_mut` invalidated our old `current_span` stacked_borrows.for_each(range, |offset, stack, history, exposed_tags| { stack.grant( @@ -901,6 +961,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx current_span, history, exposed_tags, + threads, ) })?; @@ -960,6 +1021,10 @@ impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mi pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { fn retag(&mut self, kind: RetagKind, place: &PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); + let retag_fields = this.machine.stacked_borrows.as_mut().unwrap().get_mut().retag_fields; + let mut visitor = RetagVisitor { ecx: this, kind, retag_fields }; + return visitor.visit_value(place); + // Determine mutability and whether to add a protector. // Cannot use `builtin_deref` because that reports *immutable* for `Box`, // making it useless. @@ -976,90 +1041,67 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Raw pointers need to be enabled. ty::RawPtr(tym) if kind == RetagKind::Raw => Some((RefKind::Raw { mutable: tym.mutbl == Mutability::Mut }, false)), - // Boxes are handled separately due to that allocator situation. + // Boxes are handled separately due to that allocator situation, + // see the visitor below. _ => None, } } - // We need a visitor to visit all references. However, that requires - // a `MPlaceTy` (or `OpTy`), so we have a fast path for reference types that - // avoids allocating. - - if let Some((ref_kind, protector)) = qualify(place.layout.ty, kind) { - // Fast path. - let val = this.read_immediate(&this.place_to_op(place)?)?; - let val = this.retag_reference(&val, ref_kind, protector)?; - this.write_immediate(*val, place)?; - return Ok(()); - } - - // If we don't want to recurse, we are already done. - // EXCEPT if this is a `Box`, then we have to recurse because allocators. - // (Yes this means we technically also recursively retag the allocator itself even if field - // retagging is not enabled. *shrug*) - if !this.machine.stacked_borrows.as_mut().unwrap().get_mut().retag_fields - && !place.layout.ty.ty_adt_def().is_some_and(|adt| adt.is_box()) - { - return Ok(()); - } - - // Skip some types that have no further structure we might care about. - if matches!( - place.layout.ty.kind(), - ty::RawPtr(..) - | ty::Ref(..) - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Bool - | ty::Char - ) { - return Ok(()); - } - // Now go visit this thing. - let place = this.force_allocation(place)?; - - let mut visitor = RetagVisitor { ecx: this, kind }; - return visitor.visit_value(&place); - // The actual visitor. struct RetagVisitor<'ecx, 'mir, 'tcx> { ecx: &'ecx mut MiriEvalContext<'mir, 'tcx>, kind: RetagKind, + retag_fields: bool, + } + impl<'ecx, 'mir, 'tcx> RetagVisitor<'ecx, 'mir, 'tcx> { + #[inline(always)] // yes this helps in our benchmarks + fn retag_place( + &mut self, + place: &PlaceTy<'tcx, Tag>, + ref_kind: RefKind, + protector: bool, + ) -> InterpResult<'tcx> { + let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?; + let val = self.ecx.retag_reference(&val, ref_kind, protector)?; + self.ecx.write_immediate(*val, place)?; + Ok(()) + } } impl<'ecx, 'mir, 'tcx> MutValueVisitor<'mir, 'tcx, Evaluator<'mir, 'tcx>> for RetagVisitor<'ecx, 'mir, 'tcx> { - type V = MPlaceTy<'tcx, Tag>; + type V = PlaceTy<'tcx, Tag>; #[inline(always)] fn ecx(&mut self) -> &mut MiriEvalContext<'mir, 'tcx> { self.ecx } - fn visit_box(&mut self, place: &MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { + fn visit_box(&mut self, place: &PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { // Boxes do not get a protector: protectors reflect that references outlive the call // they were passed in to; that's just not the case for boxes. - let (ref_kind, protector) = (RefKind::Unique { two_phase: false }, false); - - let val = self.ecx.read_immediate(&place.into())?; - let val = self.ecx.retag_reference(&val, ref_kind, protector)?; - self.ecx.write_immediate(*val, &place.into())?; - Ok(()) + self.retag_place( + place, + RefKind::Unique { two_phase: false }, + /*protector*/ false, + ) } - fn visit_value(&mut self, place: &MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { + fn visit_value(&mut self, place: &PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { if let Some((ref_kind, protector)) = qualify(place.layout.ty, self.kind) { - let val = self.ecx.read_immediate(&place.into())?; - let val = self.ecx.retag_reference(&val, ref_kind, protector)?; - self.ecx.write_immediate(*val, &place.into())?; + self.retag_place(place, ref_kind, protector)?; } else if matches!(place.layout.ty.kind(), ty::RawPtr(..)) { // Wide raw pointers *do* have fields and their types are strange. // vtables have a type like `&[*const (); 3]` or so! // Do *not* recurse into them. - // (No need to worry about wide references or boxes, those always "qualify".) - } else { - // Maybe we need to go deeper. + // (No need to worry about wide references, those always "qualify". And Boxes + // are handles specially by the visitor anyway.) + } else if self.retag_fields + || place.layout.ty.ty_adt_def().is_some_and(|adt| adt.is_box()) + { + // Recurse deeper. Need to always recurse for `Box` to even hit `visit_box`. + // (Yes this means we technically also recursively retag the allocator itself + // even if field retagging is not enabled. *shrug*) self.walk_value(place)?; } Ok(()) diff --git a/src/stacked_borrows/stack.rs b/src/stacked_borrows/stack.rs index ccdd85eafd..bc7ffd4fae 100644 --- a/src/stacked_borrows/stack.rs +++ b/src/stacked_borrows/stack.rs @@ -37,7 +37,7 @@ pub struct Stack { } /// A very small cache of searches of the borrow stack -/// This maps tags to locations in the borrow stack. Any use of this still needs to do a +/// This maps items to locations in the borrow stack. Any use of this still needs to do a /// probably-cold random access into the borrow stack to figure out what `Permission` an /// `SbTag` grants. We could avoid this by also storing the `Permission` in the cache, but /// most lookups into the cache are immediately followed by access of the full borrow stack anyway. @@ -48,7 +48,7 @@ pub struct Stack { #[cfg(feature = "stack-cache")] #[derive(Clone, Debug)] struct StackCache { - tags: [SbTag; CACHE_LEN], // Hot in find_granting + items: [Item; CACHE_LEN], // Hot in find_granting idx: [usize; CACHE_LEN], // Hot in grant } @@ -59,11 +59,11 @@ impl StackCache { /// We use the position in the cache to represent how recently a tag was used; the first position /// is the most recently used tag. So an add shifts every element towards the end, and inserts /// the new element at the start. We lose the last element. - /// This strategy is effective at keeping the most-accessed tags in the cache, but it costs a + /// This strategy is effective at keeping the most-accessed items in the cache, but it costs a /// linear shift across the entire cache when we add a new tag. - fn add(&mut self, idx: usize, tag: SbTag) { - self.tags.copy_within(0..CACHE_LEN - 1, 1); - self.tags[0] = tag; + fn add(&mut self, idx: usize, item: Item) { + self.items.copy_within(0..CACHE_LEN - 1, 1); + self.items[0] = item; self.idx.copy_within(0..CACHE_LEN - 1, 1); self.idx[0] = idx; } @@ -80,20 +80,20 @@ impl Eq for Stack {} impl<'tcx> Stack { /// Panics if any of the caching mechanisms have broken, - /// - The StackCache indices don't refer to the parallel tags, - /// - There are no Unique tags outside of first_unique..last_unique - #[cfg(feature = "expensive-debug-assertions")] + /// - The StackCache indices don't refer to the parallel items, + /// - There are no Unique items outside of first_unique..last_unique + #[cfg(debug_assertions)] fn verify_cache_consistency(&self) { // Only a full cache needs to be valid. Also see the comments in find_granting_cache // and set_unknown_bottom. if self.borrows.len() >= CACHE_LEN { - for (tag, stack_idx) in self.cache.tags.iter().zip(self.cache.idx.iter()) { - assert_eq!(self.borrows[*stack_idx].tag, *tag); + for (tag, stack_idx) in self.cache.items.iter().zip(self.cache.idx.iter()) { + assert_eq!(self.borrows[*stack_idx], *tag); } } for (idx, item) in self.borrows.iter().enumerate() { - if item.perm == Permission::Unique { + if item.perm() == Permission::Unique { assert!( self.unique_range.contains(&idx), "{:?} {:?}", @@ -115,7 +115,7 @@ impl<'tcx> Stack { tag: SbTagExtra, exposed_tags: &FxHashSet, ) -> Result, ()> { - #[cfg(feature = "expensive-debug-assertions")] + #[cfg(debug_assertions)] self.verify_cache_consistency(); let SbTagExtra::Concrete(tag) = tag else { @@ -128,7 +128,7 @@ impl<'tcx> Stack { .rev() // search top-to-bottom .find_map(|(idx, item)| { // If the item fits and *might* be this wildcard, use it. - if item.perm.grants(access) && exposed_tags.contains(&item.tag) { + if item.perm().grants(access) && exposed_tags.contains(&item.tag()) { Some(idx) } else { None @@ -161,9 +161,9 @@ impl<'tcx> Stack { // If we didn't find the tag in the cache, fall back to a linear search of the // whole stack, and add the tag to the cache. for (stack_idx, item) in self.borrows.iter().enumerate().rev() { - if tag == item.tag && item.perm.grants(access) { + if tag == item.tag() && item.perm().grants(access) { #[cfg(feature = "stack-cache")] - self.cache.add(stack_idx, tag); + self.cache.add(stack_idx, *item); return Some(stack_idx); } } @@ -175,7 +175,7 @@ impl<'tcx> Stack { // This looks like a common-sense optimization; we're going to do a linear search of the // cache or the borrow stack to scan the shorter of the two. This optimization is miniscule // and this check actually ensures we do not access an invalid cache. - // When a stack is created and when tags are removed from the top of the borrow stack, we + // When a stack is created and when items are removed from the top of the borrow stack, we // need some valid value to populate the cache. In both cases, we try to use the bottom // item. But when the stack is cleared in `set_unknown_bottom` there is nothing we could // place in the cache that is correct. But due to the way we populate the cache in @@ -185,21 +185,23 @@ impl<'tcx> Stack { return None; } // Search the cache for the tag we're looking up - let cache_idx = self.cache.tags.iter().position(|t| *t == tag)?; + let cache_idx = self.cache.items.iter().position(|t| t.tag() == tag)?; let stack_idx = self.cache.idx[cache_idx]; // If we found the tag, look up its position in the stack to see if it grants // the required permission - if self.borrows[stack_idx].perm.grants(access) { + if self.cache.items[cache_idx].perm().grants(access) { // If it does, and it's not already in the most-recently-used position, re-insert it at // the most-recently-used position. This technically reduces the efficiency of the // cache by duplicating elements, but current benchmarks do not seem to benefit from // avoiding this duplication. // But if the tag is in position 1, avoiding the duplicating add is trivial. + // If it does, and it's not already in the most-recently-used position, move it there. + // Except if the tag is in position 1, this is equivalent to just a swap, so do that. if cache_idx == 1 { - self.cache.tags.swap(0, 1); + self.cache.items.swap(0, 1); self.cache.idx.swap(0, 1); } else if cache_idx > 1 { - self.cache.add(stack_idx, tag); + self.cache.add(stack_idx, self.cache.items[cache_idx]); } Some(stack_idx) } else { @@ -224,7 +226,7 @@ impl<'tcx> Stack { if self.unique_range.end >= new_idx { self.unique_range.end += 1; } - if new.perm == Permission::Unique { + if new.perm() == Permission::Unique { // Make sure the possibly-unique range contains the new borrow self.unique_range.start = self.unique_range.start.min(new_idx); self.unique_range.end = self.unique_range.end.max(new_idx + 1); @@ -233,7 +235,7 @@ impl<'tcx> Stack { // The above insert changes the meaning of every index in the cache >= new_idx, so now // we need to find every one of those indexes and increment it. // But if the insert is at the end (equivalent to a push), we can skip this step because - // it didn't change the position of any other tags. + // it didn't change the position of any other items. if new_idx != self.borrows.len() - 1 { for idx in &mut self.cache.idx { if *idx >= new_idx { @@ -243,9 +245,9 @@ impl<'tcx> Stack { } // This primes the cache for the next access, which is almost always the just-added tag. - self.cache.add(new_idx, new.tag); + self.cache.add(new_idx, new); - #[cfg(feature = "expensive-debug-assertions")] + #[cfg(debug_assertions)] self.verify_cache_consistency(); } @@ -255,9 +257,9 @@ impl<'tcx> Stack { borrows: vec![item], unknown_bottom: None, #[cfg(feature = "stack-cache")] - cache: StackCache { idx: [0; CACHE_LEN], tags: [item.tag; CACHE_LEN] }, + cache: StackCache { idx: [0; CACHE_LEN], items: [item; CACHE_LEN] }, #[cfg(feature = "stack-cache")] - unique_range: if item.perm == Permission::Unique { 0..1 } else { 0..0 }, + unique_range: if item.perm() == Permission::Unique { 0..1 } else { 0..0 }, } } @@ -298,10 +300,16 @@ impl<'tcx> Stack { let lower = unique_range.start.max(disable_start); let upper = (unique_range.end + 1).min(self.borrows.len()); for item in &mut self.borrows[lower..upper] { - if item.perm == Permission::Unique { + if item.perm() == Permission::Unique { log::trace!("access: disabling item {:?}", item); visitor(*item)?; - item.perm = Permission::Disabled; + item.set_permission(Permission::Disabled); + // Also update all copies of this item in the cache. + for it in &mut self.cache.items { + if it.tag() == item.tag() { + it.set_permission(Permission::Disabled); + } + } } } } @@ -317,7 +325,7 @@ impl<'tcx> Stack { self.unique_range.end = self.unique_range.end.min(disable_start + 1); } - #[cfg(feature = "expensive-debug-assertions")] + #[cfg(debug_assertions)] self.verify_cache_consistency(); Ok(()) @@ -341,7 +349,7 @@ impl<'tcx> Stack { // also possible that the whole cache is still valid. So we call this method to repair what // aspects of the cache are now invalid, instead of resetting the whole thing to a trivially // valid default state. - let base_tag = self.borrows[0].tag; + let base_tag = self.borrows[0]; let mut removed = 0; let mut cursor = 0; // Remove invalid entries from the cache by rotating them to the end of the cache, then @@ -350,7 +358,7 @@ impl<'tcx> Stack { for _ in 0..CACHE_LEN - 1 { if self.cache.idx[cursor] >= start { self.cache.idx[cursor..CACHE_LEN - removed].rotate_left(1); - self.cache.tags[cursor..CACHE_LEN - removed].rotate_left(1); + self.cache.items[cursor..CACHE_LEN - removed].rotate_left(1); removed += 1; } else { cursor += 1; @@ -358,7 +366,7 @@ impl<'tcx> Stack { } for i in CACHE_LEN - removed - 1..CACHE_LEN { self.cache.idx[i] = 0; - self.cache.tags[i] = base_tag; + self.cache.items[i] = base_tag; } if start < self.unique_range.start.saturating_sub(1) { @@ -372,7 +380,7 @@ impl<'tcx> Stack { self.unique_range = 0..0; } - #[cfg(feature = "expensive-debug-assertions")] + #[cfg(debug_assertions)] self.verify_cache_consistency(); Ok(()) } diff --git a/src/thread.rs b/src/thread.rs index 420eeb810f..96135d093d 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -281,6 +281,10 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { &mut self.threads[self.active_thread].stack } + pub fn all_stacks(&self) -> impl Iterator>]> { + self.threads.iter().map(|t| &t.stack[..]) + } + /// Create a new thread and returns its id. fn create_thread(&mut self) -> ThreadId { let new_thread_id = ThreadId::new(self.threads.len()); diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 754fccd63b..ec49e80ca9 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -2,13 +2,13 @@ use colored::*; use regex::Regex; use std::env; use std::path::PathBuf; -use ui_test::{Config, Mode, OutputConflictHandling}; +use ui_test::{color_eyre::Result, Config, Mode, OutputConflictHandling}; fn miri_path() -> PathBuf { PathBuf::from(option_env!("MIRI").unwrap_or(env!("CARGO_BIN_EXE_miri"))) } -fn run_tests(mode: Mode, path: &str, target: Option) { +fn run_tests(mode: Mode, path: &str, target: Option) -> Result<()> { let in_rustc_test_suite = option_env!("RUSTC_STAGE").is_some(); // Add some flags we always want. @@ -19,9 +19,10 @@ fn run_tests(mode: Mode, path: &str, target: Option) { // Less aggressive warnings to make the rustc toolstate management less painful. // (We often get warnings when e.g. a feature gets stabilized or some lint gets added/improved.) flags.push("-Astable-features".to_owned()); + flags.push("-Aunused".to_owned()); } else { flags.push("-Dwarnings".to_owned()); - flags.push("-Dunused".to_owned()); // overwrite the -Aunused in compiletest-rs + flags.push("-Dunused".to_owned()); } if let Ok(sysroot) = env::var("MIRI_SYSROOT") { flags.push("--sysroot".to_string()); @@ -97,7 +98,7 @@ regexes! { // erase specific alignments "alignment [0-9]+" => "alignment ALIGN", // erase thread caller ids - r"\(call [0-9]+\)" => "(call ID)", + r"call [0-9]+" => "call ID", // erase platform module paths "sys::[a-z]+::" => "sys::PLATFORM::", // Windows file paths @@ -108,7 +109,7 @@ regexes! { "sys/[a-z]+/" => "sys/PLATFORM/", } -fn ui(mode: Mode, path: &str) { +fn ui(mode: Mode, path: &str) -> Result<()> { let target = get_target(); let msg = format!( @@ -117,20 +118,24 @@ fn ui(mode: Mode, path: &str) { ); eprintln!("{}", msg.green().bold()); - run_tests(mode, path, target); + run_tests(mode, path, target) } fn get_target() -> Option { env::var("MIRI_TEST_TARGET").ok() } -fn main() { +fn main() -> Result<()> { + ui_test::color_eyre::install()?; + // Add a test env var to do environment communication tests. env::set_var("MIRI_ENV_VAR_TEST", "0"); // Let the tests know where to store temp files (they might run for a different target, which can make this hard to find). env::set_var("MIRI_TEMP", env::temp_dir()); - ui(Mode::Pass, "tests/pass"); - ui(Mode::Panic, "tests/panic"); - ui(Mode::Fail, "tests/fail"); + ui(Mode::Pass, "tests/pass")?; + ui(Mode::Panic, "tests/panic")?; + ui(Mode::Fail, "tests/fail")?; + + Ok(()) } diff --git a/tests/external_C/test.c b/tests/external_C/test.c new file mode 100644 index 0000000000..f015ccc17e --- /dev/null +++ b/tests/external_C/test.c @@ -0,0 +1,31 @@ +#include + +int add_one_int(int x) { + return 2 + x; +} + +double add_int_get_float(int x) { + return 2.75 + x; +} + +void printer() { + printf("printing from C\n"); +} + +// function with many arguments, to test functionality when some args are stored +// on the stack +int test_stack_spill(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l) { + return a+b+c+d+e+f+g+h+i+j+k+l; +} + +unsigned int get_unsigned_int() { + return -10; +} + +short add_int16(short x) { + return x + 3; +} + +long add_short_to_long(short x, long y) { + return x + y; +} diff --git a/tests/fail/alloc/deallocate-bad-alignment.rs b/tests/fail/alloc/deallocate-bad-alignment.rs index 852a066021..a07d8254ad 100644 --- a/tests/fail/alloc/deallocate-bad-alignment.rs +++ b/tests/fail/alloc/deallocate-bad-alignment.rs @@ -1,6 +1,6 @@ use std::alloc::{alloc, dealloc, Layout}; -// error-pattern: has size 1 and alignment 1, but gave size 1 and alignment 2 +//@error-pattern: has size 1 and alignment 1, but gave size 1 and alignment 2 fn main() { unsafe { diff --git a/tests/fail/alloc/deallocate-bad-size.rs b/tests/fail/alloc/deallocate-bad-size.rs index 167cc015c2..47aaef1935 100644 --- a/tests/fail/alloc/deallocate-bad-size.rs +++ b/tests/fail/alloc/deallocate-bad-size.rs @@ -1,6 +1,6 @@ use std::alloc::{alloc, dealloc, Layout}; -// error-pattern: has size 1 and alignment 1, but gave size 2 and alignment 1 +//@error-pattern: has size 1 and alignment 1, but gave size 2 and alignment 1 fn main() { unsafe { diff --git a/tests/fail/alloc/deallocate-twice.rs b/tests/fail/alloc/deallocate-twice.rs index 67312b0d96..1eb9bbf91c 100644 --- a/tests/fail/alloc/deallocate-twice.rs +++ b/tests/fail/alloc/deallocate-twice.rs @@ -1,6 +1,6 @@ use std::alloc::{alloc, dealloc, Layout}; -// error-pattern: dereferenced after this allocation got freed +//@error-pattern: dereferenced after this allocation got freed fn main() { unsafe { diff --git a/tests/fail/alloc/global_system_mixup.rs b/tests/fail/alloc/global_system_mixup.rs index 735c52500a..47b098c71a 100644 --- a/tests/fail/alloc/global_system_mixup.rs +++ b/tests/fail/alloc/global_system_mixup.rs @@ -1,10 +1,10 @@ // Make sure we detect when the `Global` and `System` allocators are mixed // (even when the default `Global` uses `System`). -// error-pattern: which is Rust heap memory, using +//@error-pattern: /deallocating .*, which is Rust heap memory, using .* heap deallocation operation/ -// normalize-stderr-test: "using [A-Za-z]+ heap deallocation operation" -> "using PLATFORM heap deallocation operation" -// normalize-stderr-test: "\| +\^+" -> "| ^" -// normalize-stderr-test: "libc::free\([^()]*\)|unsafe \{ HeapFree\([^()]*\) \};" -> "FREE();" +//@normalize-stderr-test: "using [A-Za-z]+ heap deallocation operation" -> "using PLATFORM heap deallocation operation" +//@normalize-stderr-test: "\| +\^+" -> "| ^" +//@normalize-stderr-test: "libc::free\([^()]*\)|unsafe \{ HeapFree\([^()]*\) \};" -> "FREE();" #![feature(allocator_api, slice_ptr_get)] diff --git a/tests/fail/alloc/no_global_allocator.stderr b/tests/fail/alloc/no_global_allocator.stderr index 5c11df9539..7024756750 100644 --- a/tests/fail/alloc/no_global_allocator.stderr +++ b/tests/fail/alloc/no_global_allocator.stderr @@ -1,8 +1,8 @@ -error: unsupported operation: can't call foreign function: __rust_alloc +error: unsupported operation: can't call foreign function: __rust_alloc; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile --> $DIR/no_global_allocator.rs:LL:CC | LL | __rust_alloc(1, 1); - | ^^^^^^^^^^^^^^^^^^ can't call foreign function: __rust_alloc + | ^^^^^^^^^^^^^^^^^^ can't call foreign function: __rust_alloc; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support = note: backtrace: diff --git a/tests/fail/alloc/reallocate-bad-size.rs b/tests/fail/alloc/reallocate-bad-size.rs index d6a27c930a..145c3393d6 100644 --- a/tests/fail/alloc/reallocate-bad-size.rs +++ b/tests/fail/alloc/reallocate-bad-size.rs @@ -1,6 +1,6 @@ use std::alloc::{alloc, realloc, Layout}; -// error-pattern: has size 1 and alignment 1, but gave size 2 and alignment 1 +//@error-pattern: has size 1 and alignment 1, but gave size 2 and alignment 1 fn main() { unsafe { diff --git a/tests/fail/alloc/reallocate-change-alloc.rs b/tests/fail/alloc/reallocate-change-alloc.rs index 14d05e7457..3ad56da2c2 100644 --- a/tests/fail/alloc/reallocate-change-alloc.rs +++ b/tests/fail/alloc/reallocate-change-alloc.rs @@ -4,6 +4,6 @@ fn main() { unsafe { let x = alloc(Layout::from_size_align_unchecked(1, 1)); let _y = realloc(x, Layout::from_size_align_unchecked(1, 1), 1); - let _z = *x; //~ ERROR dereferenced after this allocation got freed + let _z = *x; //~ ERROR: dereferenced after this allocation got freed } } diff --git a/tests/fail/alloc/reallocate-dangling.rs b/tests/fail/alloc/reallocate-dangling.rs index 62eb7582a2..34f1658344 100644 --- a/tests/fail/alloc/reallocate-dangling.rs +++ b/tests/fail/alloc/reallocate-dangling.rs @@ -1,6 +1,6 @@ use std::alloc::{alloc, dealloc, realloc, Layout}; -// error-pattern: dereferenced after this allocation got freed +//@error-pattern: dereferenced after this allocation got freed fn main() { unsafe { diff --git a/tests/fail/alloc/stack_free.rs b/tests/fail/alloc/stack_free.rs index d854fa993a..baf53decc4 100644 --- a/tests/fail/alloc/stack_free.rs +++ b/tests/fail/alloc/stack_free.rs @@ -1,7 +1,7 @@ // Validation/SB changes why we fail -// compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows -// error-pattern: which is stack variable memory, using Rust heap deallocation operation +//@error-pattern: /deallocating .*, which is stack variable memory, using Rust heap deallocation operation/ fn main() { let x = 42; diff --git a/tests/fail/backtrace/bad-backtrace-decl.rs b/tests/fail/backtrace/bad-backtrace-decl.rs index 23379992d5..97a70103e6 100644 --- a/tests/fail/backtrace/bad-backtrace-decl.rs +++ b/tests/fail/backtrace/bad-backtrace-decl.rs @@ -7,7 +7,7 @@ fn main() { let frames = unsafe { miri_get_backtrace(0) }; for frame in frames.into_iter() { unsafe { - miri_resolve_frame(*frame, 0); //~ ERROR Undefined Behavior: bad declaration of miri_resolve_frame - should return a struct with 5 fields + miri_resolve_frame(*frame, 0); //~ ERROR: Undefined Behavior: bad declaration of miri_resolve_frame - should return a struct with 5 fields } } } diff --git a/tests/fail/backtrace/bad-backtrace-flags.rs b/tests/fail/backtrace/bad-backtrace-flags.rs index 9e24d32a33..a4e186eaa9 100644 --- a/tests/fail/backtrace/bad-backtrace-flags.rs +++ b/tests/fail/backtrace/bad-backtrace-flags.rs @@ -4,6 +4,6 @@ extern "Rust" { fn main() { unsafe { - miri_get_backtrace(2, std::ptr::null_mut()); //~ ERROR unsupported operation: unknown `miri_get_backtrace` flags 2 + miri_get_backtrace(2, std::ptr::null_mut()); //~ ERROR: unsupported operation: unknown `miri_get_backtrace` flags 2 } } diff --git a/tests/fail/backtrace/bad-backtrace-ptr.rs b/tests/fail/backtrace/bad-backtrace-ptr.rs index a435b0a695..843d0d1187 100644 --- a/tests/fail/backtrace/bad-backtrace-ptr.rs +++ b/tests/fail/backtrace/bad-backtrace-ptr.rs @@ -4,6 +4,6 @@ extern "Rust" { fn main() { unsafe { - miri_resolve_frame(std::ptr::null_mut(), 0); //~ ERROR null pointer is a dangling pointer + miri_resolve_frame(std::ptr::null_mut(), 0); //~ ERROR: null pointer is a dangling pointer } } diff --git a/tests/fail/backtrace/bad-backtrace-resolve-flags.rs b/tests/fail/backtrace/bad-backtrace-resolve-flags.rs index 2d4d619502..31e3915f3d 100644 --- a/tests/fail/backtrace/bad-backtrace-resolve-flags.rs +++ b/tests/fail/backtrace/bad-backtrace-resolve-flags.rs @@ -20,6 +20,6 @@ fn main() { miri_get_backtrace(1, buf.as_mut_ptr()); // miri_resolve_frame will error from an invalid backtrace before it will from invalid flags - miri_resolve_frame(buf[0], 2); //~ ERROR unsupported operation: unknown `miri_resolve_frame` flags 2 + miri_resolve_frame(buf[0], 2); //~ ERROR: unsupported operation: unknown `miri_resolve_frame` flags 2 } } diff --git a/tests/fail/backtrace/bad-backtrace-resolve-names-flags.rs b/tests/fail/backtrace/bad-backtrace-resolve-names-flags.rs index 6cea1fec1b..44c3c02504 100644 --- a/tests/fail/backtrace/bad-backtrace-resolve-names-flags.rs +++ b/tests/fail/backtrace/bad-backtrace-resolve-names-flags.rs @@ -11,6 +11,6 @@ fn main() { miri_get_backtrace(1, buf.as_mut_ptr()); // miri_resolve_frame_names will error from an invalid backtrace before it will from invalid flags - miri_resolve_frame_names(buf[0], 2, std::ptr::null_mut(), std::ptr::null_mut()); //~ ERROR unsupported operation: unknown `miri_resolve_frame_names` flags 2 + miri_resolve_frame_names(buf[0], 2, std::ptr::null_mut(), std::ptr::null_mut()); //~ ERROR: unsupported operation: unknown `miri_resolve_frame_names` flags 2 } } diff --git a/tests/fail/backtrace/bad-backtrace-size-flags.rs b/tests/fail/backtrace/bad-backtrace-size-flags.rs index 25eded9e48..bba74c71a5 100644 --- a/tests/fail/backtrace/bad-backtrace-size-flags.rs +++ b/tests/fail/backtrace/bad-backtrace-size-flags.rs @@ -4,6 +4,6 @@ extern "Rust" { fn main() { unsafe { - miri_backtrace_size(2); //~ ERROR unsupported operation: unknown `miri_backtrace_size` flags 2 + miri_backtrace_size(2); //~ ERROR: unsupported operation: unknown `miri_backtrace_size` flags 2 } } diff --git a/tests/fail/box-cell-alias.rs b/tests/fail/box-cell-alias.rs index d3f505d2da..1a4a3b97ea 100644 --- a/tests/fail/box-cell-alias.rs +++ b/tests/fail/box-cell-alias.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-strict-provenance +//@compile-flags: -Zmiri-strict-provenance // Taken from . @@ -6,7 +6,7 @@ use std::cell::Cell; fn helper(val: Box>, ptr: *const Cell) -> u8 { val.set(10); - unsafe { (*ptr).set(20) }; //~ ERROR does not exist in the borrow stack + unsafe { (*ptr).set(20) }; //~ ERROR: does not exist in the borrow stack val.get() } diff --git a/tests/fail/box-cell-alias.stderr b/tests/fail/box-cell-alias.stderr index c7fd1c6415..705348ba0f 100644 --- a/tests/fail/box-cell-alias.stderr +++ b/tests/fail/box-cell-alias.stderr @@ -1,10 +1,10 @@ -error: Undefined Behavior: trying to reborrow for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location --> $DIR/box-cell-alias.rs:LL:CC | LL | unsafe { (*ptr).set(20) }; | ^^^^^^^^^^^^^^ | | - | trying to reborrow for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x0..0x1] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/branchless-select-i128-pointer.rs b/tests/fail/branchless-select-i128-pointer.rs index a3b4021ba0..2b861e5447 100644 --- a/tests/fail/branchless-select-i128-pointer.rs +++ b/tests/fail/branchless-select-i128-pointer.rs @@ -12,7 +12,7 @@ fn main() { // However, it drops provenance when transmuting to TwoPtrs, so this is UB. let val = unsafe { transmute::<_, &str>( - //~^ ERROR constructing invalid value: encountered a dangling reference + //~^ ERROR: constructing invalid value: encountered a dangling reference !mask & transmute::<_, TwoPtrs>("false !") | mask & transmute::<_, TwoPtrs>("true !"), ) diff --git a/tests/fail/breakpoint.rs b/tests/fail/breakpoint.rs index d0a0239eb9..fb1d4d958e 100644 --- a/tests/fail/breakpoint.rs +++ b/tests/fail/breakpoint.rs @@ -2,6 +2,6 @@ fn main() { unsafe { - core::intrinsics::breakpoint() //~ ERROR Trace/breakpoint trap + core::intrinsics::breakpoint() //~ ERROR: Trace/breakpoint trap }; } diff --git a/tests/fail/concurrency/libc_pthread_create_main_terminate.rs b/tests/fail/concurrency/libc_pthread_create_main_terminate.rs index 9b576bbb08..169a021215 100644 --- a/tests/fail/concurrency/libc_pthread_create_main_terminate.rs +++ b/tests/fail/concurrency/libc_pthread_create_main_terminate.rs @@ -1,5 +1,5 @@ -// ignore-windows: No libc on Windows -// error-pattern: the main thread terminated without waiting for all remaining threads +//@ignore-target-windows: No libc on Windows +//@error-pattern: the main thread terminated without waiting for all remaining threads // Check that we terminate the program when the main thread terminates. diff --git a/tests/fail/concurrency/libc_pthread_join_detached.rs b/tests/fail/concurrency/libc_pthread_join_detached.rs index dcd06596de..6f0d45e2dc 100644 --- a/tests/fail/concurrency/libc_pthread_join_detached.rs +++ b/tests/fail/concurrency/libc_pthread_join_detached.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows // Joining a detached thread is undefined behavior. diff --git a/tests/fail/concurrency/libc_pthread_join_joined.rs b/tests/fail/concurrency/libc_pthread_join_joined.rs index 26f33f1f5f..77f59fabca 100644 --- a/tests/fail/concurrency/libc_pthread_join_joined.rs +++ b/tests/fail/concurrency/libc_pthread_join_joined.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows // Joining an already joined thread is undefined behavior. diff --git a/tests/fail/concurrency/libc_pthread_join_main.rs b/tests/fail/concurrency/libc_pthread_join_main.rs index 15e43776ab..aff28bcc9b 100644 --- a/tests/fail/concurrency/libc_pthread_join_main.rs +++ b/tests/fail/concurrency/libc_pthread_join_main.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows // Joining the main thread is undefined behavior. diff --git a/tests/fail/concurrency/libc_pthread_join_multiple.rs b/tests/fail/concurrency/libc_pthread_join_multiple.rs index d86233a676..d4d54d3a23 100644 --- a/tests/fail/concurrency/libc_pthread_join_multiple.rs +++ b/tests/fail/concurrency/libc_pthread_join_multiple.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows // Joining the same thread from multiple threads is undefined behavior. diff --git a/tests/fail/concurrency/libc_pthread_join_self.rs b/tests/fail/concurrency/libc_pthread_join_self.rs index db45b33c14..b911b2db3a 100644 --- a/tests/fail/concurrency/libc_pthread_join_self.rs +++ b/tests/fail/concurrency/libc_pthread_join_self.rs @@ -1,6 +1,6 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows // We are making scheduler assumptions here. -// compile-flags: -Zmiri-preemption-rate=0 +//@compile-flags: -Zmiri-preemption-rate=0 // Joining itself is undefined behavior. diff --git a/tests/fail/concurrency/thread-spawn.rs b/tests/fail/concurrency/thread-spawn.rs index 948ce946d8..84848e35a0 100644 --- a/tests/fail/concurrency/thread-spawn.rs +++ b/tests/fail/concurrency/thread-spawn.rs @@ -1,8 +1,8 @@ -// only-windows: Only Windows is not supported. +//@only-target-windows: Only Windows is not supported. use std::thread; -// error-pattern: can't create threads on Windows +//@error-pattern: can't create threads on Windows fn main() { thread::spawn(|| {}); diff --git a/tests/fail/concurrency/thread_local_static_dealloc.rs b/tests/fail/concurrency/thread_local_static_dealloc.rs index e6031b5e4c..7c54e3bca7 100644 --- a/tests/fail/concurrency/thread_local_static_dealloc.rs +++ b/tests/fail/concurrency/thread_local_static_dealloc.rs @@ -1,4 +1,4 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +//@ignore-target-windows: Concurrency on Windows is not supported yet. //! Ensure that thread-local statics get deallocated when the thread dies. @@ -13,6 +13,6 @@ unsafe impl Send for SendRaw {} fn main() { unsafe { let dangling_ptr = std::thread::spawn(|| SendRaw(&TLS as *const u8)).join().unwrap(); - let _val = *dangling_ptr.0; //~ ERROR dereferenced after this allocation got freed + let _val = *dangling_ptr.0; //~ ERROR: dereferenced after this allocation got freed } } diff --git a/tests/fail/concurrency/too_few_args.rs b/tests/fail/concurrency/too_few_args.rs index 23fa38d881..11e97ca290 100644 --- a/tests/fail/concurrency/too_few_args.rs +++ b/tests/fail/concurrency/too_few_args.rs @@ -1,4 +1,4 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +//@ignore-target-windows: Concurrency on Windows is not supported yet. //! The thread function must have exactly one argument. diff --git a/tests/fail/concurrency/too_many_args.rs b/tests/fail/concurrency/too_many_args.rs index af5a377a04..dd44207a62 100644 --- a/tests/fail/concurrency/too_many_args.rs +++ b/tests/fail/concurrency/too_many_args.rs @@ -1,4 +1,4 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +//@ignore-target-windows: Concurrency on Windows is not supported yet. //! The thread function must have exactly one argument. diff --git a/tests/fail/concurrency/unwind_top_of_stack.rs b/tests/fail/concurrency/unwind_top_of_stack.rs index 39f7ae8baf..179ff9c146 100644 --- a/tests/fail/concurrency/unwind_top_of_stack.rs +++ b/tests/fail/concurrency/unwind_top_of_stack.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-disable-abi-check +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-disable-abi-check //! Unwinding past the top frame of a stack is Undefined Behavior. @@ -10,7 +10,7 @@ extern crate libc; use std::{mem, ptr}; extern "C-unwind" fn thread_start(_null: *mut libc::c_void) -> *mut libc::c_void { - //~^ ERROR unwinding past the topmost frame of the stack + //~^ ERROR: unwinding past the topmost frame of the stack panic!() } diff --git a/tests/fail/dangling_pointers/dangling_pointer_addr_of.rs b/tests/fail/dangling_pointers/dangling_pointer_addr_of.rs index 5de4138711..4249c1cbf0 100644 --- a/tests/fail/dangling_pointers/dangling_pointer_addr_of.rs +++ b/tests/fail/dangling_pointers/dangling_pointer_addr_of.rs @@ -1,5 +1,5 @@ // Make sure we find these even with many checks disabled. -// compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation use std::ptr; fn main() { @@ -7,6 +7,6 @@ fn main() { let b = Box::new(42); &*b as *const i32 }; - let x = unsafe { ptr::addr_of!(*p) }; //~ ERROR dereferenced after this allocation got freed + let x = unsafe { ptr::addr_of!(*p) }; //~ ERROR: dereferenced after this allocation got freed panic!("this should never print: {:?}", x); } diff --git a/tests/fail/dangling_pointers/dangling_pointer_deref.rs b/tests/fail/dangling_pointers/dangling_pointer_deref.rs index e088a55325..ad2a599b60 100644 --- a/tests/fail/dangling_pointers/dangling_pointer_deref.rs +++ b/tests/fail/dangling_pointers/dangling_pointer_deref.rs @@ -1,11 +1,11 @@ // Make sure we find these even with many checks disabled. -// compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation fn main() { let p = { let b = Box::new(42); &*b as *const i32 }; - let x = unsafe { *p }; //~ ERROR dereferenced after this allocation got freed + let x = unsafe { *p }; //~ ERROR: dereferenced after this allocation got freed panic!("this should never print: {}", x); } diff --git a/tests/fail/dangling_pointers/dangling_zst_deref.rs b/tests/fail/dangling_pointers/dangling_zst_deref.rs index 01e864213d..534d7d5f42 100644 --- a/tests/fail/dangling_pointers/dangling_zst_deref.rs +++ b/tests/fail/dangling_pointers/dangling_zst_deref.rs @@ -1,11 +1,11 @@ // Make sure we find these even with many checks disabled. // Some optimizations remove ZST accesses, thus masking this UB. -// compile-flags: -Zmir-opt-level=0 -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation +//@compile-flags: -Zmir-opt-level=0 -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation fn main() { let p = { let b = Box::new(42); &*b as *const i32 as *const () }; - let _x = unsafe { *p }; //~ ERROR dereferenced after this allocation got freed + let _x = unsafe { *p }; //~ ERROR: dereferenced after this allocation got freed } diff --git a/tests/fail/dangling_pointers/deref-invalid-ptr.rs b/tests/fail/dangling_pointers/deref-invalid-ptr.rs index 31b52da774..57e95ef19d 100644 --- a/tests/fail/dangling_pointers/deref-invalid-ptr.rs +++ b/tests/fail/dangling_pointers/deref-invalid-ptr.rs @@ -1,7 +1,7 @@ // This should fail even without validation. -// compile-flags: -Zmiri-disable-validation -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-disable-validation -Zmiri-permissive-provenance fn main() { let x = 16usize as *const u32; - let _y = unsafe { &*x as *const u32 }; //~ ERROR is a dangling pointer + let _y = unsafe { &*x as *const u32 }; //~ ERROR: is a dangling pointer } diff --git a/tests/fail/dangling_pointers/deref-partially-dangling.rs b/tests/fail/dangling_pointers/deref-partially-dangling.rs index b7fcf4559e..27040c26dc 100644 --- a/tests/fail/dangling_pointers/deref-partially-dangling.rs +++ b/tests/fail/dangling_pointers/deref-partially-dangling.rs @@ -3,6 +3,6 @@ fn main() { let x = (1, 13); let xptr = &x as *const _ as *const (i32, i32, i32); - let val = unsafe { (*xptr).1 }; //~ ERROR pointer to 12 bytes starting at offset 0 is out-of-bounds + let val = unsafe { (*xptr).1 }; //~ ERROR: pointer to 12 bytes starting at offset 0 is out-of-bounds assert_eq!(val, 13); } diff --git a/tests/fail/dangling_pointers/dyn_size.rs b/tests/fail/dangling_pointers/dyn_size.rs index 56de3970f1..54f353ebeb 100644 --- a/tests/fail/dangling_pointers/dyn_size.rs +++ b/tests/fail/dangling_pointers/dyn_size.rs @@ -1,5 +1,5 @@ // should find the bug even without these, but gets masked by optimizations -// compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows -Zmir-opt-level=0 +//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows -Zmir-opt-level=0 struct SliceWithHead(u8, [u8]); @@ -9,5 +9,5 @@ fn main() { // That should be UB, as the reference is not fully dereferencable. let ptr: *const SliceWithHead = unsafe { std::mem::transmute((&buf, 4usize)) }; // Re-borrow that. This should be UB. - let _ptr = unsafe { &*ptr }; //~ ERROR pointer to 5 bytes starting at offset 0 is out-of-bounds + let _ptr = unsafe { &*ptr }; //~ ERROR: pointer to 5 bytes starting at offset 0 is out-of-bounds } diff --git a/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs b/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs index 357eadf91c..a48a3189db 100644 --- a/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs +++ b/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs @@ -1,8 +1,8 @@ // Some optimizations remove ZST accesses, thus masking this UB. -// compile-flags: -Zmir-opt-level=0 +//@compile-flags: -Zmir-opt-level=0 fn main() { // This pointer *could* be NULL so we cannot load from it, not even at ZST let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *const (); - let _x: () = unsafe { *ptr }; //~ ERROR out-of-bounds + let _x: () = unsafe { *ptr }; //~ ERROR: out-of-bounds } diff --git a/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs b/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs index 4a8d498aa1..449c65d218 100644 --- a/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs +++ b/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs @@ -1,5 +1,5 @@ // Some optimizations remove ZST accesses, thus masking this UB. -// compile-flags: -Zmir-opt-level=0 +//@compile-flags: -Zmir-opt-level=0 fn main() { // This pointer *could* be NULL so we cannot load from it, not even at ZST. @@ -7,5 +7,5 @@ fn main() { // Also not assigning directly as that's array initialization, not assignment. let zst_val = [1u8; 0]; let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *mut [u8; 0]; - unsafe { *ptr = zst_val }; //~ ERROR out-of-bounds + unsafe { *ptr = zst_val }; //~ ERROR: out-of-bounds } diff --git a/tests/fail/dangling_pointers/null_pointer_deref.rs b/tests/fail/dangling_pointers/null_pointer_deref.rs index dad6de85e0..a0773c63cf 100644 --- a/tests/fail/dangling_pointers/null_pointer_deref.rs +++ b/tests/fail/dangling_pointers/null_pointer_deref.rs @@ -1,5 +1,5 @@ #[allow(deref_nullptr)] fn main() { - let x: i32 = unsafe { *std::ptr::null() }; //~ ERROR null pointer is a dangling pointer + let x: i32 = unsafe { *std::ptr::null() }; //~ ERROR: null pointer is a dangling pointer panic!("this should never print: {}", x); } diff --git a/tests/fail/dangling_pointers/null_pointer_deref_zst.rs b/tests/fail/dangling_pointers/null_pointer_deref_zst.rs index 21b0ce37d8..d6a607c61c 100644 --- a/tests/fail/dangling_pointers/null_pointer_deref_zst.rs +++ b/tests/fail/dangling_pointers/null_pointer_deref_zst.rs @@ -1,8 +1,8 @@ // Some optimizations remove ZST accesses, thus masking this UB. -// compile-flags: -Zmir-opt-level=0 +//@compile-flags: -Zmir-opt-level=0 #[allow(deref_nullptr)] fn main() { - let x: () = unsafe { *std::ptr::null() }; //~ ERROR dereferencing pointer failed: null pointer is a dangling pointer + let x: () = unsafe { *std::ptr::null() }; //~ ERROR: dereferencing pointer failed: null pointer is a dangling pointer panic!("this should never print: {:?}", x); } diff --git a/tests/fail/dangling_pointers/null_pointer_write.rs b/tests/fail/dangling_pointers/null_pointer_write.rs index c7255baf66..954596f575 100644 --- a/tests/fail/dangling_pointers/null_pointer_write.rs +++ b/tests/fail/dangling_pointers/null_pointer_write.rs @@ -1,4 +1,4 @@ #[allow(deref_nullptr)] fn main() { - unsafe { *std::ptr::null_mut() = 0i32 }; //~ ERROR null pointer is a dangling pointer + unsafe { *std::ptr::null_mut() = 0i32 }; //~ ERROR: null pointer is a dangling pointer } diff --git a/tests/fail/dangling_pointers/null_pointer_write_zst.rs b/tests/fail/dangling_pointers/null_pointer_write_zst.rs index 60e2d7c663..7181a5979b 100644 --- a/tests/fail/dangling_pointers/null_pointer_write_zst.rs +++ b/tests/fail/dangling_pointers/null_pointer_write_zst.rs @@ -1,6 +1,6 @@ // Some optimizations remove ZST accesses, thus masking this UB. -// compile-flags: -Zmir-opt-level=0 -// error-pattern: memory access failed: null pointer is a dangling pointer +//@compile-flags: -Zmir-opt-level=0 +//@error-pattern: memory access failed: null pointer is a dangling pointer #[allow(deref_nullptr)] fn main() { diff --git a/tests/fail/dangling_pointers/out_of_bounds_read1.rs b/tests/fail/dangling_pointers/out_of_bounds_read1.rs index ef5cdeec99..58a64eecac 100644 --- a/tests/fail/dangling_pointers/out_of_bounds_read1.rs +++ b/tests/fail/dangling_pointers/out_of_bounds_read1.rs @@ -1,5 +1,5 @@ fn main() { let v: Vec = vec![1, 2]; - let x = unsafe { *v.as_ptr().wrapping_offset(5) }; //~ ERROR out-of-bounds + let x = unsafe { *v.as_ptr().wrapping_offset(5) }; //~ ERROR: out-of-bounds panic!("this should never print: {}", x); } diff --git a/tests/fail/dangling_pointers/out_of_bounds_read2.rs b/tests/fail/dangling_pointers/out_of_bounds_read2.rs index ef5cdeec99..58a64eecac 100644 --- a/tests/fail/dangling_pointers/out_of_bounds_read2.rs +++ b/tests/fail/dangling_pointers/out_of_bounds_read2.rs @@ -1,5 +1,5 @@ fn main() { let v: Vec = vec![1, 2]; - let x = unsafe { *v.as_ptr().wrapping_offset(5) }; //~ ERROR out-of-bounds + let x = unsafe { *v.as_ptr().wrapping_offset(5) }; //~ ERROR: out-of-bounds panic!("this should never print: {}", x); } diff --git a/tests/fail/dangling_pointers/stack_temporary.rs b/tests/fail/dangling_pointers/stack_temporary.rs index cbd788bbf4..1373773f68 100644 --- a/tests/fail/dangling_pointers/stack_temporary.rs +++ b/tests/fail/dangling_pointers/stack_temporary.rs @@ -1,5 +1,5 @@ // This should fail even without validation, but some MIR opts mask the error -// compile-flags: -Zmiri-disable-validation -Zmir-opt-level=0 +//@compile-flags: -Zmiri-disable-validation -Zmir-opt-level=0 unsafe fn make_ref<'a>(x: *mut i32) -> &'a mut i32 { &mut *x @@ -8,7 +8,7 @@ unsafe fn make_ref<'a>(x: *mut i32) -> &'a mut i32 { fn main() { unsafe { let x = make_ref(&mut 0); // The temporary storing "0" is deallocated at the ";"! - let val = *x; //~ ERROR dereferenced after this allocation got freed + let val = *x; //~ ERROR: dereferenced after this allocation got freed println!("{}", val); } } diff --git a/tests/fail/dangling_pointers/storage_dead_dangling.rs b/tests/fail/dangling_pointers/storage_dead_dangling.rs index 64ed37d151..03113585d1 100644 --- a/tests/fail/dangling_pointers/storage_dead_dangling.rs +++ b/tests/fail/dangling_pointers/storage_dead_dangling.rs @@ -1,5 +1,5 @@ // This should fail even without validation, but some MIR opts mask the error -// compile-flags: -Zmiri-disable-validation -Zmir-opt-level=0 -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-disable-validation -Zmir-opt-level=0 -Zmiri-permissive-provenance static mut LEAK: usize = 0; @@ -10,7 +10,7 @@ fn fill(v: &mut i32) { } fn evil() { - unsafe { &mut *(LEAK as *mut i32) }; //~ ERROR is a dangling pointer + unsafe { &mut *(LEAK as *mut i32) }; //~ ERROR: is a dangling pointer } fn main() { diff --git a/tests/fail/dangling_pointers/wild_pointer_deref.rs b/tests/fail/dangling_pointers/wild_pointer_deref.rs index 9f6b370c05..9ffc681465 100644 --- a/tests/fail/dangling_pointers/wild_pointer_deref.rs +++ b/tests/fail/dangling_pointers/wild_pointer_deref.rs @@ -1,7 +1,7 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance fn main() { let p = 44 as *const i32; - let x = unsafe { *p }; //~ ERROR is a dangling pointer + let x = unsafe { *p }; //~ ERROR: is a dangling pointer panic!("this should never print: {}", x); } diff --git a/tests/fail/data_race/alloc_read_race.rs b/tests/fail/data_race/alloc_read_race.rs index 1eac8ce0f2..f3f63aeb2b 100644 --- a/tests/fail/data_race/alloc_read_race.rs +++ b/tests/fail/data_race/alloc_read_race.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 #![feature(new_uninit)] use std::mem::MaybeUninit; @@ -38,7 +38,7 @@ pub fn main() { let pointer = &*ptr.0; // Note: could also error due to reading uninitialized memory, but the data-race detector triggers first. - *pointer.load(Ordering::Relaxed) //~ ERROR Data race detected between Read on thread `` and Allocate on thread `` + *pointer.load(Ordering::Relaxed) //~ ERROR: Data race detected between Read on thread `` and Allocate on thread `` }); j1.join().unwrap(); diff --git a/tests/fail/data_race/alloc_write_race.rs b/tests/fail/data_race/alloc_write_race.rs index e618b72a82..a2738c3879 100644 --- a/tests/fail/data_race/alloc_write_race.rs +++ b/tests/fail/data_race/alloc_write_race.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 #![feature(new_uninit)] use std::ptr::null_mut; @@ -36,7 +36,7 @@ pub fn main() { let j2 = spawn(move || { let pointer = &*ptr.0; - *pointer.load(Ordering::Relaxed) = 2; //~ ERROR Data race detected between Write on thread `` and Allocate on thread `` + *pointer.load(Ordering::Relaxed) = 2; //~ ERROR: Data race detected between Write on thread `` and Allocate on thread `` }); j1.join().unwrap(); diff --git a/tests/fail/data_race/atomic_read_na_write_race1.rs b/tests/fail/data_race/atomic_read_na_write_race1.rs index 3b948eea98..1ef021edc8 100644 --- a/tests/fail/data_race/atomic_read_na_write_race1.rs +++ b/tests/fail/data_race/atomic_read_na_write_race1.rs @@ -1,4 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +// We want to control preemption here. +//@compile-flags: -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. #![feature(core_intrinsics)] use std::intrinsics; @@ -22,7 +24,7 @@ pub fn main() { let j2 = spawn(move || { //Equivalent to: (&*c.0).load(Ordering::SeqCst) - intrinsics::atomic_load_seqcst(c.0 as *mut usize) //~ ERROR Data race detected between Atomic Load on thread `` and Write on thread `` + intrinsics::atomic_load_seqcst(c.0 as *mut usize) //~ ERROR: Data race detected between Atomic Load on thread `` and Write on thread `` }); j1.join().unwrap(); diff --git a/tests/fail/data_race/atomic_read_na_write_race2.rs b/tests/fail/data_race/atomic_read_na_write_race2.rs index 44b4eebee8..493985f6cf 100644 --- a/tests/fail/data_race/atomic_read_na_write_race2.rs +++ b/tests/fail/data_race/atomic_read_na_write_race2.rs @@ -1,4 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +// We want to control preemption here. +//@compile-flags: -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; @@ -22,7 +24,7 @@ pub fn main() { let j2 = spawn(move || { let atomic_ref = &mut *c.0; - *atomic_ref.get_mut() = 32; //~ ERROR Data race detected between Write on thread `` and Atomic Load on thread `` + *atomic_ref.get_mut() = 32; //~ ERROR: Data race detected between Write on thread `` and Atomic Load on thread `` }); j1.join().unwrap(); diff --git a/tests/fail/data_race/atomic_write_na_read_race1.rs b/tests/fail/data_race/atomic_write_na_read_race1.rs index 44dc1a9084..ca9c28abf1 100644 --- a/tests/fail/data_race/atomic_write_na_read_race1.rs +++ b/tests/fail/data_race/atomic_write_na_read_race1.rs @@ -1,4 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +// We want to control preemption here. +//@compile-flags: -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; @@ -22,7 +24,7 @@ pub fn main() { let j2 = spawn(move || { let atomic_ref = &mut *c.0; - *atomic_ref.get_mut() //~ ERROR Data race detected between Read on thread `` and Atomic Store on thread `` + *atomic_ref.get_mut() //~ ERROR: Data race detected between Read on thread `` and Atomic Store on thread `` }); j1.join().unwrap(); diff --git a/tests/fail/data_race/atomic_write_na_read_race2.rs b/tests/fail/data_race/atomic_write_na_read_race2.rs index b4b21b64fc..77b2945f73 100644 --- a/tests/fail/data_race/atomic_write_na_read_race2.rs +++ b/tests/fail/data_race/atomic_write_na_read_race2.rs @@ -1,4 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +// We want to control preemption here. +//@compile-flags: -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. #![feature(core_intrinsics)] use std::intrinsics::atomic_store; @@ -22,7 +24,7 @@ pub fn main() { let j2 = spawn(move || { //Equivalent to: (&*c.0).store(32, Ordering::SeqCst) - atomic_store(c.0 as *mut usize, 32); //~ ERROR Data race detected between Atomic Store on thread `` and Read on thread `` + atomic_store(c.0 as *mut usize, 32); //~ ERROR: Data race detected between Atomic Store on thread `` and Read on thread `` }); j1.join().unwrap(); diff --git a/tests/fail/data_race/atomic_write_na_write_race1.rs b/tests/fail/data_race/atomic_write_na_write_race1.rs index b1a4cfb98b..34922dd595 100644 --- a/tests/fail/data_race/atomic_write_na_write_race1.rs +++ b/tests/fail/data_race/atomic_write_na_write_race1.rs @@ -1,4 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +// We want to control preemption here. +//@compile-flags: -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. #![feature(core_intrinsics)] use std::intrinsics::atomic_store; @@ -22,7 +24,7 @@ pub fn main() { let j2 = spawn(move || { //Equivalent to: (&*c.0).store(64, Ordering::SeqCst) - atomic_store(c.0 as *mut usize, 64); //~ ERROR Data race detected between Atomic Store on thread `` and Write on thread `` + atomic_store(c.0 as *mut usize, 64); //~ ERROR: Data race detected between Atomic Store on thread `` and Write on thread `` }); j1.join().unwrap(); diff --git a/tests/fail/data_race/atomic_write_na_write_race2.rs b/tests/fail/data_race/atomic_write_na_write_race2.rs index dbdce8f623..1d242ff3cd 100644 --- a/tests/fail/data_race/atomic_write_na_write_race2.rs +++ b/tests/fail/data_race/atomic_write_na_write_race2.rs @@ -1,4 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +// We want to control preemption here. +//@compile-flags: -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; @@ -22,7 +24,7 @@ pub fn main() { let j2 = spawn(move || { let atomic_ref = &mut *c.0; - *atomic_ref.get_mut() = 32; //~ ERROR Data race detected between Write on thread `` and Atomic Store on thread `` + *atomic_ref.get_mut() = 32; //~ ERROR: Data race detected between Write on thread `` and Atomic Store on thread `` }); j1.join().unwrap(); diff --git a/tests/fail/data_race/dangling_thread_async_race.rs b/tests/fail/data_race/dangling_thread_async_race.rs index 65325b60f2..6264255265 100644 --- a/tests/fail/data_race/dangling_thread_async_race.rs +++ b/tests/fail/data_race/dangling_thread_async_race.rs @@ -1,5 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-disable-isolation +// We want to control preemption here. +//@compile-flags: -Zmiri-disable-isolation -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. use std::mem; use std::thread::{sleep, spawn}; @@ -34,7 +35,7 @@ fn main() { let join2 = unsafe { spawn(move || { - *c.0 = 64; //~ ERROR Data race detected between Write on thread `` and Write on thread `` + *c.0 = 64; //~ ERROR: Data race detected between Write on thread `` and Write on thread `` }) }; diff --git a/tests/fail/data_race/dangling_thread_race.rs b/tests/fail/data_race/dangling_thread_race.rs index 09e7032c93..6f44fc69db 100644 --- a/tests/fail/data_race/dangling_thread_race.rs +++ b/tests/fail/data_race/dangling_thread_race.rs @@ -1,5 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-disable-isolation +// We want to control preemption here. +//@compile-flags: -Zmiri-disable-isolation -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. use std::mem; use std::thread::{sleep, spawn}; @@ -33,6 +34,6 @@ fn main() { spawn(|| ()).join().unwrap(); unsafe { - *c.0 = 64; //~ ERROR Data race detected between Write on thread `main` and Write on thread `` + *c.0 = 64; //~ ERROR: Data race detected between Write on thread `main` and Write on thread `` } } diff --git a/tests/fail/data_race/dealloc_read_race1.rs b/tests/fail/data_race/dealloc_read_race1.rs index ff2ac8ca52..5349073ec3 100644 --- a/tests/fail/data_race/dealloc_read_race1.rs +++ b/tests/fail/data_race/dealloc_read_race1.rs @@ -1,4 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +// We want to control preemption here. +//@compile-flags: -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. use std::thread::spawn; @@ -24,7 +26,7 @@ pub fn main() { let j2 = spawn(move || { __rust_dealloc( - //~^ ERROR Data race detected between Deallocate on thread `` and Read on thread `` + //~^ ERROR: Data race detected between Deallocate on thread `` and Read on thread `` ptr.0 as *mut _, std::mem::size_of::(), std::mem::align_of::(), diff --git a/tests/fail/data_race/dealloc_read_race2.rs b/tests/fail/data_race/dealloc_read_race2.rs index 4bb6444f6a..bb9070ef75 100644 --- a/tests/fail/data_race/dealloc_read_race2.rs +++ b/tests/fail/data_race/dealloc_read_race2.rs @@ -1,4 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +// We want to control preemption here. +//@compile-flags: -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. use std::thread::spawn; @@ -29,7 +31,7 @@ pub fn main() { let j2 = spawn(move || { // Also an error of the form: Data race detected between Read on thread `` and Deallocate on thread `` // but the invalid allocation is detected first. - *ptr.0 //~ ERROR dereferenced after this allocation got freed + *ptr.0 //~ ERROR: dereferenced after this allocation got freed }); j1.join().unwrap(); diff --git a/tests/fail/data_race/dealloc_read_race_stack.rs b/tests/fail/data_race/dealloc_read_race_stack.rs index e079581a0d..e114fbb8b4 100644 --- a/tests/fail/data_race/dealloc_read_race_stack.rs +++ b/tests/fail/data_race/dealloc_read_race_stack.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-disable-isolation -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-disable-isolation -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 use std::ptr::null_mut; use std::sync::atomic::{AtomicPtr, Ordering}; @@ -36,7 +36,7 @@ pub fn main() { sleep(Duration::from_millis(200)); // Now `stack_var` gets deallocated. - } //~ ERROR Data race detected between Deallocate on thread `` and Read on thread `` + } //~ ERROR: Data race detected between Deallocate on thread `` and Read on thread `` }); let j2 = spawn(move || { diff --git a/tests/fail/data_race/dealloc_write_race1.rs b/tests/fail/data_race/dealloc_write_race1.rs index 9cd0ebc642..35949920e1 100644 --- a/tests/fail/data_race/dealloc_write_race1.rs +++ b/tests/fail/data_race/dealloc_write_race1.rs @@ -1,4 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +// We want to control preemption here. +//@compile-flags: -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. use std::thread::spawn; @@ -23,7 +25,7 @@ pub fn main() { let j2 = spawn(move || { __rust_dealloc( - //~^ ERROR Data race detected between Deallocate on thread `` and Write on thread `` + //~^ ERROR: Data race detected between Deallocate on thread `` and Write on thread `` ptr.0 as *mut _, std::mem::size_of::(), std::mem::align_of::(), diff --git a/tests/fail/data_race/dealloc_write_race2.rs b/tests/fail/data_race/dealloc_write_race2.rs index 9b1b8f0614..b569086c8c 100644 --- a/tests/fail/data_race/dealloc_write_race2.rs +++ b/tests/fail/data_race/dealloc_write_race2.rs @@ -1,4 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +// We want to control preemption here. +//@compile-flags: -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. use std::thread::spawn; @@ -28,7 +30,7 @@ pub fn main() { let j2 = spawn(move || { // Also an error of the form: Data race detected between Write on thread `` and Deallocate on thread `` // but the invalid allocation is detected first. - *ptr.0 = 2; //~ ERROR dereferenced after this allocation got freed + *ptr.0 = 2; //~ ERROR: dereferenced after this allocation got freed }); j1.join().unwrap(); diff --git a/tests/fail/data_race/dealloc_write_race_stack.rs b/tests/fail/data_race/dealloc_write_race_stack.rs index 2f12570892..0c74c7adf5 100644 --- a/tests/fail/data_race/dealloc_write_race_stack.rs +++ b/tests/fail/data_race/dealloc_write_race_stack.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-disable-isolation -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-disable-isolation -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 use std::ptr::null_mut; use std::sync::atomic::{AtomicPtr, Ordering}; @@ -36,7 +36,7 @@ pub fn main() { sleep(Duration::from_millis(200)); // Now `stack_var` gets deallocated. - } //~ ERROR Data race detected between Deallocate on thread `` and Write on thread `` + } //~ ERROR: Data race detected between Deallocate on thread `` and Write on thread `` }); let j2 = spawn(move || { diff --git a/tests/fail/data_race/enable_after_join_to_main.rs b/tests/fail/data_race/enable_after_join_to_main.rs index 6f0735fac8..f2235b9525 100644 --- a/tests/fail/data_race/enable_after_join_to_main.rs +++ b/tests/fail/data_race/enable_after_join_to_main.rs @@ -1,4 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +// We want to control preemption here. +//@compile-flags: -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. use std::thread::spawn; @@ -29,7 +31,7 @@ pub fn main() { }); let j2 = spawn(move || { - *c.0 = 64; //~ ERROR Data race detected between Write on thread `` and Write on thread `` + *c.0 = 64; //~ ERROR: Data race detected between Write on thread `` and Write on thread `` }); j1.join().unwrap(); diff --git a/tests/fail/data_race/fence_after_load.rs b/tests/fail/data_race/fence_after_load.rs index 5a8c2e585f..8acbe12e82 100644 --- a/tests/fail/data_race/fence_after_load.rs +++ b/tests/fail/data_race/fence_after_load.rs @@ -1,6 +1,6 @@ // We want to control preemption here. -// compile-flags: -Zmiri-disable-isolation -Zmiri-preemption-rate=0 -// ignore-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-disable-isolation -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. use std::sync::atomic::{fence, AtomicUsize, Ordering}; use std::sync::Arc; use std::thread; @@ -21,5 +21,5 @@ fn main() { // The fence is useless, since it did not happen-after the `store` in the other thread. // Hence this is a data race. // Also see https://github.com/rust-lang/miri/issues/2192. - unsafe { V = 2 } //~ERROR Data race detected between Write on thread `main` and Write on thread `` + unsafe { V = 2 } //~ERROR: Data race detected between Write on thread `main` and Write on thread `` } diff --git a/tests/fail/data_race/read_write_race.rs b/tests/fail/data_race/read_write_race.rs index eeb49bb42a..cff868fb69 100644 --- a/tests/fail/data_race/read_write_race.rs +++ b/tests/fail/data_race/read_write_race.rs @@ -1,4 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +// We want to control preemption here. +//@compile-flags: -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. use std::thread::spawn; @@ -18,7 +20,7 @@ pub fn main() { }); let j2 = spawn(move || { - *c.0 = 64; //~ ERROR Data race detected between Write on thread `` and Read on thread `` + *c.0 = 64; //~ ERROR: Data race detected between Write on thread `` and Read on thread `` }); j1.join().unwrap(); diff --git a/tests/fail/data_race/read_write_race_stack.rs b/tests/fail/data_race/read_write_race_stack.rs index 124f12d1ec..b22173fa5a 100644 --- a/tests/fail/data_race/read_write_race_stack.rs +++ b/tests/fail/data_race/read_write_race_stack.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-disable-isolation -Zmir-opt-level=0 -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-disable-isolation -Zmir-opt-level=0 -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 // Note: mir-opt-level set to 0 to prevent the read of stack_var in thread 1 // from being optimized away and preventing the detection of the data-race. @@ -43,7 +43,7 @@ pub fn main() { sleep(Duration::from_millis(200)); - stack_var //~ ERROR Data race detected between Read on thread `` and Write on thread `` + stack_var //~ ERROR: Data race detected between Read on thread `` and Write on thread `` }); let j2 = spawn(move || { diff --git a/tests/fail/data_race/relax_acquire_race.rs b/tests/fail/data_race/relax_acquire_race.rs index faa23a150e..b99388b892 100644 --- a/tests/fail/data_race/relax_acquire_race.rs +++ b/tests/fail/data_race/relax_acquire_race.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; @@ -38,7 +38,7 @@ pub fn main() { let j3 = spawn(move || { if SYNC.load(Ordering::Acquire) == 2 { - *c.0 //~ ERROR Data race detected between Read on thread `` and Write on thread `` + *c.0 //~ ERROR: Data race detected between Read on thread `` and Write on thread `` } else { 0 } diff --git a/tests/fail/data_race/release_seq_race.rs b/tests/fail/data_race/release_seq_race.rs index ab6926102a..31fc5d9a47 100644 --- a/tests/fail/data_race/release_seq_race.rs +++ b/tests/fail/data_race/release_seq_race.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-disable-isolation -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-disable-isolation -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::{sleep, spawn}; @@ -42,7 +42,7 @@ pub fn main() { let j3 = spawn(move || { sleep(Duration::from_millis(500)); if SYNC.load(Ordering::Acquire) == 3 { - *c.0 //~ ERROR Data race detected between Read on thread `` and Write on thread `` + *c.0 //~ ERROR: Data race detected between Read on thread `` and Write on thread `` } else { 0 } diff --git a/tests/fail/data_race/release_seq_race_same_thread.rs b/tests/fail/data_race/release_seq_race_same_thread.rs index d3d18f0e25..c0ce437047 100644 --- a/tests/fail/data_race/release_seq_race_same_thread.rs +++ b/tests/fail/data_race/release_seq_race_same_thread.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-disable-isolation -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-disable-isolation -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; @@ -38,7 +38,7 @@ pub fn main() { let j2 = spawn(move || { if SYNC.load(Ordering::Acquire) == 2 { - *c.0 //~ ERROR Data race detected between Read on thread `` and Write on thread `` + *c.0 //~ ERROR: Data race detected between Read on thread `` and Write on thread `` } else { 0 } diff --git a/tests/fail/data_race/rmw_race.rs b/tests/fail/data_race/rmw_race.rs index 800b1043c0..540b01b093 100644 --- a/tests/fail/data_race/rmw_race.rs +++ b/tests/fail/data_race/rmw_race.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; @@ -39,7 +39,7 @@ pub fn main() { let j3 = spawn(move || { if SYNC.load(Ordering::Acquire) == 3 { - *c.0 //~ ERROR Data race detected between Read on thread `` and Write on thread `` + *c.0 //~ ERROR: Data race detected between Read on thread `` and Write on thread `` } else { 0 } diff --git a/tests/fail/data_race/stack_pop_race.rs b/tests/fail/data_race/stack_pop_race.rs new file mode 100644 index 0000000000..8f371a680f --- /dev/null +++ b/tests/fail/data_race/stack_pop_race.rs @@ -0,0 +1,25 @@ +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-preemption-rate=0 +use std::thread; + +#[derive(Copy, Clone)] +struct MakeSend(*const i32); +unsafe impl Send for MakeSend {} + +fn main() { + race(0); +} + +// Using an argument for the ptr to point to, since those do not get StorageDead. +fn race(local: i32) { + let ptr = MakeSend(&local as *const i32); + thread::spawn(move || { + let ptr = ptr; + let _val = unsafe { *ptr.0 }; + }); + // Make the other thread go first so that it does not UAF. + thread::yield_now(); + // Deallocating the local (when `main` returns) + // races with the read in the other thread. + // Make sure the error points at this function's end, not just the call site. +} //~ERROR: Data race detected between Deallocate on thread `main` and Read on thread `` diff --git a/tests/fail/data_race/stack_pop_race.stderr b/tests/fail/data_race/stack_pop_race.stderr new file mode 100644 index 0000000000..ba830753f6 --- /dev/null +++ b/tests/fail/data_race/stack_pop_race.stderr @@ -0,0 +1,20 @@ +error: Undefined Behavior: Data race detected between Deallocate on thread `main` and Read on thread `` at ALLOC + --> $DIR/stack_pop_race.rs:LL:CC + | +LL | } + | ^ Data race detected between Deallocate on thread `main` and Read on thread `` at ALLOC + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: backtrace: + = note: inside `race` at $DIR/stack_pop_race.rs:LL:CC +note: inside `main` at $DIR/stack_pop_race.rs:LL:CC + --> $DIR/stack_pop_race.rs:LL:CC + | +LL | race(0); + | ^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/data_race/write_write_race.rs b/tests/fail/data_race/write_write_race.rs index 989ae31a6d..ac75c771e4 100644 --- a/tests/fail/data_race/write_write_race.rs +++ b/tests/fail/data_race/write_write_race.rs @@ -1,4 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +// We want to control preemption here. +//@compile-flags: -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. use std::thread::spawn; @@ -18,7 +20,7 @@ pub fn main() { }); let j2 = spawn(move || { - *c.0 = 64; //~ ERROR Data race detected between Write on thread `` and Write on thread `` + *c.0 = 64; //~ ERROR: Data race detected between Write on thread `` and Write on thread `` }); j1.join().unwrap(); diff --git a/tests/fail/data_race/write_write_race_stack.rs b/tests/fail/data_race/write_write_race_stack.rs index 3c1eabbf25..5193b15551 100644 --- a/tests/fail/data_race/write_write_race_stack.rs +++ b/tests/fail/data_race/write_write_race_stack.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-disable-isolation -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-disable-isolation -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 use std::ptr::null_mut; use std::sync::atomic::{AtomicPtr, Ordering}; @@ -40,7 +40,7 @@ pub fn main() { sleep(Duration::from_millis(200)); - stack_var = 1usize; //~ ERROR Data race detected between Write on thread `` and Write on thread `` + stack_var = 1usize; //~ ERROR: Data race detected between Write on thread `` and Write on thread `` // read to silence errors stack_var diff --git a/tests/fail/environ-gets-deallocated.rs b/tests/fail/environ-gets-deallocated.rs index f50a66e2c7..e4597140b8 100644 --- a/tests/fail/environ-gets-deallocated.rs +++ b/tests/fail/environ-gets-deallocated.rs @@ -1,4 +1,4 @@ -// ignore-windows: Windows does not have a global environ list that the program can access directly +//@ignore-target-windows: Windows does not have a global environ list that the program can access directly #[cfg(any(target_os = "linux", target_os = "freebsd"))] fn get_environ() -> *const *const u8 { @@ -20,5 +20,5 @@ fn main() { let pointer = get_environ(); let _x = unsafe { *pointer }; std::env::set_var("FOO", "BAR"); - let _y = unsafe { *pointer }; //~ ERROR dereferenced after this allocation got freed + let _y = unsafe { *pointer }; //~ ERROR: dereferenced after this allocation got freed } diff --git a/tests/fail/erroneous_const.rs b/tests/fail/erroneous_const.rs index 8975694f51..c35a905035 100644 --- a/tests/fail/erroneous_const.rs +++ b/tests/fail/erroneous_const.rs @@ -1,18 +1,18 @@ //! Make sure we detect erroneous constants post-monomorphization even when they are unused. //! (https://github.com/rust-lang/miri/issues/1382) // Inlining changes the error location -// compile-flags: -Zmir-opt-level=0 +//@compile-flags: -Zmir-opt-level=0 #![feature(never_type)] #![warn(warnings, const_err)] struct PrintName(T); impl PrintName { - const VOID: ! = panic!(); //~ERROR evaluation of `PrintName::::VOID` failed + const VOID: ! = panic!(); //~ERROR: evaluation of `PrintName::::VOID` failed } fn no_codegen() { if false { - let _ = PrintName::::VOID; //~ERROR post-monomorphization error + let _ = PrintName::::VOID; //~ERROR: post-monomorphization error } } fn main() { diff --git a/tests/fail/erroneous_const2.rs b/tests/fail/erroneous_const2.rs index dc68cd64d7..6628166cfa 100644 --- a/tests/fail/erroneous_const2.rs +++ b/tests/fail/erroneous_const2.rs @@ -1,13 +1,13 @@ const X: u32 = 5; const Y: u32 = 6; const FOO: u32 = [X - Y, Y - X][(X < Y) as usize]; -//~^ERROR any use of this value -//~|WARN previously accepted +//~^ERROR: any use of this value +//~|WARN: previously accepted #[rustfmt::skip] // rustfmt bug: https://github.com/rust-lang/rustfmt/issues/5391 fn main() { - println!("{}", FOO); //~ERROR post-monomorphization error - //~|ERROR evaluation of constant value failed - //~|ERROR erroneous constant used - //~|WARN previously accepted + println!("{}", FOO); //~ERROR: post-monomorphization error + //~|ERROR: evaluation of constant value failed + //~|ERROR: erroneous constant used + //~|WARN: previously accepted } diff --git a/tests/fail/extern_static.rs b/tests/fail/extern_static.rs index f3466d5e71..f8805db8d1 100644 --- a/tests/fail/extern_static.rs +++ b/tests/fail/extern_static.rs @@ -5,5 +5,5 @@ extern "C" { } fn main() { - let _val = unsafe { std::ptr::addr_of!(FOO) }; //~ ERROR is not supported by Miri + let _val = unsafe { std::ptr::addr_of!(FOO) }; //~ ERROR: is not supported by Miri } diff --git a/tests/fail/extern_static_in_const.rs b/tests/fail/extern_static_in_const.rs index 4c1de6ace5..31c192bf44 100644 --- a/tests/fail/extern_static_in_const.rs +++ b/tests/fail/extern_static_in_const.rs @@ -7,5 +7,5 @@ extern "C" { static X: &'static [u8; 0] = unsafe { &E }; fn main() { - let _val = X; //~ ERROR is not supported by Miri + let _val = X; //~ ERROR: is not supported by Miri } diff --git a/tests/fail/external_C/function_not_in_SO.rs b/tests/fail/external_C/function_not_in_SO.rs new file mode 100644 index 0000000000..8e40502e0a --- /dev/null +++ b/tests/fail/external_C/function_not_in_SO.rs @@ -0,0 +1,13 @@ +//@only-target-linux +//@only-on-host +//@compile-flags: -Zmiri-external_c_so_file=tests/external_C/libtestlib.so + +extern "C" { + fn foo(); +} + +fn main() { + unsafe { + foo(); //~ ERROR: unsupported operation: can't call foreign function: foo; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile + } +} diff --git a/tests/fail/external_C/function_not_in_SO.stderr b/tests/fail/external_C/function_not_in_SO.stderr new file mode 100644 index 0000000000..53a06d5db5 --- /dev/null +++ b/tests/fail/external_C/function_not_in_SO.stderr @@ -0,0 +1,14 @@ +error: unsupported operation: can't call foreign function: foo; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile + --> $DIR/function_not_in_SO.rs:LL:CC + | +LL | foo(); + | ^^^^^ can't call foreign function: foo; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = note: backtrace: + = note: inside `main` at $DIR/function_not_in_SO.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/fast_math_both.rs b/tests/fail/fast_math_both.rs index 844e4e9521..dd2787bf40 100644 --- a/tests/fail/fast_math_both.rs +++ b/tests/fail/fast_math_both.rs @@ -2,6 +2,6 @@ fn main() { unsafe { - let _x: f32 = core::intrinsics::fsub_fast(f32::NAN, f32::NAN); //~ ERROR `fsub_fast` intrinsic called with non-finite value as both parameters + let _x: f32 = core::intrinsics::fsub_fast(f32::NAN, f32::NAN); //~ ERROR: `fsub_fast` intrinsic called with non-finite value as both parameters } } diff --git a/tests/fail/fast_math_first.rs b/tests/fail/fast_math_first.rs index 470ebe6200..e495498ab2 100644 --- a/tests/fail/fast_math_first.rs +++ b/tests/fail/fast_math_first.rs @@ -2,6 +2,6 @@ fn main() { unsafe { - let _x: f32 = core::intrinsics::frem_fast(f32::NAN, 3.2); //~ ERROR `frem_fast` intrinsic called with non-finite value as first parameter + let _x: f32 = core::intrinsics::frem_fast(f32::NAN, 3.2); //~ ERROR: `frem_fast` intrinsic called with non-finite value as first parameter } } diff --git a/tests/fail/fast_math_second.rs b/tests/fail/fast_math_second.rs index e8d70a4a79..408c461077 100644 --- a/tests/fail/fast_math_second.rs +++ b/tests/fail/fast_math_second.rs @@ -2,6 +2,6 @@ fn main() { unsafe { - let _x: f32 = core::intrinsics::fmul_fast(3.4f32, f32::INFINITY); //~ ERROR `fmul_fast` intrinsic called with non-finite value as second parameter + let _x: f32 = core::intrinsics::fmul_fast(3.4f32, f32::INFINITY); //~ ERROR: `fmul_fast` intrinsic called with non-finite value as second parameter } } diff --git a/tests/fail/fs/close_stdout.rs b/tests/fail/fs/close_stdout.rs index 4f10d5e0c9..bc709fe36d 100644 --- a/tests/fail/fs/close_stdout.rs +++ b/tests/fail/fs/close_stdout.rs @@ -1,5 +1,5 @@ -// ignore-windows: No libc on Windows -// compile-flags: -Zmiri-disable-isolation +//@ignore-target-windows: No libc on Windows +//@compile-flags: -Zmiri-disable-isolation // FIXME: standard handles cannot be closed (https://github.com/rust-lang/rust/issues/40032) @@ -9,6 +9,6 @@ extern crate libc; fn main() { unsafe { - libc::close(1); //~ ERROR stdout cannot be closed + libc::close(1); //~ ERROR: stdout cannot be closed } } diff --git a/tests/fail/fs/isolated_file.rs b/tests/fail/fs/isolated_file.rs index 5b7270f189..9b664ffe52 100644 --- a/tests/fail/fs/isolated_file.rs +++ b/tests/fail/fs/isolated_file.rs @@ -1,5 +1,5 @@ -// ignore-windows: File handling is not implemented yet -// error-pattern: `open` not available when isolation is enabled +//@ignore-target-windows: File handling is not implemented yet +//@error-pattern: `open` not available when isolation is enabled fn main() { let _file = std::fs::File::open("file.txt").unwrap(); diff --git a/tests/fail/fs/isolated_stdin.rs b/tests/fail/fs/isolated_stdin.rs index 4098a10476..cd54de3bce 100644 --- a/tests/fail/fs/isolated_stdin.rs +++ b/tests/fail/fs/isolated_stdin.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] @@ -7,7 +7,7 @@ extern crate libc; fn main() -> std::io::Result<()> { let mut bytes = [0u8; 512]; unsafe { - libc::read(0, bytes.as_mut_ptr() as *mut libc::c_void, 512); //~ ERROR `read` from stdin not available when isolation is enabled + libc::read(0, bytes.as_mut_ptr() as *mut libc::c_void, 512); //~ ERROR: `read` from stdin not available when isolation is enabled } Ok(()) } diff --git a/tests/fail/fs/read_from_stdout.rs b/tests/fail/fs/read_from_stdout.rs index 17f1735f6a..949fe88f43 100644 --- a/tests/fail/fs/read_from_stdout.rs +++ b/tests/fail/fs/read_from_stdout.rs @@ -1,5 +1,5 @@ -// compile-flags: -Zmiri-disable-isolation -// ignore-windows: No libc on Windows +//@compile-flags: -Zmiri-disable-isolation +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] @@ -8,7 +8,7 @@ extern crate libc; fn main() -> std::io::Result<()> { let mut bytes = [0u8; 512]; unsafe { - libc::read(1, bytes.as_mut_ptr() as *mut libc::c_void, 512); //~ ERROR cannot read from stdout + libc::read(1, bytes.as_mut_ptr() as *mut libc::c_void, 512); //~ ERROR: cannot read from stdout } Ok(()) } diff --git a/tests/fail/fs/unix_open_missing_required_mode.rs b/tests/fail/fs/unix_open_missing_required_mode.rs index bd2ae6094b..1f6beadcb8 100644 --- a/tests/fail/fs/unix_open_missing_required_mode.rs +++ b/tests/fail/fs/unix_open_missing_required_mode.rs @@ -1,5 +1,5 @@ -// ignore-windows: No libc on Windows -// compile-flags: -Zmiri-disable-isolation +//@ignore-target-windows: No libc on Windows +//@compile-flags: -Zmiri-disable-isolation #![feature(rustc_private)] @@ -12,5 +12,5 @@ fn main() { fn test_file_open_missing_needed_mode() { let name = b"missing_arg.txt\0"; let name_ptr = name.as_ptr().cast::(); - let _fd = unsafe { libc::open(name_ptr, libc::O_CREAT) }; //~ ERROR Undefined Behavior: incorrect number of arguments for `open` with `O_CREAT`: got 2, expected at least 3 + let _fd = unsafe { libc::open(name_ptr, libc::O_CREAT) }; //~ ERROR: Undefined Behavior: incorrect number of arguments for `open` with `O_CREAT`: got 2, expected at least 3 } diff --git a/tests/fail/fs/write_to_stdin.rs b/tests/fail/fs/write_to_stdin.rs index c2754636c8..4ad7b648e1 100644 --- a/tests/fail/fs/write_to_stdin.rs +++ b/tests/fail/fs/write_to_stdin.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] @@ -7,7 +7,7 @@ extern crate libc; fn main() -> std::io::Result<()> { let bytes = b"hello"; unsafe { - libc::write(0, bytes.as_ptr() as *const libc::c_void, 5); //~ ERROR cannot write to stdin + libc::write(0, bytes.as_ptr() as *const libc::c_void, 5); //~ ERROR: cannot write to stdin } Ok(()) } diff --git a/tests/fail/function_calls/check_arg_abi.rs b/tests/fail/function_calls/check_arg_abi.rs index 5656c7a0e4..ffa0443ce5 100644 --- a/tests/fail/function_calls/check_arg_abi.rs +++ b/tests/fail/function_calls/check_arg_abi.rs @@ -4,6 +4,6 @@ fn main() { } unsafe { - let _ = malloc(0); //~ ERROR calling a function with ABI C using caller ABI Rust + let _ = malloc(0); //~ ERROR: calling a function with ABI C using caller ABI Rust }; } diff --git a/tests/fail/function_calls/check_arg_count_abort.rs b/tests/fail/function_calls/check_arg_count_abort.rs index 85e1b9deeb..967a78bf83 100644 --- a/tests/fail/function_calls/check_arg_count_abort.rs +++ b/tests/fail/function_calls/check_arg_count_abort.rs @@ -5,6 +5,6 @@ fn main() { unsafe { abort(1); - //~^ ERROR Undefined Behavior: incorrect number of arguments: got 1, expected 0 + //~^ ERROR: Undefined Behavior: incorrect number of arguments: got 1, expected 0 } } diff --git a/tests/fail/function_calls/check_arg_count_too_few_args.rs b/tests/fail/function_calls/check_arg_count_too_few_args.rs index e1cea99eb9..223c95ffca 100644 --- a/tests/fail/function_calls/check_arg_count_too_few_args.rs +++ b/tests/fail/function_calls/check_arg_count_too_few_args.rs @@ -4,6 +4,6 @@ fn main() { } unsafe { - let _ = malloc(); //~ ERROR Undefined Behavior: incorrect number of arguments: got 0, expected 1 + let _ = malloc(); //~ ERROR: Undefined Behavior: incorrect number of arguments: got 0, expected 1 }; } diff --git a/tests/fail/function_calls/check_arg_count_too_many_args.rs b/tests/fail/function_calls/check_arg_count_too_many_args.rs index c4028b940f..7ee9c40bf7 100644 --- a/tests/fail/function_calls/check_arg_count_too_many_args.rs +++ b/tests/fail/function_calls/check_arg_count_too_many_args.rs @@ -4,6 +4,6 @@ fn main() { } unsafe { - let _ = malloc(1, 2); //~ ERROR Undefined Behavior: incorrect number of arguments: got 2, expected 1 + let _ = malloc(1, 2); //~ ERROR: Undefined Behavior: incorrect number of arguments: got 2, expected 1 }; } diff --git a/tests/fail/function_calls/check_callback_abi.rs b/tests/fail/function_calls/check_callback_abi.rs index 0a5a2d48d2..883d5ae809 100644 --- a/tests/fail/function_calls/check_callback_abi.rs +++ b/tests/fail/function_calls/check_callback_abi.rs @@ -9,7 +9,7 @@ fn main() { // Make sure we check the ABI when Miri itself invokes a function // as part of a shim implementation. std::intrinsics::r#try( - //~^ ERROR calling a function with ABI C using caller ABI Rust + //~^ ERROR: calling a function with ABI C using caller ABI Rust std::mem::transmute::(try_fn), std::ptr::null_mut(), |_, _| unreachable!(), diff --git a/tests/fail/function_calls/exported_symbol_abi_mismatch.rs b/tests/fail/function_calls/exported_symbol_abi_mismatch.rs index c337e1f29f..dbf72b5b61 100644 --- a/tests/fail/function_calls/exported_symbol_abi_mismatch.rs +++ b/tests/fail/function_calls/exported_symbol_abi_mismatch.rs @@ -1,4 +1,4 @@ -// revisions: no_cache cache fn_ptr +//@revisions: no_cache cache fn_ptr #[no_mangle] fn foo() {} @@ -12,7 +12,7 @@ fn main() { #[cfg(fn_ptr)] unsafe { std::mem::transmute::(foo)(); - //[fn_ptr]~^ ERROR calling a function with calling convention Rust using calling convention C + //[fn_ptr]~^ ERROR: calling a function with calling convention Rust using calling convention C } // `Instance` caching should not suppress ABI check. @@ -28,8 +28,8 @@ fn main() { } unsafe { foo(); - //[no_cache]~^ ERROR calling a function with calling convention Rust using calling convention C - //[cache]~| ERROR calling a function with calling convention Rust using calling convention C + //[no_cache]~^ ERROR: calling a function with calling convention Rust using calling convention C + //[cache]~| ERROR: calling a function with calling convention Rust using calling convention C } } } diff --git a/tests/fail/function_calls/exported_symbol_bad_unwind1.rs b/tests/fail/function_calls/exported_symbol_bad_unwind1.rs index 91b0e8fc03..5f4df7c6a1 100644 --- a/tests/fail/function_calls/exported_symbol_bad_unwind1.rs +++ b/tests/fail/function_calls/exported_symbol_bad_unwind1.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-disable-abi-check +//@compile-flags: -Zmiri-disable-abi-check #![feature(c_unwind)] #[no_mangle] @@ -11,5 +11,5 @@ fn main() { fn unwind(); } unsafe { unwind() } - //~^ ERROR unwinding past a stack frame that does not allow unwinding + //~^ ERROR: unwinding past a stack frame that does not allow unwinding } diff --git a/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr b/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr index 48df075ba6..7d9302e3e3 100644 --- a/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr +++ b/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr @@ -4,8 +4,8 @@ error: abnormal termination: the program aborted execution --> $DIR/exported_symbol_bad_unwind2.rs:LL:CC | LL | / extern "C-unwind" fn nounwind() { -LL | | //[definition]~^ ERROR abnormal termination: the program aborted execution -LL | | //[both]~^^ ERROR abnormal termination: the program aborted execution +LL | | +LL | | LL | | panic!(); LL | | } | |_^ the program aborted execution diff --git a/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr b/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr index 48df075ba6..7d9302e3e3 100644 --- a/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr +++ b/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr @@ -4,8 +4,8 @@ error: abnormal termination: the program aborted execution --> $DIR/exported_symbol_bad_unwind2.rs:LL:CC | LL | / extern "C-unwind" fn nounwind() { -LL | | //[definition]~^ ERROR abnormal termination: the program aborted execution -LL | | //[both]~^^ ERROR abnormal termination: the program aborted execution +LL | | +LL | | LL | | panic!(); LL | | } | |_^ the program aborted execution diff --git a/tests/fail/function_calls/exported_symbol_bad_unwind2.rs b/tests/fail/function_calls/exported_symbol_bad_unwind2.rs index e80a79d102..d9aacdb8ae 100644 --- a/tests/fail/function_calls/exported_symbol_bad_unwind2.rs +++ b/tests/fail/function_calls/exported_symbol_bad_unwind2.rs @@ -1,11 +1,11 @@ -// revisions: extern_block definition both +//@revisions: extern_block definition both #![feature(rustc_attrs, c_unwind)] #[cfg_attr(any(definition, both), rustc_allocator_nounwind)] #[no_mangle] extern "C-unwind" fn nounwind() { - //[definition]~^ ERROR abnormal termination: the program aborted execution - //[both]~^^ ERROR abnormal termination: the program aborted execution + //[definition]~^ ERROR: abnormal termination: the program aborted execution + //[both]~^^ ERROR: abnormal termination: the program aborted execution panic!(); } @@ -15,5 +15,5 @@ fn main() { fn nounwind(); } unsafe { nounwind() } - //[extern_block]~^ ERROR unwinding past a stack frame that does not allow unwinding + //[extern_block]~^ ERROR: unwinding past a stack frame that does not allow unwinding } diff --git a/tests/fail/function_calls/exported_symbol_clashing.rs b/tests/fail/function_calls/exported_symbol_clashing.rs index 0e64778d89..45ad412e7a 100644 --- a/tests/fail/function_calls/exported_symbol_clashing.rs +++ b/tests/fail/function_calls/exported_symbol_clashing.rs @@ -1,15 +1,15 @@ #[no_mangle] fn foo() {} -//~^ HELP it's first defined here, in crate `exported_symbol_clashing` +//~^ HELP: it's first defined here, in crate `exported_symbol_clashing` #[export_name = "foo"] fn bar() {} -//~^ HELP then it's defined here again, in crate `exported_symbol_clashing` +//~^ HELP: then it's defined here again, in crate `exported_symbol_clashing` fn main() { extern "Rust" { fn foo(); } unsafe { foo() } - //~^ ERROR multiple definitions of symbol `foo` + //~^ ERROR: multiple definitions of symbol `foo` } diff --git a/tests/fail/function_calls/exported_symbol_shim_clashing.rs b/tests/fail/function_calls/exported_symbol_shim_clashing.rs index c46d57cee0..dffae7adbb 100644 --- a/tests/fail/function_calls/exported_symbol_shim_clashing.rs +++ b/tests/fail/function_calls/exported_symbol_shim_clashing.rs @@ -1,6 +1,6 @@ #[no_mangle] extern "C" fn malloc(_: usize) -> *mut std::ffi::c_void { - //~^ HELP the `malloc` symbol is defined here + //~^ HELP: the `malloc` symbol is defined here unreachable!() } @@ -10,6 +10,6 @@ fn main() { } unsafe { malloc(0); - //~^ ERROR found `malloc` symbol definition that clashes with a built-in shim + //~^ ERROR: found `malloc` symbol definition that clashes with a built-in shim } } diff --git a/tests/fail/function_calls/exported_symbol_wrong_arguments.rs b/tests/fail/function_calls/exported_symbol_wrong_arguments.rs index 8fb364bb9b..a108944c5e 100644 --- a/tests/fail/function_calls/exported_symbol_wrong_arguments.rs +++ b/tests/fail/function_calls/exported_symbol_wrong_arguments.rs @@ -5,5 +5,5 @@ fn main() { extern "Rust" { fn foo(_: i32); } - unsafe { foo(1) } //~ ERROR calling a function with more arguments than it expected + unsafe { foo(1) } //~ ERROR: calling a function with more arguments than it expected } diff --git a/tests/fail/function_calls/exported_symbol_wrong_type.rs b/tests/fail/function_calls/exported_symbol_wrong_type.rs index 3ffd506c94..e273e35433 100644 --- a/tests/fail/function_calls/exported_symbol_wrong_type.rs +++ b/tests/fail/function_calls/exported_symbol_wrong_type.rs @@ -5,5 +5,5 @@ fn main() { extern "C" { fn FOO(); } - unsafe { FOO() } //~ ERROR attempt to call an exported symbol that is not defined as a function + unsafe { FOO() } //~ ERROR: attempt to call an exported symbol that is not defined as a function } diff --git a/tests/fail/function_pointers/cast_box_int_to_fn_ptr.rs b/tests/fail/function_pointers/cast_box_int_to_fn_ptr.rs index f7640cadcb..9815569b60 100644 --- a/tests/fail/function_pointers/cast_box_int_to_fn_ptr.rs +++ b/tests/fail/function_pointers/cast_box_int_to_fn_ptr.rs @@ -1,9 +1,9 @@ // Validation makes this fail in the wrong place -// compile-flags: -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-validation fn main() { let b = Box::new(42); let g = unsafe { std::mem::transmute::<&Box, &fn(i32)>(&b) }; - (*g)(42) //~ ERROR it does not point to a function + (*g)(42) //~ ERROR: it does not point to a function } diff --git a/tests/fail/function_pointers/cast_fn_ptr1.rs b/tests/fail/function_pointers/cast_fn_ptr1.rs index e4463210dd..c0e96a43cc 100644 --- a/tests/fail/function_pointers/cast_fn_ptr1.rs +++ b/tests/fail/function_pointers/cast_fn_ptr1.rs @@ -3,5 +3,5 @@ fn main() { let g = unsafe { std::mem::transmute::(f) }; - g(42) //~ ERROR calling a function with more arguments than it expected + g(42) //~ ERROR: calling a function with more arguments than it expected } diff --git a/tests/fail/function_pointers/cast_fn_ptr2.rs b/tests/fail/function_pointers/cast_fn_ptr2.rs index 5d3222548a..20384f0965 100644 --- a/tests/fail/function_pointers/cast_fn_ptr2.rs +++ b/tests/fail/function_pointers/cast_fn_ptr2.rs @@ -3,5 +3,5 @@ fn main() { let g = unsafe { std::mem::transmute::(f) }; - g(42) //~ ERROR calling a function with argument of type (i32, i32) passing data of type i32 + g(42) //~ ERROR: calling a function with argument of type (i32, i32) passing data of type i32 } diff --git a/tests/fail/function_pointers/cast_fn_ptr3.rs b/tests/fail/function_pointers/cast_fn_ptr3.rs index 943175c347..920fb51abb 100644 --- a/tests/fail/function_pointers/cast_fn_ptr3.rs +++ b/tests/fail/function_pointers/cast_fn_ptr3.rs @@ -3,5 +3,5 @@ fn main() { let g = unsafe { std::mem::transmute::(f) }; - g() //~ ERROR calling a function with fewer arguments than it requires + g() //~ ERROR: calling a function with fewer arguments than it requires } diff --git a/tests/fail/function_pointers/cast_fn_ptr4.rs b/tests/fail/function_pointers/cast_fn_ptr4.rs index 238b09b162..f0ea5ccfe0 100644 --- a/tests/fail/function_pointers/cast_fn_ptr4.rs +++ b/tests/fail/function_pointers/cast_fn_ptr4.rs @@ -3,5 +3,5 @@ fn main() { let g = unsafe { std::mem::transmute::(f) }; - g(&42 as *const i32) //~ ERROR calling a function with argument of type *const [i32] passing data of type *const i32 + g(&42 as *const i32) //~ ERROR: calling a function with argument of type *const [i32] passing data of type *const i32 } diff --git a/tests/fail/function_pointers/cast_fn_ptr5.rs b/tests/fail/function_pointers/cast_fn_ptr5.rs index effbd6db18..0fdab49b94 100644 --- a/tests/fail/function_pointers/cast_fn_ptr5.rs +++ b/tests/fail/function_pointers/cast_fn_ptr5.rs @@ -5,5 +5,5 @@ fn main() { let g = unsafe { std::mem::transmute:: u32, fn()>(f) }; - g() //~ ERROR calling a function with return type u32 passing return place of type () + g() //~ ERROR: calling a function with return type u32 passing return place of type () } diff --git a/tests/fail/function_pointers/cast_int_to_fn_ptr.rs b/tests/fail/function_pointers/cast_int_to_fn_ptr.rs index e287533ffc..dbf8a560fb 100644 --- a/tests/fail/function_pointers/cast_int_to_fn_ptr.rs +++ b/tests/fail/function_pointers/cast_int_to_fn_ptr.rs @@ -1,8 +1,8 @@ // Validation makes this fail in the wrong place -// compile-flags: -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-validation fn main() { let g = unsafe { std::mem::transmute::(42) }; - g(42) //~ ERROR is a dangling pointer + g(42) //~ ERROR: is a dangling pointer } diff --git a/tests/fail/function_pointers/deref_fn_ptr.rs b/tests/fail/function_pointers/deref_fn_ptr.rs index f22f73487b..f071b63902 100644 --- a/tests/fail/function_pointers/deref_fn_ptr.rs +++ b/tests/fail/function_pointers/deref_fn_ptr.rs @@ -2,7 +2,7 @@ fn f() {} fn main() { let x: u8 = unsafe { - *std::mem::transmute::(f) //~ ERROR out-of-bounds + *std::mem::transmute::(f) //~ ERROR: out-of-bounds }; panic!("this should never print: {}", x); } diff --git a/tests/fail/function_pointers/execute_memory.rs b/tests/fail/function_pointers/execute_memory.rs index 2e6b58a753..967933e769 100644 --- a/tests/fail/function_pointers/execute_memory.rs +++ b/tests/fail/function_pointers/execute_memory.rs @@ -1,5 +1,5 @@ // Validation makes this fail in the wrong place -// compile-flags: -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-validation #![feature(box_syntax)] @@ -7,6 +7,6 @@ fn main() { let x = box 42; unsafe { let f = std::mem::transmute::, fn()>(x); - f() //~ ERROR function pointer but it does not point to a function + f() //~ ERROR: function pointer but it does not point to a function } } diff --git a/tests/fail/function_pointers/fn_ptr_offset.rs b/tests/fail/function_pointers/fn_ptr_offset.rs index 04c54c0159..eba0953ac8 100644 --- a/tests/fail/function_pointers/fn_ptr_offset.rs +++ b/tests/fail/function_pointers/fn_ptr_offset.rs @@ -1,5 +1,5 @@ // Validation makes this fail in the wrong place -// compile-flags: -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-validation use std::mem; @@ -10,5 +10,5 @@ fn main() { let y: *mut u8 = unsafe { mem::transmute(x) }; let y = y.wrapping_offset(1); let x: fn() = unsafe { mem::transmute(y) }; - x(); //~ ERROR function pointer but it does not point to a function + x(); //~ ERROR: function pointer but it does not point to a function } diff --git a/tests/fail/generator-pinned-moved.rs b/tests/fail/generator-pinned-moved.rs index 8c8e828470..240ae18cc4 100644 --- a/tests/fail/generator-pinned-moved.rs +++ b/tests/fail/generator-pinned-moved.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-validation #![feature(generators, generator_trait)] use std::{ @@ -12,7 +12,7 @@ fn firstn() -> impl Generator { let num = &mut num; yield *num; - *num += 1; //~ ERROR dereferenced after this allocation got freed + *num += 1; //~ ERROR: dereferenced after this allocation got freed } } diff --git a/tests/fail/intrinsics/assume.rs b/tests/fail/intrinsics/assume.rs index ad193d8499..be06d0a7a5 100644 --- a/tests/fail/intrinsics/assume.rs +++ b/tests/fail/intrinsics/assume.rs @@ -5,6 +5,6 @@ fn main() { unsafe { std::intrinsics::assume(x < 10); std::intrinsics::assume(x > 1); - std::intrinsics::assume(x > 42); //~ ERROR `assume` intrinsic called with `false` + std::intrinsics::assume(x > 42); //~ ERROR: `assume` intrinsic called with `false` } } diff --git a/tests/fail/intrinsics/copy_overflow.rs b/tests/fail/intrinsics/copy_overflow.rs index b09aedcf74..c4ce192c44 100644 --- a/tests/fail/intrinsics/copy_overflow.rs +++ b/tests/fail/intrinsics/copy_overflow.rs @@ -1,4 +1,4 @@ -// error-pattern: overflow computing total size +//@error-pattern: overflow computing total size use std::mem; fn main() { diff --git a/tests/fail/intrinsics/copy_overlapping.rs b/tests/fail/intrinsics/copy_overlapping.rs index 8d3c681393..3df881bd43 100644 --- a/tests/fail/intrinsics/copy_overlapping.rs +++ b/tests/fail/intrinsics/copy_overlapping.rs @@ -10,6 +10,6 @@ fn main() { unsafe { let a = data.as_mut_ptr(); let b = a.wrapping_offset(1) as *mut _; - copy_nonoverlapping(a, b, 2); //~ ERROR copy_nonoverlapping called on overlapping ranges + copy_nonoverlapping(a, b, 2); //~ ERROR: copy_nonoverlapping called on overlapping ranges } } diff --git a/tests/fail/intrinsics/copy_unaligned.rs b/tests/fail/intrinsics/copy_unaligned.rs index 162f06bfac..281217f06f 100644 --- a/tests/fail/intrinsics/copy_unaligned.rs +++ b/tests/fail/intrinsics/copy_unaligned.rs @@ -10,6 +10,6 @@ fn main() { let ptr = (&mut data[0] as *mut u16 as *mut u8).wrapping_add(1) as *mut u16; // Even copying 0 elements to something unaligned should error unsafe { - copy_nonoverlapping(&data[5], ptr, 0); //~ ERROR accessing memory with alignment 1, but alignment 2 is required + copy_nonoverlapping(&data[5], ptr, 0); //~ ERROR: accessing memory with alignment 1, but alignment 2 is required } } diff --git a/tests/fail/intrinsics/ctlz_nonzero.rs b/tests/fail/intrinsics/ctlz_nonzero.rs index e82ed89da1..c26cd4cadb 100644 --- a/tests/fail/intrinsics/ctlz_nonzero.rs +++ b/tests/fail/intrinsics/ctlz_nonzero.rs @@ -10,6 +10,6 @@ pub fn main() { unsafe { use crate::rusti::*; - ctlz_nonzero(0u8); //~ ERROR `ctlz_nonzero` called on 0 + ctlz_nonzero(0u8); //~ ERROR: `ctlz_nonzero` called on 0 } } diff --git a/tests/fail/intrinsics/cttz_nonzero.rs b/tests/fail/intrinsics/cttz_nonzero.rs index 205b552081..25a0501fdd 100644 --- a/tests/fail/intrinsics/cttz_nonzero.rs +++ b/tests/fail/intrinsics/cttz_nonzero.rs @@ -10,6 +10,6 @@ pub fn main() { unsafe { use crate::rusti::*; - cttz_nonzero(0u8); //~ ERROR `cttz_nonzero` called on 0 + cttz_nonzero(0u8); //~ ERROR: `cttz_nonzero` called on 0 } } diff --git a/tests/fail/intrinsics/div-by-zero.rs b/tests/fail/intrinsics/div-by-zero.rs index d67d06dc1e..78c05c543a 100644 --- a/tests/fail/intrinsics/div-by-zero.rs +++ b/tests/fail/intrinsics/div-by-zero.rs @@ -4,6 +4,6 @@ use std::intrinsics::*; fn main() { unsafe { - let _n = unchecked_div(1i64, 0); //~ERROR dividing by zero + let _n = unchecked_div(1i64, 0); //~ERROR: dividing by zero } } diff --git a/tests/fail/intrinsics/exact_div1.rs b/tests/fail/intrinsics/exact_div1.rs index f926424a4b..3dda9d1090 100644 --- a/tests/fail/intrinsics/exact_div1.rs +++ b/tests/fail/intrinsics/exact_div1.rs @@ -1,5 +1,5 @@ #![feature(core_intrinsics)] fn main() { // divison by 0 - unsafe { std::intrinsics::exact_div(2, 0) }; //~ ERROR divisor of zero + unsafe { std::intrinsics::exact_div(2, 0) }; //~ ERROR: divisor of zero } diff --git a/tests/fail/intrinsics/exact_div2.rs b/tests/fail/intrinsics/exact_div2.rs index fc252aa798..00064fa0b9 100644 --- a/tests/fail/intrinsics/exact_div2.rs +++ b/tests/fail/intrinsics/exact_div2.rs @@ -1,5 +1,5 @@ #![feature(core_intrinsics)] fn main() { // divison with a remainder - unsafe { std::intrinsics::exact_div(2u16, 3) }; //~ ERROR 2_u16 cannot be divided by 3_u16 without remainder + unsafe { std::intrinsics::exact_div(2u16, 3) }; //~ ERROR: 2_u16 cannot be divided by 3_u16 without remainder } diff --git a/tests/fail/intrinsics/exact_div3.rs b/tests/fail/intrinsics/exact_div3.rs index 4d2511adc1..a61abcd137 100644 --- a/tests/fail/intrinsics/exact_div3.rs +++ b/tests/fail/intrinsics/exact_div3.rs @@ -1,5 +1,5 @@ #![feature(core_intrinsics)] fn main() { // signed divison with a remainder - unsafe { std::intrinsics::exact_div(-19i8, 2) }; //~ ERROR -19_i8 cannot be divided by 2_i8 without remainder + unsafe { std::intrinsics::exact_div(-19i8, 2) }; //~ ERROR: -19_i8 cannot be divided by 2_i8 without remainder } diff --git a/tests/fail/intrinsics/exact_div4.rs b/tests/fail/intrinsics/exact_div4.rs index df6433d1cb..b5b60190b4 100644 --- a/tests/fail/intrinsics/exact_div4.rs +++ b/tests/fail/intrinsics/exact_div4.rs @@ -1,5 +1,5 @@ #![feature(core_intrinsics)] fn main() { // divison of MIN by -1 - unsafe { std::intrinsics::exact_div(i64::MIN, -1) }; //~ ERROR overflow in signed remainder (dividing MIN by -1) + unsafe { std::intrinsics::exact_div(i64::MIN, -1) }; //~ ERROR: overflow in signed remainder (dividing MIN by -1) } diff --git a/tests/fail/intrinsics/out_of_bounds_ptr_1.rs b/tests/fail/intrinsics/out_of_bounds_ptr_1.rs index d200b3eb02..0109bff696 100644 --- a/tests/fail/intrinsics/out_of_bounds_ptr_1.rs +++ b/tests/fail/intrinsics/out_of_bounds_ptr_1.rs @@ -1,4 +1,4 @@ -// error-pattern: pointer to 5 bytes starting at offset 0 is out-of-bounds +//@error-pattern: pointer to 5 bytes starting at offset 0 is out-of-bounds fn main() { let v = [0i8; 4]; let x = &v as *const i8; diff --git a/tests/fail/intrinsics/out_of_bounds_ptr_2.rs b/tests/fail/intrinsics/out_of_bounds_ptr_2.rs index 52b385b8e3..74d391dc21 100644 --- a/tests/fail/intrinsics/out_of_bounds_ptr_2.rs +++ b/tests/fail/intrinsics/out_of_bounds_ptr_2.rs @@ -1,4 +1,4 @@ -// error-pattern: overflowing in-bounds pointer arithmetic +//@error-pattern: overflowing in-bounds pointer arithmetic fn main() { let v = [0i8; 4]; let x = &v as *const i8; diff --git a/tests/fail/intrinsics/out_of_bounds_ptr_3.rs b/tests/fail/intrinsics/out_of_bounds_ptr_3.rs index cd0861efe5..b54cf3dd25 100644 --- a/tests/fail/intrinsics/out_of_bounds_ptr_3.rs +++ b/tests/fail/intrinsics/out_of_bounds_ptr_3.rs @@ -1,4 +1,4 @@ -// error-pattern: pointer to 1 byte starting at offset -1 is out-of-bounds +//@error-pattern: pointer to 1 byte starting at offset -1 is out-of-bounds fn main() { let v = [0i8; 4]; let x = &v as *const i8; diff --git a/tests/fail/intrinsics/ptr_offset_0_plus_0.rs b/tests/fail/intrinsics/ptr_offset_0_plus_0.rs index 4098d6b0ce..32974a825a 100644 --- a/tests/fail/intrinsics/ptr_offset_0_plus_0.rs +++ b/tests/fail/intrinsics/ptr_offset_0_plus_0.rs @@ -1,5 +1,5 @@ -// error-pattern: null pointer is a dangling pointer -// compile-flags: -Zmiri-permissive-provenance +//@error-pattern: null pointer is a dangling pointer +//@compile-flags: -Zmiri-permissive-provenance fn main() { let x = 0 as *mut i32; diff --git a/tests/fail/intrinsics/ptr_offset_from_oob.rs b/tests/fail/intrinsics/ptr_offset_from_oob.rs index ef1ca1e272..1fd5100a97 100644 --- a/tests/fail/intrinsics/ptr_offset_from_oob.rs +++ b/tests/fail/intrinsics/ptr_offset_from_oob.rs @@ -7,5 +7,5 @@ fn main() { let length = 10; let end_ptr = start_ptr.wrapping_add(length); // Even if the offset is 0, a dangling OOB pointer is not allowed. - unsafe { ptr_offset_from(end_ptr, end_ptr) }; //~ERROR pointer at offset 10 is out-of-bounds + unsafe { ptr_offset_from(end_ptr, end_ptr) }; //~ERROR: pointer at offset 10 is out-of-bounds } diff --git a/tests/fail/intrinsics/ptr_offset_int_plus_int.rs b/tests/fail/intrinsics/ptr_offset_int_plus_int.rs index 817a8b9801..f51e00746e 100644 --- a/tests/fail/intrinsics/ptr_offset_int_plus_int.rs +++ b/tests/fail/intrinsics/ptr_offset_int_plus_int.rs @@ -1,5 +1,5 @@ -// error-pattern: is a dangling pointer -// compile-flags: -Zmiri-permissive-provenance +//@error-pattern: is a dangling pointer +//@compile-flags: -Zmiri-permissive-provenance fn main() { // Can't offset an integer pointer by non-zero offset. diff --git a/tests/fail/intrinsics/ptr_offset_int_plus_ptr.rs b/tests/fail/intrinsics/ptr_offset_int_plus_ptr.rs index ed6370bf7f..8fc4d7fe73 100644 --- a/tests/fail/intrinsics/ptr_offset_int_plus_ptr.rs +++ b/tests/fail/intrinsics/ptr_offset_int_plus_ptr.rs @@ -1,5 +1,5 @@ -// error-pattern: is a dangling pointer -// compile-flags: -Zmiri-permissive-provenance +//@error-pattern: is a dangling pointer +//@compile-flags: -Zmiri-permissive-provenance fn main() { let ptr = Box::into_raw(Box::new(0u32)); diff --git a/tests/fail/intrinsics/ptr_offset_overflow.rs b/tests/fail/intrinsics/ptr_offset_overflow.rs index 734547b601..829cf90c85 100644 --- a/tests/fail/intrinsics/ptr_offset_overflow.rs +++ b/tests/fail/intrinsics/ptr_offset_overflow.rs @@ -1,4 +1,4 @@ -// error-pattern: overflowing in-bounds pointer arithmetic +//@error-pattern: overflowing in-bounds pointer arithmetic fn main() { let v = [1i8, 2]; let x = &v[1] as *const i8; diff --git a/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs b/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs index 8760345409..f8ee7d0b50 100644 --- a/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs +++ b/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs @@ -1,4 +1,4 @@ -// error-pattern: pointer at offset 32 is out-of-bounds +//@error-pattern: pointer at offset 32 is out-of-bounds fn main() { let x = Box::into_raw(Box::new(0u32)); diff --git a/tests/fail/intrinsics/rem-by-zero.rs b/tests/fail/intrinsics/rem-by-zero.rs index e904049e3b..ac80852e8d 100644 --- a/tests/fail/intrinsics/rem-by-zero.rs +++ b/tests/fail/intrinsics/rem-by-zero.rs @@ -4,6 +4,6 @@ use std::intrinsics::*; fn main() { unsafe { - let _n = unchecked_rem(3u32, 0); //~ ERROR calculating the remainder with a divisor of zero + let _n = unchecked_rem(3u32, 0); //~ ERROR: calculating the remainder with a divisor of zero } } diff --git a/tests/fail/intrinsics/simd-div-by-zero.rs b/tests/fail/intrinsics/simd-div-by-zero.rs index 6fdcb875ac..5fa6f69d00 100644 --- a/tests/fail/intrinsics/simd-div-by-zero.rs +++ b/tests/fail/intrinsics/simd-div-by-zero.rs @@ -12,6 +12,6 @@ fn main() { unsafe { let x = i32x2(1, 1); let y = i32x2(1, 0); - simd_div(x, y); //~ERROR Undefined Behavior: dividing by zero + simd_div(x, y); //~ERROR: Undefined Behavior: dividing by zero } } diff --git a/tests/fail/intrinsics/simd-div-overflow.rs b/tests/fail/intrinsics/simd-div-overflow.rs index 6d52a72e4c..57712b1b83 100644 --- a/tests/fail/intrinsics/simd-div-overflow.rs +++ b/tests/fail/intrinsics/simd-div-overflow.rs @@ -12,6 +12,6 @@ fn main() { unsafe { let x = i32x2(1, i32::MIN); let y = i32x2(1, -1); - simd_div(x, y); //~ERROR Undefined Behavior: overflow in signed division + simd_div(x, y); //~ERROR: Undefined Behavior: overflow in signed division } } diff --git a/tests/fail/intrinsics/simd-float-to-int.rs b/tests/fail/intrinsics/simd-float-to-int.rs index bb9adf07c9..a5bae36d92 100644 --- a/tests/fail/intrinsics/simd-float-to-int.rs +++ b/tests/fail/intrinsics/simd-float-to-int.rs @@ -1,4 +1,4 @@ -// error-pattern: cannot be represented in target type `i32` +//@error-pattern: cannot be represented in target type `i32` #![feature(portable_simd)] use std::simd::*; diff --git a/tests/fail/intrinsics/simd-gather.rs b/tests/fail/intrinsics/simd-gather.rs index ab9cb56ed0..e394cce9a4 100644 --- a/tests/fail/intrinsics/simd-gather.rs +++ b/tests/fail/intrinsics/simd-gather.rs @@ -1,4 +1,4 @@ -// error-pattern: pointer to 1 byte starting at offset 9 is out-of-bounds +//@error-pattern: pointer to 1 byte starting at offset 9 is out-of-bounds #![feature(portable_simd)] use std::simd::*; diff --git a/tests/fail/intrinsics/simd-reduce-invalid-bool.rs b/tests/fail/intrinsics/simd-reduce-invalid-bool.rs index c697fd526f..354f821312 100644 --- a/tests/fail/intrinsics/simd-reduce-invalid-bool.rs +++ b/tests/fail/intrinsics/simd-reduce-invalid-bool.rs @@ -11,6 +11,6 @@ struct i32x2(i32, i32); fn main() { unsafe { let x = i32x2(0, 1); - simd_reduce_any(x); //~ERROR must be all-0-bits or all-1-bits + simd_reduce_any(x); //~ERROR: must be all-0-bits or all-1-bits } } diff --git a/tests/fail/intrinsics/simd-rem-by-zero.rs b/tests/fail/intrinsics/simd-rem-by-zero.rs index 82cbaed462..625889bb67 100644 --- a/tests/fail/intrinsics/simd-rem-by-zero.rs +++ b/tests/fail/intrinsics/simd-rem-by-zero.rs @@ -12,6 +12,6 @@ fn main() { unsafe { let x = i32x2(1, 1); let y = i32x2(1, 0); - simd_rem(x, y); //~ERROR Undefined Behavior: calculating the remainder with a divisor of zero + simd_rem(x, y); //~ERROR: Undefined Behavior: calculating the remainder with a divisor of zero } } diff --git a/tests/fail/intrinsics/simd-scatter.rs b/tests/fail/intrinsics/simd-scatter.rs index d7a7e344aa..d2bc733995 100644 --- a/tests/fail/intrinsics/simd-scatter.rs +++ b/tests/fail/intrinsics/simd-scatter.rs @@ -1,4 +1,4 @@ -// error-pattern: pointer to 1 byte starting at offset 9 is out-of-bounds +//@error-pattern: pointer to 1 byte starting at offset 9 is out-of-bounds #![feature(portable_simd)] use std::simd::*; diff --git a/tests/fail/intrinsics/simd-select-bitmask-invalid.rs b/tests/fail/intrinsics/simd-select-bitmask-invalid.rs index cc9170c646..8a3895ac14 100644 --- a/tests/fail/intrinsics/simd-select-bitmask-invalid.rs +++ b/tests/fail/intrinsics/simd-select-bitmask-invalid.rs @@ -12,6 +12,6 @@ struct i32x2(i32, i32); fn main() { unsafe { let x = i32x2(0, 1); - simd_select_bitmask(0b11111111u8, x, x); //~ERROR bitmask less than 8 bits long must be filled with 0s for the remaining bits + simd_select_bitmask(0b11111111u8, x, x); //~ERROR: bitmask less than 8 bits long must be filled with 0s for the remaining bits } } diff --git a/tests/fail/intrinsics/simd-select-invalid-bool.rs b/tests/fail/intrinsics/simd-select-invalid-bool.rs index 8ccf4c362c..7f7ee3af49 100644 --- a/tests/fail/intrinsics/simd-select-invalid-bool.rs +++ b/tests/fail/intrinsics/simd-select-invalid-bool.rs @@ -12,6 +12,6 @@ struct i32x2(i32, i32); fn main() { unsafe { let x = i32x2(0, 1); - simd_select(x, x, x); //~ERROR must be all-0-bits or all-1-bits + simd_select(x, x, x); //~ERROR: must be all-0-bits or all-1-bits } } diff --git a/tests/fail/intrinsics/simd-shl-too-far.rs b/tests/fail/intrinsics/simd-shl-too-far.rs index e971b04206..5c517c17b3 100644 --- a/tests/fail/intrinsics/simd-shl-too-far.rs +++ b/tests/fail/intrinsics/simd-shl-too-far.rs @@ -12,6 +12,6 @@ fn main() { unsafe { let x = i32x2(1, 1); let y = i32x2(100, 0); - simd_shl(x, y); //~ERROR overflowing shift by 100 in `simd_shl` in SIMD lane 0 + simd_shl(x, y); //~ERROR: overflowing shift by 100 in `simd_shl` in SIMD lane 0 } } diff --git a/tests/fail/intrinsics/simd-shr-too-far.rs b/tests/fail/intrinsics/simd-shr-too-far.rs index ae071f0b7e..5f1475a677 100644 --- a/tests/fail/intrinsics/simd-shr-too-far.rs +++ b/tests/fail/intrinsics/simd-shr-too-far.rs @@ -12,6 +12,6 @@ fn main() { unsafe { let x = i32x2(1, 1); let y = i32x2(20, 40); - simd_shr(x, y); //~ERROR overflowing shift by 40 in `simd_shr` in SIMD lane 1 + simd_shr(x, y); //~ERROR: overflowing shift by 40 in `simd_shr` in SIMD lane 1 } } diff --git a/tests/fail/intrinsics/unchecked_add1.rs b/tests/fail/intrinsics/unchecked_add1.rs index 5409f66e80..25dbb817fa 100644 --- a/tests/fail/intrinsics/unchecked_add1.rs +++ b/tests/fail/intrinsics/unchecked_add1.rs @@ -2,6 +2,6 @@ fn main() { // MAX overflow unsafe { - std::intrinsics::unchecked_add(40000u16, 30000); //~ ERROR overflow executing `unchecked_add` + std::intrinsics::unchecked_add(40000u16, 30000); //~ ERROR: overflow executing `unchecked_add` } } diff --git a/tests/fail/intrinsics/unchecked_add2.rs b/tests/fail/intrinsics/unchecked_add2.rs index 5deef7ee5c..a454f64780 100644 --- a/tests/fail/intrinsics/unchecked_add2.rs +++ b/tests/fail/intrinsics/unchecked_add2.rs @@ -2,6 +2,6 @@ fn main() { // MIN overflow unsafe { - std::intrinsics::unchecked_add(-30000i16, -8000); //~ ERROR overflow executing `unchecked_add` + std::intrinsics::unchecked_add(-30000i16, -8000); //~ ERROR: overflow executing `unchecked_add` } } diff --git a/tests/fail/intrinsics/unchecked_div1.rs b/tests/fail/intrinsics/unchecked_div1.rs index c06a5bdce9..6706cee30e 100644 --- a/tests/fail/intrinsics/unchecked_div1.rs +++ b/tests/fail/intrinsics/unchecked_div1.rs @@ -2,6 +2,6 @@ fn main() { // MIN/-1 cannot be represented unsafe { - std::intrinsics::unchecked_div(i16::MIN, -1); //~ ERROR overflow in signed division (dividing MIN by -1) + std::intrinsics::unchecked_div(i16::MIN, -1); //~ ERROR: overflow in signed division (dividing MIN by -1) } } diff --git a/tests/fail/intrinsics/unchecked_mul1.rs b/tests/fail/intrinsics/unchecked_mul1.rs index 140bc9e6c7..514eb60602 100644 --- a/tests/fail/intrinsics/unchecked_mul1.rs +++ b/tests/fail/intrinsics/unchecked_mul1.rs @@ -2,6 +2,6 @@ fn main() { // MAX overflow unsafe { - std::intrinsics::unchecked_mul(300u16, 250u16); //~ ERROR overflow executing `unchecked_mul` + std::intrinsics::unchecked_mul(300u16, 250u16); //~ ERROR: overflow executing `unchecked_mul` } } diff --git a/tests/fail/intrinsics/unchecked_mul2.rs b/tests/fail/intrinsics/unchecked_mul2.rs index c4d16084b3..e103c1e7ad 100644 --- a/tests/fail/intrinsics/unchecked_mul2.rs +++ b/tests/fail/intrinsics/unchecked_mul2.rs @@ -2,6 +2,6 @@ fn main() { // MIN overflow unsafe { - std::intrinsics::unchecked_mul(1_000_000_000i32, -4); //~ ERROR overflow executing `unchecked_mul` + std::intrinsics::unchecked_mul(1_000_000_000i32, -4); //~ ERROR: overflow executing `unchecked_mul` } } diff --git a/tests/fail/intrinsics/unchecked_sub1.rs b/tests/fail/intrinsics/unchecked_sub1.rs index cff79f9135..e99f88edc8 100644 --- a/tests/fail/intrinsics/unchecked_sub1.rs +++ b/tests/fail/intrinsics/unchecked_sub1.rs @@ -2,6 +2,6 @@ fn main() { // MIN overflow unsafe { - std::intrinsics::unchecked_sub(14u32, 22); //~ ERROR overflow executing `unchecked_sub` + std::intrinsics::unchecked_sub(14u32, 22); //~ ERROR: overflow executing `unchecked_sub` } } diff --git a/tests/fail/intrinsics/unchecked_sub2.rs b/tests/fail/intrinsics/unchecked_sub2.rs index 9b741c2663..f83f6843c9 100644 --- a/tests/fail/intrinsics/unchecked_sub2.rs +++ b/tests/fail/intrinsics/unchecked_sub2.rs @@ -2,6 +2,6 @@ fn main() { // MAX overflow unsafe { - std::intrinsics::unchecked_sub(30000i16, -7000); //~ ERROR overflow executing `unchecked_sub` + std::intrinsics::unchecked_sub(30000i16, -7000); //~ ERROR: overflow executing `unchecked_sub` } } diff --git a/tests/fail/intrinsics/write_bytes_null.rs b/tests/fail/intrinsics/write_bytes_null.rs index 81b155da44..2f46c820fb 100644 --- a/tests/fail/intrinsics/write_bytes_null.rs +++ b/tests/fail/intrinsics/write_bytes_null.rs @@ -6,5 +6,5 @@ extern "rust-intrinsic" { } fn main() { - unsafe { write_bytes::(std::ptr::null_mut(), 0, 0) }; //~ ERROR memory access failed: null pointer is a dangling pointer + unsafe { write_bytes::(std::ptr::null_mut(), 0, 0) }; //~ ERROR: memory access failed: null pointer is a dangling pointer } diff --git a/tests/fail/intrinsics/write_bytes_overflow.rs b/tests/fail/intrinsics/write_bytes_overflow.rs index a6bf2acb16..5c49dc0016 100644 --- a/tests/fail/intrinsics/write_bytes_overflow.rs +++ b/tests/fail/intrinsics/write_bytes_overflow.rs @@ -1,4 +1,4 @@ -// error-pattern: overflow computing total size of `write_bytes` +//@error-pattern: overflow computing total size of `write_bytes` use std::mem; fn main() { diff --git a/tests/fail/invalid_bool.rs b/tests/fail/invalid_bool.rs index c0c982b8ca..525f8831c1 100644 --- a/tests/fail/invalid_bool.rs +++ b/tests/fail/invalid_bool.rs @@ -1,9 +1,9 @@ // Validation makes this fail in the wrong place // Make sure we find these even with many checks disabled. -// compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation #![feature(bench_black_box)] fn main() { let b = unsafe { std::mem::transmute::(2) }; - let _x = b == std::hint::black_box(true); //~ ERROR interpreting an invalid 8-bit value as a bool + let _x = b == std::hint::black_box(true); //~ ERROR: interpreting an invalid 8-bit value as a bool } diff --git a/tests/fail/invalid_char.rs b/tests/fail/invalid_char.rs index 9d485b73f2..6992482294 100644 --- a/tests/fail/invalid_char.rs +++ b/tests/fail/invalid_char.rs @@ -1,10 +1,10 @@ // Validation makes this fail in the wrong place // Make sure we find these even with many checks disabled. -// compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation fn main() { let c = 0xFFFFFFu32; assert!(std::char::from_u32(c).is_none()); let c = unsafe { std::mem::transmute::(c) }; - let _x = c == 'x'; //~ ERROR interpreting an invalid 32-bit value as a char + let _x = c == 'x'; //~ ERROR: interpreting an invalid 32-bit value as a char } diff --git a/tests/fail/invalid_enum_tag.rs b/tests/fail/invalid_enum_tag.rs index 7ba4da9018..92cc1d1d34 100644 --- a/tests/fail/invalid_enum_tag.rs +++ b/tests/fail/invalid_enum_tag.rs @@ -1,8 +1,8 @@ // Validation makes this fail in the wrong place // Make sure we find these even with many checks disabled. -// compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation -// error-pattern: enum value has invalid tag +//@error-pattern: enum value has invalid tag use std::mem; diff --git a/tests/fail/invalid_int.rs b/tests/fail/invalid_int.rs index 26a8580207..4f76f8b6d9 100644 --- a/tests/fail/invalid_int.rs +++ b/tests/fail/invalid_int.rs @@ -1,8 +1,8 @@ // Validation makes this fail in the wrong place // Make sure we find these even with many checks disabled. -// compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation fn main() { let i = unsafe { std::mem::MaybeUninit::::uninit().assume_init() }; - let _x = i + 0; //~ ERROR this operation requires initialized memory + let _x = i + 0; //~ ERROR: this operation requires initialized memory } diff --git a/tests/fail/issue-miri-1112.rs b/tests/fail/issue-miri-1112.rs index caf2b28c5a..abf627bb2a 100644 --- a/tests/fail/issue-miri-1112.rs +++ b/tests/fail/issue-miri-1112.rs @@ -28,7 +28,7 @@ impl FunnyPointer { data: data as *const _ as *const (), vtable: ptr as *const _ as *const (), }; - let obj = std::mem::transmute::(obj); //~ ERROR invalid drop function pointer in vtable + let obj = std::mem::transmute::(obj); //~ ERROR: invalid drop function pointer in vtable &*obj } } diff --git a/tests/fail/memleak.rs b/tests/fail/memleak.rs index 7dee9ddf1c..d384caf81a 100644 --- a/tests/fail/memleak.rs +++ b/tests/fail/memleak.rs @@ -1,5 +1,5 @@ -// error-pattern: the evaluated program leaked memory -// normalize-stderr-test: ".*│.*" -> "$$stripped$$" +//@error-pattern: the evaluated program leaked memory +//@normalize-stderr-test: ".*│.*" -> "$$stripped$$" fn main() { std::mem::forget(Box::new(42)); diff --git a/tests/fail/memleak_rc.rs b/tests/fail/memleak_rc.rs index 9ea809f762..76ecd71b01 100644 --- a/tests/fail/memleak_rc.rs +++ b/tests/fail/memleak_rc.rs @@ -1,6 +1,6 @@ -// error-pattern: the evaluated program leaked memory -// stderr-per-bitwidth -// normalize-stderr-test: ".*│.*" -> "$$stripped$$" +//@error-pattern: the evaluated program leaked memory +//@stderr-per-bitwidth +//@normalize-stderr-test: ".*│.*" -> "$$stripped$$" use std::cell::RefCell; use std::rc::Rc; diff --git a/tests/fail/memleak_rc.stderr b/tests/fail/memleak_rc.stderr new file mode 100644 index 0000000000..290de49c82 --- /dev/null +++ b/tests/fail/memleak_rc.stderr @@ -0,0 +1,11 @@ +The following memory was leaked: ALLOC (Rust heap, size: 32, align: 8) { + 0x00 │ 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 │ ................ + 0x10 │ 00 00 00 00 00 00 00 00 ╾$HEX[a1765]─╼ │ ........╾──────╼ +} + +error: the evaluated program leaked memory + +note: pass `-Zmiri-ignore-leaks` to disable this check + +error: aborting due to previous error + diff --git a/tests/fail/modifying_constants.rs b/tests/fail/modifying_constants.rs index 0c884142bf..2783ebd155 100644 --- a/tests/fail/modifying_constants.rs +++ b/tests/fail/modifying_constants.rs @@ -1,9 +1,9 @@ // This should fail even without validation/SB -// compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows fn main() { let x = &1; // the `&1` is promoted to a constant, but it used to be that only the pointer is marked static, not the pointee let y = unsafe { &mut *(x as *const i32 as *mut i32) }; - *y = 42; //~ ERROR read-only + *y = 42; //~ ERROR: read-only assert_eq!(*x, 42); } diff --git a/tests/fail/never_say_never.rs b/tests/fail/never_say_never.rs index 7aae8a2921..f6d3dc790b 100644 --- a/tests/fail/never_say_never.rs +++ b/tests/fail/never_say_never.rs @@ -1,5 +1,5 @@ // This should fail even without validation -// compile-flags: -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-validation #![feature(never_type)] #![allow(unreachable_code)] @@ -7,7 +7,7 @@ fn main() { let y = &5; let x: ! = unsafe { - *(y as *const _ as *const !) //~ ERROR entering unreachable code + *(y as *const _ as *const !) //~ ERROR: entering unreachable code }; f(x) } diff --git a/tests/fail/never_transmute_humans.rs b/tests/fail/never_transmute_humans.rs index 8a7d7bfcc6..de723433dc 100644 --- a/tests/fail/never_transmute_humans.rs +++ b/tests/fail/never_transmute_humans.rs @@ -1,5 +1,5 @@ // This should fail even without validation -// compile-flags: -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-validation #![feature(never_type)] @@ -7,6 +7,6 @@ struct Human; fn main() { let _x: ! = unsafe { - std::mem::transmute::(Human) //~ ERROR transmuting to uninhabited + std::mem::transmute::(Human) //~ ERROR: transmuting to uninhabited }; } diff --git a/tests/fail/never_transmute_void.rs b/tests/fail/never_transmute_void.rs index f5d0f914da..19473e9ac2 100644 --- a/tests/fail/never_transmute_void.rs +++ b/tests/fail/never_transmute_void.rs @@ -1,5 +1,6 @@ // This should fail even without validation -// compile-flags: -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-validation +//@require-annotations-for-level: ERROR #![feature(never_type)] #![allow(unused, invalid_value)] @@ -9,11 +10,11 @@ mod m { pub struct Void(VoidI); pub fn f(v: Void) -> ! { - match v.0 {} //~ ERROR entering unreachable code + match v.0 {} //~ ERROR: entering unreachable code } } fn main() { let v = unsafe { std::mem::transmute::<(), m::Void>(()) }; - m::f(v); //~ inside `main` + m::f(v); //~ NOTE: inside `main` } diff --git a/tests/fail/no_main.rs b/tests/fail/no_main.rs index a9e8e81682..e282050408 100644 --- a/tests/fail/no_main.rs +++ b/tests/fail/no_main.rs @@ -1,2 +1,2 @@ -// error-pattern: miri can only run programs that have a main function +//@error-pattern: miri can only run programs that have a main function #![no_main] diff --git a/tests/fail/panic/bad_miri_start_panic.rs b/tests/fail/panic/bad_miri_start_panic.rs index 089cd86f1b..4b0ae60b10 100644 --- a/tests/fail/panic/bad_miri_start_panic.rs +++ b/tests/fail/panic/bad_miri_start_panic.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-disable-abi-check +//@compile-flags: -Zmiri-disable-abi-check // This feature is required to trigger the error using the "C" ABI. #![feature(c_unwind)] @@ -8,5 +8,5 @@ extern "C" { fn main() { unsafe { miri_start_panic(&mut 0) } - //~^ ERROR unwinding past a stack frame that does not allow unwinding + //~^ ERROR: unwinding past a stack frame that does not allow unwinding } diff --git a/tests/fail/panic/double_panic.rs b/tests/fail/panic/double_panic.rs index f3af66a79a..8919d51bb2 100644 --- a/tests/fail/panic/double_panic.rs +++ b/tests/fail/panic/double_panic.rs @@ -1,6 +1,6 @@ -// error-pattern: the program aborted -// normalize-stderr-test: "\| +\^+" -> "| ^" -// normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@error-pattern: the program aborted +//@normalize-stderr-test: "\| +\^+" -> "| ^" +//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" struct Foo; impl Drop for Foo { diff --git a/tests/fail/panic/panic_abort1.rs b/tests/fail/panic/panic_abort1.rs index 9c094c6598..00a01ce6e8 100644 --- a/tests/fail/panic/panic_abort1.rs +++ b/tests/fail/panic/panic_abort1.rs @@ -1,7 +1,7 @@ -// error-pattern: the program aborted execution -// normalize-stderr-test: "\| +\^+" -> "| ^" -// normalize-stderr-test: "libc::abort\(\);|core::intrinsics::abort\(\);" -> "ABORT();" -// compile-flags: -C panic=abort +//@error-pattern: the program aborted execution +//@normalize-stderr-test: "\| +\^+" -> "| ^" +//@normalize-stderr-test: "libc::abort\(\);|core::intrinsics::abort\(\);" -> "ABORT();" +//@compile-flags: -C panic=abort fn main() { std::panic!("panicking from libstd"); diff --git a/tests/fail/panic/panic_abort2.rs b/tests/fail/panic/panic_abort2.rs index 7eb9a3c24a..dee0de9670 100644 --- a/tests/fail/panic/panic_abort2.rs +++ b/tests/fail/panic/panic_abort2.rs @@ -1,7 +1,7 @@ -// error-pattern: the program aborted execution -// normalize-stderr-test: "\| +\^+" -> "| ^" -// normalize-stderr-test: "libc::abort\(\);|core::intrinsics::abort\(\);" -> "ABORT();" -// compile-flags: -C panic=abort +//@error-pattern: the program aborted execution +//@normalize-stderr-test: "\| +\^+" -> "| ^" +//@normalize-stderr-test: "libc::abort\(\);|core::intrinsics::abort\(\);" -> "ABORT();" +//@compile-flags: -C panic=abort fn main() { std::panic!("{}-panicking from libstd", 42); diff --git a/tests/fail/panic/panic_abort3.rs b/tests/fail/panic/panic_abort3.rs index 1940b48bad..a448aab3ea 100644 --- a/tests/fail/panic/panic_abort3.rs +++ b/tests/fail/panic/panic_abort3.rs @@ -1,7 +1,7 @@ -// error-pattern: the program aborted execution -// normalize-stderr-test: "\| +\^+" -> "| ^" -// normalize-stderr-test: "libc::abort\(\);|core::intrinsics::abort\(\);" -> "ABORT();" -// compile-flags: -C panic=abort +//@error-pattern: the program aborted execution +//@normalize-stderr-test: "\| +\^+" -> "| ^" +//@normalize-stderr-test: "libc::abort\(\);|core::intrinsics::abort\(\);" -> "ABORT();" +//@compile-flags: -C panic=abort fn main() { core::panic!("panicking from libcore"); diff --git a/tests/fail/panic/panic_abort4.rs b/tests/fail/panic/panic_abort4.rs index e5190ea076..4995dad9d7 100644 --- a/tests/fail/panic/panic_abort4.rs +++ b/tests/fail/panic/panic_abort4.rs @@ -1,7 +1,7 @@ -// error-pattern: the program aborted execution -// normalize-stderr-test: "\| +\^+" -> "| ^" -// normalize-stderr-test: "libc::abort\(\);|core::intrinsics::abort\(\);" -> "ABORT();" -// compile-flags: -C panic=abort +//@error-pattern: the program aborted execution +//@normalize-stderr-test: "\| +\^+" -> "| ^" +//@normalize-stderr-test: "libc::abort\(\);|core::intrinsics::abort\(\);" -> "ABORT();" +//@compile-flags: -C panic=abort fn main() { core::panic!("{}-panicking from libcore", 42); diff --git a/tests/fail/panic/unwind_panic_abort.rs b/tests/fail/panic/unwind_panic_abort.rs index 6afcd7ae7f..c21fa85a90 100644 --- a/tests/fail/panic/unwind_panic_abort.rs +++ b/tests/fail/panic/unwind_panic_abort.rs @@ -1,4 +1,4 @@ -// compile-flags: -Cpanic=abort +//@compile-flags: -Cpanic=abort //! Unwinding despite `-C panic=abort` is an error. @@ -8,6 +8,6 @@ extern "Rust" { fn main() { unsafe { - miri_start_panic(&mut 0); //~ ERROR unwinding past a stack frame that does not allow unwinding + miri_start_panic(&mut 0); //~ ERROR: unwinding past a stack frame that does not allow unwinding } } diff --git a/tests/fail/pointer_partial_overwrite.rs b/tests/fail/pointer_partial_overwrite.rs index 1bbb33aa2b..63f0649b8e 100644 --- a/tests/fail/pointer_partial_overwrite.rs +++ b/tests/fail/pointer_partial_overwrite.rs @@ -1,5 +1,5 @@ // Make sure we find these even with many checks disabled. -// compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation // Test what happens when we overwrite parts of a pointer. // Also see . @@ -12,6 +12,6 @@ fn main() { // "attempted to interpret some raw bytes as a pointer address" instead of // "attempted to read undefined bytes" } - let x = *p; //~ ERROR this operation requires initialized memory + let x = *p; //~ ERROR: this operation requires initialized memory panic!("this should never print: {}", x); } diff --git a/tests/fail/provenance/provenance_transmute.rs b/tests/fail/provenance/provenance_transmute.rs index 28e6ba6230..abcfc060e5 100644 --- a/tests/fail/provenance/provenance_transmute.rs +++ b/tests/fail/provenance/provenance_transmute.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance #![feature(strict_provenance)] use std::mem; @@ -13,7 +13,7 @@ unsafe fn deref(left: *const u8, right: *const u8) { // The compiler is allowed to replace `left_int` by `right_int` here... let left_ptr: *const u8 = mem::transmute(left_int); // ...which however means here it could be dereferencing the wrong pointer. - let _val = *left_ptr; //~ERROR dereferencing pointer failed + let _val = *left_ptr; //~ERROR: dereferencing pointer failed } } diff --git a/tests/fail/provenance/ptr_int_unexposed.rs b/tests/fail/provenance/ptr_int_unexposed.rs index ad29d38dc3..07de41d10a 100644 --- a/tests/fail/provenance/ptr_int_unexposed.rs +++ b/tests/fail/provenance/ptr_int_unexposed.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance #![feature(strict_provenance)] fn main() { @@ -8,5 +8,5 @@ fn main() { let x_usize: usize = x_ptr.addr(); // Cast back an address that did *not* get exposed. let ptr = std::ptr::from_exposed_addr::(x_usize); - assert_eq!(unsafe { *ptr }, 3); //~ ERROR Undefined Behavior: dereferencing pointer failed + assert_eq!(unsafe { *ptr }, 3); //~ ERROR: is a dangling pointer } diff --git a/tests/fail/provenance/ptr_invalid.rs b/tests/fail/provenance/ptr_invalid.rs index be5666b2ef..d7d32d83e0 100644 --- a/tests/fail/provenance/ptr_invalid.rs +++ b/tests/fail/provenance/ptr_invalid.rs @@ -5,5 +5,5 @@ fn main() { let x = 42; let xptr = &x as *const i32; let xptr_invalid = std::ptr::invalid::(xptr.expose_addr()); - let _val = unsafe { *xptr_invalid }; //~ ERROR is a dangling pointer + let _val = unsafe { *xptr_invalid }; //~ ERROR: is a dangling pointer } diff --git a/tests/fail/provenance/ptr_invalid_offset.rs b/tests/fail/provenance/ptr_invalid_offset.rs index 08fb57a656..a3510d8e89 100644 --- a/tests/fail/provenance/ptr_invalid_offset.rs +++ b/tests/fail/provenance/ptr_invalid_offset.rs @@ -1,5 +1,5 @@ -// compile-flags: -Zmiri-strict-provenance -// error-pattern: is a dangling pointer +//@compile-flags: -Zmiri-strict-provenance +//@error-pattern: is a dangling pointer #![feature(strict_provenance)] fn main() { diff --git a/tests/fail/provenance/strict_provenance_cast.rs b/tests/fail/provenance/strict_provenance_cast.rs index 968c4dfded..0016e78792 100644 --- a/tests/fail/provenance/strict_provenance_cast.rs +++ b/tests/fail/provenance/strict_provenance_cast.rs @@ -1,6 +1,6 @@ -// compile-flags: -Zmiri-strict-provenance +//@compile-flags: -Zmiri-strict-provenance fn main() { let addr = &0 as *const i32 as usize; - let _ptr = addr as *const i32; //~ ERROR integer-to-pointer casts and `ptr::from_exposed_addr` are not supported + let _ptr = addr as *const i32; //~ ERROR: integer-to-pointer casts and `ptr::from_exposed_addr` are not supported } diff --git a/tests/fail/rc_as_ptr.rs b/tests/fail/rc_as_ptr.rs index 049330ef36..6aea187074 100644 --- a/tests/fail/rc_as_ptr.rs +++ b/tests/fail/rc_as_ptr.rs @@ -1,5 +1,5 @@ // This should fail even without validation -// compile-flags: -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-validation use std::ptr; use std::rc::{Rc, Weak}; @@ -16,5 +16,5 @@ fn main() { drop(strong); // But not any more. We can do Weak::as_raw(&weak), but accessing the pointer would lead to // undefined behaviour. - assert_eq!(42, **unsafe { &*Weak::as_ptr(&weak) }); //~ ERROR dereferenced after this allocation got freed + assert_eq!(42, **unsafe { &*Weak::as_ptr(&weak) }); //~ ERROR: dereferenced after this allocation got freed } diff --git a/tests/fail/reading_half_a_pointer.rs b/tests/fail/reading_half_a_pointer.rs index 2a3b096b2f..a8cdb11f40 100644 --- a/tests/fail/reading_half_a_pointer.rs +++ b/tests/fail/reading_half_a_pointer.rs @@ -24,6 +24,6 @@ fn main() { // starts 1 byte to the right, so using it would actually be wrong! let d_alias = &mut w.data as *mut _ as *mut *const u8; unsafe { - let _x = *d_alias; //~ ERROR unable to turn pointer into raw bytes + let _x = *d_alias; //~ ERROR: unable to turn pointer into raw bytes } } diff --git a/tests/fail/rustc-error.rs b/tests/fail/rustc-error.rs index 3579a143f5..7fc73bf365 100644 --- a/tests/fail/rustc-error.rs +++ b/tests/fail/rustc-error.rs @@ -1,4 +1,4 @@ // Make sure we exit with non-0 status code when the program fails to build. fn main() { - println("Hello, world!"); //~ ERROR expected function, found macro + println("Hello, world!"); //~ ERROR: expected function, found macro } diff --git a/tests/fail/shim_arg_size.rs b/tests/fail/shim_arg_size.rs index 37557de0a5..383df286d4 100644 --- a/tests/fail/shim_arg_size.rs +++ b/tests/fail/shim_arg_size.rs @@ -1,4 +1,4 @@ -// stderr-per-bitwidth +//@stderr-per-bitwidth fn main() { extern "C" { @@ -12,6 +12,6 @@ fn main() { } unsafe { - let _p1 = malloc(42); //~ ERROR Undefined Behavior: scalar size mismatch + let _p1 = malloc(42); //~ ERROR: Undefined Behavior: scalar size mismatch }; } diff --git a/tests/fail/should-pass/cpp20_rwc_syncs.rs b/tests/fail/should-pass/cpp20_rwc_syncs.rs index 85c24246db..dbac6469fb 100644 --- a/tests/fail/should-pass/cpp20_rwc_syncs.rs +++ b/tests/fail/should-pass/cpp20_rwc_syncs.rs @@ -1,6 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-ignore-leaks -// error-pattern: unreachable +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-ignore-leaks +//@error-pattern: unreachable // https://plv.mpi-sws.org/scfix/paper.pdf // 2.2 Second Problem: SC Fences are Too Weak diff --git a/tests/fail/stacked_borrows/alias_through_mutation.rs b/tests/fail/stacked_borrows/alias_through_mutation.rs index 15d8e45f8b..73095bb2fc 100644 --- a/tests/fail/stacked_borrows/alias_through_mutation.rs +++ b/tests/fail/stacked_borrows/alias_through_mutation.rs @@ -11,5 +11,5 @@ fn main() { retarget(&mut target_alias, target); // now `target_alias` points to the same thing as `target` *target = 13; - let _val = *target_alias; //~ ERROR borrow stack + let _val = *target_alias; //~ ERROR: /read access .* tag does not exist in the borrow stack/ } diff --git a/tests/fail/stacked_borrows/aliasing_mut1.rs b/tests/fail/stacked_borrows/aliasing_mut1.rs index ebee134a8a..14a27d8e9d 100644 --- a/tests/fail/stacked_borrows/aliasing_mut1.rs +++ b/tests/fail/stacked_borrows/aliasing_mut1.rs @@ -1,6 +1,6 @@ use std::mem; -pub fn safe(_x: &mut i32, _y: &mut i32) {} //~ ERROR protect +pub fn safe(_x: &mut i32, _y: &mut i32) {} //~ ERROR: protect fn main() { let mut x = 0; diff --git a/tests/fail/stacked_borrows/aliasing_mut1.stderr b/tests/fail/stacked_borrows/aliasing_mut1.stderr index 34a53aa8f3..b821d1e6ed 100644 --- a/tests/fail/stacked_borrows/aliasing_mut1.stderr +++ b/tests/fail/stacked_borrows/aliasing_mut1.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: not granting access to tag because incompatible item is protected: [Unique for (call ID)] +error: Undefined Behavior: not granting access to tag because incompatible item [Unique for ] is protected by call ID --> $DIR/aliasing_mut1.rs:LL:CC | LL | pub fn safe(_x: &mut i32, _y: &mut i32) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not granting access to tag because incompatible item is protected: [Unique for (call ID)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not granting access to tag because incompatible item [Unique for ] is protected by call ID | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information diff --git a/tests/fail/stacked_borrows/aliasing_mut2.rs b/tests/fail/stacked_borrows/aliasing_mut2.rs index 971dbb63a9..84d901f83b 100644 --- a/tests/fail/stacked_borrows/aliasing_mut2.rs +++ b/tests/fail/stacked_borrows/aliasing_mut2.rs @@ -1,6 +1,6 @@ use std::mem; -pub fn safe(_x: &i32, _y: &mut i32) {} //~ ERROR protect +pub fn safe(_x: &i32, _y: &mut i32) {} //~ ERROR: protect fn main() { let mut x = 0; diff --git a/tests/fail/stacked_borrows/aliasing_mut2.stderr b/tests/fail/stacked_borrows/aliasing_mut2.stderr index 7830648ee8..594b578fc0 100644 --- a/tests/fail/stacked_borrows/aliasing_mut2.stderr +++ b/tests/fail/stacked_borrows/aliasing_mut2.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: not granting access to tag because incompatible item is protected: [SharedReadOnly for (call ID)] +error: Undefined Behavior: not granting access to tag because incompatible item [SharedReadOnly for ] is protected by call ID --> $DIR/aliasing_mut2.rs:LL:CC | LL | pub fn safe(_x: &i32, _y: &mut i32) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not granting access to tag because incompatible item is protected: [SharedReadOnly for (call ID)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not granting access to tag because incompatible item [SharedReadOnly for ] is protected by call ID | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information diff --git a/tests/fail/stacked_borrows/aliasing_mut3.rs b/tests/fail/stacked_borrows/aliasing_mut3.rs index 91904b0b1d..f1ba06b6e4 100644 --- a/tests/fail/stacked_borrows/aliasing_mut3.rs +++ b/tests/fail/stacked_borrows/aliasing_mut3.rs @@ -1,6 +1,6 @@ use std::mem; -pub fn safe(_x: &mut i32, _y: &i32) {} //~ ERROR borrow stack +pub fn safe(_x: &mut i32, _y: &i32) {} //~ ERROR: borrow stack fn main() { let mut x = 0; diff --git a/tests/fail/stacked_borrows/aliasing_mut3.stderr b/tests/fail/stacked_borrows/aliasing_mut3.stderr index 8e489b7386..0bb7b8d3a8 100644 --- a/tests/fail/stacked_borrows/aliasing_mut3.stderr +++ b/tests/fail/stacked_borrows/aliasing_mut3.stderr @@ -1,10 +1,10 @@ -error: Undefined Behavior: trying to reborrow for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location --> $DIR/aliasing_mut3.rs:LL:CC | LL | pub fn safe(_x: &mut i32, _y: &i32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | trying to reborrow for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x0..0x4] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/stacked_borrows/aliasing_mut4.rs b/tests/fail/stacked_borrows/aliasing_mut4.rs index 79caed5dd6..52081b5622 100644 --- a/tests/fail/stacked_borrows/aliasing_mut4.rs +++ b/tests/fail/stacked_borrows/aliasing_mut4.rs @@ -2,7 +2,7 @@ use std::cell::Cell; use std::mem; // Make sure &mut UnsafeCell also is exclusive -pub fn safe(_x: &i32, _y: &mut Cell) {} //~ ERROR protect +pub fn safe(_x: &i32, _y: &mut Cell) {} //~ ERROR: protect fn main() { let mut x = 0; diff --git a/tests/fail/stacked_borrows/aliasing_mut4.stderr b/tests/fail/stacked_borrows/aliasing_mut4.stderr index f62ce1e519..0c7d85ae57 100644 --- a/tests/fail/stacked_borrows/aliasing_mut4.stderr +++ b/tests/fail/stacked_borrows/aliasing_mut4.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: not granting access to tag because incompatible item is protected: [SharedReadOnly for (call ID)] +error: Undefined Behavior: not granting access to tag because incompatible item [SharedReadOnly for ] is protected by call ID --> $DIR/aliasing_mut4.rs:LL:CC | LL | pub fn safe(_x: &i32, _y: &mut Cell) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not granting access to tag because incompatible item is protected: [SharedReadOnly for (call ID)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not granting access to tag because incompatible item [SharedReadOnly for ] is protected by call ID | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information diff --git a/tests/fail/stacked_borrows/box_exclusive_violation1.rs b/tests/fail/stacked_borrows/box_exclusive_violation1.rs index 66d092d627..87a6b7bbd6 100644 --- a/tests/fail/stacked_borrows/box_exclusive_violation1.rs +++ b/tests/fail/stacked_borrows/box_exclusive_violation1.rs @@ -1,4 +1,4 @@ -fn demo_mut_advanced_unique(mut our: Box) -> i32 { +fn demo_box_advanced_unique(mut our: Box) -> i32 { unknown_code_1(&*our); // This "re-asserts" uniqueness of the reference: After writing, we know @@ -24,10 +24,10 @@ fn unknown_code_1(x: &i32) { fn unknown_code_2() { unsafe { - *LEAK = 7; //~ ERROR borrow stack + *LEAK = 7; //~ ERROR: /write access .* tag does not exist in the borrow stack/ } } fn main() { - demo_mut_advanced_unique(Box::new(0)); + demo_box_advanced_unique(Box::new(0)); } diff --git a/tests/fail/stacked_borrows/box_exclusive_violation1.stderr b/tests/fail/stacked_borrows/box_exclusive_violation1.stderr index 88ecca8796..7214ef27be 100644 --- a/tests/fail/stacked_borrows/box_exclusive_violation1.stderr +++ b/tests/fail/stacked_borrows/box_exclusive_violation1.stderr @@ -21,7 +21,7 @@ LL | *our = 5; | ^^^^^^^^ = note: backtrace: = note: inside `unknown_code_2` at $DIR/box_exclusive_violation1.rs:LL:CC -note: inside `demo_mut_advanced_unique` at $DIR/box_exclusive_violation1.rs:LL:CC +note: inside `demo_box_advanced_unique` at $DIR/box_exclusive_violation1.rs:LL:CC --> $DIR/box_exclusive_violation1.rs:LL:CC | LL | unknown_code_2(); @@ -29,7 +29,7 @@ LL | unknown_code_2(); note: inside `main` at $DIR/box_exclusive_violation1.rs:LL:CC --> $DIR/box_exclusive_violation1.rs:LL:CC | -LL | demo_mut_advanced_unique(Box::new(0)); +LL | demo_box_advanced_unique(Box::new(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/tests/fail/stacked_borrows/buggy_as_mut_slice.rs b/tests/fail/stacked_borrows/buggy_as_mut_slice.rs index 6744e4ef44..8615a9a58a 100644 --- a/tests/fail/stacked_borrows/buggy_as_mut_slice.rs +++ b/tests/fail/stacked_borrows/buggy_as_mut_slice.rs @@ -11,5 +11,5 @@ fn main() { let v1 = safe::as_mut_slice(&v); let _v2 = safe::as_mut_slice(&v); v1[1] = 5; - //~^ ERROR borrow stack + //~^ ERROR: /write access .* tag does not exist in the borrow stack/ } diff --git a/tests/fail/stacked_borrows/buggy_split_at_mut.rs b/tests/fail/stacked_borrows/buggy_split_at_mut.rs index a2ef0fcf17..db64d559c5 100644 --- a/tests/fail/stacked_borrows/buggy_split_at_mut.rs +++ b/tests/fail/stacked_borrows/buggy_split_at_mut.rs @@ -19,7 +19,7 @@ mod safe { fn main() { let mut array = [1, 2, 3, 4]; let (a, b) = safe::split_at_mut(&mut array, 0); - //~^ ERROR borrow stack + //~^ ERROR: /reborrow .* tag does not exist in the borrow stack/ a[1] = 5; b[1] = 6; } diff --git a/tests/fail/stacked_borrows/buggy_split_at_mut.stderr b/tests/fail/stacked_borrows/buggy_split_at_mut.stderr index 6e6dc38aaf..41ab9cfa92 100644 --- a/tests/fail/stacked_borrows/buggy_split_at_mut.stderr +++ b/tests/fail/stacked_borrows/buggy_split_at_mut.stderr @@ -1,10 +1,10 @@ -error: Undefined Behavior: trying to reborrow for Unique permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for Unique permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location --> $DIR/buggy_split_at_mut.rs:LL:CC | LL | let (a, b) = safe::split_at_mut(&mut array, 0); | ^ | | - | trying to reborrow for Unique permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for Unique permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x0..0x10] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/stacked_borrows/deallocate_against_barrier1.rs b/tests/fail/stacked_borrows/deallocate_against_barrier1.rs index bf18c9a058..9b710424c5 100644 --- a/tests/fail/stacked_borrows/deallocate_against_barrier1.rs +++ b/tests/fail/stacked_borrows/deallocate_against_barrier1.rs @@ -1,4 +1,4 @@ -// error-pattern: deallocating while item is protected +//@error-pattern: /deallocating while item \[Unique for .*\] is protected/ fn inner(x: &mut i32, f: fn(&mut i32)) { // `f` may mutate, but it may not deallocate! diff --git a/tests/fail/stacked_borrows/deallocate_against_barrier1.stderr b/tests/fail/stacked_borrows/deallocate_against_barrier1.stderr index c3ea7a437c..689c0a5dea 100644 --- a/tests/fail/stacked_borrows/deallocate_against_barrier1.stderr +++ b/tests/fail/stacked_borrows/deallocate_against_barrier1.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: deallocating while item is protected: [Unique for (call ID)] +error: Undefined Behavior: deallocating while item [Unique for ] is protected by call ID --> RUSTLIB/alloc/src/alloc.rs:LL:CC | LL | unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deallocating while item is protected: [Unique for (call ID)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deallocating while item [Unique for ] is protected by call ID | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information diff --git a/tests/fail/stacked_borrows/deallocate_against_barrier2.rs b/tests/fail/stacked_borrows/deallocate_against_barrier2.rs index 4680f6769a..36e133e383 100644 --- a/tests/fail/stacked_borrows/deallocate_against_barrier2.rs +++ b/tests/fail/stacked_borrows/deallocate_against_barrier2.rs @@ -1,4 +1,4 @@ -// error-pattern: deallocating while item is protected +//@error-pattern: /deallocating while item \[SharedReadWrite for .*\] is protected/ use std::marker::PhantomPinned; pub struct NotUnpin(i32, PhantomPinned); diff --git a/tests/fail/stacked_borrows/deallocate_against_barrier2.stderr b/tests/fail/stacked_borrows/deallocate_against_barrier2.stderr index d6c31e0649..a1a7ce0c6b 100644 --- a/tests/fail/stacked_borrows/deallocate_against_barrier2.stderr +++ b/tests/fail/stacked_borrows/deallocate_against_barrier2.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: deallocating while item is protected: [SharedReadWrite for (call ID)] +error: Undefined Behavior: deallocating while item [SharedReadWrite for ] is protected by call ID --> RUSTLIB/alloc/src/alloc.rs:LL:CC | LL | unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deallocating while item is protected: [SharedReadWrite for (call ID)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deallocating while item [SharedReadWrite for ] is protected by call ID | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information diff --git a/tests/fail/stacked_borrows/exposed_only_ro.rs b/tests/fail/stacked_borrows/exposed_only_ro.rs index 9b4234499d..0b4fb0ccd3 100644 --- a/tests/fail/stacked_borrows/exposed_only_ro.rs +++ b/tests/fail/stacked_borrows/exposed_only_ro.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance #![feature(strict_provenance)] // If we have only exposed read-only pointers, doing a write through a wildcard ptr should fail. @@ -8,5 +8,5 @@ fn main() { let _fool = &mut x as *mut i32; // this would have fooled the old untagged pointer logic let addr = (&x as *const i32).expose_addr(); let ptr = std::ptr::from_exposed_addr_mut::(addr); - unsafe { *ptr = 0 }; //~ ERROR: borrow stack + unsafe { *ptr = 0 }; //~ ERROR: /write access using .* no exposed tags have suitable permission in the borrow stack/ } diff --git a/tests/fail/stacked_borrows/illegal_read1.rs b/tests/fail/stacked_borrows/illegal_read1.rs index b573496406..1dea282739 100644 --- a/tests/fail/stacked_borrows/illegal_read1.rs +++ b/tests/fail/stacked_borrows/illegal_read1.rs @@ -8,7 +8,7 @@ fn main() { let xref = unsafe { &mut *xraw }; // derived from raw, so using raw is still ok... callee(xraw); let _val = *xref; // ...but any use of raw will invalidate our ref. - //~^ ERROR: borrow stack + //~^ ERROR: /read access .* tag does not exist in the borrow stack/ } fn callee(xraw: *mut i32) { diff --git a/tests/fail/stacked_borrows/illegal_read2.rs b/tests/fail/stacked_borrows/illegal_read2.rs index ed6972c1de..b765b4d9ce 100644 --- a/tests/fail/stacked_borrows/illegal_read2.rs +++ b/tests/fail/stacked_borrows/illegal_read2.rs @@ -8,7 +8,7 @@ fn main() { let xref = unsafe { &mut *xraw }; // derived from raw, so using raw is still ok... callee(xraw); let _val = *xref; // ...but any use of raw will invalidate our ref. - //~^ ERROR: borrow stack + //~^ ERROR: /read access .* tag does not exist in the borrow stack/ } fn callee(xraw: *mut i32) { diff --git a/tests/fail/stacked_borrows/illegal_read3.rs b/tests/fail/stacked_borrows/illegal_read3.rs index 0173ca14b2..43ea0a0e84 100644 --- a/tests/fail/stacked_borrows/illegal_read3.rs +++ b/tests/fail/stacked_borrows/illegal_read3.rs @@ -18,7 +18,7 @@ fn main() { callee(xref1_sneaky); // ... though any use of it will invalidate our ref. let _val = *xref2; - //~^ ERROR: borrow stack + //~^ ERROR: /read access .* tag does not exist in the borrow stack/ } fn callee(xref1: HiddenRef) { diff --git a/tests/fail/stacked_borrows/illegal_read4.rs b/tests/fail/stacked_borrows/illegal_read4.rs index d7e281e3ff..a9ecb88d35 100644 --- a/tests/fail/stacked_borrows/illegal_read4.rs +++ b/tests/fail/stacked_borrows/illegal_read4.rs @@ -5,5 +5,5 @@ fn main() { let xraw = xref1 as *mut _; let xref2 = unsafe { &mut *xraw }; let _val = unsafe { *xraw }; // use the raw again, this invalidates xref2 *even* with the special read except for uniq refs - let _illegal = *xref2; //~ ERROR borrow stack + let _illegal = *xref2; //~ ERROR: /read access .* tag does not exist in the borrow stack/ } diff --git a/tests/fail/stacked_borrows/illegal_read5.rs b/tests/fail/stacked_borrows/illegal_read5.rs index 71af84e5b5..228c15f72e 100644 --- a/tests/fail/stacked_borrows/illegal_read5.rs +++ b/tests/fail/stacked_borrows/illegal_read5.rs @@ -1,6 +1,6 @@ // We *can* have aliasing &RefCell and &mut T, but we cannot read through the former. // Else we couldn't optimize based on the assumption that `xref` below is truly unique. -// normalize-stderr-test: "0x[0-9a-fA-F]+" -> "$$HEX" +//@normalize-stderr-test: "0x[0-9a-fA-F]+" -> "$$HEX" use std::cell::RefCell; use std::{mem, ptr}; @@ -14,5 +14,5 @@ fn main() { let _val = *xref; // we can even still use our mutable reference mem::forget(unsafe { ptr::read(xshr) }); // but after reading through the shared ref let _val = *xref; // the mutable one is dead and gone - //~^ ERROR borrow stack + //~^ ERROR: /read access .* tag does not exist in the borrow stack/ } diff --git a/tests/fail/stacked_borrows/illegal_read6.rs b/tests/fail/stacked_borrows/illegal_read6.rs index af89566f65..4af22580ab 100644 --- a/tests/fail/stacked_borrows/illegal_read6.rs +++ b/tests/fail/stacked_borrows/illegal_read6.rs @@ -5,6 +5,6 @@ fn main() { let raw = x as *mut _; let x = &mut *x; // kill `raw` let _y = &*x; // this should not activate `raw` again - let _val = *raw; //~ ERROR borrow stack + let _val = *raw; //~ ERROR: /read access .* tag does not exist in the borrow stack/ } } diff --git a/tests/fail/stacked_borrows/illegal_read7.rs b/tests/fail/stacked_borrows/illegal_read7.rs index e960bd5388..c0d3816f44 100644 --- a/tests/fail/stacked_borrows/illegal_read7.rs +++ b/tests/fail/stacked_borrows/illegal_read7.rs @@ -17,6 +17,6 @@ fn main() { // without invalidating `x`. That would be bad! It would mean that creating `shr` // leaked `x` to `raw`. let _val = ptr::read(raw); - let _val = *x.get_mut(); //~ ERROR borrow stack + let _val = *x.get_mut(); //~ ERROR: /reborrow .* tag does not exist in the borrow stack/ } } diff --git a/tests/fail/stacked_borrows/illegal_read7.stderr b/tests/fail/stacked_borrows/illegal_read7.stderr index 56ebd93918..11335df4da 100644 --- a/tests/fail/stacked_borrows/illegal_read7.stderr +++ b/tests/fail/stacked_borrows/illegal_read7.stderr @@ -1,10 +1,10 @@ -error: Undefined Behavior: trying to reborrow for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location --> $DIR/illegal_read7.rs:LL:CC | LL | let _val = *x.get_mut(); | ^^^^^^^^^^^ | | - | trying to reborrow for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x0..0x4] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/stacked_borrows/illegal_read8.rs b/tests/fail/stacked_borrows/illegal_read8.rs index 9fef673fe6..f0a1658a5a 100644 --- a/tests/fail/stacked_borrows/illegal_read8.rs +++ b/tests/fail/stacked_borrows/illegal_read8.rs @@ -10,6 +10,6 @@ fn main() { let _val = *y2; let _val = *y1; *y2 += 1; - let _fail = *y1; //~ ERROR borrow stack + let _fail = *y1; //~ ERROR: /read access .* tag does not exist in the borrow stack/ } } diff --git a/tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs b/tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs index 61a5e05d34..76516b7d92 100644 --- a/tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs +++ b/tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance fn main() { unsafe { @@ -12,6 +12,6 @@ fn main() { // And we test that it has uniqueness by doing a conflicting write. *exposed_ptr = 0; // Stack: Unknown( u32 { *a = 1; let _b = &*a; - unsafe { *y = 2 }; //~ ERROR: not granting access to tag + unsafe { *y = 2 }; //~ ERROR: /not granting access .* because incompatible item .* is protected/ return *a; } diff --git a/tests/fail/stacked_borrows/illegal_write6.stderr b/tests/fail/stacked_borrows/illegal_write6.stderr index cbf4a22f23..42f7b3f8b5 100644 --- a/tests/fail/stacked_borrows/illegal_write6.stderr +++ b/tests/fail/stacked_borrows/illegal_write6.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: not granting access to tag because incompatible item is protected: [Unique for (call ID)] +error: Undefined Behavior: not granting access to tag because incompatible item [Unique for ] is protected by call ID --> $DIR/illegal_write6.rs:LL:CC | LL | unsafe { *y = 2 }; - | ^^^^^^ not granting access to tag because incompatible item is protected: [Unique for (call ID)] + | ^^^^^^ not granting access to tag because incompatible item [Unique for ] is protected by call ID | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information diff --git a/tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs b/tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs index b50399b9df..0e34c5c98f 100644 --- a/tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs +++ b/tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance fn main() { unsafe { @@ -9,8 +9,9 @@ fn main() { let root2 = &*exposed_ptr; // Stack: Unknown( for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location --> $DIR/interior_mut1.rs:LL:CC | LL | let _val = *inner_shr.get(); | ^^^^^^^^^^^^^^^ | | - | trying to reborrow for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x0..0x4] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/stacked_borrows/interior_mut2.rs b/tests/fail/stacked_borrows/interior_mut2.rs index 4433f28e34..7f8c16c488 100644 --- a/tests/fail/stacked_borrows/interior_mut2.rs +++ b/tests/fail/stacked_borrows/interior_mut2.rs @@ -25,6 +25,6 @@ fn main() { // stack: [c: SharedReadWrite] // now this does not work any more - let _val = *inner_shr.get(); //~ ERROR borrow stack + let _val = *inner_shr.get(); //~ ERROR: /reborrow .* tag does not exist in the borrow stack/ } } diff --git a/tests/fail/stacked_borrows/interior_mut2.stderr b/tests/fail/stacked_borrows/interior_mut2.stderr index 74e91d49c4..0dbf6e2ea9 100644 --- a/tests/fail/stacked_borrows/interior_mut2.stderr +++ b/tests/fail/stacked_borrows/interior_mut2.stderr @@ -1,10 +1,10 @@ -error: Undefined Behavior: trying to reborrow for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location --> $DIR/interior_mut2.rs:LL:CC | LL | let _val = *inner_shr.get(); | ^^^^^^^^^^^^^^^ | | - | trying to reborrow for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x0..0x4] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/stacked_borrows/invalidate_against_barrier1.rs b/tests/fail/stacked_borrows/invalidate_against_barrier1.rs index 3a214a75b5..d0f43510c2 100644 --- a/tests/fail/stacked_borrows/invalidate_against_barrier1.rs +++ b/tests/fail/stacked_borrows/invalidate_against_barrier1.rs @@ -2,7 +2,7 @@ fn inner(x: *mut i32, _y: &mut i32) { // If `x` and `y` alias, retagging is fine with this... but we really // shouldn't be allowed to use `x` at all because `y` was assumed to be // unique for the duration of this call. - let _val = unsafe { *x }; //~ ERROR protect + let _val = unsafe { *x }; //~ ERROR: protect } fn main() { diff --git a/tests/fail/stacked_borrows/invalidate_against_barrier1.stderr b/tests/fail/stacked_borrows/invalidate_against_barrier1.stderr index 5f54134a24..4a1b14e460 100644 --- a/tests/fail/stacked_borrows/invalidate_against_barrier1.stderr +++ b/tests/fail/stacked_borrows/invalidate_against_barrier1.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: not granting access to tag because incompatible item is protected: [Unique for (call ID)] +error: Undefined Behavior: not granting access to tag because incompatible item [Unique for ] is protected by call ID --> $DIR/invalidate_against_barrier1.rs:LL:CC | LL | let _val = unsafe { *x }; - | ^^ not granting access to tag because incompatible item is protected: [Unique for (call ID)] + | ^^ not granting access to tag because incompatible item [Unique for ] is protected by call ID | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information diff --git a/tests/fail/stacked_borrows/invalidate_against_barrier2.rs b/tests/fail/stacked_borrows/invalidate_against_barrier2.rs index 86e4a84287..f4e767302f 100644 --- a/tests/fail/stacked_borrows/invalidate_against_barrier2.rs +++ b/tests/fail/stacked_borrows/invalidate_against_barrier2.rs @@ -2,7 +2,7 @@ fn inner(x: *mut i32, _y: &i32) { // If `x` and `y` alias, retagging is fine with this... but we really // shouldn't be allowed to write to `x` at all because `y` was assumed to be // immutable for the duration of this call. - unsafe { *x = 0 }; //~ ERROR protect + unsafe { *x = 0 }; //~ ERROR: protect } fn main() { diff --git a/tests/fail/stacked_borrows/invalidate_against_barrier2.stderr b/tests/fail/stacked_borrows/invalidate_against_barrier2.stderr index 15bc91e869..c6f158316f 100644 --- a/tests/fail/stacked_borrows/invalidate_against_barrier2.stderr +++ b/tests/fail/stacked_borrows/invalidate_against_barrier2.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: not granting access to tag because incompatible item is protected: [SharedReadOnly for (call ID)] +error: Undefined Behavior: not granting access to tag because incompatible item [SharedReadOnly for ] is protected by call ID --> $DIR/invalidate_against_barrier2.rs:LL:CC | LL | unsafe { *x = 0 }; - | ^^^^^^ not granting access to tag because incompatible item is protected: [SharedReadOnly for (call ID)] + | ^^^^^^ not granting access to tag because incompatible item [SharedReadOnly for ] is protected by call ID | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information diff --git a/tests/fail/stacked_borrows/issue-miri-1050-1.rs b/tests/fail/stacked_borrows/issue-miri-1050-1.rs index c1fc695e0d..1e44cc6c80 100644 --- a/tests/fail/stacked_borrows/issue-miri-1050-1.rs +++ b/tests/fail/stacked_borrows/issue-miri-1050-1.rs @@ -1,8 +1,8 @@ -// error-pattern: pointer to 4 bytes starting at offset 0 is out-of-bounds +//@error-pattern: pointer to 4 bytes starting at offset 0 is out-of-bounds fn main() { unsafe { let ptr = Box::into_raw(Box::new(0u16)); - Box::from_raw(ptr as *mut u32); + drop(Box::from_raw(ptr as *mut u32)); } } diff --git a/tests/fail/stacked_borrows/issue-miri-1050-1.stderr b/tests/fail/stacked_borrows/issue-miri-1050-1.stderr index 946e3e8e66..4d8488fa76 100644 --- a/tests/fail/stacked_borrows/issue-miri-1050-1.stderr +++ b/tests/fail/stacked_borrows/issue-miri-1050-1.stderr @@ -12,8 +12,8 @@ LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) note: inside `main` at $DIR/issue-miri-1050-1.rs:LL:CC --> $DIR/issue-miri-1050-1.rs:LL:CC | -LL | Box::from_raw(ptr as *mut u32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | drop(Box::from_raw(ptr as *mut u32)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/tests/fail/stacked_borrows/issue-miri-1050-2.rs b/tests/fail/stacked_borrows/issue-miri-1050-2.rs index 2a969686d4..6e90559a9e 100644 --- a/tests/fail/stacked_borrows/issue-miri-1050-2.rs +++ b/tests/fail/stacked_borrows/issue-miri-1050-2.rs @@ -1,9 +1,9 @@ -// error-pattern: is a dangling pointer +//@error-pattern: is a dangling pointer use std::ptr::NonNull; fn main() { unsafe { let ptr = NonNull::::dangling(); - Box::from_raw(ptr.as_ptr()); + drop(Box::from_raw(ptr.as_ptr())); } } diff --git a/tests/fail/stacked_borrows/issue-miri-1050-2.stderr b/tests/fail/stacked_borrows/issue-miri-1050-2.stderr index 33ac311766..562a82fb61 100644 --- a/tests/fail/stacked_borrows/issue-miri-1050-2.stderr +++ b/tests/fail/stacked_borrows/issue-miri-1050-2.stderr @@ -12,8 +12,8 @@ LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) note: inside `main` at $DIR/issue-miri-1050-2.rs:LL:CC --> $DIR/issue-miri-1050-2.rs:LL:CC | -LL | Box::from_raw(ptr.as_ptr()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | drop(Box::from_raw(ptr.as_ptr())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/tests/fail/stacked_borrows/load_invalid_mut.rs b/tests/fail/stacked_borrows/load_invalid_mut.rs index c2c4ce6726..4e3a16b96d 100644 --- a/tests/fail/stacked_borrows/load_invalid_mut.rs +++ b/tests/fail/stacked_borrows/load_invalid_mut.rs @@ -1,5 +1,5 @@ // Make sure we catch this even without validation -// compile-flags: -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-validation // Make sure that we cannot load from memory a `&mut` that got already invalidated. fn main() { @@ -8,5 +8,5 @@ fn main() { let xref = unsafe { &mut *xraw }; let xref_in_mem = Box::new(xref); let _val = unsafe { *xraw }; // invalidate xref - let _val = *xref_in_mem; //~ ERROR borrow stack + let _val = *xref_in_mem; //~ ERROR: /reborrow .* tag does not exist in the borrow stack/ } diff --git a/tests/fail/stacked_borrows/load_invalid_mut.stderr b/tests/fail/stacked_borrows/load_invalid_mut.stderr index 293d85cc9f..f8e03c631e 100644 --- a/tests/fail/stacked_borrows/load_invalid_mut.stderr +++ b/tests/fail/stacked_borrows/load_invalid_mut.stderr @@ -1,10 +1,10 @@ -error: Undefined Behavior: trying to reborrow for Unique permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for Unique permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location --> $DIR/load_invalid_mut.rs:LL:CC | LL | let _val = *xref_in_mem; | ^^^^^^^^^^^^ | | - | trying to reborrow for Unique permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for Unique permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x0..0x4] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/stacked_borrows/load_invalid_shr.rs b/tests/fail/stacked_borrows/load_invalid_shr.rs index 7d681f649a..fb279e4710 100644 --- a/tests/fail/stacked_borrows/load_invalid_shr.rs +++ b/tests/fail/stacked_borrows/load_invalid_shr.rs @@ -1,5 +1,5 @@ // Make sure we catch this even without validation -// compile-flags: -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-validation // Make sure that we cannot load from memory a `&` that got already invalidated. fn main() { @@ -8,5 +8,5 @@ fn main() { let xref = unsafe { &*xraw }; let xref_in_mem = Box::new(xref); unsafe { *xraw = 42 }; // unfreeze - let _val = *xref_in_mem; //~ ERROR borrow stack + let _val = *xref_in_mem; //~ ERROR: /reborrow .* tag does not exist in the borrow stack/ } diff --git a/tests/fail/stacked_borrows/load_invalid_shr.stderr b/tests/fail/stacked_borrows/load_invalid_shr.stderr index 5749daa51f..a9546c9a76 100644 --- a/tests/fail/stacked_borrows/load_invalid_shr.stderr +++ b/tests/fail/stacked_borrows/load_invalid_shr.stderr @@ -1,10 +1,10 @@ -error: Undefined Behavior: trying to reborrow for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location --> $DIR/load_invalid_shr.rs:LL:CC | LL | let _val = *xref_in_mem; | ^^^^^^^^^^^^ | | - | trying to reborrow for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x0..0x4] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/stacked_borrows/mut_exclusive_violation1.rs b/tests/fail/stacked_borrows/mut_exclusive_violation1.rs index a1cb7107ee..f6fcdf1acd 100644 --- a/tests/fail/stacked_borrows/mut_exclusive_violation1.rs +++ b/tests/fail/stacked_borrows/mut_exclusive_violation1.rs @@ -24,7 +24,7 @@ fn unknown_code_1(x: &i32) { fn unknown_code_2() { unsafe { - *LEAK = 7; //~ ERROR borrow stack + *LEAK = 7; //~ ERROR: /write access .* tag does not exist in the borrow stack/ } } diff --git a/tests/fail/stacked_borrows/mut_exclusive_violation2.rs b/tests/fail/stacked_borrows/mut_exclusive_violation2.rs index a5bf8353ba..2305ce7465 100644 --- a/tests/fail/stacked_borrows/mut_exclusive_violation2.rs +++ b/tests/fail/stacked_borrows/mut_exclusive_violation2.rs @@ -7,6 +7,6 @@ fn main() { let mut ptr2 = ptr1.clone(); let raw1 = ptr1.as_mut(); let _raw2 = ptr2.as_mut(); - let _val = *raw1; //~ ERROR borrow stack + let _val = *raw1; //~ ERROR: /read access .* tag does not exist in the borrow stack/ } } diff --git a/tests/fail/stacked_borrows/newtype_retagging.rs b/tests/fail/stacked_borrows/newtype_retagging.rs index 8f932f0808..f9cceb761a 100644 --- a/tests/fail/stacked_borrows/newtype_retagging.rs +++ b/tests/fail/stacked_borrows/newtype_retagging.rs @@ -1,5 +1,5 @@ -// compile-flags: -Zmiri-retag-fields -// error-pattern: incompatible item is protected +//@compile-flags: -Zmiri-retag-fields +//@error-pattern: is protected by call struct Newtype<'a>(&'a mut i32); fn dealloc_while_running(_n: Newtype<'_>, dealloc: impl FnOnce()) { diff --git a/tests/fail/stacked_borrows/newtype_retagging.stderr b/tests/fail/stacked_borrows/newtype_retagging.stderr index 860a628492..d9aebecfda 100644 --- a/tests/fail/stacked_borrows/newtype_retagging.stderr +++ b/tests/fail/stacked_borrows/newtype_retagging.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: not granting access to tag because incompatible item is protected: [Unique for (call ID)] +error: Undefined Behavior: not granting access to tag because incompatible item [Unique for ] is protected by call ID --> RUSTLIB/alloc/src/boxed.rs:LL:CC | LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not granting access to tag because incompatible item is protected: [Unique for (call ID)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not granting access to tag because incompatible item [Unique for ] is protected by call ID | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information diff --git a/tests/fail/stacked_borrows/outdated_local.rs b/tests/fail/stacked_borrows/outdated_local.rs index 4cb655366e..4262157d1e 100644 --- a/tests/fail/stacked_borrows/outdated_local.rs +++ b/tests/fail/stacked_borrows/outdated_local.rs @@ -3,7 +3,7 @@ fn main() { let y: *const i32 = &x; x = 1; // this invalidates y by reactivating the lowermost uniq borrow for this local - assert_eq!(unsafe { *y }, 1); //~ ERROR borrow stack + assert_eq!(unsafe { *y }, 1); //~ ERROR: /read access .* tag does not exist in the borrow stack/ assert_eq!(x, 1); } diff --git a/tests/fail/stacked_borrows/pass_invalid_mut.rs b/tests/fail/stacked_borrows/pass_invalid_mut.rs index d8a53b7a96..55c93981e3 100644 --- a/tests/fail/stacked_borrows/pass_invalid_mut.rs +++ b/tests/fail/stacked_borrows/pass_invalid_mut.rs @@ -6,5 +6,5 @@ fn main() { let xraw = x as *mut _; let xref = unsafe { &mut *xraw }; let _val = unsafe { *xraw }; // invalidate xref - foo(xref); //~ ERROR borrow stack + foo(xref); //~ ERROR: /reborrow .* tag does not exist in the borrow stack/ } diff --git a/tests/fail/stacked_borrows/pass_invalid_mut.stderr b/tests/fail/stacked_borrows/pass_invalid_mut.stderr index 933ef1ad59..f254fd16dc 100644 --- a/tests/fail/stacked_borrows/pass_invalid_mut.stderr +++ b/tests/fail/stacked_borrows/pass_invalid_mut.stderr @@ -1,10 +1,10 @@ -error: Undefined Behavior: trying to reborrow for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location --> $DIR/pass_invalid_mut.rs:LL:CC | LL | foo(xref); | ^^^^ | | - | trying to reborrow for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x0..0x4] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/stacked_borrows/pass_invalid_shr.rs b/tests/fail/stacked_borrows/pass_invalid_shr.rs index 091604a283..db8e6681e0 100644 --- a/tests/fail/stacked_borrows/pass_invalid_shr.rs +++ b/tests/fail/stacked_borrows/pass_invalid_shr.rs @@ -6,5 +6,5 @@ fn main() { let xraw = x as *mut _; let xref = unsafe { &*xraw }; unsafe { *xraw = 42 }; // unfreeze - foo(xref); //~ ERROR borrow stack + foo(xref); //~ ERROR: /reborrow .* tag does not exist in the borrow stack/ } diff --git a/tests/fail/stacked_borrows/pass_invalid_shr.stderr b/tests/fail/stacked_borrows/pass_invalid_shr.stderr index 02cf62f033..28500836aa 100644 --- a/tests/fail/stacked_borrows/pass_invalid_shr.stderr +++ b/tests/fail/stacked_borrows/pass_invalid_shr.stderr @@ -1,10 +1,10 @@ -error: Undefined Behavior: trying to reborrow for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location --> $DIR/pass_invalid_shr.rs:LL:CC | LL | foo(xref); | ^^^^ | | - | trying to reborrow for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for SharedReadOnly permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x0..0x4] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/stacked_borrows/pointer_smuggling.rs b/tests/fail/stacked_borrows/pointer_smuggling.rs index f724cdd2a7..e1c3ff4928 100644 --- a/tests/fail/stacked_borrows/pointer_smuggling.rs +++ b/tests/fail/stacked_borrows/pointer_smuggling.rs @@ -8,7 +8,7 @@ fn fun1(x: &mut u8) { fn fun2() { // Now we use a pointer we are not allowed to use - let _x = unsafe { *PTR }; //~ ERROR borrow stack + let _x = unsafe { *PTR }; //~ ERROR: /read access .* tag does not exist in the borrow stack/ } fn main() { diff --git a/tests/fail/stacked_borrows/raw_tracking.rs b/tests/fail/stacked_borrows/raw_tracking.rs index 49fe983125..15306e0825 100644 --- a/tests/fail/stacked_borrows/raw_tracking.rs +++ b/tests/fail/stacked_borrows/raw_tracking.rs @@ -6,6 +6,6 @@ fn main() { let raw2 = &mut l as *mut _; // invalidates raw1 // Without raw pointer tracking, Stacked Borrows cannot distinguish raw1 and raw2, and thus // fails to realize that raw1 should not be used any more. - unsafe { *raw1 = 13 }; //~ ERROR does not exist in the borrow stack + unsafe { *raw1 = 13 }; //~ ERROR: /write access .* tag does not exist in the borrow stack/ unsafe { *raw2 = 13 }; } diff --git a/tests/fail/stacked_borrows/return_invalid_mut.rs b/tests/fail/stacked_borrows/return_invalid_mut.rs index 54004ec438..1f379d4a77 100644 --- a/tests/fail/stacked_borrows/return_invalid_mut.rs +++ b/tests/fail/stacked_borrows/return_invalid_mut.rs @@ -3,7 +3,7 @@ fn foo(x: &mut (i32, i32)) -> &mut i32 { let xraw = x as *mut (i32, i32); let ret = unsafe { &mut (*xraw).1 }; let _val = unsafe { *xraw }; // invalidate xref - ret //~ ERROR borrow stack + ret //~ ERROR: /reborrow .* tag does not exist in the borrow stack/ } fn main() { diff --git a/tests/fail/stacked_borrows/return_invalid_mut.stderr b/tests/fail/stacked_borrows/return_invalid_mut.stderr index 230c3e82ff..7c80070443 100644 --- a/tests/fail/stacked_borrows/return_invalid_mut.stderr +++ b/tests/fail/stacked_borrows/return_invalid_mut.stderr @@ -1,10 +1,10 @@ -error: Undefined Behavior: trying to reborrow for Unique permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for Unique permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location --> $DIR/return_invalid_mut.rs:LL:CC | LL | ret | ^^^ | | - | trying to reborrow for Unique permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for Unique permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x4..0x8] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/stacked_borrows/return_invalid_mut_option.rs b/tests/fail/stacked_borrows/return_invalid_mut_option.rs index ccdb3dc505..da3401260e 100644 --- a/tests/fail/stacked_borrows/return_invalid_mut_option.rs +++ b/tests/fail/stacked_borrows/return_invalid_mut_option.rs @@ -10,7 +10,7 @@ fn foo(x: &mut (i32, i32)) -> Option<&mut i32> { fn main() { match foo(&mut (1, 2)) { - Some(_x) => {} //~ ERROR borrow stack + Some(_x) => {} //~ ERROR: /reborrow .* tag does not exist in the borrow stack/ None => {} } } diff --git a/tests/fail/stacked_borrows/return_invalid_mut_option.stderr b/tests/fail/stacked_borrows/return_invalid_mut_option.stderr index f278113e42..cab997b473 100644 --- a/tests/fail/stacked_borrows/return_invalid_mut_option.stderr +++ b/tests/fail/stacked_borrows/return_invalid_mut_option.stderr @@ -1,10 +1,10 @@ -error: Undefined Behavior: trying to reborrow for Unique permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for Unique permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location --> $DIR/return_invalid_mut_option.rs:LL:CC | LL | Some(_x) => {} | ^^ | | - | trying to reborrow for Unique permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for Unique permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x4..0x8] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/stacked_borrows/return_invalid_mut_tuple.rs b/tests/fail/stacked_borrows/return_invalid_mut_tuple.rs index fbd9a6e5d2..2184d20b1c 100644 --- a/tests/fail/stacked_borrows/return_invalid_mut_tuple.rs +++ b/tests/fail/stacked_borrows/return_invalid_mut_tuple.rs @@ -8,5 +8,5 @@ fn foo(x: &mut (i32, i32)) -> (&mut i32,) { } fn main() { - foo(&mut (1, 2)).0; //~ ERROR: borrow stack + foo(&mut (1, 2)).0; //~ ERROR: /reborrow .* tag does not exist in the borrow stack/ } diff --git a/tests/fail/stacked_borrows/return_invalid_mut_tuple.stderr b/tests/fail/stacked_borrows/return_invalid_mut_tuple.stderr index 9465fd072e..46ef6a737a 100644 --- a/tests/fail/stacked_borrows/return_invalid_mut_tuple.stderr +++ b/tests/fail/stacked_borrows/return_invalid_mut_tuple.stderr @@ -1,10 +1,10 @@ -error: Undefined Behavior: trying to reborrow for Unique permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for Unique permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location --> $DIR/return_invalid_mut_tuple.rs:LL:CC | LL | foo(&mut (1, 2)).0; | ^^^^^^^^^^^^^^^^^^ | | - | trying to reborrow for Unique permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for Unique permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x4..0x8] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/stacked_borrows/return_invalid_shr.rs b/tests/fail/stacked_borrows/return_invalid_shr.rs index eab026f9a4..b1d16c025e 100644 --- a/tests/fail/stacked_borrows/return_invalid_shr.rs +++ b/tests/fail/stacked_borrows/return_invalid_shr.rs @@ -3,7 +3,7 @@ fn foo(x: &mut (i32, i32)) -> &i32 { let xraw = x as *mut (i32, i32); let ret = unsafe { &(*xraw).1 }; unsafe { *xraw = (42, 23) }; // unfreeze - ret //~ ERROR borrow stack + ret //~ ERROR: /reborrow .* tag does not exist in the borrow stack/ } fn main() { diff --git a/tests/fail/stacked_borrows/return_invalid_shr.stderr b/tests/fail/stacked_borrows/return_invalid_shr.stderr index 75ea8bb89e..6f745b69fc 100644 --- a/tests/fail/stacked_borrows/return_invalid_shr.stderr +++ b/tests/fail/stacked_borrows/return_invalid_shr.stderr @@ -1,10 +1,10 @@ -error: Undefined Behavior: trying to reborrow for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location --> $DIR/return_invalid_shr.rs:LL:CC | LL | ret | ^^^ | | - | trying to reborrow for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x4..0x8] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/stacked_borrows/return_invalid_shr_option.rs b/tests/fail/stacked_borrows/return_invalid_shr_option.rs index 42b4871c46..e9faa94520 100644 --- a/tests/fail/stacked_borrows/return_invalid_shr_option.rs +++ b/tests/fail/stacked_borrows/return_invalid_shr_option.rs @@ -9,7 +9,7 @@ fn foo(x: &mut (i32, i32)) -> Option<&i32> { fn main() { match foo(&mut (1, 2)) { - Some(_x) => {} //~ ERROR borrow stack + Some(_x) => {} //~ ERROR: /reborrow .* tag does not exist in the borrow stack/ None => {} } } diff --git a/tests/fail/stacked_borrows/return_invalid_shr_option.stderr b/tests/fail/stacked_borrows/return_invalid_shr_option.stderr index 27656b46cc..2441c9aa1c 100644 --- a/tests/fail/stacked_borrows/return_invalid_shr_option.stderr +++ b/tests/fail/stacked_borrows/return_invalid_shr_option.stderr @@ -1,10 +1,10 @@ -error: Undefined Behavior: trying to reborrow for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location --> $DIR/return_invalid_shr_option.rs:LL:CC | LL | Some(_x) => {} | ^^ | | - | trying to reborrow for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x4..0x8] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/stacked_borrows/return_invalid_shr_tuple.rs b/tests/fail/stacked_borrows/return_invalid_shr_tuple.rs index d7494d6ee6..11eb4de56c 100644 --- a/tests/fail/stacked_borrows/return_invalid_shr_tuple.rs +++ b/tests/fail/stacked_borrows/return_invalid_shr_tuple.rs @@ -8,5 +8,5 @@ fn foo(x: &mut (i32, i32)) -> (&i32,) { } fn main() { - foo(&mut (1, 2)).0; //~ ERROR borrow stack + foo(&mut (1, 2)).0; //~ ERROR: /reborrow .* tag does not exist in the borrow stack/ } diff --git a/tests/fail/stacked_borrows/return_invalid_shr_tuple.stderr b/tests/fail/stacked_borrows/return_invalid_shr_tuple.stderr index 9fdc878086..5102f7cb53 100644 --- a/tests/fail/stacked_borrows/return_invalid_shr_tuple.stderr +++ b/tests/fail/stacked_borrows/return_invalid_shr_tuple.stderr @@ -1,10 +1,10 @@ -error: Undefined Behavior: trying to reborrow for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location --> $DIR/return_invalid_shr_tuple.rs:LL:CC | LL | foo(&mut (1, 2)).0; | ^^^^^^^^^^^^^^^^^^ | | - | trying to reborrow for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x4..0x8] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/stacked_borrows/shared_rw_borrows_are_weak1.rs b/tests/fail/stacked_borrows/shared_rw_borrows_are_weak1.rs index a08d2b716e..01b2775d9d 100644 --- a/tests/fail/stacked_borrows/shared_rw_borrows_are_weak1.rs +++ b/tests/fail/stacked_borrows/shared_rw_borrows_are_weak1.rs @@ -11,6 +11,6 @@ fn main() { let y: &mut Cell = mem::transmute(&mut *x); // launder lifetime let shr_rw = &*x; // thanks to interior mutability this will be a SharedReadWrite shr_rw.set(1); - y.get_mut(); //~ ERROR borrow stack + y.get_mut(); //~ ERROR: /reborrow .* tag does not exist in the borrow stack/ } } diff --git a/tests/fail/stacked_borrows/shared_rw_borrows_are_weak1.stderr b/tests/fail/stacked_borrows/shared_rw_borrows_are_weak1.stderr index 9909778ab2..a412add67e 100644 --- a/tests/fail/stacked_borrows/shared_rw_borrows_are_weak1.stderr +++ b/tests/fail/stacked_borrows/shared_rw_borrows_are_weak1.stderr @@ -1,10 +1,10 @@ -error: Undefined Behavior: trying to reborrow for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location --> $DIR/shared_rw_borrows_are_weak1.rs:LL:CC | LL | y.get_mut(); | ^^^^^^^^^^^ | | - | trying to reborrow for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for SharedReadWrite permission at ALLOC[0x0], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x0..0x4] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/stacked_borrows/shared_rw_borrows_are_weak2.rs b/tests/fail/stacked_borrows/shared_rw_borrows_are_weak2.rs index 07163456ce..012e9561ca 100644 --- a/tests/fail/stacked_borrows/shared_rw_borrows_are_weak2.rs +++ b/tests/fail/stacked_borrows/shared_rw_borrows_are_weak2.rs @@ -1,7 +1,7 @@ // We want to test that granting a SharedReadWrite will be added // *below* an already granted SharedReadWrite -- so writing to // the SharedReadWrite will invalidate the SharedReadWrite. -// normalize-stderr-test: "0x[0-9a-fA-F]+" -> "$$HEX" +//@normalize-stderr-test: "0x[0-9a-fA-F]+" -> "$$HEX" use std::cell::RefCell; use std::mem; @@ -12,6 +12,6 @@ fn main() { let y: &i32 = mem::transmute(&*x.borrow()); // launder lifetime let shr_rw = &*x; // thanks to interior mutability this will be a SharedReadWrite shr_rw.replace(1); - let _val = *y; //~ ERROR borrow stack + let _val = *y; //~ ERROR: /read access .* tag does not exist in the borrow stack/ } } diff --git a/tests/fail/stacked_borrows/shr_frozen_violation1.rs b/tests/fail/stacked_borrows/shr_frozen_violation1.rs index a6dd9ef4c2..d1322dc6e5 100644 --- a/tests/fail/stacked_borrows/shr_frozen_violation1.rs +++ b/tests/fail/stacked_borrows/shr_frozen_violation1.rs @@ -10,6 +10,6 @@ fn main() { fn unknown_code(x: &i32) { unsafe { - *(x as *const i32 as *mut i32) = 7; //~ ERROR only grants SharedReadOnly permission + *(x as *const i32 as *mut i32) = 7; //~ ERROR: /write access .* only grants SharedReadOnly permission/ } } diff --git a/tests/fail/stacked_borrows/static_memory_modification.rs b/tests/fail/stacked_borrows/static_memory_modification.rs index 417a03bb03..84d7878b26 100644 --- a/tests/fail/stacked_borrows/static_memory_modification.rs +++ b/tests/fail/stacked_borrows/static_memory_modification.rs @@ -3,6 +3,6 @@ static X: usize = 5; #[allow(mutable_transmutes)] fn main() { let _x = unsafe { - std::mem::transmute::<&usize, &mut usize>(&X) //~ ERROR writing to alloc1 which is read-only + std::mem::transmute::<&usize, &mut usize>(&X) //~ ERROR: writing to alloc1 which is read-only }; } diff --git a/tests/fail/stacked_borrows/transmute-is-no-escape.rs b/tests/fail/stacked_borrows/transmute-is-no-escape.rs index 45035683d5..233b927dfc 100644 --- a/tests/fail/stacked_borrows/transmute-is-no-escape.rs +++ b/tests/fail/stacked_borrows/transmute-is-no-escape.rs @@ -10,5 +10,5 @@ fn main() { let _raw: *mut i32 = unsafe { mem::transmute(&mut x[0]) }; // `raw` still carries a tag, so we get another pointer to the same location that does not carry a tag let raw = (&mut x[1] as *mut i32).wrapping_offset(-1); - unsafe { *raw = 13 }; //~ ERROR borrow stack + unsafe { *raw = 13 }; //~ ERROR: /write access .* tag does not exist in the borrow stack/ } diff --git a/tests/fail/stacked_borrows/unescaped_local.rs b/tests/fail/stacked_borrows/unescaped_local.rs index c994f6c381..49c0e66d85 100644 --- a/tests/fail/stacked_borrows/unescaped_local.rs +++ b/tests/fail/stacked_borrows/unescaped_local.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance // Make sure we cannot use raw ptrs to access a local that // we took the direct address of. @@ -7,6 +7,6 @@ fn main() { let raw = &mut x as *mut i32 as usize as *mut i32; let _ptr = &mut x; unsafe { - *raw = 13; //~ ERROR borrow stack + *raw = 13; //~ ERROR: /write access .* no exposed tags/ } } diff --git a/tests/fail/stacked_borrows/unescaped_static.rs b/tests/fail/stacked_borrows/unescaped_static.rs index 0f0467fc5c..a25d9b5162 100644 --- a/tests/fail/stacked_borrows/unescaped_static.rs +++ b/tests/fail/stacked_borrows/unescaped_static.rs @@ -3,5 +3,5 @@ static ARRAY: [u8; 2] = [0, 1]; fn main() { let ptr_to_first = &ARRAY[0] as *const u8; // Illegally use this to access the 2nd element. - let _val = unsafe { *ptr_to_first.add(1) }; //~ ERROR borrow stack + let _val = unsafe { *ptr_to_first.add(1) }; //~ ERROR: /read access .* tag does not exist in the borrow stack/ } diff --git a/tests/fail/stacked_borrows/vtable.rs b/tests/fail/stacked_borrows/vtable.rs index dd9ba1dfb2..27e035c404 100644 --- a/tests/fail/stacked_borrows/vtable.rs +++ b/tests/fail/stacked_borrows/vtable.rs @@ -1,4 +1,4 @@ -// error-pattern: vtable pointer does not have permission +//@error-pattern: vtable pointer does not have permission #![feature(ptr_metadata)] trait Foo {} diff --git a/tests/fail/stacked_borrows/zst_slice.rs b/tests/fail/stacked_borrows/zst_slice.rs index d45b3dcac0..11065186fc 100644 --- a/tests/fail/stacked_borrows/zst_slice.rs +++ b/tests/fail/stacked_borrows/zst_slice.rs @@ -1,5 +1,5 @@ -// compile-flags: -Zmiri-strict-provenance -// error-pattern: does not exist in the borrow stack +//@compile-flags: -Zmiri-strict-provenance +//@error-pattern: /reborrow .* tag does not exist in the borrow stack/ fn main() { unsafe { diff --git a/tests/fail/stacked_borrows/zst_slice.stderr b/tests/fail/stacked_borrows/zst_slice.stderr index 2878f4c069..d47e967b54 100644 --- a/tests/fail/stacked_borrows/zst_slice.stderr +++ b/tests/fail/stacked_borrows/zst_slice.stderr @@ -1,10 +1,10 @@ -error: Undefined Behavior: trying to reborrow for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location +error: Undefined Behavior: trying to reborrow from for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location --> RUSTLIB/core/src/slice/mod.rs:LL:CC | LL | unsafe { &*index.get_unchecked(self) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | trying to reborrow for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location + | trying to reborrow from for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location | this error occurs as part of a reborrow at ALLOC[0x4..0x8] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental diff --git a/tests/fail/static_memory_modification1.rs b/tests/fail/static_memory_modification1.rs index 6284fec160..66794e7535 100644 --- a/tests/fail/static_memory_modification1.rs +++ b/tests/fail/static_memory_modification1.rs @@ -1,12 +1,12 @@ // Stacked Borrows detects that we are casting & to &mut and so it changes why we fail -// compile-flags: -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-disable-stacked-borrows static X: usize = 5; #[allow(mutable_transmutes)] fn main() { unsafe { - *std::mem::transmute::<&usize, &mut usize>(&X) = 6; //~ ERROR read-only + *std::mem::transmute::<&usize, &mut usize>(&X) = 6; //~ ERROR: read-only assert_eq!(X, 6); } } diff --git a/tests/fail/static_memory_modification2.rs b/tests/fail/static_memory_modification2.rs index 558070d8a7..d8ae3a57c5 100644 --- a/tests/fail/static_memory_modification2.rs +++ b/tests/fail/static_memory_modification2.rs @@ -1,5 +1,5 @@ // Stacked Borrows detects that we are casting & to &mut and so it changes why we fail -// compile-flags: -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-disable-stacked-borrows use std::mem::transmute; @@ -7,6 +7,6 @@ use std::mem::transmute; fn main() { unsafe { let s = "this is a test"; - transmute::<&[u8], &mut [u8]>(s.as_bytes())[4] = 42; //~ ERROR read-only + transmute::<&[u8], &mut [u8]>(s.as_bytes())[4] = 42; //~ ERROR: read-only } } diff --git a/tests/fail/static_memory_modification3.rs b/tests/fail/static_memory_modification3.rs index 93df1c5945..b8e2c6470f 100644 --- a/tests/fail/static_memory_modification3.rs +++ b/tests/fail/static_memory_modification3.rs @@ -1,5 +1,5 @@ // Stacked Borrows detects that we are casting & to &mut and so it changes why we fail -// compile-flags: -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-disable-stacked-borrows use std::mem::transmute; @@ -7,6 +7,6 @@ use std::mem::transmute; fn main() { unsafe { let bs = b"this is a test"; - transmute::<&[u8], &mut [u8]>(bs)[4] = 42; //~ ERROR read-only + transmute::<&[u8], &mut [u8]>(bs)[4] = 42; //~ ERROR: read-only } } diff --git a/tests/fail/sync/libc_pthread_cond_double_destroy.rs b/tests/fail/sync/libc_pthread_cond_double_destroy.rs index 18be75b308..d0a4ac46cb 100644 --- a/tests/fail/sync/libc_pthread_cond_double_destroy.rs +++ b/tests/fail/sync/libc_pthread_cond_double_destroy.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] /// Test that destroying a pthread_cond twice fails, even without a check for number validity @@ -17,6 +17,6 @@ fn main() { libc::pthread_cond_destroy(cond.as_mut_ptr()); libc::pthread_cond_destroy(cond.as_mut_ptr()); - //~^ ERROR Undefined Behavior: using uninitialized data, but this operation requires initialized memory + //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory } } diff --git a/tests/fail/sync/libc_pthread_condattr_double_destroy.rs b/tests/fail/sync/libc_pthread_condattr_double_destroy.rs index 1543a5841a..c64b323813 100644 --- a/tests/fail/sync/libc_pthread_condattr_double_destroy.rs +++ b/tests/fail/sync/libc_pthread_condattr_double_destroy.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] /// Test that destroying a pthread_condattr twice fails, even without a check for number validity @@ -14,6 +14,6 @@ fn main() { libc::pthread_condattr_destroy(attr.as_mut_ptr()); libc::pthread_condattr_destroy(attr.as_mut_ptr()); - //~^ ERROR Undefined Behavior: using uninitialized data, but this operation requires initialized memory + //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory } } diff --git a/tests/fail/sync/libc_pthread_mutex_NULL_deadlock.rs b/tests/fail/sync/libc_pthread_mutex_NULL_deadlock.rs index 3a737b2e3e..8797e895d8 100644 --- a/tests/fail/sync/libc_pthread_mutex_NULL_deadlock.rs +++ b/tests/fail/sync/libc_pthread_mutex_NULL_deadlock.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows // // Check that if we pass NULL attribute, then we get the default mutex type. @@ -11,6 +11,6 @@ fn main() { let mut mutex: libc::pthread_mutex_t = std::mem::zeroed(); assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, std::ptr::null() as *const _), 0); assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); - libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR Undefined Behavior: trying to acquire already locked default mutex + libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: Undefined Behavior: trying to acquire already locked default mutex } } diff --git a/tests/fail/sync/libc_pthread_mutex_deadlock.rs b/tests/fail/sync/libc_pthread_mutex_deadlock.rs index 5d04635a36..7da6e51600 100644 --- a/tests/fail/sync/libc_pthread_mutex_deadlock.rs +++ b/tests/fail/sync/libc_pthread_mutex_deadlock.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] diff --git a/tests/fail/sync/libc_pthread_mutex_default_deadlock.rs b/tests/fail/sync/libc_pthread_mutex_default_deadlock.rs index 0f6f570d70..70a85aa0f9 100644 --- a/tests/fail/sync/libc_pthread_mutex_default_deadlock.rs +++ b/tests/fail/sync/libc_pthread_mutex_default_deadlock.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows // // Check that if we do not set the mutex type, it is the default. @@ -12,6 +12,6 @@ fn main() { let mut mutex: libc::pthread_mutex_t = std::mem::zeroed(); assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0); assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); - libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR Undefined Behavior: trying to acquire already locked default mutex + libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: Undefined Behavior: trying to acquire already locked default mutex } } diff --git a/tests/fail/sync/libc_pthread_mutex_destroy_locked.rs b/tests/fail/sync/libc_pthread_mutex_destroy_locked.rs index 85a37db341..fc69ace369 100644 --- a/tests/fail/sync/libc_pthread_mutex_destroy_locked.rs +++ b/tests/fail/sync/libc_pthread_mutex_destroy_locked.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] @@ -14,6 +14,6 @@ fn main() { let mut mutex: libc::pthread_mutex_t = std::mem::zeroed(); assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0); assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); - libc::pthread_mutex_destroy(&mut mutex as *mut _); //~ ERROR destroyed a locked mutex + libc::pthread_mutex_destroy(&mut mutex as *mut _); //~ ERROR: destroyed a locked mutex } } diff --git a/tests/fail/sync/libc_pthread_mutex_double_destroy.rs b/tests/fail/sync/libc_pthread_mutex_double_destroy.rs index 3710810cd2..9b539afc19 100644 --- a/tests/fail/sync/libc_pthread_mutex_double_destroy.rs +++ b/tests/fail/sync/libc_pthread_mutex_double_destroy.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] /// Test that destroying a pthread_mutex twice fails, even without a check for number validity @@ -18,6 +18,6 @@ fn main() { libc::pthread_mutex_destroy(mutex.as_mut_ptr()); libc::pthread_mutex_destroy(mutex.as_mut_ptr()); - //~^ ERROR Undefined Behavior: using uninitialized data, but this operation requires initialized memory + //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory } } diff --git a/tests/fail/sync/libc_pthread_mutex_normal_deadlock.rs b/tests/fail/sync/libc_pthread_mutex_normal_deadlock.rs index 7e29a41920..944e86e106 100644 --- a/tests/fail/sync/libc_pthread_mutex_normal_deadlock.rs +++ b/tests/fail/sync/libc_pthread_mutex_normal_deadlock.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] @@ -14,6 +14,6 @@ fn main() { let mut mutex: libc::pthread_mutex_t = std::mem::zeroed(); assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0); assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); - libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR deadlock: the evaluated program deadlocked + libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: deadlock: the evaluated program deadlocked } } diff --git a/tests/fail/sync/libc_pthread_mutex_normal_unlock_unlocked.rs b/tests/fail/sync/libc_pthread_mutex_normal_unlock_unlocked.rs index 1f1a2ef34b..c2bdce82b6 100644 --- a/tests/fail/sync/libc_pthread_mutex_normal_unlock_unlocked.rs +++ b/tests/fail/sync/libc_pthread_mutex_normal_unlock_unlocked.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] @@ -15,6 +15,6 @@ fn main() { assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0); assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0); - libc::pthread_mutex_unlock(&mut mutex as *mut _); //~ ERROR was not locked + libc::pthread_mutex_unlock(&mut mutex as *mut _); //~ ERROR: was not locked } } diff --git a/tests/fail/sync/libc_pthread_mutex_wrong_owner.rs b/tests/fail/sync/libc_pthread_mutex_wrong_owner.rs index d69929d4ed..eea4db7115 100644 --- a/tests/fail/sync/libc_pthread_mutex_wrong_owner.rs +++ b/tests/fail/sync/libc_pthread_mutex_wrong_owner.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] diff --git a/tests/fail/sync/libc_pthread_mutexattr_double_destroy.rs b/tests/fail/sync/libc_pthread_mutexattr_double_destroy.rs index c232780ee2..620dbb94a7 100644 --- a/tests/fail/sync/libc_pthread_mutexattr_double_destroy.rs +++ b/tests/fail/sync/libc_pthread_mutexattr_double_destroy.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] /// Test that destroying a pthread_mutexattr twice fails, even without a check for number validity @@ -14,6 +14,6 @@ fn main() { libc::pthread_mutexattr_destroy(attr.as_mut_ptr()); libc::pthread_mutexattr_destroy(attr.as_mut_ptr()); - //~^ ERROR Undefined Behavior: using uninitialized data, but this operation requires initialized memory + //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory } } diff --git a/tests/fail/sync/libc_pthread_rwlock_destroy_read_locked.rs b/tests/fail/sync/libc_pthread_rwlock_destroy_read_locked.rs index 8750a7388f..67b77ff286 100644 --- a/tests/fail/sync/libc_pthread_rwlock_destroy_read_locked.rs +++ b/tests/fail/sync/libc_pthread_rwlock_destroy_read_locked.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] @@ -8,6 +8,6 @@ fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); unsafe { assert_eq!(libc::pthread_rwlock_rdlock(rw.get()), 0); - libc::pthread_rwlock_destroy(rw.get()); //~ ERROR destroyed a locked rwlock + libc::pthread_rwlock_destroy(rw.get()); //~ ERROR: destroyed a locked rwlock } } diff --git a/tests/fail/sync/libc_pthread_rwlock_destroy_write_locked.rs b/tests/fail/sync/libc_pthread_rwlock_destroy_write_locked.rs index aecccfa503..5bc5fe3c6b 100644 --- a/tests/fail/sync/libc_pthread_rwlock_destroy_write_locked.rs +++ b/tests/fail/sync/libc_pthread_rwlock_destroy_write_locked.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] @@ -8,6 +8,6 @@ fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); unsafe { assert_eq!(libc::pthread_rwlock_wrlock(rw.get()), 0); - libc::pthread_rwlock_destroy(rw.get()); //~ ERROR destroyed a locked rwlock + libc::pthread_rwlock_destroy(rw.get()); //~ ERROR: destroyed a locked rwlock } } diff --git a/tests/fail/sync/libc_pthread_rwlock_double_destroy.rs b/tests/fail/sync/libc_pthread_rwlock_double_destroy.rs index 055bb1af48..7e756c5bb8 100644 --- a/tests/fail/sync/libc_pthread_rwlock_double_destroy.rs +++ b/tests/fail/sync/libc_pthread_rwlock_double_destroy.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] /// Test that destroying a pthread_rwlock twice fails, even without a check for number validity @@ -11,6 +11,6 @@ fn main() { libc::pthread_rwlock_destroy(&mut lock); libc::pthread_rwlock_destroy(&mut lock); - //~^ ERROR Undefined Behavior: using uninitialized data, but this operation requires initialized memory + //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory } } diff --git a/tests/fail/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.rs b/tests/fail/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.rs index dd4707d60e..76fceb315f 100644 --- a/tests/fail/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.rs +++ b/tests/fail/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] diff --git a/tests/fail/sync/libc_pthread_rwlock_read_wrong_owner.rs b/tests/fail/sync/libc_pthread_rwlock_read_wrong_owner.rs index a73a8496a3..e971fd8c30 100644 --- a/tests/fail/sync/libc_pthread_rwlock_read_wrong_owner.rs +++ b/tests/fail/sync/libc_pthread_rwlock_read_wrong_owner.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] diff --git a/tests/fail/sync/libc_pthread_rwlock_unlock_unlocked.rs b/tests/fail/sync/libc_pthread_rwlock_unlock_unlocked.rs index 8b3de53828..29cfd36caf 100644 --- a/tests/fail/sync/libc_pthread_rwlock_unlock_unlocked.rs +++ b/tests/fail/sync/libc_pthread_rwlock_unlock_unlocked.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] @@ -7,6 +7,6 @@ extern crate libc; fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); unsafe { - libc::pthread_rwlock_unlock(rw.get()); //~ ERROR was not locked + libc::pthread_rwlock_unlock(rw.get()); //~ ERROR: was not locked } } diff --git a/tests/fail/sync/libc_pthread_rwlock_write_read_deadlock.rs b/tests/fail/sync/libc_pthread_rwlock_write_read_deadlock.rs index 19dce431c8..e9c5c17f3e 100644 --- a/tests/fail/sync/libc_pthread_rwlock_write_read_deadlock.rs +++ b/tests/fail/sync/libc_pthread_rwlock_write_read_deadlock.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] diff --git a/tests/fail/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.rs b/tests/fail/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.rs index 1b460e7174..5ed25344e7 100644 --- a/tests/fail/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.rs +++ b/tests/fail/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] diff --git a/tests/fail/sync/libc_pthread_rwlock_write_write_deadlock.rs b/tests/fail/sync/libc_pthread_rwlock_write_write_deadlock.rs index 098c1c2fe2..3d15370e83 100644 --- a/tests/fail/sync/libc_pthread_rwlock_write_write_deadlock.rs +++ b/tests/fail/sync/libc_pthread_rwlock_write_write_deadlock.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] diff --git a/tests/fail/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.rs b/tests/fail/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.rs index cc327ec46b..14361bee54 100644 --- a/tests/fail/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.rs +++ b/tests/fail/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] diff --git a/tests/fail/sync/libc_pthread_rwlock_write_wrong_owner.rs b/tests/fail/sync/libc_pthread_rwlock_write_wrong_owner.rs index 663dedb6f6..668ccb3eca 100644 --- a/tests/fail/sync/libc_pthread_rwlock_write_wrong_owner.rs +++ b/tests/fail/sync/libc_pthread_rwlock_write_wrong_owner.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] diff --git a/tests/fail/transmute-pair-uninit.rs b/tests/fail/transmute-pair-uninit.rs index 0835287b9e..bc95f3cb7a 100644 --- a/tests/fail/transmute-pair-uninit.rs +++ b/tests/fail/transmute-pair-uninit.rs @@ -17,7 +17,7 @@ fn main() { assert_eq!(byte, 0); } let v = unsafe { *z.offset(first_undef) }; - //~^ ERROR uninitialized + //~^ ERROR: uninitialized if v == 0 { println!("it is zero"); } diff --git a/tests/fail/type-too-large.rs b/tests/fail/type-too-large.rs index 08f7a49b02..21b272f8ec 100644 --- a/tests/fail/type-too-large.rs +++ b/tests/fail/type-too-large.rs @@ -1,6 +1,6 @@ -// ignore-32bit +//@ignore-32bit fn main() { let _fat: [u8; (1 << 61) + (1 << 31)]; - _fat = [0; (1u64 << 61) as usize + (1u64 << 31) as usize]; //~ ERROR post-monomorphization error + _fat = [0; (1u64 << 61) as usize + (1u64 << 31) as usize]; //~ ERROR: post-monomorphization error } diff --git a/tests/fail/unaligned_pointers/alignment.rs b/tests/fail/unaligned_pointers/alignment.rs index abee75ec71..438e74e5b8 100644 --- a/tests/fail/unaligned_pointers/alignment.rs +++ b/tests/fail/unaligned_pointers/alignment.rs @@ -1,4 +1,4 @@ -// normalize-stderr-test: "\| +\^+" -> "| ^" +//@normalize-stderr-test: "\| +\^+" -> "| ^" fn main() { // No retry needed, this fails reliably. diff --git a/tests/fail/unaligned_pointers/atomic_unaligned.rs b/tests/fail/unaligned_pointers/atomic_unaligned.rs index 74dd0b415c..9dd652fd82 100644 --- a/tests/fail/unaligned_pointers/atomic_unaligned.rs +++ b/tests/fail/unaligned_pointers/atomic_unaligned.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-symbolic-alignment-check +//@compile-flags: -Zmiri-symbolic-alignment-check #![feature(core_intrinsics)] fn main() { @@ -8,6 +8,6 @@ fn main() { let zptr = &z as *const _ as *const u64; unsafe { ::std::intrinsics::atomic_load_seqcst(zptr); - //~^ERROR accessing memory with alignment 4, but alignment 8 is required + //~^ERROR: accessing memory with alignment 4, but alignment 8 is required } } diff --git a/tests/fail/unaligned_pointers/dyn_alignment.rs b/tests/fail/unaligned_pointers/dyn_alignment.rs index 730dd87cbb..b943c7db7c 100644 --- a/tests/fail/unaligned_pointers/dyn_alignment.rs +++ b/tests/fail/unaligned_pointers/dyn_alignment.rs @@ -1,5 +1,5 @@ // should find the bug even without validation and stacked borrows, but gets masked by optimizations -// compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows -Zmir-opt-level=0 +//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows -Zmir-opt-level=0 #[repr(align(256))] #[derive(Debug)] @@ -19,6 +19,6 @@ fn main() { (&mut ptr as *mut _ as *mut *const u8).write(&buf as *const _ as *const u8); } // Re-borrow that. This should be UB. - let _ptr = &*ptr; //~ERROR alignment 256 is required + let _ptr = &*ptr; //~ERROR: alignment 256 is required } } diff --git a/tests/fail/unaligned_pointers/intptrcast_alignment_check.rs b/tests/fail/unaligned_pointers/intptrcast_alignment_check.rs index dea9335ab7..da4cadc1c8 100644 --- a/tests/fail/unaligned_pointers/intptrcast_alignment_check.rs +++ b/tests/fail/unaligned_pointers/intptrcast_alignment_check.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-symbolic-alignment-check -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-symbolic-alignment-check -Zmiri-permissive-provenance // With the symbolic alignment check, even with intptrcast and without // validation, we want to be *sure* to catch bugs that arise from pointers being // insufficiently aligned. The only way to achieve that is not not let programs @@ -12,6 +12,6 @@ fn main() { // Manually make sure the pointer is properly aligned. let base_addr_aligned = if base_addr % 2 == 0 { base_addr } else { base_addr + 1 }; let u16_ptr = base_addr_aligned as *mut u16; - unsafe { *u16_ptr = 2 }; //~ERROR memory with alignment 1, but alignment 2 is required + unsafe { *u16_ptr = 2 }; //~ERROR: memory with alignment 1, but alignment 2 is required println!("{:?}", x); } diff --git a/tests/fail/unaligned_pointers/reference_to_packed.rs b/tests/fail/unaligned_pointers/reference_to_packed.rs index c42f0e27ae..752210dca4 100644 --- a/tests/fail/unaligned_pointers/reference_to_packed.rs +++ b/tests/fail/unaligned_pointers/reference_to_packed.rs @@ -1,5 +1,5 @@ // This should fail even without validation/SB -// compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows #![allow(dead_code, unused_variables, unaligned_references)] @@ -14,6 +14,6 @@ fn main() { for _ in 0..10 { let foo = Foo { x: 42, y: 99 }; let p = &foo.x; - let i = *p; //~ERROR alignment 4 is required + let i = *p; //~ERROR: alignment 4 is required } } diff --git a/tests/fail/unaligned_pointers/unaligned_ptr1.rs b/tests/fail/unaligned_pointers/unaligned_ptr1.rs index 7d192e5d39..73adc4dc44 100644 --- a/tests/fail/unaligned_pointers/unaligned_ptr1.rs +++ b/tests/fail/unaligned_pointers/unaligned_ptr1.rs @@ -1,5 +1,5 @@ // This should fail even without validation or Stacked Borrows. -// compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows fn main() { // Try many times as this might work by chance. @@ -7,6 +7,6 @@ fn main() { let x = [2u16, 3, 4]; // Make it big enough so we don't get an out-of-bounds error. let x = &x[0] as *const _ as *const u32; // This must fail because alignment is violated: the allocation's base is not sufficiently aligned. - let _x = unsafe { *x }; //~ERROR memory with alignment 2, but alignment 4 is required + let _x = unsafe { *x }; //~ERROR: memory with alignment 2, but alignment 4 is required } } diff --git a/tests/fail/unaligned_pointers/unaligned_ptr2.rs b/tests/fail/unaligned_pointers/unaligned_ptr2.rs index 49612e2b8a..c252944ffb 100644 --- a/tests/fail/unaligned_pointers/unaligned_ptr2.rs +++ b/tests/fail/unaligned_pointers/unaligned_ptr2.rs @@ -1,5 +1,5 @@ // This should fail even without validation or Stacked Borrows. -// compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows fn main() { // No retry needed, this fails reliably. @@ -8,5 +8,5 @@ fn main() { let x = (x.as_ptr() as *const u8).wrapping_offset(3) as *const u32; // This must fail because alignment is violated: the offset is not sufficiently aligned. // Also make the offset not a power of 2, that used to ICE. - let _x = unsafe { *x }; //~ERROR memory with alignment 1, but alignment 4 is required + let _x = unsafe { *x }; //~ERROR: memory with alignment 1, but alignment 4 is required } diff --git a/tests/fail/unaligned_pointers/unaligned_ptr3.rs b/tests/fail/unaligned_pointers/unaligned_ptr3.rs index 748a31681a..7605dd175a 100644 --- a/tests/fail/unaligned_pointers/unaligned_ptr3.rs +++ b/tests/fail/unaligned_pointers/unaligned_ptr3.rs @@ -1,5 +1,5 @@ // This should fail even without validation or Stacked Borrows. -// compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows fn main() { // Try many times as this might work by chance. @@ -8,6 +8,6 @@ fn main() { let x = &x[0] as *const _ as *const *const u8; // cast to ptr-to-ptr, so that we load a ptr // This must fail because alignment is violated. Test specifically for loading pointers, // which have special code in miri's memory. - let _x = unsafe { *x }; //~ERROR but alignment + let _x = unsafe { *x }; //~ERROR: but alignment } } diff --git a/tests/fail/unaligned_pointers/unaligned_ptr4.rs b/tests/fail/unaligned_pointers/unaligned_ptr4.rs index d01cabfa31..852febe4c0 100644 --- a/tests/fail/unaligned_pointers/unaligned_ptr4.rs +++ b/tests/fail/unaligned_pointers/unaligned_ptr4.rs @@ -1,5 +1,5 @@ // This should fail even without validation or Stacked Borrows. -// compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows fn main() { // Make sure we notice when a u16 is loaded at offset 1 into a u8 allocation. @@ -9,6 +9,6 @@ fn main() { for _ in 0..10 { let x = [0u8; 4]; let ptr = x.as_ptr().wrapping_offset(1).cast::(); - let _val = unsafe { *ptr }; //~ERROR but alignment + let _val = unsafe { *ptr }; //~ERROR: but alignment } } diff --git a/tests/fail/unaligned_pointers/unaligned_ptr_addr_of.rs b/tests/fail/unaligned_pointers/unaligned_ptr_addr_of.rs index dff92d56d7..e439cf2b03 100644 --- a/tests/fail/unaligned_pointers/unaligned_ptr_addr_of.rs +++ b/tests/fail/unaligned_pointers/unaligned_ptr_addr_of.rs @@ -1,5 +1,5 @@ // This should fail even without validation or Stacked Borrows. -// compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows use std::ptr; fn main() { @@ -9,6 +9,6 @@ fn main() { let x = &x[0] as *const _ as *const u32; // This must fail because alignment is violated: the allocation's base is not sufficiently aligned. // The deref is UB even if we just put the result into a raw pointer. - let _x = unsafe { ptr::addr_of!(*x) }; //~ ERROR memory with alignment 2, but alignment 4 is required + let _x = unsafe { ptr::addr_of!(*x) }; //~ ERROR: memory with alignment 2, but alignment 4 is required } } diff --git a/tests/fail/unaligned_pointers/unaligned_ptr_zst.rs b/tests/fail/unaligned_pointers/unaligned_ptr_zst.rs index 8252ea83c8..9076581b55 100644 --- a/tests/fail/unaligned_pointers/unaligned_ptr_zst.rs +++ b/tests/fail/unaligned_pointers/unaligned_ptr_zst.rs @@ -1,6 +1,6 @@ // This should fail even without validation // Some optimizations remove ZST accesses, thus masking this UB. -// compile-flags: -Zmir-opt-level=0 -Zmiri-disable-validation +//@compile-flags: -Zmir-opt-level=0 -Zmiri-disable-validation fn main() { // Try many times as this might work by chance. @@ -8,6 +8,6 @@ fn main() { let x = i as u8; let x = &x as *const _ as *const [u32; 0]; // This must fail because alignment is violated. Test specifically for loading ZST. - let _x = unsafe { *x }; //~ERROR alignment 4 is required + let _x = unsafe { *x }; //~ERROR: alignment 4 is required } } diff --git a/tests/fail/uninit_buffer.rs b/tests/fail/uninit_buffer.rs index 351181016e..d21371225e 100644 --- a/tests/fail/uninit_buffer.rs +++ b/tests/fail/uninit_buffer.rs @@ -1,4 +1,4 @@ -// error-pattern: 12 bytes are uninitialized +//@error-pattern: memory is uninitialized at [0x4..0x10] use std::alloc::{alloc, dealloc, Layout}; use std::slice::from_raw_parts; diff --git a/tests/fail/uninit_buffer.stderr b/tests/fail/uninit_buffer.stderr index e8faf8dd8b..879c827eb8 100644 --- a/tests/fail/uninit_buffer.stderr +++ b/tests/fail/uninit_buffer.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: reading 16 bytes of memory starting at ALLOC, but 12 bytes are uninitialized starting at ALLOC+0x4, and this operation requires initialized memory +error: Undefined Behavior: reading memory at ALLOC[0x0..0x10], but memory is uninitialized at [0x4..0x10], and this operation requires initialized memory --> RUSTLIB/core/src/slice/cmp.rs:LL:CC | LL | let mut order = unsafe { memcmp(left.as_ptr(), right.as_ptr(), len) as isize }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reading 16 bytes of memory starting at ALLOC, but 12 bytes are uninitialized starting at ALLOC+0x4, and this operation requires initialized memory + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reading memory at ALLOC[0x0..0x10], but memory is uninitialized at [0x4..0x10], and this operation requires initialized memory | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information @@ -17,7 +17,7 @@ LL | drop(slice1.cmp(slice2)); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -Uninitialized read occurred at ALLOC[0x4..0x10], in this allocation: +Uninitialized memory occurred at ALLOC[0x4..0x10], in this allocation: ALLOC (Rust heap, size: 32, align: 8) { 0x00 │ 41 42 43 44 __ __ __ __ __ __ __ __ __ __ __ __ │ ABCD░░░░░░░░░░░░ 0x10 │ 00 __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ .░░░░░░░░░░░░░░░ diff --git a/tests/fail/uninit_byte_read.rs b/tests/fail/uninit_byte_read.rs index 683088e78b..f1dace0cff 100644 --- a/tests/fail/uninit_byte_read.rs +++ b/tests/fail/uninit_byte_read.rs @@ -1,7 +1,7 @@ -// compile-flags: -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-disable-stacked-borrows fn main() { let v: Vec = Vec::with_capacity(10); - let undef = unsafe { *v.get_unchecked(5) }; //~ ERROR uninitialized + let undef = unsafe { *v.get_unchecked(5) }; //~ ERROR: uninitialized let x = undef + 1; panic!("this should never print: {}", x); } diff --git a/tests/fail/uninit_raw_ptr.rs b/tests/fail/uninit_raw_ptr.rs index c2ede1bb14..e5a34f4a26 100644 --- a/tests/fail/uninit_raw_ptr.rs +++ b/tests/fail/uninit_raw_ptr.rs @@ -1,4 +1,4 @@ fn main() { let _val = unsafe { std::mem::MaybeUninit::<*const u8>::uninit().assume_init() }; - //~^ ERROR constructing invalid value at .value: encountered uninitialized raw pointer + //~^ ERROR: constructing invalid value at .value: encountered uninitialized raw pointer } diff --git a/tests/fail/unreachable.rs b/tests/fail/unreachable.rs index 245ef29cad..ef2bb42c65 100644 --- a/tests/fail/unreachable.rs +++ b/tests/fail/unreachable.rs @@ -1,4 +1,4 @@ -// error-pattern: entering unreachable code +//@error-pattern: entering unreachable code fn main() { unsafe { std::hint::unreachable_unchecked() } } diff --git a/tests/fail/unsized-local.rs b/tests/fail/unsized-local.rs new file mode 100644 index 0000000000..ceccae4e3e --- /dev/null +++ b/tests/fail/unsized-local.rs @@ -0,0 +1,23 @@ +#![feature(unsized_locals)] +#![allow(incomplete_features)] + +fn main() { + pub trait Foo { + fn foo(self) -> String; + } + + struct A; + + impl Foo for A { + fn foo(self) -> String { + format!("hello") + } + } + + let x = *(Box::new(A) as Box); //~ERROR: unsized locals are not supported + assert_eq!(x.foo(), format!("hello")); + + // I'm not sure whether we want this to work + let x = Box::new(A) as Box; + assert_eq!(x.foo(), format!("hello")); +} diff --git a/tests/fail/unsized-local.stderr b/tests/fail/unsized-local.stderr new file mode 100644 index 0000000000..8277bc4546 --- /dev/null +++ b/tests/fail/unsized-local.stderr @@ -0,0 +1,14 @@ +error: unsupported operation: unsized locals are not supported + --> $DIR/unsized-local.rs:LL:CC + | +LL | let x = *(Box::new(A) as Box); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsized locals are not supported + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = note: backtrace: + = note: inside `main` at $DIR/unsized-local.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/unsupported_foreign_function.rs b/tests/fail/unsupported_foreign_function.rs index b7f4d9038e..dfd099e734 100644 --- a/tests/fail/unsupported_foreign_function.rs +++ b/tests/fail/unsupported_foreign_function.rs @@ -4,6 +4,6 @@ fn main() { } unsafe { - foo(); //~ ERROR unsupported operation: can't call foreign function: foo + foo(); //~ ERROR: unsupported operation: can't call foreign function: foo } } diff --git a/tests/fail/unsupported_foreign_function.stderr b/tests/fail/unsupported_foreign_function.stderr index 3298f57dd5..e74a83c623 100644 --- a/tests/fail/unsupported_foreign_function.stderr +++ b/tests/fail/unsupported_foreign_function.stderr @@ -1,8 +1,8 @@ -error: unsupported operation: can't call foreign function: foo +error: unsupported operation: can't call foreign function: foo; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile --> $DIR/unsupported_foreign_function.rs:LL:CC | LL | foo(); - | ^^^^^ can't call foreign function: foo + | ^^^^^ can't call foreign function: foo; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support = note: backtrace: diff --git a/tests/fail/unsupported_signal.rs b/tests/fail/unsupported_signal.rs index 3e76d1c3f3..e9bd340fca 100644 --- a/tests/fail/unsupported_signal.rs +++ b/tests/fail/unsupported_signal.rs @@ -1,6 +1,6 @@ //! `signal()` is special on Linux and macOS that it's only supported within libstd. //! The implementation is not complete enough to permit user code to call it. -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] extern crate libc; @@ -8,6 +8,6 @@ extern crate libc; fn main() { unsafe { libc::signal(libc::SIGPIPE, libc::SIG_IGN); - //~^ ERROR unsupported operation: can't call foreign function: signal + //~^ ERROR: unsupported operation: can't call foreign function: signal } } diff --git a/tests/fail/unsupported_signal.stderr b/tests/fail/unsupported_signal.stderr index 622b1876e0..f5006da001 100644 --- a/tests/fail/unsupported_signal.stderr +++ b/tests/fail/unsupported_signal.stderr @@ -1,8 +1,8 @@ -error: unsupported operation: can't call foreign function: signal +error: unsupported operation: can't call foreign function: signal; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile --> $DIR/unsupported_signal.rs:LL:CC | LL | libc::signal(libc::SIGPIPE, libc::SIG_IGN); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't call foreign function: signal + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't call foreign function: signal; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support = note: backtrace: diff --git a/tests/fail/validity/cast_fn_ptr1.rs b/tests/fail/validity/cast_fn_ptr1.rs index eb5774fe79..6ab73569c6 100644 --- a/tests/fail/validity/cast_fn_ptr1.rs +++ b/tests/fail/validity/cast_fn_ptr1.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance fn main() { // Cast a function pointer such that on a call, the argument gets transmuted @@ -9,5 +9,5 @@ fn main() { let g: fn(*const i32) = unsafe { std::mem::transmute(f as fn(&i32)) }; g(0usize as *const i32) - //~^ ERROR encountered a null reference + //~^ ERROR: encountered a null reference } diff --git a/tests/fail/validity/cast_fn_ptr2.rs b/tests/fail/validity/cast_fn_ptr2.rs index 1cf4ca7d19..64ddb563be 100644 --- a/tests/fail/validity/cast_fn_ptr2.rs +++ b/tests/fail/validity/cast_fn_ptr2.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance fn main() { // Cast a function pointer such that when returning, the return value gets transmuted @@ -11,5 +11,5 @@ fn main() { let g: fn() -> &'static i32 = unsafe { std::mem::transmute(f as fn() -> *const i32) }; let _x = g(); - //~^ ERROR encountered a null reference + //~^ ERROR: encountered a null reference } diff --git a/tests/fail/validity/dangling_ref1.rs b/tests/fail/validity/dangling_ref1.rs index 78425cde4a..6bf2d9295a 100644 --- a/tests/fail/validity/dangling_ref1.rs +++ b/tests/fail/validity/dangling_ref1.rs @@ -1,7 +1,7 @@ // Make sure we catch this even without Stacked Borrows -// compile-flags: -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-disable-stacked-borrows use std::mem; fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR encountered a dangling reference (address 0x10 is unallocated) + let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address 0x10 is unallocated) } diff --git a/tests/fail/validity/dangling_ref2.rs b/tests/fail/validity/dangling_ref2.rs index 7aff1a4978..77d2358ae7 100644 --- a/tests/fail/validity/dangling_ref2.rs +++ b/tests/fail/validity/dangling_ref2.rs @@ -1,9 +1,9 @@ // Make sure we catch this even without Stacked Borrows -// compile-flags: -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-disable-stacked-borrows use std::mem; fn main() { let val = 14; let ptr = (&val as *const i32).wrapping_offset(1); - let _x: &i32 = unsafe { mem::transmute(ptr) }; //~ ERROR dangling reference (going beyond the bounds of its allocation) + let _x: &i32 = unsafe { mem::transmute(ptr) }; //~ ERROR: dangling reference (going beyond the bounds of its allocation) } diff --git a/tests/fail/validity/dangling_ref3.rs b/tests/fail/validity/dangling_ref3.rs index 495a266a85..8e8a75bd7e 100644 --- a/tests/fail/validity/dangling_ref3.rs +++ b/tests/fail/validity/dangling_ref3.rs @@ -1,5 +1,5 @@ // Make sure we catch this even without Stacked Borrows -// compile-flags: -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-disable-stacked-borrows use std::mem; fn dangling() -> *const u8 { @@ -8,5 +8,5 @@ fn dangling() -> *const u8 { } fn main() { - let _x: &i32 = unsafe { mem::transmute(dangling()) }; //~ ERROR dangling reference (use-after-free) + let _x: &i32 = unsafe { mem::transmute(dangling()) }; //~ ERROR: dangling reference (use-after-free) } diff --git a/tests/fail/validity/invalid_bool.rs b/tests/fail/validity/invalid_bool.rs index df6c19df7f..4f11bb2629 100644 --- a/tests/fail/validity/invalid_bool.rs +++ b/tests/fail/validity/invalid_bool.rs @@ -1,3 +1,3 @@ fn main() { - let _b = unsafe { std::mem::transmute::(2) }; //~ ERROR expected a boolean + let _b = unsafe { std::mem::transmute::(2) }; //~ ERROR: expected a boolean } diff --git a/tests/fail/validity/invalid_bool_uninit.rs b/tests/fail/validity/invalid_bool_uninit.rs index 89b57b2d50..f90fae3ab9 100644 --- a/tests/fail/validity/invalid_bool_uninit.rs +++ b/tests/fail/validity/invalid_bool_uninit.rs @@ -6,5 +6,5 @@ union MyUninit { } fn main() { - let _b = unsafe { MyUninit { init: () }.uninit }; //~ ERROR encountered uninitialized bytes, but expected a boolean + let _b = unsafe { MyUninit { init: () }.uninit }; //~ ERROR: encountered uninitialized bytes, but expected a boolean } diff --git a/tests/fail/validity/invalid_char.rs b/tests/fail/validity/invalid_char.rs index 80749fd7c7..568892e591 100644 --- a/tests/fail/validity/invalid_char.rs +++ b/tests/fail/validity/invalid_char.rs @@ -1,7 +1,7 @@ fn main() { assert!(std::char::from_u32(-1_i32 as u32).is_none()); let _val = match unsafe { std::mem::transmute::(-1) } { - //~^ ERROR encountered 0xffffffff, but expected a valid unicode scalar value + //~^ ERROR: encountered 0xffffffff, but expected a valid unicode scalar value 'a' => true, 'b' => false, _ => true, diff --git a/tests/fail/validity/invalid_char_uninit.rs b/tests/fail/validity/invalid_char_uninit.rs index cb885d001c..cb82ce7463 100644 --- a/tests/fail/validity/invalid_char_uninit.rs +++ b/tests/fail/validity/invalid_char_uninit.rs @@ -6,5 +6,5 @@ union MyUninit { } fn main() { - let _b = unsafe { MyUninit { init: () }.uninit }; //~ ERROR encountered uninitialized bytes, but expected a valid unicode scalar value + let _b = unsafe { MyUninit { init: () }.uninit }; //~ ERROR: encountered uninitialized bytes, but expected a valid unicode scalar value } diff --git a/tests/fail/validity/invalid_enum_tag.rs b/tests/fail/validity/invalid_enum_tag.rs index 4bc60ba51e..fa115e1e78 100644 --- a/tests/fail/validity/invalid_enum_tag.rs +++ b/tests/fail/validity/invalid_enum_tag.rs @@ -7,5 +7,5 @@ pub enum Foo { } fn main() { - let _f = unsafe { std::mem::transmute::(42) }; //~ ERROR constructing invalid value at .: encountered 0x0000002a, but expected a valid enum tag + let _f = unsafe { std::mem::transmute::(42) }; //~ ERROR: constructing invalid value at .: encountered 0x0000002a, but expected a valid enum tag } diff --git a/tests/fail/validity/invalid_enum_tag_256variants_uninit.rs b/tests/fail/validity/invalid_enum_tag_256variants_uninit.rs index 7573a64d0e..708e0cafa9 100644 --- a/tests/fail/validity/invalid_enum_tag_256variants_uninit.rs +++ b/tests/fail/validity/invalid_enum_tag_256variants_uninit.rs @@ -1,5 +1,5 @@ // Even when uninit numbers are allowed, this enum is not. -// compile-flags: -Zmiri-allow-uninit-numbers +//@compile-flags: -Zmiri-allow-uninit-numbers #![allow(unused, deprecated, invalid_value)] #[derive(Copy, Clone)] @@ -268,5 +268,5 @@ union MyUninit { } fn main() { - let _a = unsafe { MyUninit { init: () }.uninit }; //~ ERROR constructing invalid value at .: encountered uninitialized bytes, but expected a valid enum tag + let _a = unsafe { MyUninit { init: () }.uninit }; //~ ERROR: constructing invalid value at .: encountered uninitialized bytes, but expected a valid enum tag } diff --git a/tests/fail/validity/invalid_fnptr_null.rs b/tests/fail/validity/invalid_fnptr_null.rs index 0634fba36a..8d2045ca4a 100644 --- a/tests/fail/validity/invalid_fnptr_null.rs +++ b/tests/fail/validity/invalid_fnptr_null.rs @@ -1,5 +1,5 @@ #![allow(invalid_value)] fn main() { - let _b: fn() = unsafe { std::mem::transmute(0usize) }; //~ ERROR encountered a null function pointer + let _b: fn() = unsafe { std::mem::transmute(0usize) }; //~ ERROR: encountered a null function pointer } diff --git a/tests/fail/validity/invalid_fnptr_uninit.rs b/tests/fail/validity/invalid_fnptr_uninit.rs index 2d479dd319..26f958bd64 100644 --- a/tests/fail/validity/invalid_fnptr_uninit.rs +++ b/tests/fail/validity/invalid_fnptr_uninit.rs @@ -6,5 +6,5 @@ union MyUninit { } fn main() { - let _b = unsafe { MyUninit { init: () }.uninit }; //~ ERROR encountered uninitialized bytes + let _b = unsafe { MyUninit { init: () }.uninit }; //~ ERROR: encountered uninitialized bytes } diff --git a/tests/fail/validity/nonzero.rs b/tests/fail/validity/nonzero.rs index 8ff19a2b43..384c94a556 100644 --- a/tests/fail/validity/nonzero.rs +++ b/tests/fail/validity/nonzero.rs @@ -1,5 +1,5 @@ // gets masked by optimizations -// compile-flags: -Zmir-opt-level=0 +//@compile-flags: -Zmir-opt-level=0 #![feature(rustc_attrs)] #![allow(unused_attributes)] @@ -9,5 +9,5 @@ pub(crate) struct NonZero(pub(crate) T); fn main() { // Make sure that we detect this even when no function call is happening along the way - let _x = Some(unsafe { NonZero(0) }); //~ ERROR encountered 0, but expected something greater or equal to 1 + let _x = Some(unsafe { NonZero(0) }); //~ ERROR: encountered 0, but expected something greater or equal to 1 } diff --git a/tests/fail/validity/ptr_integer_array_transmute.rs b/tests/fail/validity/ptr_integer_array_transmute.rs index 92c635ff22..c8613d274c 100644 --- a/tests/fail/validity/ptr_integer_array_transmute.rs +++ b/tests/fail/validity/ptr_integer_array_transmute.rs @@ -1,4 +1,4 @@ fn main() { let r = &mut 42; - let _i: [usize; 1] = unsafe { std::mem::transmute(r) }; //~ ERROR encountered a pointer, but expected plain (non-pointer) bytes + let _i: [usize; 1] = unsafe { std::mem::transmute(r) }; //~ ERROR: encountered a pointer, but expected plain (non-pointer) bytes } diff --git a/tests/fail/validity/ref_to_uninhabited1.rs b/tests/fail/validity/ref_to_uninhabited1.rs index a46ce017c5..2e6be8b971 100644 --- a/tests/fail/validity/ref_to_uninhabited1.rs +++ b/tests/fail/validity/ref_to_uninhabited1.rs @@ -3,7 +3,7 @@ use std::mem::{forget, transmute}; fn main() { unsafe { - let x: Box = transmute(&mut 42); //~ERROR encountered a box pointing to uninhabited type ! + let x: Box = transmute(&mut 42); //~ERROR: encountered a box pointing to uninhabited type ! forget(x); } } diff --git a/tests/fail/validity/ref_to_uninhabited2.rs b/tests/fail/validity/ref_to_uninhabited2.rs index 0a791d1e7f..8934a06b5d 100644 --- a/tests/fail/validity/ref_to_uninhabited2.rs +++ b/tests/fail/validity/ref_to_uninhabited2.rs @@ -4,6 +4,6 @@ enum Void {} fn main() { unsafe { - let _x: &(i32, Void) = transmute(&42); //~ERROR encountered a reference pointing to uninhabited type (i32, Void) + let _x: &(i32, Void) = transmute(&42); //~ERROR: encountered a reference pointing to uninhabited type (i32, Void) } } diff --git a/tests/fail/validity/transmute_through_ptr.rs b/tests/fail/validity/transmute_through_ptr.rs index 9db2ba9953..60b3bdd6cd 100644 --- a/tests/fail/validity/transmute_through_ptr.rs +++ b/tests/fail/validity/transmute_through_ptr.rs @@ -14,6 +14,6 @@ fn main() { let mut x = Bool::True; evil(&mut x); let y = x; // reading this ought to be enough to trigger validation - //~^ ERROR constructing invalid value at .: encountered 0x0000002c, but expected a valid enum tag + //~^ ERROR: constructing invalid value at .: encountered 0x0000002c, but expected a valid enum tag println!("{:?}", y); // make sure it is used (and not optimized away) } diff --git a/tests/fail/validity/uninit_float.rs b/tests/fail/validity/uninit_float.rs index 43748570d9..0f4a22cf5b 100644 --- a/tests/fail/validity/uninit_float.rs +++ b/tests/fail/validity/uninit_float.rs @@ -1,6 +1,8 @@ +#![allow(deprecated)] // This test is adapted from https://github.com/rust-lang/miri/issues/1340#issue-600900312. fn main() { - let _val = unsafe { std::mem::MaybeUninit::::uninit().assume_init() }; - //~^ ERROR constructing invalid value at .value: encountered uninitialized bytes, but expected initialized bytes + // Deliberately using `mem::uninitialized` to make sure that despite all the mitigations, we consider this UB. + let _val: f32 = unsafe { std::mem::uninitialized() }; + //~^ ERROR: constructing invalid value at .value: encountered uninitialized bytes, but expected initialized bytes } diff --git a/tests/fail/validity/uninit_float.stderr b/tests/fail/validity/uninit_float.stderr index 2fe27c9043..d9611af79e 100644 --- a/tests/fail/validity/uninit_float.stderr +++ b/tests/fail/validity/uninit_float.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: constructing invalid value at .value: encountered uninitialized bytes, but expected initialized bytes --> $DIR/uninit_float.rs:LL:CC | -LL | let _val = unsafe { std::mem::MaybeUninit::::uninit().assume_init() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .value: encountered uninitialized bytes, but expected initialized bytes +LL | let _val: f32 = unsafe { std::mem::uninitialized() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .value: encountered uninitialized bytes, but expected initialized bytes | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/tests/fail/validity/uninit_integer.rs b/tests/fail/validity/uninit_integer.rs index b5a367ba8c..0a1253b5b9 100644 --- a/tests/fail/validity/uninit_integer.rs +++ b/tests/fail/validity/uninit_integer.rs @@ -2,5 +2,5 @@ fn main() { let _val = unsafe { std::mem::MaybeUninit::::uninit().assume_init() }; - //~^ ERROR constructing invalid value at .value: encountered uninitialized bytes, but expected initialized bytes + //~^ ERROR: constructing invalid value at .value: encountered uninitialized bytes, but expected initialized bytes } diff --git a/tests/fail/validity/uninit_integer_signed.rs b/tests/fail/validity/uninit_integer_signed.rs deleted file mode 100644 index 609bad7e4f..0000000000 --- a/tests/fail/validity/uninit_integer_signed.rs +++ /dev/null @@ -1,6 +0,0 @@ -// This test is adapted from https://github.com/rust-lang/miri/issues/1340#issue-600900312. - -fn main() { - let _val = unsafe { std::mem::MaybeUninit::::uninit().assume_init() }; - //~^ ERROR constructing invalid value at .value: encountered uninitialized bytes, but expected initialized bytes -} diff --git a/tests/fail/validity/uninit_integer_signed.stderr b/tests/fail/validity/uninit_integer_signed.stderr deleted file mode 100644 index c53c96c596..0000000000 --- a/tests/fail/validity/uninit_integer_signed.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: constructing invalid value at .value: encountered uninitialized bytes, but expected initialized bytes - --> $DIR/uninit_integer_signed.rs:LL:CC - | -LL | let _val = unsafe { std::mem::MaybeUninit::::uninit().assume_init() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .value: encountered uninitialized bytes, but expected initialized bytes - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: backtrace: - = note: inside `main` at $DIR/uninit_integer_signed.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to previous error - diff --git a/tests/fail/weak_memory/racing_mixed_size.rs b/tests/fail/weak_memory/racing_mixed_size.rs index 6d53670a4e..a7c5a41917 100644 --- a/tests/fail/weak_memory/racing_mixed_size.rs +++ b/tests/fail/weak_memory/racing_mixed_size.rs @@ -1,4 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +// We want to control preemption here. +//@compile-flags: -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. #![feature(core_intrinsics)] diff --git a/tests/fail/weak_memory/racing_mixed_size_read.rs b/tests/fail/weak_memory/racing_mixed_size_read.rs index 0129b55aff..aeaa6b68f6 100644 --- a/tests/fail/weak_memory/racing_mixed_size_read.rs +++ b/tests/fail/weak_memory/racing_mixed_size_read.rs @@ -1,4 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +// We want to control preemption here. +//@compile-flags: -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. #![feature(core_intrinsics)] diff --git a/tests/fail/zst1.rs b/tests/fail/zst1.rs index d400fba5d0..cc81481e4f 100644 --- a/tests/fail/zst1.rs +++ b/tests/fail/zst1.rs @@ -1,5 +1,5 @@ fn main() { // make sure ZST locals cannot be accessed let x = &() as *const () as *const i8; - let _val = unsafe { *x }; //~ ERROR out-of-bounds + let _val = unsafe { *x }; //~ ERROR: out-of-bounds } diff --git a/tests/fail/zst2.rs b/tests/fail/zst2.rs index 9f92e8994d..82470866f1 100644 --- a/tests/fail/zst2.rs +++ b/tests/fail/zst2.rs @@ -1,5 +1,5 @@ // Some optimizations remove ZST accesses, thus masking this UB. -// compile-flags: -Zmir-opt-level=0 +//@compile-flags: -Zmir-opt-level=0 fn main() { // Not using the () type here, as writes of that type do not even have MIR generated. @@ -11,5 +11,5 @@ fn main() { let mut x_box = Box::new(1u8); let x = &mut *x_box as *mut _ as *mut [u8; 0]; drop(x_box); - unsafe { *x = zst_val }; //~ ERROR dereferenced after this allocation got freed + unsafe { *x = zst_val }; //~ ERROR: dereferenced after this allocation got freed } diff --git a/tests/fail/zst3.rs b/tests/fail/zst3.rs index 3f3b0af14d..a511f38998 100644 --- a/tests/fail/zst3.rs +++ b/tests/fail/zst3.rs @@ -1,5 +1,5 @@ // Some optimizations remove ZST accesses, thus masking this UB. -// compile-flags: -Zmir-opt-level=0 +//@compile-flags: -Zmir-opt-level=0 fn main() { // Not using the () type here, as writes of that type do not even have MIR generated. @@ -14,5 +14,5 @@ fn main() { unsafe { *(x as *mut [u8; 0]) = zst_val }; // One byte further is OOB. let x = x.wrapping_offset(1); - unsafe { *(x as *mut [u8; 0]) = zst_val }; //~ ERROR out-of-bounds + unsafe { *(x as *mut [u8; 0]) = zst_val }; //~ ERROR: out-of-bounds } diff --git a/tests/panic/external_C/incorrect_SO_file.rs b/tests/panic/external_C/incorrect_SO_file.rs new file mode 100644 index 0000000000..751278e112 --- /dev/null +++ b/tests/panic/external_C/incorrect_SO_file.rs @@ -0,0 +1,16 @@ +//@rustc-env: RUST_BACKTRACE=0 +//@only-target-linux +//@only-on-host +//@compile-flags: -Zmiri-external_c_so_file=tests/external_C/badpath.so +//@normalize-stderr-test: "note: rustc.*running on.*" -> "" + +extern "C" { + fn printer(); +} + +fn main() { + unsafe { + // calling a function from a shared object file that doesn't exist + printer(); + } +} diff --git a/tests/panic/external_C/incorrect_SO_file.stderr b/tests/panic/external_C/incorrect_SO_file.stderr new file mode 100644 index 0000000000..f4063b8e05 --- /dev/null +++ b/tests/panic/external_C/incorrect_SO_file.stderr @@ -0,0 +1,15 @@ +thread 'main' panicked at '-Zmiri-external_c_so_file path tests/external_C/badpath.so does not exist', src/bin/miri.rs:LL:CC +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + +error: internal compiler error: unexpected panic + +note: the compiler unexpectedly panicked. this is a bug. + +note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md + + + +note: compiler flags: -Z ui-testing -Z miri-external_c_so_file=tests/external_C/badpath.so + +query stack during panic: +end of query stack diff --git a/tests/panic/panic/panic1.rs b/tests/panic/panic/panic1.rs index e15d7656de..dbddf41fdb 100644 --- a/tests/panic/panic/panic1.rs +++ b/tests/panic/panic/panic1.rs @@ -1,5 +1,5 @@ -// rustc-env: RUST_BACKTRACE=1 -// compile-flags: -Zmiri-disable-isolation +//@rustc-env: RUST_BACKTRACE=1 +//@compile-flags: -Zmiri-disable-isolation fn main() { std::panic!("panicking from libstd"); diff --git a/tests/panic/panic/unsupported_foreign_function.rs b/tests/panic/panic/unsupported_foreign_function.rs index bc3d02c5f2..a78646528f 100644 --- a/tests/panic/panic/unsupported_foreign_function.rs +++ b/tests/panic/panic/unsupported_foreign_function.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-panic-on-unsupported +//@compile-flags: -Zmiri-panic-on-unsupported fn main() { extern "Rust" { diff --git a/tests/panic/panic/unsupported_foreign_function.stderr b/tests/panic/panic/unsupported_foreign_function.stderr index 9af3e48655..f7d1600b1f 100644 --- a/tests/panic/panic/unsupported_foreign_function.stderr +++ b/tests/panic/panic/unsupported_foreign_function.stderr @@ -1,2 +1,2 @@ -thread 'main' panicked at 'unsupported Miri functionality: can't call foreign function: foo', $DIR/unsupported_foreign_function.rs:LL:CC +thread 'main' panicked at 'unsupported Miri functionality: can't call foreign function: foo; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile', $DIR/unsupported_foreign_function.rs:LL:CC note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/tests/panic/panic/unsupported_syscall.rs b/tests/panic/panic/unsupported_syscall.rs index 2e62a5d8ae..a689172814 100644 --- a/tests/panic/panic/unsupported_syscall.rs +++ b/tests/panic/panic/unsupported_syscall.rs @@ -1,6 +1,6 @@ -// ignore-windows: No libc on Windows -// ignore-apple: `syscall` is not supported on macOS -// compile-flags: -Zmiri-panic-on-unsupported +//@ignore-target-windows: No libc on Windows +//@ignore-target-apple: `syscall` is not supported on macOS +//@compile-flags: -Zmiri-panic-on-unsupported #![feature(rustc_private)] extern crate libc; diff --git a/tests/pass/0weak_memory_consistency.rs b/tests/pass/0weak_memory_consistency.rs index 0f798d2b57..b5b6b83cce 100644 --- a/tests/pass/0weak_memory_consistency.rs +++ b/tests/pass/0weak_memory_consistency.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows // The following tests check whether our weak memory emulation produces // any inconsistent execution outcomes @@ -20,8 +20,8 @@ // "Mathematizing C++ concurrency", ACM SIGPLAN Notices, vol. 46, no. 1, pp. 55-66, 2011. // Available: https://ss265.host.cs.st-andrews.ac.uk/papers/n3132.pdf. -use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::*; +use std::sync::atomic::{fence, AtomicBool, AtomicI32}; use std::thread::spawn; #[derive(Copy, Clone)] @@ -32,13 +32,19 @@ unsafe impl Sync for EvilSend {} // We can't create static items because we need to run each test // multiple times -fn static_atomic(val: usize) -> &'static AtomicUsize { - let ret = Box::leak(Box::new(AtomicUsize::new(val))); +fn static_atomic(val: i32) -> &'static AtomicI32 { + let ret = Box::leak(Box::new(AtomicI32::new(val))); + ret.store(val, Relaxed); // work around https://github.com/rust-lang/miri/issues/2164 + ret +} +fn static_atomic_bool(val: bool) -> &'static AtomicBool { + let ret = Box::leak(Box::new(AtomicBool::new(val))); + ret.store(val, Relaxed); // work around https://github.com/rust-lang/miri/issues/2164 ret } // Spins until it acquires a pre-determined value. -fn acquires_value(loc: &AtomicUsize, val: usize) -> usize { +fn acquires_value(loc: &AtomicI32, val: i32) -> i32 { while loc.load(Acquire) != val { std::hint::spin_loop(); } @@ -207,7 +213,7 @@ fn test_sc_store_buffering() { } fn test_single_thread() { - let x = AtomicUsize::new(42); + let x = AtomicI32::new(42); assert_eq!(x.load(Relaxed), 42); @@ -216,6 +222,42 @@ fn test_single_thread() { assert_eq!(x.load(Relaxed), 43); } +fn test_sync_through_rmw_and_fences() { + // Example from https://github.com/llvm/llvm-project/issues/56450#issuecomment-1183695905 + #[no_mangle] + pub fn rdmw(storing: &AtomicI32, sync: &AtomicI32, loading: &AtomicI32) -> i32 { + storing.store(1, Relaxed); + fence(Release); + sync.fetch_add(0, Relaxed); + fence(Acquire); + loading.load(Relaxed) + } + + let x = static_atomic(0); + let y = static_atomic(0); + let z = static_atomic(0); + + // Since each thread is so short, we need to make sure that they truely run at the same time + // Otherwise t1 will finish before t2 even starts + let go = static_atomic_bool(false); + + let t1 = spawn(move || { + while !go.load(Relaxed) {} + rdmw(y, x, z) + }); + + let t2 = spawn(move || { + while !go.load(Relaxed) {} + rdmw(z, x, y) + }); + + go.store(true, Relaxed); + + let a = t1.join().unwrap(); + let b = t2.join().unwrap(); + assert_ne!((a, b), (0, 0)); +} + pub fn main() { for _ in 0..50 { test_single_thread(); @@ -225,5 +267,6 @@ pub fn main() { test_wrc(); test_corr(); test_sc_store_buffering(); + test_sync_through_rmw_and_fences(); } } diff --git a/tests/pass/adjacent-allocs.rs b/tests/pass/adjacent-allocs.rs index d0dc8b5d87..0051c62121 100644 --- a/tests/pass/adjacent-allocs.rs +++ b/tests/pass/adjacent-allocs.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance fn ensure_allocs_can_be_adjacent() { for _ in 0..512 { diff --git a/tests/pass/align.rs b/tests/pass/align.rs index f412541bde..997abd7339 100644 --- a/tests/pass/align.rs +++ b/tests/pass/align.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance /// This manually makes sure that we have a pointer with the proper alignment. fn manual_alignment() { diff --git a/tests/pass/align_offset_symbolic.rs b/tests/pass/align_offset_symbolic.rs index b57a23ab83..b3e5733836 100644 --- a/tests/pass/align_offset_symbolic.rs +++ b/tests/pass/align_offset_symbolic.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-symbolic-alignment-check +//@compile-flags: -Zmiri-symbolic-alignment-check fn test_align_offset() { let d = Box::new([0u32; 4]); diff --git a/tests/pass/atomic-compare-exchange-weak-never-fail.rs b/tests/pass/atomic-compare-exchange-weak-never-fail.rs index 2c2d4e61d9..8d3d71869f 100644 --- a/tests/pass/atomic-compare-exchange-weak-never-fail.rs +++ b/tests/pass/atomic-compare-exchange-weak-never-fail.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-compare-exchange-weak-failure-rate=0.0 +//@compile-flags: -Zmiri-compare-exchange-weak-failure-rate=0.0 use std::sync::atomic::{AtomicBool, Ordering::*}; // Ensure that compare_exchange_weak never fails. diff --git a/tests/pass/atomic.rs b/tests/pass/atomic.rs index b0c3cc889d..e3d80a7891 100644 --- a/tests/pass/atomic.rs +++ b/tests/pass/atomic.rs @@ -1,10 +1,15 @@ -use std::sync::atomic::{compiler_fence, fence, AtomicBool, AtomicIsize, AtomicU64, Ordering::*}; +//@compile-flags: -Zmiri-strict-provenance +#![feature(strict_provenance, strict_provenance_atomic_ptr)] +use std::sync::atomic::{ + compiler_fence, fence, AtomicBool, AtomicIsize, AtomicPtr, AtomicU64, Ordering::*, +}; fn main() { atomic_bool(); atomic_all_ops(); atomic_u64(); atomic_fences(); + atomic_ptr(); weak_sometimes_fails(); } @@ -46,18 +51,22 @@ fn atomic_all_ops() { static ATOMIC: AtomicIsize = AtomicIsize::new(0); static ATOMIC_UNSIGNED: AtomicU64 = AtomicU64::new(0); + let load_orders = [Relaxed, Acquire, SeqCst]; + let stored_orders = [Relaxed, Release, SeqCst]; + let rmw_orders = [Relaxed, Release, Acquire, AcqRel, SeqCst]; + // loads - for o in [Relaxed, Acquire, SeqCst] { + for o in load_orders { ATOMIC.load(o); } // stores - for o in [Relaxed, Release, SeqCst] { + for o in stored_orders { ATOMIC.store(1, o); } // most RMWs - for o in [Relaxed, Release, Acquire, AcqRel, SeqCst] { + for o in rmw_orders { ATOMIC.swap(0, o); ATOMIC.fetch_or(0, o); ATOMIC.fetch_xor(0, o); @@ -71,29 +80,13 @@ fn atomic_all_ops() { ATOMIC_UNSIGNED.fetch_max(0, o); } - // RMWs with deparate failure ordering - ATOMIC.store(0, SeqCst); - assert_eq!(ATOMIC.compare_exchange(0, 1, Relaxed, Relaxed), Ok(0)); - assert_eq!(ATOMIC.compare_exchange(0, 2, Acquire, Relaxed), Err(1)); - assert_eq!(ATOMIC.compare_exchange(0, 1, Release, Relaxed), Err(1)); - assert_eq!(ATOMIC.compare_exchange(1, 0, AcqRel, Relaxed), Ok(1)); - ATOMIC.compare_exchange(0, 1, SeqCst, Relaxed).ok(); - ATOMIC.compare_exchange(0, 1, Acquire, Acquire).ok(); - ATOMIC.compare_exchange(0, 1, AcqRel, Acquire).ok(); - ATOMIC.compare_exchange(0, 1, SeqCst, Acquire).ok(); - ATOMIC.compare_exchange(0, 1, SeqCst, SeqCst).ok(); - - ATOMIC.store(0, SeqCst); - compare_exchange_weak_loop!(ATOMIC, 0, 1, Relaxed, Relaxed); - assert_eq!(ATOMIC.compare_exchange_weak(0, 2, Acquire, Relaxed), Err(1)); - assert_eq!(ATOMIC.compare_exchange_weak(0, 1, Release, Relaxed), Err(1)); - compare_exchange_weak_loop!(ATOMIC, 1, 0, AcqRel, Relaxed); - assert_eq!(ATOMIC.load(Relaxed), 0); - ATOMIC.compare_exchange_weak(0, 1, SeqCst, Relaxed).ok(); - ATOMIC.compare_exchange_weak(0, 1, Acquire, Acquire).ok(); - ATOMIC.compare_exchange_weak(0, 1, AcqRel, Acquire).ok(); - ATOMIC.compare_exchange_weak(0, 1, SeqCst, Acquire).ok(); - ATOMIC.compare_exchange_weak(0, 1, SeqCst, SeqCst).ok(); + // RMWs with separate failure ordering + for o1 in rmw_orders { + for o2 in load_orders { + let _res = ATOMIC.compare_exchange(0, 0, o1, o2); + let _res = ATOMIC.compare_exchange_weak(0, 0, o1, o2); + } + } } fn atomic_u64() { @@ -101,7 +94,12 @@ fn atomic_u64() { ATOMIC.store(1, SeqCst); assert_eq!(ATOMIC.compare_exchange(0, 0x100, AcqRel, Acquire), Err(1)); + assert_eq!(ATOMIC.compare_exchange(0, 1, Release, Relaxed), Err(1)); + assert_eq!(ATOMIC.compare_exchange(1, 0, AcqRel, Relaxed), Ok(1)); + assert_eq!(ATOMIC.compare_exchange(0, 1, Relaxed, Relaxed), Ok(0)); compare_exchange_weak_loop!(ATOMIC, 1, 0x100, AcqRel, Acquire); + assert_eq!(ATOMIC.compare_exchange_weak(0, 2, Acquire, Relaxed), Err(0x100)); + assert_eq!(ATOMIC.compare_exchange_weak(0, 1, Release, Relaxed), Err(0x100)); assert_eq!(ATOMIC.load(Relaxed), 0x100); assert_eq!(ATOMIC.fetch_max(0x10, SeqCst), 0x100); @@ -130,6 +128,54 @@ fn atomic_fences() { compiler_fence(AcqRel); } +fn atomic_ptr() { + use std::ptr; + let array: Vec = (0..100).into_iter().collect(); // a target to point to, to test provenance things + let x = array.as_ptr() as *mut i32; + + let ptr = AtomicPtr::::new(ptr::null_mut()); + assert!(ptr.load(Relaxed).addr() == 0); + ptr.store(ptr::invalid_mut(13), SeqCst); + assert!(ptr.swap(x, Relaxed).addr() == 13); + unsafe { assert!(*ptr.load(Acquire) == 0) }; + + // comparison ignores provenance + assert_eq!( + ptr.compare_exchange( + (&mut 0 as *mut i32).with_addr(x.addr()), + ptr::invalid_mut(0), + SeqCst, + SeqCst + ) + .unwrap() + .addr(), + x.addr(), + ); + assert_eq!( + ptr.compare_exchange( + (&mut 0 as *mut i32).with_addr(x.addr()), + ptr::invalid_mut(0), + SeqCst, + SeqCst + ) + .unwrap_err() + .addr(), + 0, + ); + ptr.store(x, Relaxed); + + assert_eq!(ptr.fetch_ptr_add(13, AcqRel).addr(), x.addr()); + unsafe { assert_eq!(*ptr.load(SeqCst), 13) }; // points to index 13 now + assert_eq!(ptr.fetch_ptr_sub(4, AcqRel).addr(), x.addr() + 13 * 4); + unsafe { assert_eq!(*ptr.load(SeqCst), 9) }; + assert_eq!(ptr.fetch_or(3, AcqRel).addr(), x.addr() + 9 * 4); // ptr is 4-aligned, so set the last 2 bits + assert_eq!(ptr.fetch_and(!3, AcqRel).addr(), (x.addr() + 9 * 4) | 3); // and unset them again + unsafe { assert_eq!(*ptr.load(SeqCst), 9) }; + assert_eq!(ptr.fetch_xor(0xdeadbeef, AcqRel).addr(), x.addr() + 9 * 4); + assert_eq!(ptr.fetch_xor(0xdeadbeef, AcqRel).addr(), (x.addr() + 9 * 4) ^ 0xdeadbeef); + unsafe { assert_eq!(*ptr.load(SeqCst), 9) }; // after XORing twice with the same thing, we get our ptr back +} + fn weak_sometimes_fails() { let atomic = AtomicBool::new(false); let tries = 100; diff --git a/tests/pass/backtrace/backtrace-api-v0.rs b/tests/pass/backtrace/backtrace-api-v0.rs index 32fd47d8c5..5cd12959ca 100644 --- a/tests/pass/backtrace/backtrace-api-v0.rs +++ b/tests/pass/backtrace/backtrace-api-v0.rs @@ -1,4 +1,4 @@ -// normalize-stderr-test: "::<.*>" -> "" +//@normalize-stderr-test: "::<.*>" -> "" #[inline(never)] fn func_a() -> Box<[*mut ()]> { diff --git a/tests/pass/backtrace/backtrace-api-v1.rs b/tests/pass/backtrace/backtrace-api-v1.rs index c24a5f3e81..1e35574b39 100644 --- a/tests/pass/backtrace/backtrace-api-v1.rs +++ b/tests/pass/backtrace/backtrace-api-v1.rs @@ -1,4 +1,4 @@ -// normalize-stderr-test: "::<.*>" -> "" +//@normalize-stderr-test: "::<.*>" -> "" #[inline(never)] fn func_a() -> Box<[*mut ()]> { diff --git a/tests/pass/backtrace/backtrace-global-alloc.rs b/tests/pass/backtrace/backtrace-global-alloc.rs index 8c51bf6270..45d6535bc1 100644 --- a/tests/pass/backtrace/backtrace-global-alloc.rs +++ b/tests/pass/backtrace/backtrace-global-alloc.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-disable-isolation +//@compile-flags: -Zmiri-disable-isolation #![feature(backtrace)] diff --git a/tests/pass/backtrace/backtrace-std.rs b/tests/pass/backtrace/backtrace-std.rs index 5de7cdd6a5..488b87ede8 100644 --- a/tests/pass/backtrace/backtrace-std.rs +++ b/tests/pass/backtrace/backtrace-std.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-disable-isolation +//@compile-flags: -Zmiri-disable-isolation #![feature(backtrace)] diff --git a/tests/pass/btreemap.rs b/tests/pass/btreemap.rs index 413d7ef53d..040c648d47 100644 --- a/tests/pass/btreemap.rs +++ b/tests/pass/btreemap.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-strict-provenance +//@compile-flags: -Zmiri-strict-provenance #![feature(btree_drain_filter)] use std::collections::{BTreeMap, BTreeSet}; use std::mem; diff --git a/tests/pass/calloc.rs b/tests/pass/calloc.rs index 9f614ce971..a9efb776b6 100644 --- a/tests/pass/calloc.rs +++ b/tests/pass/calloc.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] diff --git a/tests/pass/concurrency/channels.rs b/tests/pass/concurrency/channels.rs index 0d6c1749eb..40d729f042 100644 --- a/tests/pass/concurrency/channels.rs +++ b/tests/pass/concurrency/channels.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-strict-provenance +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-strict-provenance use std::sync::mpsc::{channel, sync_channel}; use std::thread; diff --git a/tests/pass/concurrency/concurrent_caller_location.rs b/tests/pass/concurrency/concurrent_caller_location.rs index 003b9e9ca9..a07f6b13e6 100644 --- a/tests/pass/concurrency/concurrent_caller_location.rs +++ b/tests/pass/concurrency/concurrent_caller_location.rs @@ -1,4 +1,4 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +//@ignore-target-windows: Concurrency on Windows is not supported yet. use std::panic::Location; use std::thread::spawn; diff --git a/tests/pass/concurrency/data_race.rs b/tests/pass/concurrency/data_race.rs index 812003ef4d..5d1e1bb266 100644 --- a/tests/pass/concurrency/data_race.rs +++ b/tests/pass/concurrency/data_race.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-disable-weak-memory-emulation +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-disable-weak-memory-emulation use std::sync::atomic::{fence, AtomicUsize, Ordering}; use std::thread::spawn; diff --git a/tests/pass/concurrency/disable_data_race_detector.rs b/tests/pass/concurrency/disable_data_race_detector.rs index 14e2d5651d..a4852b4674 100644 --- a/tests/pass/concurrency/disable_data_race_detector.rs +++ b/tests/pass/concurrency/disable_data_race_detector.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-disable-data-race-detector +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-disable-data-race-detector use std::thread::spawn; diff --git a/tests/pass/concurrency/issue1643.rs b/tests/pass/concurrency/issue1643.rs index 1238a1bd6f..f4fe43d88c 100644 --- a/tests/pass/concurrency/issue1643.rs +++ b/tests/pass/concurrency/issue1643.rs @@ -1,4 +1,4 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +//@ignore-target-windows: Concurrency on Windows is not supported yet. use std::thread::spawn; diff --git a/tests/pass/concurrency/libc_pthread_cond.rs b/tests/pass/concurrency/libc_pthread_cond.rs index 1f6f46cbeb..eb491486be 100644 --- a/tests/pass/concurrency/libc_pthread_cond.rs +++ b/tests/pass/concurrency/libc_pthread_cond.rs @@ -1,6 +1,6 @@ -// ignore-windows: No libc on Windows -// ignore-apple: pthread_condattr_setclock is not supported on MacOS. -// compile-flags: -Zmiri-disable-isolation +//@ignore-target-windows: No libc on Windows +//@ignore-target-apple: pthread_condattr_setclock is not supported on MacOS. +//@compile-flags: -Zmiri-disable-isolation #![feature(rustc_private)] diff --git a/tests/pass/concurrency/linux-futex.rs b/tests/pass/concurrency/linux-futex.rs index b65dd46d59..f9c87e0723 100644 --- a/tests/pass/concurrency/linux-futex.rs +++ b/tests/pass/concurrency/linux-futex.rs @@ -1,5 +1,5 @@ -// only-linux -// compile-flags: -Zmiri-disable-isolation +//@only-target-linux +//@compile-flags: -Zmiri-disable-isolation #![feature(rustc_private)] extern crate libc; diff --git a/tests/pass/concurrency/mutex_leak.rs b/tests/pass/concurrency/mutex_leak.rs index 7fbc6dd301..3ac0c9336b 100644 --- a/tests/pass/concurrency/mutex_leak.rs +++ b/tests/pass/concurrency/mutex_leak.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-ignore-leaks +//@compile-flags: -Zmiri-ignore-leaks use std::mem; use std::sync::Mutex; diff --git a/tests/pass/concurrency/simple.rs b/tests/pass/concurrency/simple.rs index 48c1f3d9fb..1d85c7fc9b 100644 --- a/tests/pass/concurrency/simple.rs +++ b/tests/pass/concurrency/simple.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-strict-provenance +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-strict-provenance use std::thread; diff --git a/tests/pass/concurrency/spin_loop.rs b/tests/pass/concurrency/spin_loop.rs index e11f0789bb..5f34168dbb 100644 --- a/tests/pass/concurrency/spin_loop.rs +++ b/tests/pass/concurrency/spin_loop.rs @@ -1,4 +1,4 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +//@ignore-target-windows: Concurrency on Windows is not supported yet. use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; diff --git a/tests/pass/concurrency/spin_loops_nopreempt.rs b/tests/pass/concurrency/spin_loops_nopreempt.rs index 99a5410c95..34be0dd394 100644 --- a/tests/pass/concurrency/spin_loops_nopreempt.rs +++ b/tests/pass/concurrency/spin_loops_nopreempt.rs @@ -1,6 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +//@ignore-target-windows: Concurrency on Windows is not supported yet. // This specifically tests behavior *without* preemption. -// compile-flags: -Zmiri-preemption-rate=0 +//@compile-flags: -Zmiri-preemption-rate=0 use std::cell::Cell; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; diff --git a/tests/pass/concurrency/sync.rs b/tests/pass/concurrency/sync.rs index 396c1a97e0..268153b612 100644 --- a/tests/pass/concurrency/sync.rs +++ b/tests/pass/concurrency/sync.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-disable-isolation -Zmiri-strict-provenance +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-disable-isolation -Zmiri-strict-provenance use std::sync::{Arc, Barrier, Condvar, Mutex, Once, RwLock}; use std::thread; @@ -219,10 +219,8 @@ fn park_unpark() { // know Miri's timed synchronization primitives do not do that. assert!((200..1000).contains(&start.elapsed().as_millis())); -} -fn check_condvar() { - let _ = std::sync::Condvar::new(); + t2.join().unwrap(); } fn main() { @@ -236,5 +234,4 @@ fn main() { check_once(); park_timeout(); park_unpark(); - check_condvar(); } diff --git a/tests/pass/concurrency/sync_nopreempt.rs b/tests/pass/concurrency/sync_nopreempt.rs index b5e726dac7..a53f143fef 100644 --- a/tests/pass/concurrency/sync_nopreempt.rs +++ b/tests/pass/concurrency/sync_nopreempt.rs @@ -1,6 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +//@ignore-target-windows: Concurrency on Windows is not supported yet. // We are making scheduler assumptions here. -// compile-flags: -Zmiri-strict-provenance -Zmiri-preemption-rate=0 +//@compile-flags: -Zmiri-strict-provenance -Zmiri-preemption-rate=0 use std::sync::{Arc, Condvar, Mutex, RwLock}; use std::thread; diff --git a/tests/pass/concurrency/thread_locals.rs b/tests/pass/concurrency/thread_locals.rs index 5b11539f7f..34431def33 100644 --- a/tests/pass/concurrency/thread_locals.rs +++ b/tests/pass/concurrency/thread_locals.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-strict-provenance +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-strict-provenance //! The main purpose of this test is to check that if we take a pointer to //! thread's `t1` thread-local `A` and send it to another thread `t2`, diff --git a/tests/pass/concurrency/tls_lib_drop.rs b/tests/pass/concurrency/tls_lib_drop.rs index fe46406c28..6c66cd3a62 100644 --- a/tests/pass/concurrency/tls_lib_drop.rs +++ b/tests/pass/concurrency/tls_lib_drop.rs @@ -1,4 +1,4 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +//@ignore-target-windows: Concurrency on Windows is not supported yet. use std::cell::RefCell; use std::thread; diff --git a/tests/pass/concurrency/tls_pthread_drop_order.rs b/tests/pass/concurrency/tls_pthread_drop_order.rs index 29c57bf49a..c9e8b9271c 100644 --- a/tests/pass/concurrency/tls_pthread_drop_order.rs +++ b/tests/pass/concurrency/tls_pthread_drop_order.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] extern crate libc; diff --git a/tests/pass/current_dir.rs b/tests/pass/current_dir.rs index a88f820951..069b462ab3 100644 --- a/tests/pass/current_dir.rs +++ b/tests/pass/current_dir.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-disable-isolation +//@compile-flags: -Zmiri-disable-isolation use std::env; use std::io::ErrorKind; diff --git a/tests/pass/current_dir_with_isolation.rs b/tests/pass/current_dir_with_isolation.rs index 98c44d57b6..9dbcfeae2d 100644 --- a/tests/pass/current_dir_with_isolation.rs +++ b/tests/pass/current_dir_with_isolation.rs @@ -1,6 +1,6 @@ -// compile-flags: -Zmiri-isolation-error=warn-nobacktrace -// normalize-stderr-test: "(getcwd|GetCurrentDirectoryW)" -> "$$GETCWD" -// normalize-stderr-test: "(chdir|SetCurrentDirectoryW)" -> "$$SETCWD" +//@compile-flags: -Zmiri-isolation-error=warn-nobacktrace +//@normalize-stderr-test: "(getcwd|GetCurrentDirectoryW)" -> "$$GETCWD" +//@normalize-stderr-test: "(chdir|SetCurrentDirectoryW)" -> "$$SETCWD" use std::env; use std::io::ErrorKind; diff --git a/tests/pass/disable-alignment-check.rs b/tests/pass/disable-alignment-check.rs index 2fb0dd8369..366aff4a9f 100644 --- a/tests/pass/disable-alignment-check.rs +++ b/tests/pass/disable-alignment-check.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-disable-alignment-check +//@compile-flags: -Zmiri-disable-alignment-check fn main() { let mut x = [0u8; 20]; diff --git a/tests/pass/dyn-traits.rs b/tests/pass/dyn-traits.rs index 00667757b0..908d521a0d 100644 --- a/tests/pass/dyn-traits.rs +++ b/tests/pass/dyn-traits.rs @@ -1,6 +1,3 @@ -#![feature(unsized_locals, unsized_fn_params)] -#![allow(incomplete_features)] - fn ref_box_dyn() { struct Struct(i32); @@ -75,6 +72,9 @@ fn box_box_trait() { assert!(unsafe { DROPPED }); } +// Disabled for now: unsized locals are not supported, +// their current MIR encoding is just not great. +/* fn unsized_dyn() { pub trait Foo { fn foo(self) -> String; @@ -95,7 +95,6 @@ fn unsized_dyn() { let x = Box::new(A) as Box; assert_eq!(x.foo(), format!("hello")); } - fn unsized_dyn_autoderef() { pub trait Foo { fn foo(self) -> String; @@ -140,12 +139,9 @@ fn unsized_dyn_autoderef() { let x = Box::new(|| "hello".to_owned()) as Box String>; assert_eq!(&x.foo() as &str, "hello"); } +*/ fn main() { ref_box_dyn(); box_box_trait(); - - // "exotic" receivers - unsized_dyn(); - unsized_dyn_autoderef(); } diff --git a/tests/pass/enum_discriminant_ptr_value.rs b/tests/pass/enum_discriminant_ptr_value.rs index 618d503cd5..4a3820777c 100644 --- a/tests/pass/enum_discriminant_ptr_value.rs +++ b/tests/pass/enum_discriminant_ptr_value.rs @@ -1,6 +1,6 @@ // A niche-optimized enum where the discriminant is a pointer value -- relies on ptr-to-int casts in // the niche handling code. -// compile-flags: -Zmiri-disable-stacked-borrows -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-stacked-borrows -Zmiri-disable-validation fn main() { let x = 42; diff --git a/tests/pass/env-exclude.rs b/tests/pass/env-exclude.rs index 1e251084f0..14ad827463 100644 --- a/tests/pass/env-exclude.rs +++ b/tests/pass/env-exclude.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-disable-isolation -Zmiri-env-exclude=MIRI_ENV_VAR_TEST +//@compile-flags: -Zmiri-disable-isolation -Zmiri-env-exclude=MIRI_ENV_VAR_TEST fn main() { assert!(std::env::var("MIRI_ENV_VAR_TEST").is_err()); diff --git a/tests/pass/env-forward.rs b/tests/pass/env-forward.rs index 8eebc45f55..da7730b00f 100644 --- a/tests/pass/env-forward.rs +++ b/tests/pass/env-forward.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-env-forward=MIRI_ENV_VAR_TEST +//@compile-flags: -Zmiri-env-forward=MIRI_ENV_VAR_TEST fn main() { assert_eq!(std::env::var("MIRI_ENV_VAR_TEST"), Ok("0".to_owned())); diff --git a/tests/pass/env-without-isolation.rs b/tests/pass/env-without-isolation.rs index 6384762098..3d7461eecf 100644 --- a/tests/pass/env-without-isolation.rs +++ b/tests/pass/env-without-isolation.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-disable-isolation +//@compile-flags: -Zmiri-disable-isolation fn main() { assert_eq!(std::env::var("MIRI_ENV_VAR_TEST"), Ok("0".to_owned())); diff --git a/tests/pass/external_C/int_c_tests.rs b/tests/pass/external_C/int_c_tests.rs new file mode 100644 index 0000000000..73b01f7cc7 --- /dev/null +++ b/tests/pass/external_C/int_c_tests.rs @@ -0,0 +1,43 @@ +//@only-target-linux +//@only-on-host +//@compile-flags: -Zmiri-external_c_so_file=tests/external_C/libtestlib.so + +extern "C" { + fn add_one_int(x: i32) -> i32; + fn add_int16(x: i16) -> i16; + fn test_stack_spill( + a: i32, + b: i32, + c: i32, + d: i32, + e: i32, + f: i32, + g: i32, + h: i32, + i: i32, + j: i32, + k: i32, + l: i32, + ) -> i32; + fn add_short_to_long(x: i16, y: i64) -> i64; + fn get_unsigned_int() -> u32; +} + +fn main() { + unsafe { + // test function that adds 2 to a provided int + assert_eq!(add_one_int(1), 3); + + // test function that takes the sum of its 12 arguments + assert_eq!(test_stack_spill(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), 78); + + // test function that adds 3 to a 16 bit int + assert_eq!(add_int16(-1i16), 2i16); + + // test function that adds an i16 to an i64 + assert_eq!(add_short_to_long(-1i16, 123456789123i64), 123456789122i64); + + // test function that returns -10 as an unsigned int + assert_eq!(get_unsigned_int(), (-10i32) as u32); + } +} diff --git a/tests/pass/external_C/print_from_c.rs b/tests/pass/external_C/print_from_c.rs new file mode 100644 index 0000000000..06be7a9ebf --- /dev/null +++ b/tests/pass/external_C/print_from_c.rs @@ -0,0 +1,15 @@ +//@only-target-linux +//@only-on-host +//@compile-flags: -Zmiri-external_c_so_file=tests/external_C/libtestlib.so + +extern "C" { + fn printer(); +} + +fn main() { + unsafe { + // test void function that prints from C -- call it twice + printer(); + printer(); + } +} diff --git a/tests/pass/external_C/print_from_c.stdout b/tests/pass/external_C/print_from_c.stdout new file mode 100644 index 0000000000..df3475a9ea --- /dev/null +++ b/tests/pass/external_C/print_from_c.stdout @@ -0,0 +1,2 @@ +printing from C +printing from C diff --git a/tests/pass/fs.rs b/tests/pass/fs.rs index e106ca5d02..aa5cd83c69 100644 --- a/tests/pass/fs.rs +++ b/tests/pass/fs.rs @@ -1,5 +1,5 @@ -// ignore-windows: File handling is not implemented yet -// compile-flags: -Zmiri-disable-isolation +//@ignore-target-windows: File handling is not implemented yet +//@compile-flags: -Zmiri-disable-isolation #![feature(rustc_private)] #![feature(io_error_more)] diff --git a/tests/pass/fs_with_isolation.rs b/tests/pass/fs_with_isolation.rs index 6753145e92..41ada94e27 100644 --- a/tests/pass/fs_with_isolation.rs +++ b/tests/pass/fs_with_isolation.rs @@ -1,6 +1,6 @@ -// ignore-windows: File handling is not implemented yet -// compile-flags: -Zmiri-isolation-error=warn-nobacktrace -// normalize-stderr-test: "(stat(x)?)" -> "$$STAT" +//@ignore-target-windows: File handling is not implemented yet +//@compile-flags: -Zmiri-isolation-error=warn-nobacktrace +//@normalize-stderr-test: "(stat(x)?)" -> "$$STAT" #![feature(rustc_private)] diff --git a/tests/pass/function_calls/disable_abi_check.rs b/tests/pass/function_calls/disable_abi_check.rs index 1f85547413..e6251b5355 100644 --- a/tests/pass/function_calls/disable_abi_check.rs +++ b/tests/pass/function_calls/disable_abi_check.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-disable-abi-check +//@compile-flags: -Zmiri-disable-abi-check #![feature(core_intrinsics)] fn main() { diff --git a/tests/pass/getpid.rs b/tests/pass/getpid.rs index 258fdeaa84..733545462e 100644 --- a/tests/pass/getpid.rs +++ b/tests/pass/getpid.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-disable-isolation +//@compile-flags: -Zmiri-disable-isolation fn getpid() -> u32 { std::process::id() diff --git a/tests/pass/hide_stdout.rs b/tests/pass/hide_stdout.rs index 3ee68d01f4..cfd05a8396 100644 --- a/tests/pass/hide_stdout.rs +++ b/tests/pass/hide_stdout.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-mute-stdout-stderr +//@compile-flags: -Zmiri-mute-stdout-stderr fn main() { println!("print to stdout"); diff --git a/tests/pass/integer-ops.rs b/tests/pass/integer-ops.rs index 8e2799d689..8608d12d4d 100644 --- a/tests/pass/integer-ops.rs +++ b/tests/pass/integer-ops.rs @@ -1,4 +1,4 @@ -// compile-flags: -Coverflow-checks=off +//@compile-flags: -Coverflow-checks=off #![feature(int_log)] #![allow(arithmetic_overflow)] diff --git a/tests/pass/intptrcast.rs b/tests/pass/intptrcast.rs index aebf5b2223..e7ff90cb6b 100644 --- a/tests/pass/intptrcast.rs +++ b/tests/pass/intptrcast.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance use std::mem; diff --git a/tests/pass/intrinsics.rs b/tests/pass/intrinsics.rs index 0042872a3b..756744bada 100644 --- a/tests/pass/intrinsics.rs +++ b/tests/pass/intrinsics.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance #![feature(core_intrinsics, const_raw_ptr_comparison)] #![feature(layout_for_ptr)] diff --git a/tests/pass/issues/issue-miri-1925.rs b/tests/pass/issues/issue-miri-1925.rs index 262889f56e..8655681349 100644 --- a/tests/pass/issues/issue-miri-1925.rs +++ b/tests/pass/issues/issue-miri-1925.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-symbolic-alignment-check +//@compile-flags: -Zmiri-symbolic-alignment-check use std::mem::size_of; diff --git a/tests/pass/issues/issue-miri-2068-2.rs b/tests/pass/issues/issue-miri-2068-2.rs index 204a4dd056..f33806e8b4 100644 --- a/tests/pass/issues/issue-miri-2068-2.rs +++ b/tests/pass/issues/issue-miri-2068-2.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-validation use std::mem::MaybeUninit; diff --git a/tests/pass/libc.rs b/tests/pass/libc.rs index d08430a432..99b7c3f249 100644 --- a/tests/pass/libc.rs +++ b/tests/pass/libc.rs @@ -1,11 +1,12 @@ -// ignore-windows: No libc on Windows -// compile-flags: -Zmiri-disable-isolation - +//@ignore-target-windows: No libc on Windows +//@compile-flags: -Zmiri-disable-isolation #![feature(rustc_private)] +use std::fs::{remove_file, File}; +use std::os::unix::io::AsRawFd; + extern crate libc; -#[cfg(any(target_os = "linux", target_os = "freebsd"))] fn tmp() -> std::path::PathBuf { std::env::var("MIRI_TEMP") .map(std::path::PathBuf::from) @@ -15,9 +16,7 @@ fn tmp() -> std::path::PathBuf { #[cfg(any(target_os = "linux", target_os = "freebsd"))] fn test_posix_fadvise() { use std::convert::TryInto; - use std::fs::{remove_file, File}; use std::io::Write; - use std::os::unix::io::AsRawFd; let path = tmp().join("miri_test_libc_posix_fadvise.txt"); // Cleanup before test @@ -44,9 +43,7 @@ fn test_posix_fadvise() { #[cfg(any(target_os = "linux"))] fn test_sync_file_range() { - use std::fs::{remove_file, File}; use std::io::Write; - use std::os::unix::io::AsRawFd; let path = tmp().join("miri_test_libc_sync_file_range.txt"); // Cleanup before test. @@ -311,6 +308,30 @@ fn test_posix_gettimeofday() { assert_eq!(is_error, -1); } +fn test_isatty() { + // Testing whether our isatty shim returns the right value would require controlling whether + // these streams are actually TTYs, which is hard. + // For now, we just check that these calls are supported at all. + unsafe { + libc::isatty(libc::STDIN_FILENO); + libc::isatty(libc::STDOUT_FILENO); + libc::isatty(libc::STDERR_FILENO); + + // But when we open a file, it is definitely not a TTY. + let path = tmp().join("notatty.txt"); + // Cleanup before test. + remove_file(&path).ok(); + let file = File::create(&path).unwrap(); + + assert_eq!(libc::isatty(file.as_raw_fd()), 0); + assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::ENOTTY); + + // Cleanup after test. + drop(file); + remove_file(&path).unwrap(); + } +} + fn main() { #[cfg(any(target_os = "linux", target_os = "freebsd"))] test_posix_fadvise(); @@ -335,4 +356,6 @@ fn main() { #[cfg(any(target_os = "linux"))] test_clocks(); + + test_isatty(); } diff --git a/tests/pass/linux-getrandom-without-isolation.rs b/tests/pass/linux-getrandom-without-isolation.rs index 56a5369947..fea3bb3fdc 100644 --- a/tests/pass/linux-getrandom-without-isolation.rs +++ b/tests/pass/linux-getrandom-without-isolation.rs @@ -1,5 +1,5 @@ -// only-linux -// compile-flags: -Zmiri-disable-isolation +//@only-target-linux +//@compile-flags: -Zmiri-disable-isolation #![feature(rustc_private)] extern crate libc; diff --git a/tests/pass/linux-getrandom.rs b/tests/pass/linux-getrandom.rs index a3596e4c7a..1d0ab6ed74 100644 --- a/tests/pass/linux-getrandom.rs +++ b/tests/pass/linux-getrandom.rs @@ -1,4 +1,4 @@ -// only-linux +//@only-target-linux #![feature(rustc_private)] extern crate libc; diff --git a/tests/pass/malloc.rs b/tests/pass/malloc.rs index 72abc68bb9..d20ceddbb8 100644 --- a/tests/pass/malloc.rs +++ b/tests/pass/malloc.rs @@ -1,4 +1,4 @@ -// ignore-windows: No libc on Windows +//@ignore-target-windows: No libc on Windows #![feature(rustc_private)] diff --git a/tests/pass/memleak_ignored.rs b/tests/pass/memleak_ignored.rs index fddf14121e..60e06094e1 100644 --- a/tests/pass/memleak_ignored.rs +++ b/tests/pass/memleak_ignored.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-ignore-leaks +//@compile-flags: -Zmiri-ignore-leaks fn main() { std::mem::forget(Box::new(42)); diff --git a/tests/pass/move-uninit-primval.rs b/tests/pass/move-uninit-primval.rs index 1ca3873d1d..220470b637 100644 --- a/tests/pass/move-uninit-primval.rs +++ b/tests/pass/move-uninit-primval.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-allow-uninit-numbers +//@compile-flags: -Zmiri-allow-uninit-numbers #![allow(deprecated)] struct Foo { diff --git a/tests/pass/no_std.rs b/tests/pass/no_std.rs index 6808dab814..10632c2cce 100644 --- a/tests/pass/no_std.rs +++ b/tests/pass/no_std.rs @@ -3,7 +3,7 @@ // windows tls dtors go through libstd right now, thus this test // cannot pass. When windows tls dtors go through the special magic // windows linker section, we can run this test on windows again. -// ignore-windows +//@ignore-target-windows #[start] fn start(_: isize, _: *const *const u8) -> isize { diff --git a/tests/pass/observed_local_mut.rs b/tests/pass/observed_local_mut.rs index 888b6f85e3..ca0f569860 100644 --- a/tests/pass/observed_local_mut.rs +++ b/tests/pass/observed_local_mut.rs @@ -1,5 +1,5 @@ // Stacked Borrows catches this (correctly) as UB. -// compile-flags: -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-disable-stacked-borrows // This test is intended to guard against the problem described in commit // 39bb1254d1eaf74f45a4e741097e33fc942168d5. diff --git a/tests/pass/overflow_checks_off.rs b/tests/pass/overflow_checks_off.rs index 2896d3161f..79aa510ef9 100644 --- a/tests/pass/overflow_checks_off.rs +++ b/tests/pass/overflow_checks_off.rs @@ -1,4 +1,4 @@ -// compile-flags: -C overflow-checks=off +//@compile-flags: -C overflow-checks=off // Check that we correctly implement the intended behavior of these operators // when they are not being overflow-checked. diff --git a/tests/pass/panic/catch_panic.rs b/tests/pass/panic/catch_panic.rs index 3979fb3b07..3089044065 100644 --- a/tests/pass/panic/catch_panic.rs +++ b/tests/pass/panic/catch_panic.rs @@ -1,5 +1,5 @@ // We test the `align_offset` panic below, make sure we test the interpreter impl and not the "real" one. -// compile-flags: -Zmiri-symbolic-alignment-check -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-symbolic-alignment-check -Zmiri-permissive-provenance #![feature(never_type)] #![allow(unconditional_panic, non_fmt_panics)] diff --git a/tests/pass/panic/concurrent-panic.rs b/tests/pass/panic/concurrent-panic.rs index 7b17ac4fa7..1acae69b8d 100644 --- a/tests/pass/panic/concurrent-panic.rs +++ b/tests/pass/panic/concurrent-panic.rs @@ -1,6 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +//@ignore-target-windows: Concurrency on Windows is not supported yet. // We are making scheduler assumptions here. -// compile-flags: -Zmiri-preemption-rate=0 +//@compile-flags: -Zmiri-preemption-rate=0 //! Cause a panic in one thread while another thread is unwinding. This checks //! that separate threads have their own panicking state. diff --git a/tests/pass/portable-simd.rs b/tests/pass/portable-simd.rs index ffbaa1832e..0dfe617bd8 100644 --- a/tests/pass/portable-simd.rs +++ b/tests/pass/portable-simd.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-strict-provenance +//@compile-flags: -Zmiri-strict-provenance #![feature(portable_simd, platform_intrinsics)] use std::simd::*; diff --git a/tests/pass/ptr_int_casts.rs b/tests/pass/ptr_int_casts.rs index ffe6a114c6..3044ac092b 100644 --- a/tests/pass/ptr_int_casts.rs +++ b/tests/pass/ptr_int_casts.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance use std::mem; use std::ptr; diff --git a/tests/pass/ptr_int_from_exposed.rs b/tests/pass/ptr_int_from_exposed.rs index dc9cb393b7..ef7ff34d26 100644 --- a/tests/pass/ptr_int_from_exposed.rs +++ b/tests/pass/ptr_int_from_exposed.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance #![feature(strict_provenance)] use std::ptr; diff --git a/tests/pass/ptr_offset.rs b/tests/pass/ptr_offset.rs index b16a06a726..5270e8663b 100644 --- a/tests/pass/ptr_offset.rs +++ b/tests/pass/ptr_offset.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance use std::{mem, ptr}; fn main() { diff --git a/tests/pass/rc.rs b/tests/pass/rc.rs index 260e350f27..569dbc459a 100644 --- a/tests/pass/rc.rs +++ b/tests/pass/rc.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-strict-provenance +//@compile-flags: -Zmiri-strict-provenance #![feature(new_uninit)] #![feature(get_mut_unchecked)] diff --git a/tests/pass/slices.rs b/tests/pass/slices.rs index 3a13ec59a0..a56b97a508 100644 --- a/tests/pass/slices.rs +++ b/tests/pass/slices.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-strict-provenance +//@compile-flags: -Zmiri-strict-provenance #![feature(new_uninit)] #![feature(slice_as_chunks)] #![feature(slice_partition_dedup)] diff --git a/tests/pass/stacked-borrows/int-to-ptr.rs b/tests/pass/stacked-borrows/int-to-ptr.rs index dc36754062..c3e30627a7 100644 --- a/tests/pass/stacked-borrows/int-to-ptr.rs +++ b/tests/pass/stacked-borrows/int-to-ptr.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance #![feature(strict_provenance)] use std::ptr; diff --git a/tests/pass/stacked-borrows/interior_mutability.rs b/tests/pass/stacked-borrows/interior_mutability.rs index 96ad67505a..c6373a7eaf 100644 --- a/tests/pass/stacked-borrows/interior_mutability.rs +++ b/tests/pass/stacked-borrows/interior_mutability.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-retag-fields +//@compile-flags: -Zmiri-retag-fields use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell}; use std::mem::{self, MaybeUninit}; diff --git a/tests/pass/stacked-borrows/stacked-borrows.rs b/tests/pass/stacked-borrows/stacked-borrows.rs index b915a2ddf8..ef6eb346c1 100644 --- a/tests/pass/stacked-borrows/stacked-borrows.rs +++ b/tests/pass/stacked-borrows/stacked-borrows.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-retag-fields +//@compile-flags: -Zmiri-retag-fields #![feature(allocator_api)] use std::ptr; diff --git a/tests/pass/strings.rs b/tests/pass/strings.rs index ccefc69bd1..5e2d2e9b5b 100644 --- a/tests/pass/strings.rs +++ b/tests/pass/strings.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-strict-provenance +//@compile-flags: -Zmiri-strict-provenance fn empty() -> &'static str { "" diff --git a/tests/pass/threadleak_ignored.rs b/tests/pass/threadleak_ignored.rs index 36d39a72b7..077d8d0837 100644 --- a/tests/pass/threadleak_ignored.rs +++ b/tests/pass/threadleak_ignored.rs @@ -1,6 +1,6 @@ -// ignore-windows: Concurrency on Windows is not supported yet. +//@ignore-target-windows: Concurrency on Windows is not supported yet. // FIXME: disallow preemption to work around https://github.com/rust-lang/rust/issues/55005 -// compile-flags: -Zmiri-ignore-leaks -Zmiri-preemption-rate=0 +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-preemption-rate=0 //! Test that leaking threads works, and that their destructors are not executed. diff --git a/tests/pass/time.rs b/tests/pass/time.rs index 38e846309d..e1094006fb 100644 --- a/tests/pass/time.rs +++ b/tests/pass/time.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-disable-isolation +//@compile-flags: -Zmiri-disable-isolation use std::time::{Duration, Instant, SystemTime}; diff --git a/tests/pass/track-alloc-1.rs b/tests/pass/track-alloc-1.rs index bbd88ed5d5..427c800dc5 100644 --- a/tests/pass/track-alloc-1.rs +++ b/tests/pass/track-alloc-1.rs @@ -1,6 +1,6 @@ // Ensure that tracking early allocations doesn't ICE Miri. // Early allocations are probably part of the runtime and therefore uninteresting, but they // shouldn't cause a crash. -// compile-flags: -Zmiri-track-alloc-id=1 -// normalize-stderr-test: "[48] bytes" -> "SIZE bytes" +//@compile-flags: -Zmiri-track-alloc-id=1 +//@normalize-stderr-test: "[48] bytes" -> "SIZE bytes" fn main() {} diff --git a/tests/pass/transmute_fat.rs b/tests/pass/transmute_fat.rs index 1d2ec92a80..dfd78ace52 100644 --- a/tests/pass/transmute_fat.rs +++ b/tests/pass/transmute_fat.rs @@ -1,8 +1,9 @@ // Stacked Borrows disallows this becuase the reference is never cast to a raw pointer. -// compile-flags: -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-disable-stacked-borrows fn main() { // If we are careful, we can exploit data layout... + // This is a tricky case since we are transmuting a ScalarPair type to a non-ScalarPair type. let raw = unsafe { std::mem::transmute::<&[u8], [*const u8; 2]>(&[42]) }; let ptr: *const u8 = unsafe { std::mem::transmute_copy(&raw) }; assert_eq!(unsafe { *ptr }, 42); diff --git a/tests/pass/uninit_number_ignored.rs b/tests/pass/uninit_number_ignored.rs index 13aac61ba8..44f6fa2679 100644 --- a/tests/pass/uninit_number_ignored.rs +++ b/tests/pass/uninit_number_ignored.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-allow-uninit-numbers +//@compile-flags: -Zmiri-allow-uninit-numbers // This test is adapted from https://github.com/rust-lang/miri/issues/1340#issue-600900312. fn main() { diff --git a/tests/pass/union-overwrite.rs b/tests/pass/union-overwrite.rs index d3c81834bc..f1d3d9d481 100644 --- a/tests/pass/union-overwrite.rs +++ b/tests/pass/union-overwrite.rs @@ -1,5 +1,3 @@ -#![feature(untagged_unions)] - #[repr(C)] #[derive(Clone, Copy)] struct Pair(T, U); diff --git a/tests/pass/union.rs b/tests/pass/union.rs index f98a213102..8a6dd49f45 100644 --- a/tests/pass/union.rs +++ b/tests/pass/union.rs @@ -1,5 +1,3 @@ -#![feature(untagged_unions)] - fn main() { a(); b(); @@ -22,6 +20,7 @@ fn a() { } fn b() { + #[derive(Copy, Clone)] struct S { x: u32, y: u32, diff --git a/tests/pass/unsized-tuple-impls.rs b/tests/pass/unsized-tuple-impls.rs deleted file mode 100644 index bbab1125a0..0000000000 --- a/tests/pass/unsized-tuple-impls.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![feature(unsized_tuple_coercion)] -use std::mem; - -fn main() { - let x: &(i32, i32, [i32]) = &(0, 1, [2, 3]); - let y: &(i32, i32, [i32]) = &(0, 1, [2, 3, 4]); - let mut a = [y, x]; - a.sort(); - assert_eq!(a, [x, y]); - - assert_eq!(&format!("{:?}", a), "[(0, 1, [2, 3]), (0, 1, [2, 3, 4])]"); - assert_eq!(mem::size_of_val(x), 16); -} diff --git a/tests/pass/unsized.rs b/tests/pass/unsized.rs new file mode 100644 index 0000000000..d9beac4327 --- /dev/null +++ b/tests/pass/unsized.rs @@ -0,0 +1,36 @@ +#![feature(unsized_tuple_coercion)] +#![feature(unsized_fn_params)] + +use std::mem; + +fn unsized_tuple() { + let x: &(i32, i32, [i32]) = &(0, 1, [2, 3]); + let y: &(i32, i32, [i32]) = &(0, 1, [2, 3, 4]); + let mut a = [y, x]; + a.sort(); + assert_eq!(a, [x, y]); + + assert_eq!(&format!("{:?}", a), "[(0, 1, [2, 3]), (0, 1, [2, 3, 4])]"); + assert_eq!(mem::size_of_val(x), 16); +} + +fn unsized_params() { + pub fn f0(_f: dyn FnOnce()) {} + pub fn f1(_s: str) {} + pub fn f2(_x: i32, _y: [i32]) {} + pub fn f3(_p: dyn Send) {} + + let c: Box = Box::new(|| {}); + f0(*c); + let foo = "foo".to_string().into_boxed_str(); + f1(*foo); + let sl: Box<[i32]> = [0, 1, 2].to_vec().into_boxed_slice(); + f2(5, *sl); + let p: Box = Box::new((1, 2)); + f3(*p); +} + +fn main() { + unsized_tuple(); + unsized_params(); +} diff --git a/tests/pass/vec.rs b/tests/pass/vec.rs index 89c2561acd..26732cec5e 100644 --- a/tests/pass/vec.rs +++ b/tests/pass/vec.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-strict-provenance +//@compile-flags: -Zmiri-strict-provenance // Gather all references from a mutable iterator and make sure Miri notices if // using them is dangerous. fn test_all_refs<'a, T: 'a>(dummy: &mut T, iter: impl Iterator) { diff --git a/tests/pass/vecdeque.rs b/tests/pass/vecdeque.rs index d2295a7afb..6f56f9d103 100644 --- a/tests/pass/vecdeque.rs +++ b/tests/pass/vecdeque.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-strict-provenance +//@compile-flags: -Zmiri-strict-provenance use std::collections::VecDeque; fn test_all_refs<'a, T: 'a>(dummy: &mut T, iter: impl Iterator) { diff --git a/tests/pass/weak_memory/extra_cpp.rs b/tests/pass/weak_memory/extra_cpp.rs index 750c628458..52c13cbdce 100644 --- a/tests/pass/weak_memory/extra_cpp.rs +++ b/tests/pass/weak_memory/extra_cpp.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-ignore-leaks +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-ignore-leaks // Tests operations not perfomable through C++'s atomic API // but doable in safe (at least sound) Rust. diff --git a/tests/pass/weak_memory/extra_cpp_unsafe.rs b/tests/pass/weak_memory/extra_cpp_unsafe.rs index d77a090e6e..1c6c370ced 100644 --- a/tests/pass/weak_memory/extra_cpp_unsafe.rs +++ b/tests/pass/weak_memory/extra_cpp_unsafe.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-ignore-leaks +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-ignore-leaks // Tests operations not perfomable through C++'s atomic API // but doable in unsafe Rust which we think *should* be fine. diff --git a/tests/pass/weak_memory/weak.rs b/tests/pass/weak_memory/weak.rs index b9ceb61f0c..dc7982991b 100644 --- a/tests/pass/weak_memory/weak.rs +++ b/tests/pass/weak_memory/weak.rs @@ -1,5 +1,5 @@ -// ignore-windows: Concurrency on Windows is not supported yet. -// compile-flags: -Zmiri-ignore-leaks -Zmiri-preemption-rate=0 +//@ignore-target-windows: Concurrency on Windows is not supported yet. +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-preemption-rate=0 // Tests showing weak memory behaviours are exhibited. All tests // return true when the desired behaviour is seen. @@ -8,8 +8,8 @@ // Spurious failure is possible, if you are really unlucky with // the RNG and always read the latest value from the store buffer. -use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::*; +use std::sync::atomic::{fence, AtomicUsize}; use std::thread::spawn; #[derive(Copy, Clone)] @@ -70,9 +70,9 @@ fn seq_cst() -> bool { r3 == 1 } -fn initialization_write() -> bool { +fn initialization_write(add_fence: bool) -> bool { let x = static_atomic(11); - assert_eq!(x.load(Relaxed), 11); + assert_eq!(x.load(Relaxed), 11); // work around https://github.com/rust-lang/miri/issues/2164 let wait = static_atomic(0); @@ -85,6 +85,9 @@ fn initialization_write() -> bool { let j2 = spawn(move || { reads_value(wait, 1); + if add_fence { + fence(AcqRel); + } x.load(Relaxed) }); @@ -94,15 +97,55 @@ fn initialization_write() -> bool { r2 == 11 } -// Asserts that the function returns true at least once in 100 runs -macro_rules! assert_once { - ($f:ident) => { - assert!(std::iter::repeat_with(|| $f()).take(100).any(|x| x)); - }; +fn faa_replaced_by_load() -> bool { + // Example from https://github.com/llvm/llvm-project/issues/56450#issuecomment-1183695905 + #[no_mangle] + pub fn rdmw(storing: &AtomicUsize, sync: &AtomicUsize, loading: &AtomicUsize) -> usize { + storing.store(1, Relaxed); + fence(Release); + // sync.fetch_add(0, Relaxed); + sync.load(Relaxed); + fence(Acquire); + loading.load(Relaxed) + } + + let x = static_atomic(0); + assert_eq!(x.load(Relaxed), 0); // work around https://github.com/rust-lang/miri/issues/2164 + let y = static_atomic(0); + assert_eq!(y.load(Relaxed), 0); // work around https://github.com/rust-lang/miri/issues/2164 + let z = static_atomic(0); + assert_eq!(z.load(Relaxed), 0); // work around https://github.com/rust-lang/miri/issues/2164 + + // Since each thread is so short, we need to make sure that they truely run at the same time + // Otherwise t1 will finish before t2 even starts + let go = static_atomic(0); + + let t1 = spawn(move || { + while go.load(Relaxed) == 0 {} + rdmw(y, x, z) + }); + + let t2 = spawn(move || { + while go.load(Relaxed) == 0 {} + rdmw(z, x, y) + }); + + go.store(1, Relaxed); + + let a = t1.join().unwrap(); + let b = t2.join().unwrap(); + (a, b) == (0, 0) +} + +/// Asserts that the function returns true at least once in 100 runs +fn assert_once(f: fn() -> bool) { + assert!(std::iter::repeat_with(|| f()).take(100).any(|x| x)); } pub fn main() { - assert_once!(relaxed); - assert_once!(seq_cst); - assert_once!(initialization_write); + assert_once(relaxed); + assert_once(seq_cst); + assert_once(|| initialization_write(false)); + assert_once(|| initialization_write(true)); + assert_once(faa_replaced_by_load); } diff --git a/tests/pass/without-validation.rs b/tests/pass/without-validation.rs index 8cff3a5c4b..934c44a7de 100644 --- a/tests/pass/without-validation.rs +++ b/tests/pass/without-validation.rs @@ -1,5 +1,5 @@ // When we notice something breaks only without validation, we add a test here. -// compile-flags: -Zmiri-disable-validation +//@compile-flags: -Zmiri-disable-validation use std::cell::*; fn refcell_unsize() { diff --git a/tests/pass/wtf8.rs b/tests/pass/wtf8.rs index e31b00e952..be8348654a 100644 --- a/tests/pass/wtf8.rs +++ b/tests/pass/wtf8.rs @@ -1,4 +1,4 @@ -// only-windows +//@only-target-windows use std::ffi::{OsStr, OsString}; use std::os::windows::ffi::{OsStrExt, OsStringExt}; diff --git a/tests/pass/zst.rs b/tests/pass/zst.rs index fade1e0dad..a56386a691 100644 --- a/tests/pass/zst.rs +++ b/tests/pass/zst.rs @@ -1,4 +1,4 @@ -// compile-flags: -Zmiri-permissive-provenance +//@compile-flags: -Zmiri-permissive-provenance #[derive(PartialEq, Debug)] struct A; diff --git a/triagebot.toml b/triagebot.toml new file mode 100644 index 0000000000..21a154cafd --- /dev/null +++ b/triagebot.toml @@ -0,0 +1,11 @@ +[relabel] +allow-unauthenticated = [ + "A-*", + "C-*", + "E-*", + "I-*", + "S-*", + ] + +# Gives us the commands 'ready', 'author', 'blocked' +[shortcut] diff --git a/ui_test/Cargo.lock b/ui_test/Cargo.lock index 5a4cdb8927..2065cc34be 100644 --- a/ui_test/Cargo.lock +++ b/ui_test/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "0.7.18" @@ -37,12 +52,60 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "color-eyre" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ebf286c900a6d5867aeff75cfee3192857bb7f24b547d4f0df2ed6baa812c90" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + [[package]] name = "colored" version = "2.0.0" @@ -139,6 +202,22 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" +[[package]] +name = "eyre" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "gimli" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -148,6 +227,12 @@ dependencies = [ "libc", ] +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "itoa" version = "1.0.2" @@ -181,6 +266,30 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +dependencies = [ + "adler", +] + +[[package]] +name = "object" +version = "0.28.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + [[package]] name = "output_vt100" version = "0.1.3" @@ -190,6 +299,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "owo-colors" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "decf7381921fea4dcb2549c5667eda59b3ec297ab7e2b5fc33eac69d2e7da87b" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + [[package]] name = "pretty_assertions" version = "1.2.1" @@ -237,6 +358,12 @@ version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "rustc_version" version = "0.4.0" @@ -295,6 +422,15 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + [[package]] name = "syn" version = "1.0.95" @@ -306,10 +442,62 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tracing" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a713421342a5a666b7577783721d3117f1b69a393df803ee17bb73b1e122a59" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + [[package]] name = "ui_test" version = "0.1.0" dependencies = [ + "color-eyre", "colored", "crossbeam", "lazy_static", @@ -326,6 +514,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "winapi" version = "0.3.9" diff --git a/ui_test/Cargo.toml b/ui_test/Cargo.toml index 1d480b6938..cdc5e5db47 100644 --- a/ui_test/Cargo.toml +++ b/ui_test/Cargo.toml @@ -17,3 +17,5 @@ crossbeam = "0.8.1" lazy_static = "1.4.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +color-eyre = { version = "0.6.1", default-features = false, features = ["capture-spantrace"] } + diff --git a/ui_test/README.md b/ui_test/README.md index 07a0a67b91..4ecebcc8dd 100644 --- a/ui_test/README.md +++ b/ui_test/README.md @@ -7,27 +7,42 @@ A smaller version of compiletest-rs ## Supported magic comment annotations -Note that the space after `//`, when it is present, is *not* optional -- it must be exactly one. +If your test tests for failure, you need to add a `//~` annotation where the error is happening +to make sure that the test will always keep failing with a specific message at the annotated line. -* `// ignore-XXX` avoids running the test on targets whose triple contains `XXX` +`//~ ERROR: XXX` make sure the stderr output contains `XXX` for an error in the line where this comment is written + +* Also supports `HELP`, `WARN` or `NOTE` for different kind of message + * if one of those levels is specified explicitly, *all* diagnostics of this level or higher need an annotation. If you want to avoid this, just leave out the all caps level note entirely. +* If the all caps note is left out, a message of any level is matched. Leaving it out is not allowed for `ERROR` levels. +* This checks the output *before* normalization, so you can check things that get normalized away, but need to + be careful not to accidentally have a pattern that differs between platforms. +* if `XXX` is of the form `/XXX/` it is treated as a regex instead of a substring and will succeed if the regex matches. + +In order to change how a single test is tested, you can add various `//@` comments to the test. +Any other comments will be ignored, and all `//@` comments must be formatted precisely as +their command specifies, or the test will fail without even being run. + +* `//@ignore-XXX` avoids running the test on targets whose triple contains `XXX` * `XXX` can also be one of `64bit`, `32bit` or `16bit` -* `// only-XXX` avoids running the test on targets whose triple **does not** contain `XXX` + * `XXX` can also be `on-host`, which will only run the test during cross compilation testing. +* `//@only-XXX` avoids running the test on targets whose triple **does not** contain `XXX` * `XXX` can also be one of `64bit`, `32bit` or `16bit` -* `// stderr-per-bitwidth` produces one stderr file per bitwidth, as they may differ significantly sometimes -* `// error-pattern: XXX` make sure the stderr output contains `XXX` -* `//~ ERROR: XXX` make sure the stderr output contains `XXX` for an error in the line where this comment is written - * Also supports `HELP`, `WARN` or `NOTE` for different kind of message - * if one of those levels is specified explicitly, *all* diagnostics of this level or higher need an annotation. If you want to avoid this, just leave out the all caps level note entirely. - * If the all caps note is left out, a message of any level is matched. Leaving it out is not allowed for `ERROR` levels. - * This checks the output *before* normalization, so you can check things that get normalized away, but need to - be careful not to accidentally have a pattern that differs between platforms. -* `// revisions: XXX YYY` runs the test once for each space separated name in the list + * `XXX` can also be `on-host`, which will not run the test during cross compilation testing +* `//@stderr-per-bitwidth` produces one stderr file per bitwidth, as they may differ significantly sometimes +* `//@error-pattern: XXX` make sure the stderr output contains `XXX` +* `//@revisions: XXX YYY` runs the test once for each space separated name in the list * emits one stderr file per revision * `//~` comments can be restricted to specific revisions by adding the revision name before the `~` in square brackets: `//[XXX]~` -* `// compile-flags: XXX` appends `XXX` to the command line arguments passed to the rustc driver -* `// rustc-env: XXX=YYY` sets the env var `XXX` to `YYY` for the rustc driver execution. +* `//@compile-flags: XXX` appends `XXX` to the command line arguments passed to the rustc driver + * you can specify this multiple times, and all the flags will accumulate +* `//@rustc-env: XXX=YYY` sets the env var `XXX` to `YYY` for the rustc driver execution. * for Miri these env vars are used during compilation via rustc and during the emulation of the program -* `// normalize-stderr-test: "REGEX" -> "REPLACEMENT"` replaces all matches of `REGEX` in the stderr with `REPLACEMENT`. The replacement may specify `$1` and similar backreferences to paste captures. + * you can specify this multiple times, accumulating all the env vars +* `//@normalize-stderr-test: "REGEX" -> "REPLACEMENT"` replaces all matches of `REGEX` in the stderr with `REPLACEMENT`. The replacement may specify `$1` and similar backreferences to paste captures. + * you can specify multiple such commands, there is no need to create a single regex that handles multiple replacements that you want to perform. +* `//@require-annotations-for-level: LEVEL` can be used to change the level of diagnostics that require a corresponding annotation. + * this is only useful if there are any annotations like `HELP`, `WARN` or `NOTE`, as these would automatically require annotations for all other diagnostics of the same or higher level. ## Significant differences to compiletest-rs diff --git a/ui_test/src/comments.rs b/ui_test/src/comments.rs deleted file mode 100644 index 3fd65d643a..0000000000 --- a/ui_test/src/comments.rs +++ /dev/null @@ -1,175 +0,0 @@ -use std::path::Path; - -use regex::Regex; - -use crate::rustc_stderr::Level; - -#[cfg(test)] -mod tests; - -/// This crate supports various magic comments that get parsed as file-specific -/// configuration values. This struct parses them all in one go and then they -/// get processed by their respective use sites. -#[derive(Default, Debug)] -pub(crate) struct Comments { - /// List of revision names to execute. Can only be speicified once - pub revisions: Option>, - /// Don't run this test if any of these filters apply - pub ignore: Vec, - /// Only run this test if all of these filters apply - pub only: Vec, - /// Generate one .stderr file per bit width, by prepending with `.64bit` and similar - pub stderr_per_bitwidth: bool, - /// Additional flags to pass to the executable - pub compile_flags: Vec, - /// Additional env vars to set for the executable - pub env_vars: Vec<(String, String)>, - /// Normalizations to apply to the stderr output before emitting it to disk - pub normalize_stderr: Vec<(Regex, String)>, - /// An arbitrary pattern to look for in the stderr. - pub error_pattern: Option<(String, usize)>, - pub error_matches: Vec, -} - -/// The conditions used for "ignore" and "only" filters. -#[derive(Debug)] -pub(crate) enum Condition { - /// The given string must appear in the target. - Target(String), - /// Tests that the bitwidth is the given one. - Bitwidth(u8), -} - -#[derive(Debug)] -pub(crate) struct ErrorMatch { - pub matched: String, - pub revision: Option, - pub level: Option, - /// The line where the message was defined, for reporting issues with it (e.g. in case it wasn't found). - pub definition_line: usize, - /// The line this pattern is expecting to find a message in. - pub line: usize, -} - -impl Condition { - fn parse(c: &str) -> Self { - if let Some(bits) = c.strip_suffix("bit") { - let bits: u8 = bits.parse().expect( - "ignore/only filter ending in 'bit' must be of the form 'Nbit' for some integer N", - ); - Condition::Bitwidth(bits) - } else { - Condition::Target(c.to_owned()) - } - } -} - -impl Comments { - pub(crate) fn parse_file(path: &Path) -> Self { - let content = std::fs::read_to_string(path).unwrap(); - Self::parse(path, &content) - } - - /// Parse comments in `content`. - /// `path` is only used to emit diagnostics if parsing fails. - pub(crate) fn parse(path: &Path, content: &str) -> Self { - let mut this = Self::default(); - let error_pattern_regex = - Regex::new(r"//(\[(?P[^\]]+)\])?~(?P\||[\^]+)? *(?PERROR|HELP|WARN|NOTE)?:?(?P.*)") - .unwrap(); - - // The line that a `|` will refer to - let mut fallthrough_to = None; - for (l, line) in content.lines().enumerate() { - let l = l + 1; // enumerate starts at 0, but line numbers start at 1 - if let Some(revisions) = line.strip_prefix("// revisions:") { - assert_eq!( - this.revisions, - None, - "{}:{l}, cannot specifiy revisions twice", - path.display() - ); - this.revisions = - Some(revisions.split_whitespace().map(|s| s.to_string()).collect()); - } - if let Some(s) = line.strip_prefix("// ignore-") { - let s = s - .split_once(|c: char| c == ':' || c.is_whitespace()) - .map(|(s, _)| s) - .unwrap_or(s); - this.ignore.push(Condition::parse(s)); - } - if let Some(s) = line.strip_prefix("// only-") { - let s = s - .split_once(|c: char| c == ':' || c.is_whitespace()) - .map(|(s, _)| s) - .unwrap_or(s); - this.only.push(Condition::parse(s)); - } - if line.starts_with("// stderr-per-bitwidth") { - assert!( - !this.stderr_per_bitwidth, - "{}:{l}, cannot specifiy stderr-per-bitwidth twice", - path.display() - ); - this.stderr_per_bitwidth = true; - } - if let Some(s) = line.strip_prefix("// compile-flags:") { - this.compile_flags.extend(s.split_whitespace().map(|s| s.to_string())); - } - if let Some(s) = line.strip_prefix("// rustc-env:") { - for env in s.split_whitespace() { - if let Some((k, v)) = env.split_once('=') { - this.env_vars.push((k.to_string(), v.to_string())); - } - } - } - if let Some(s) = line.strip_prefix("// normalize-stderr-test:") { - let (from, to) = s.split_once("->").expect("normalize-stderr-test needs a `->`"); - let from = from.trim().trim_matches('"'); - let to = to.trim().trim_matches('"'); - let from = Regex::new(from).unwrap(); - this.normalize_stderr.push((from, to.to_string())); - } - if let Some(s) = line.strip_prefix("// error-pattern:") { - assert_eq!( - this.error_pattern, - None, - "{}:{l}, cannot specifiy error_pattern twice", - path.display() - ); - this.error_pattern = Some((s.trim().to_string(), l)); - } - if let Some(captures) = error_pattern_regex.captures(line) { - // FIXME: check that the error happens on the marked line - let matched = captures["text"].trim().to_string(); - - let revision = captures.name("revision").map(|rev| rev.as_str().to_string()); - - let level = captures.name("level").map(|rev| rev.as_str().parse().unwrap()); - - let match_line = match captures.name("offset").map(|rev| rev.as_str()) { - Some("|") => fallthrough_to.expect("`//~|` pattern without preceding line"), - Some(pat) => { - debug_assert!(pat.chars().all(|c| c == '^')); - l - pat.len() - } - None => l, - }; - - fallthrough_to = Some(match_line); - - this.error_matches.push(ErrorMatch { - matched, - revision, - level, - definition_line: l, - line: match_line, - }); - } else { - fallthrough_to = None; - } - } - this - } -} diff --git a/ui_test/src/comments/tests.rs b/ui_test/src/comments/tests.rs deleted file mode 100644 index ef96622414..0000000000 --- a/ui_test/src/comments/tests.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::path::Path; - -use super::Comments; - -#[test] -fn parse_simple_comment() { - let s = r" -use std::mem; - -fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR encountered a dangling reference (address $HEX is unallocated) -} - "; - let comments = Comments::parse(Path::new(""), s); - println!("parsed comments: {:#?}", comments); - assert_eq!(comments.error_matches[0].definition_line, 5); - assert_eq!(comments.error_matches[0].revision, None); - assert_eq!( - comments.error_matches[0].matched, - "encountered a dangling reference (address $HEX is unallocated)" - ); -} diff --git a/ui_test/src/lib.rs b/ui_test/src/lib.rs index ff0c4e1c89..16f7be30f8 100644 --- a/ui_test/src/lib.rs +++ b/ui_test/src/lib.rs @@ -7,14 +7,16 @@ use std::process::{Command, ExitStatus}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Mutex; +pub use color_eyre; +use color_eyre::eyre::Result; use colored::*; -use comments::ErrorMatch; +use parser::{ErrorMatch, Pattern}; use regex::Regex; use rustc_stderr::{Level, Message}; -use crate::comments::{Comments, Condition}; +use crate::parser::{Comments, Condition}; -mod comments; +mod parser; mod rustc_stderr; #[cfg(test)] mod tests; @@ -51,7 +53,7 @@ pub enum OutputConflictHandling { pub type Filter = Vec<(Regex, &'static str)>; -pub fn run_tests(config: Config) { +pub fn run_tests(config: Config) -> Result<()> { eprintln!(" Compiler flags: {:?}", config.args); // Get the triple with which to run the tests @@ -66,7 +68,7 @@ pub fn run_tests(config: Config) { let ignored = AtomicUsize::default(); let filtered = AtomicUsize::default(); - crossbeam::scope(|s| { + crossbeam::scope(|s| -> Result<()> { // Create a thread that is in charge of walking the directory and submitting jobs. // It closes the channel when it is done. s.spawn(|_| { @@ -92,9 +94,11 @@ pub fn run_tests(config: Config) { drop(submit); }); + let mut threads = vec![]; + // Create N worker threads that receive files to test. for _ in 0..std::thread::available_parallelism().unwrap().get() { - s.spawn(|_| { + threads.push(s.spawn(|_| -> Result<()> { for path in &receive { if !config.path_filter.is_empty() { let path_display = path.display().to_string(); @@ -103,9 +107,9 @@ pub fn run_tests(config: Config) { continue; } } - let comments = Comments::parse_file(&path); + let comments = Comments::parse_file(&path)?; // Ignore file if only/ignore rules do (not) apply - if !test_file_conditions(&comments, &target) { + if !test_file_conditions(&comments, &target, &config) { ignored.fetch_add(1, Ordering::Relaxed); eprintln!( "{} ... {}", @@ -142,10 +146,15 @@ pub fn run_tests(config: Config) { } } } - }); + Ok(()) + })); + } + for thread in threads { + thread.join().unwrap()?; } + Ok(()) }) - .unwrap(); + .unwrap()?; // Print all errors in a single thread to show reliable output let failures = failures.into_inner().unwrap(); @@ -168,7 +177,12 @@ pub fn run_tests(config: Config) { match error { Error::ExitStatus(mode, exit_status) => eprintln!("{mode:?} got {exit_status}"), Error::PatternNotFound { pattern, definition_line } => { - eprintln!("`{pattern}` {} in stderr output", "not found".red()); + match pattern { + Pattern::SubString(s) => + eprintln!("substring `{s}` {} in stderr output", "not found".red()), + Pattern::Regex(r) => + eprintln!("`/{r}/` does {} stderr output", "not match".red()), + } eprintln!( "expected because of pattern here: {}:{definition_line}", path.display().to_string().bold() @@ -206,12 +220,6 @@ pub fn run_tests(config: Config) { eprintln!(" {level:?}: {message}") } } - Error::ErrorPatternWithoutErrorAnnotation(path, line) => { - eprintln!( - "Annotation at {}:{line} matched an error diagnostic but did not have `ERROR` before its message", - path.display() - ); - } } eprintln!(); } @@ -246,6 +254,7 @@ pub fn run_tests(config: Config) { filtered.to_string().yellow(), ); eprintln!(); + Ok(()) } #[derive(Debug)] @@ -253,7 +262,7 @@ enum Error { /// Got an invalid exit status for the given mode. ExitStatus(Mode, ExitStatus), PatternNotFound { - pattern: String, + pattern: Pattern, definition_line: usize, }, /// A ui test checking for failure does not have any failure patterns @@ -270,7 +279,6 @@ enum Error { msgs: Vec, path: Option<(PathBuf, usize)>, }, - ErrorPatternWithoutErrorAnnotation(PathBuf, usize), } type Errors = Vec; @@ -377,18 +385,15 @@ fn check_annotations( ) { if let Some((ref error_pattern, definition_line)) = comments.error_pattern { // first check the diagnostics messages outside of our file. We check this first, so that - // you can mix in-file annotations with // error-pattern annotations, even if there is overlap + // you can mix in-file annotations with //@error-pattern annotations, even if there is overlap // in the messages. if let Some(i) = messages_from_unknown_file_or_line .iter() - .position(|msg| msg.message.contains(error_pattern)) + .position(|msg| error_pattern.matches(&msg.message)) { messages_from_unknown_file_or_line.remove(i); } else { - errors.push(Error::PatternNotFound { - pattern: error_pattern.to_string(), - definition_line, - }); + errors.push(Error::PatternNotFound { pattern: error_pattern.clone(), definition_line }); } } @@ -396,7 +401,7 @@ fn check_annotations( // We will ensure that *all* diagnostics of level at least `lowest_annotation_level` // are matched. let mut lowest_annotation_level = Level::Error; - for &ErrorMatch { ref matched, revision: ref rev, definition_line, line, level } in + for &ErrorMatch { ref pattern, revision: ref rev, definition_line, line, level } in &comments.error_matches { if let Some(rev) = rev { @@ -404,36 +409,31 @@ fn check_annotations( continue; } } - if let Some(level) = level { - // If we found a diagnostic with a level annotation, make sure that all - // diagnostics of that level have annotations, even if we don't end up finding a matching diagnostic - // for this pattern. - lowest_annotation_level = std::cmp::min(lowest_annotation_level, level); - } + + // If we found a diagnostic with a level annotation, make sure that all + // diagnostics of that level have annotations, even if we don't end up finding a matching diagnostic + // for this pattern. + lowest_annotation_level = std::cmp::min(lowest_annotation_level, level); if let Some(msgs) = messages.get_mut(line) { - let found = msgs.iter().position(|msg| { - msg.message.contains(matched) - // in case there is no level on the annotation, match any level. - && level.map_or(true, |level| { - msg.level == level - }) - }); + let found = + msgs.iter().position(|msg| pattern.matches(&msg.message) && msg.level == level); if let Some(found) = found { - let msg = msgs.remove(found); - if msg.level == Level::Error && level.is_none() { - errors - .push(Error::ErrorPatternWithoutErrorAnnotation(path.to_path_buf(), line)); - } + msgs.remove(found); continue; } } - errors.push(Error::PatternNotFound { pattern: matched.to_string(), definition_line }); + errors.push(Error::PatternNotFound { pattern: pattern.clone(), definition_line }); } let filter = |msgs: Vec| -> Vec<_> { - msgs.into_iter().filter(|msg| msg.level >= lowest_annotation_level).collect() + msgs.into_iter() + .filter(|msg| { + msg.level + >= comments.require_annotations_for_level.unwrap_or(lowest_annotation_level) + }) + .collect() }; let messages_from_unknown_file_or_line = filter(messages_from_unknown_file_or_line); @@ -499,19 +499,20 @@ fn output_path(path: &Path, comments: &Comments, kind: String, target: &str) -> path.with_extension(kind) } -fn test_condition(condition: &Condition, target: &str) -> bool { +fn test_condition(condition: &Condition, target: &str, config: &Config) -> bool { match condition { Condition::Bitwidth(bits) => get_pointer_width(target) == *bits, Condition::Target(t) => target.contains(t), + Condition::OnHost => config.target.is_none(), } } /// Returns whether according to the in-file conditions, this file should be run. -fn test_file_conditions(comments: &Comments, target: &str) -> bool { - if comments.ignore.iter().any(|c| test_condition(c, target)) { +fn test_file_conditions(comments: &Comments, target: &str, config: &Config) -> bool { + if comments.ignore.iter().any(|c| test_condition(c, target, config)) { return false; } - comments.only.iter().all(|c| test_condition(c, target)) + comments.only.iter().all(|c| test_condition(c, target, config)) } // Taken 1:1 from compiletest-rs diff --git a/ui_test/src/parser.rs b/ui_test/src/parser.rs new file mode 100644 index 0000000000..d583e625fa --- /dev/null +++ b/ui_test/src/parser.rs @@ -0,0 +1,327 @@ +use std::path::Path; + +use regex::Regex; + +use crate::rustc_stderr::Level; + +use color_eyre::eyre::{bail, ensure, eyre, Result}; + +#[cfg(test)] +mod tests; + +/// This crate supports various magic comments that get parsed as file-specific +/// configuration values. This struct parses them all in one go and then they +/// get processed by their respective use sites. +#[derive(Default, Debug)] +pub(crate) struct Comments { + /// List of revision names to execute. Can only be speicified once + pub revisions: Option>, + /// Don't run this test if any of these filters apply + pub ignore: Vec, + /// Only run this test if all of these filters apply + pub only: Vec, + /// Generate one .stderr file per bit width, by prepending with `.64bit` and similar + pub stderr_per_bitwidth: bool, + /// Additional flags to pass to the executable + pub compile_flags: Vec, + /// Additional env vars to set for the executable + pub env_vars: Vec<(String, String)>, + /// Normalizations to apply to the stderr output before emitting it to disk + pub normalize_stderr: Vec<(Regex, String)>, + /// An arbitrary pattern to look for in the stderr. + pub error_pattern: Option<(Pattern, usize)>, + pub error_matches: Vec, + /// Ignore diagnostics below this level. + /// `None` means pick the lowest level from the `error_pattern`s. + pub require_annotations_for_level: Option, +} + +/// The conditions used for "ignore" and "only" filters. +#[derive(Debug)] +pub(crate) enum Condition { + /// The given string must appear in the target. + Target(String), + /// Tests that the bitwidth is the given one. + Bitwidth(u8), + /// Tests that the target is the host. + OnHost, +} + +#[derive(Debug, Clone)] +pub(crate) enum Pattern { + SubString(String), + Regex(Regex), +} + +#[derive(Debug)] +pub(crate) struct ErrorMatch { + pub pattern: Pattern, + pub revision: Option, + pub level: Level, + /// The line where the message was defined, for reporting issues with it (e.g. in case it wasn't found). + pub definition_line: usize, + /// The line this pattern is expecting to find a message in. + pub line: usize, +} + +impl Condition { + fn parse(c: &str) -> Result { + if c == "on-host" { + Ok(Condition::OnHost) + } else if let Some(bits) = c.strip_suffix("bit") { + let bits: u8 = bits.parse().map_err(|_err| { + eyre!("invalid ignore/only filter ending in 'bit': {c:?} is not a valid bitwdith") + })?; + Ok(Condition::Bitwidth(bits)) + } else if let Some(target) = c.strip_prefix("target-") { + Ok(Condition::Target(target.to_owned())) + } else { + Err(eyre!("invalid ignore/only condition {c:?}")) + } + } +} + +impl Comments { + pub(crate) fn parse_file(path: &Path) -> Result { + let content = std::fs::read_to_string(path)?; + Self::parse(path, &content) + } + + /// Parse comments in `content`. + /// `path` is only used to emit diagnostics if parsing fails. + pub(crate) fn parse(path: &Path, content: &str) -> Result { + let mut this = Self::default(); + + let mut fallthrough_to = None; // The line that a `|` will refer to. + for (l, line) in content.lines().enumerate() { + let l = l + 1; // enumerate starts at 0, but line numbers start at 1 + this.parse_checked_line(l, &mut fallthrough_to, line).map_err(|err| { + err.wrap_err(format!("{}:{l}: failed to parse annotation", path.display())) + })?; + } + Ok(this) + } + + fn parse_checked_line( + &mut self, + l: usize, + fallthrough_to: &mut Option, + line: &str, + ) -> Result<()> { + if let Some((_, command)) = line.split_once("//@") { + self.parse_command(command.trim(), l) + } else if let Some((_, pattern)) = line.split_once("//~") { + self.parse_pattern(pattern, fallthrough_to, l) + } else if let Some((_, pattern)) = line.split_once("//[") { + self.parse_revisioned_pattern(pattern, fallthrough_to, l) + } else { + *fallthrough_to = None; + Ok(()) + } + } + + fn parse_command(&mut self, command: &str, l: usize) -> Result<()> { + // Commands are letters or dashes, grab everything until the first character that is neither of those. + let (command, args) = + match command.chars().position(|c: char| !c.is_alphanumeric() && c != '-') { + None => (command, ""), + Some(i) => { + let (command, args) = command.split_at(i); + let mut args = args.chars(); + // Commands are separated from their arguments by ':' or ' ' + let next = args + .next() + .expect("the `position` above guarantees that there is at least one char"); + ensure!(next == ':', "test command must be followed by : (or end the line)"); + (command, args.as_str().trim()) + } + }; + + match command { + "revisions" => { + ensure!(self.revisions.is_none(), "cannot specifiy revisions twice"); + self.revisions = Some(args.split_whitespace().map(|s| s.to_string()).collect()); + } + "compile-flags" => { + self.compile_flags.extend(args.split_whitespace().map(|s| s.to_string())); + } + "rustc-env" => + for env in args.split_whitespace() { + let (k, v) = env.split_once('=').ok_or_else(|| { + eyre!("environment variables must be key/value pairs separated by a `=`") + })?; + self.env_vars.push((k.to_string(), v.to_string())); + }, + "normalize-stderr-test" => { + /// Parses a string literal. `s` has to start with `"`; everything until the next `"` is + /// returned in the first component. `\` can be used to escape arbitrary character. + /// Second return component is the rest of the string with leading whitespace removed. + fn parse_str(s: &str) -> Result<(&str, &str)> { + let mut chars = s.char_indices(); + match chars.next().ok_or_else(|| eyre!("missing arguments"))?.1 { + '"' => { + let s = chars.as_str(); + let mut escaped = false; + for (i, c) in chars { + if escaped { + // Accept any character as literal after a `\`. + escaped = false; + } else if c == '"' { + return Ok((&s[..(i - 1)], s[i..].trim_start())); + } else { + escaped = c == '\\'; + } + } + bail!("no closing quotes found for {s}") + } + c => bail!("expected '\"', got {c}"), + } + } + + let (from, rest) = parse_str(args)?; + + let to = rest.strip_prefix("->").ok_or_else(|| { + eyre!("normalize-stderr-test needs a pattern and replacement separated by `->`") + })?.trim_start(); + let (to, rest) = parse_str(to)?; + + ensure!(rest.is_empty(), "trailing text after pattern replacement: {rest}"); + + let from = Regex::new(from)?; + self.normalize_stderr.push((from, to.to_string())); + } + "error-pattern" => { + ensure!( + self.error_pattern.is_none(), + "cannot specifiy error_pattern twice, previous: {:?}", + self.error_pattern + ); + self.error_pattern = Some((Pattern::parse(args.trim())?, l)); + } + "stderr-per-bitwidth" => { + // args are ignored (can be used as comment) + ensure!(!self.stderr_per_bitwidth, "cannot specifiy stderr-per-bitwidth twice"); + self.stderr_per_bitwidth = true; + } + "require-annotations-for-level" => { + ensure!( + self.require_annotations_for_level.is_none(), + "cannot specify `require-annotations-for-level` twice" + ); + self.require_annotations_for_level = Some(args.trim().parse()?); + } + command => { + if let Some(s) = command.strip_prefix("ignore-") { + // args are ignored (can be sue as comment) + self.ignore.push(Condition::parse(s)?); + return Ok(()); + } + + if let Some(s) = command.strip_prefix("only-") { + // args are ignored (can be sue as comment) + self.only.push(Condition::parse(s)?); + return Ok(()); + } + bail!("unknown command {command}"); + } + } + + Ok(()) + } + + fn parse_pattern( + &mut self, + pattern: &str, + fallthrough_to: &mut Option, + l: usize, + ) -> Result<()> { + self.parse_pattern_inner(pattern, fallthrough_to, None, l) + } + + fn parse_revisioned_pattern( + &mut self, + pattern: &str, + fallthrough_to: &mut Option, + l: usize, + ) -> Result<()> { + let (revision, pattern) = + pattern.split_once(']').ok_or_else(|| eyre!("`//[` without corresponding `]`"))?; + if let Some(pattern) = pattern.strip_prefix('~') { + self.parse_pattern_inner(pattern, fallthrough_to, Some(revision.to_owned()), l) + } else { + bail!("revisioned pattern must have `~` following the `]`"); + } + } + + // parse something like (?P\||[\^]+)? *(?PERROR|HELP|WARN|NOTE): (?P.*) + fn parse_pattern_inner( + &mut self, + pattern: &str, + fallthrough_to: &mut Option, + revision: Option, + l: usize, + ) -> Result<()> { + let (match_line, pattern) = + match pattern.chars().next().ok_or_else(|| eyre!("no pattern specified"))? { + '|' => + ( + *fallthrough_to + .as_mut() + .ok_or_else(|| eyre!("`//~|` pattern without preceding line"))?, + &pattern[1..], + ), + '^' => { + let offset = pattern.chars().take_while(|&c| c == '^').count(); + (l - offset, &pattern[offset..]) + } + _ => (l, pattern), + }; + + let pattern = pattern.trim_start(); + let offset = pattern + .chars() + .position(|c| !matches!(c, 'A'..='Z' | 'a'..='z')) + .ok_or_else(|| eyre!("pattern without level"))?; + + let level = pattern[..offset].parse()?; + let pattern = &pattern[offset..]; + let pattern = pattern.strip_prefix(':').ok_or_else(|| eyre!("no `:` after level found"))?; + + let pattern = pattern.trim(); + + ensure!(!pattern.is_empty(), "no pattern specified"); + + let pattern = Pattern::parse(pattern)?; + + *fallthrough_to = Some(match_line); + + self.error_matches.push(ErrorMatch { + pattern, + revision, + level, + definition_line: l, + line: match_line, + }); + + Ok(()) + } +} + +impl Pattern { + pub(crate) fn matches(&self, message: &str) -> bool { + match self { + Pattern::SubString(s) => message.contains(s), + Pattern::Regex(r) => r.is_match(message), + } + } + + pub(crate) fn parse(pattern: &str) -> Result { + if let Some(pattern) = pattern.strip_prefix('/') { + let regex = + pattern.strip_suffix('/').ok_or_else(|| eyre!("regex must end with `/`"))?; + Ok(Pattern::Regex(Regex::new(regex)?)) + } else { + Ok(Pattern::SubString(pattern.to_string())) + } + } +} diff --git a/ui_test/src/parser/tests.rs b/ui_test/src/parser/tests.rs new file mode 100644 index 0000000000..343857d44b --- /dev/null +++ b/ui_test/src/parser/tests.rs @@ -0,0 +1,85 @@ +use std::path::Path; + +use crate::parser::Pattern; + +use super::Comments; + +#[test] +fn parse_simple_comment() { + let s = r" +use std::mem; + +fn main() { + let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address $HEX is unallocated) +} + "; + let comments = Comments::parse(Path::new(""), s).unwrap(); + println!("parsed comments: {:#?}", comments); + assert_eq!(comments.error_matches[0].definition_line, 5); + assert_eq!(comments.error_matches[0].revision, None); + match &comments.error_matches[0].pattern { + Pattern::SubString(s) => + assert_eq!(s, "encountered a dangling reference (address $HEX is unallocated)"), + other => panic!("expected substring, got {other:?}"), + } +} + +#[test] +fn parse_missing_level() { + let s = r" +use std::mem; + +fn main() { + let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ encountered a dangling reference (address $HEX is unallocated) +} + "; + assert!(Comments::parse(Path::new(""), s).is_err(), "expected parsing to fail"); +} + +#[test] +fn parse_slash_slash_at() { + let s = r" +//@ error-pattern: foomp +use std::mem; + + "; + let comments = Comments::parse(Path::new(""), s).unwrap(); + println!("parsed comments: {:#?}", comments); + let pat = comments.error_pattern.unwrap(); + assert_eq!(format!("{:?}", pat.0), r#"SubString("foomp")"#); + assert_eq!(pat.1, 2); +} + +#[test] +fn parse_regex_error_pattern() { + let s = r" +//@ error-pattern: /foomp/ +use std::mem; + + "; + let comments = Comments::parse(Path::new(""), s).unwrap(); + println!("parsed comments: {:#?}", comments); + let pat = comments.error_pattern.unwrap(); + assert_eq!(format!("{:?}", pat.0), r#"Regex(foomp)"#); + assert_eq!(pat.1, 2); +} + +#[test] +fn parse_slash_slash_at_fail() { + let s = r" +//@ error-patttern foomp +use std::mem; + + "; + assert!(Comments::parse(Path::new(""), s).is_err(), "expected parsing to fail"); +} + +#[test] +fn missing_colon_fail() { + let s = r" +//@stderr-per-bitwidth hello +use std::mem; + + "; + assert!(Comments::parse(Path::new(""), s).is_err(), "expected parsing to fail"); +} diff --git a/ui_test/src/rustc_stderr.rs b/ui_test/src/rustc_stderr.rs index fc772c8404..8e03194758 100644 --- a/ui_test/src/rustc_stderr.rs +++ b/ui_test/src/rustc_stderr.rs @@ -3,6 +3,7 @@ use std::{ path::{Path, PathBuf}, }; +use color_eyre::eyre::{eyre, Error}; use regex::Regex; #[derive(serde::Deserialize, Debug)] @@ -45,7 +46,7 @@ struct Span { } impl std::str::FromStr for Level { - type Err = String; + type Err = Error; fn from_str(s: &str) -> Result { match s { "ERROR" | "error" => Ok(Self::Error), @@ -54,7 +55,7 @@ impl std::str::FromStr for Level { "NOTE" | "note" => Ok(Self::Note), "failure-note" => Ok(Self::FailureNote), "error: internal compiler error" => Ok(Self::Ice), - _ => Err(format!("unknown level `{s}`")), + _ => Err(eyre!("unknown level `{s}`")), } } } @@ -116,7 +117,7 @@ impl Span { } pub(crate) fn filter_annotations_from_rendered(rendered: &str) -> std::borrow::Cow<'_, str> { - let annotations = Regex::new(r" *//(\[[^\]]\])?~.*").unwrap(); + let annotations = Regex::new(r" *//(\[[a-z,]+\])?~.*").unwrap(); annotations.replace_all(rendered, "") } diff --git a/ui_test/src/tests.rs b/ui_test/src/tests.rs index a45f8f8933..96c0f362b6 100644 --- a/ui_test/src/tests.rs +++ b/ui_test/src/tests.rs @@ -25,11 +25,11 @@ fn issue_2156() { use std::mem; fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR encountered a dangling reference (address $HEX is unallocated) + let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address $HEX is unallocated) } "; let path = Path::new("$DIR/"); - let comments = Comments::parse(path, s); + let comments = Comments::parse(path, s).unwrap(); let mut errors = vec![]; let config = config(); let messages = vec![ @@ -57,10 +57,10 @@ fn find_pattern() { use std::mem; fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR encountered a dangling reference (address 0x10 is unallocated) + let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address 0x10 is unallocated) } "; - let comments = Comments::parse(Path::new(""), s); + let comments = Comments::parse(Path::new(""), s).unwrap(); let config = config(); { let messages = vec![vec![], vec![], vec![], vec![], vec![], vec![ @@ -149,11 +149,11 @@ fn duplicate_pattern() { use std::mem; fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR encountered a dangling reference (address 0x10 is unallocated) - //~^ ERROR encountered a dangling reference (address 0x10 is unallocated) + let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address 0x10 is unallocated) + //~^ ERROR: encountered a dangling reference (address 0x10 is unallocated) } "; - let comments = Comments::parse(Path::new(""), s); + let comments = Comments::parse(Path::new(""), s).unwrap(); let config = config(); let messages = vec![ vec![], vec![], vec![], vec![], vec![], @@ -178,10 +178,10 @@ fn missing_pattern() { use std::mem; fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR encountered a dangling reference (address 0x10 is unallocated) + let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address 0x10 is unallocated) } "; - let comments = Comments::parse(Path::new(""), s); + let comments = Comments::parse(Path::new(""), s).unwrap(); let config = config(); let messages = vec![ vec![], vec![], vec![], vec![], vec![], @@ -210,11 +210,11 @@ fn missing_warn_pattern() { use std::mem; fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR encountered a dangling reference (address 0x10 is unallocated) - //~^ WARN cake + let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address 0x10 is unallocated) + //~^ WARN: cake } "; - let comments = Comments::parse(Path::new(""), s); + let comments = Comments::parse(Path::new(""), s).unwrap(); let config = config(); let messages= vec![ vec![], @@ -253,13 +253,13 @@ fn main() { fn missing_implicit_warn_pattern() { let s = r" use std::mem; - +//@require-annotations-for-level: ERROR fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR encountered a dangling reference (address 0x10 is unallocated) - //~^ cake + let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address 0x10 is unallocated) + //~^ WARN: cake } "; - let comments = Comments::parse(Path::new(""), s); + let comments = Comments::parse(Path::new(""), s).unwrap(); let config = config(); let messages = vec![ vec![], @@ -289,35 +289,3 @@ fn main() { _ => panic!("{:#?}", errors), } } - -#[test] -fn implicit_err_pattern() { - let s = r" -use std::mem; - -fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ encountered a dangling reference (address 0x10 is unallocated) -} - "; - let comments = Comments::parse(Path::new(""), s); - let config = config(); - let messages = vec![ - vec![], - vec![], - vec![], - vec![], - vec![], - vec![ - Message { - message: "Undefined Behavior: type validation failed: encountered a dangling reference (address 0x10 is unallocated)".to_string(), - level: Level::Error, - }, - ], - ]; - let mut errors = vec![]; - check_annotations(messages, vec![], Path::new("moobar"), &mut errors, &config, "", &comments); - match &errors[..] { - [Error::ErrorPatternWithoutErrorAnnotation(_, 5)] => {} - _ => panic!("{:#?}", errors), - } -} From e7a6466dfb54f43855bf9ddcde5396fa4947873f Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Wed, 20 Jul 2022 04:57:21 +0000 Subject: [PATCH 02/23] PR edits requested by @RalfJung: main changes are removing the use of `hir` types, some refactoring/renaming of functions, and edits to the related test file hierarchy --- README.md | 6 + miri | 7 - src/bin/miri.rs | 10 +- src/c_ffi_support.rs | 302 ------------------ src/eval.rs | 6 +- src/lib.rs | 3 - src/machine.rs | 20 +- src/shims/ffi_support.rs | 283 ++++++++++++++++ src/shims/foreign_items.rs | 42 +-- src/shims/mod.rs | 1 + tests/compiletest.rs | 20 ++ tests/extern-so/libcode.version | 9 + tests/extern-so/libtestlib.so | Bin 0 -> 15704 bytes tests/{external_C => extern-so}/test.c | 4 - tests/fail/alloc/no_global_allocator.stderr | 4 +- tests/fail/extern-so/function_not_in_SO.rs | 13 + .../function_not_in_SO.stderr | 4 +- tests/fail/external_C/function_not_in_SO.rs | 13 - .../fail/unsupported_foreign_function.stderr | 4 +- tests/fail/unsupported_signal.stderr | 4 +- tests/panic/external_C/incorrect_SO_file.rs | 16 - .../panic/external_C/incorrect_SO_file.stderr | 15 - .../panic/unsupported_foreign_function.stderr | 2 +- .../call_extern_c_fcts.rs} | 7 +- .../call_extern_c_fcts.stdout} | 0 tests/pass/external_C/print_from_c.rs | 15 - 26 files changed, 383 insertions(+), 427 deletions(-) delete mode 100644 src/c_ffi_support.rs create mode 100644 src/shims/ffi_support.rs create mode 100644 tests/extern-so/libcode.version create mode 100755 tests/extern-so/libtestlib.so rename tests/{external_C => extern-so}/test.c (90%) create mode 100644 tests/fail/extern-so/function_not_in_SO.rs rename tests/fail/{external_C => extern-so}/function_not_in_SO.stderr (59%) delete mode 100644 tests/fail/external_C/function_not_in_SO.rs delete mode 100644 tests/panic/external_C/incorrect_SO_file.rs delete mode 100644 tests/panic/external_C/incorrect_SO_file.stderr rename tests/pass/{external_C/int_c_tests.rs => extern-so/call_extern_c_fcts.rs} (84%) rename tests/pass/{external_C/print_from_c.stdout => extern-so/call_extern_c_fcts.stdout} (100%) delete mode 100644 tests/pass/external_C/print_from_c.rs diff --git a/README.md b/README.md index 7679537e7f..b31cf97b91 100644 --- a/README.md +++ b/README.md @@ -285,6 +285,12 @@ environment variable. We first document the most relevant and most commonly used `TERM` environment variable is excluded by default to [speed up the test harness](https://github.com/rust-lang/miri/issues/1702). This has no effect unless `-Zmiri-disable-isolation` is also set. +* `-Zmiri-extern-so-file=` is an experimental flag for providing support + for FFI calls. + **WARNING**: If an invalid/incorrect SO file is specified, this can cause undefined behaviour in Miri itself! + This is [work in progress](https://github.com/rust-lang/miri/pull/2363); + current support is for functions with integer arguments/returns. + Follow [the discussion on supporting other types](https://github.com/rust-lang/miri/issues/2365). * `-Zmiri-env-forward=` forwards the `var` environment variable to the interpreted program. Can be used multiple times to forward several variables. This takes precedence over `-Zmiri-env-exclude`: if a variable is both forwarded and exluced, it *will* get forwarded. This diff --git a/miri b/miri index 5b95c00925..0964f2a893 100755 --- a/miri +++ b/miri @@ -156,13 +156,6 @@ test|bless) # First build and get a sysroot. $CARGO build $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml find_sysroot - # if we're on linux - if [[ $OSTYPE == 'linux'* ]]; then - # rebuild the C test shared object file if it's not there - if [ ! -f "tests/external_C/libtestlib.so" ]; then - $CC -shared -o tests/external_C/libtestlib.so tests/external_C/test.c - fi - fi if [ "$COMMAND" = "bless" ]; then export MIRI_BLESS="Gesundheit" fi diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 5e99483a92..4526c3b89a 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -487,18 +487,18 @@ fn main() { "full" => BacktraceStyle::Full, _ => panic!("-Zmiri-backtrace may only be 0, 1, or full"), }; - } else if let Some(param) = arg.strip_prefix("-Zmiri-external_c_so_file=") { + } else if let Some(param) = arg.strip_prefix("-Zmiri-extern-so-file=") { let filename = param.to_string(); if std::path::Path::new(&filename).exists() { - if let Some(other_filename) = miri_config.external_c_so_file { + if let Some(other_filename) = miri_config.external_so_file { panic!( - "-Zmiri-external_so_file external SO file is already set to {}", + "-Zmiri-extern-so-file external SO file is already set to {}", other_filename.display() ); } - miri_config.external_c_so_file = Some(filename.into()); + miri_config.external_so_file = Some(filename.into()); } else { - panic!("-Zmiri-external_c_so_file path {} does not exist", filename); + panic!("-Zmiri-extern-so-file path {} does not exist", filename); } } else { // Forward to rustc. diff --git a/src/c_ffi_support.rs b/src/c_ffi_support.rs deleted file mode 100644 index e0b7cad83d..0000000000 --- a/src/c_ffi_support.rs +++ /dev/null @@ -1,302 +0,0 @@ -use libffi::{high::call::*, low::CodePtr}; - -use rustc_ast::{IntTy, UintTy}; -use rustc_hir::{self as hir, FnRetTy, Node, Ty}; -use rustc_span::Symbol; -use rustc_target::abi::HasDataLayout; - -use crate::*; - -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} - -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { - /// Extract the scalar value from the result of reading a scalar from the machine, - /// and convert it to a `CArg`. - fn extract_scalar( - k: ScalarMaybeUninit, - arg_type: &Ty<'tcx>, - cx: &impl HasDataLayout, - ) -> InterpResult<'tcx, CArg> { - if let &hir::Ty { - hir_id: _, - kind: - hir::TyKind::Path(hir::QPath::Resolved( - _, - hir::Path { span: _, res: hir::def::Res::PrimTy(prim_type), .. }, - .., - )), - .. - } = arg_type - { - // If the primitive provided can be converted to a type matching the hir type pattern - // then create a `CArg` of this primitive value with the corresponding `CArg` constructor. - match prim_type { - // the ints - hir::PrimTy::Int(IntTy::I8) => { - return Ok(CArg::Int8(k.to_i8()?)); - } - hir::PrimTy::Int(IntTy::I16) => { - return Ok(CArg::Int16(k.to_i16()?)); - } - hir::PrimTy::Int(IntTy::I32) => { - return Ok(CArg::Int32(k.to_i32()?)); - } - hir::PrimTy::Int(IntTy::I64) => { - return Ok(CArg::Int64(k.to_i64()?)); - } - hir::PrimTy::Int(IntTy::Isize) => { - return Ok(CArg::ISize(k.to_machine_isize(cx)?.try_into().unwrap())); - } - // the uints - hir::PrimTy::Uint(UintTy::U8) => { - return Ok(CArg::UInt8(k.to_u8()?)); - } - hir::PrimTy::Uint(UintTy::U16) => { - return Ok(CArg::UInt16(k.to_u16()?)); - } - hir::PrimTy::Uint(UintTy::U32) => { - return Ok(CArg::UInt32(k.to_u32()?)); - } - hir::PrimTy::Uint(UintTy::U64) => { - return Ok(CArg::UInt64(k.to_u64()?)); - } - hir::PrimTy::Uint(UintTy::Usize) => { - return Ok(CArg::USize(k.to_machine_usize(cx)?.try_into().unwrap())); - } - _ => {} - } - } - // If no primitives were returned then we have an unsupported type. - throw_unsup_format!("Unsupported scalar argument type to external C function: {:?}", k); - } - - /// Call external C function and - /// store output, depending on return type in the function signature. - fn call_external_c_and_store_return<'a>( - &mut self, - external_fct_defn: ExternalCFuncDeclRep<'tcx>, - dest: &PlaceTy<'tcx, Tag>, - ptr: CodePtr, - libffi_args: Vec>, - ) -> InterpResult<'tcx, ()> { - let this = self.eval_context_mut(); - - // Unsafe because of the call to external C code. - // Because this is calling a C function it is not necessarily sound, - // but there is no way around this and we've checked as much as we can. - unsafe { - // If the return type of a function is a primitive integer type pattern in hir, - // then call the function (`ptr`) with arguments `libffi_args`, store the return value as the specified - // primitive integer type, and then write this value out to the miri memory as an integer. - if let hir::FnRetTy::Return(&hir::Ty { - hir_id: _, - kind: - hir::TyKind::Path(hir::QPath::Resolved( - _, - hir::Path { span: _, res: hir::def::Res::PrimTy(prim_type), .. }, - .., - )), - .. - }) = external_fct_defn.output_type - { - match prim_type { - // ints - hir::PrimTy::Int(IntTy::I8) => { - let x = call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - hir::PrimTy::Int(IntTy::I16) => { - let x = call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - hir::PrimTy::Int(IntTy::I32) => { - let x = call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - hir::PrimTy::Int(IntTy::I64) => { - let x = call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - hir::PrimTy::Int(IntTy::Isize) => { - let x = call::(ptr, libffi_args.as_slice()); - // `isize` doesn't `impl Into`, so convert manually. - // Convert to `i64` since this covers both 32- and 64-bit machines. - this.write_int(i64::try_from(x).unwrap(), dest)?; - return Ok(()); - } - // uints - hir::PrimTy::Uint(UintTy::U8) => { - let x = call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - hir::PrimTy::Uint(UintTy::U16) => { - let x = call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - hir::PrimTy::Uint(UintTy::U32) => { - let x = call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - hir::PrimTy::Uint(UintTy::U64) => { - let x = call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - hir::PrimTy::Uint(UintTy::Usize) => { - let x = call::(ptr, libffi_args.as_slice()); - // `usize` doesn't `impl Into`, so convert manually. - // Convert to `u64` since this covers both 32- and 64-bit machines. - this.write_int(u64::try_from(x).unwrap(), dest)?; - return Ok(()); - } - _ => {} - } - } - if matches!(external_fct_defn.output_type, hir::FnRetTy::DefaultReturn(_)) { - call::<()>(ptr, libffi_args.as_slice()); - return Ok(()); - } - // TODO ellen! deal with all the other return types - throw_unsup_format!("UNSUPPORTED RETURN TYPE -- NOT VOID"); - } - } - - /// Call specified external C function, with supplied arguments. - /// Need to convert all the arguments from their hir representations to - /// a form compatible with C (through `libffi` call). - /// Then, convert return from the C call into a corresponding form that - /// can be stored in MIRI internal memory. - fn call_and_add_external_c_fct_to_context( - &mut self, - external_fct_defn: ExternalCFuncDeclRep<'tcx>, - dest: &PlaceTy<'tcx, Tag>, - args: &[OpTy<'tcx, Tag>], - ) -> InterpResult<'tcx, bool> { - let this = self.eval_context_mut(); - let link_name = external_fct_defn.link_name; - use std::ops::Deref; - let lib = this.machine.external_c_so_lib.as_ref().unwrap(); - - // Load the C function from the library. - // Because this is getting the C function from the shared object file - // it is not necessarily a sound operation, but there is no way around - // this and we've checked as much as we can. - let func: libloading::Symbol<'_, unsafe extern "C" fn()> = unsafe { - match lib.get(link_name.as_str().as_bytes()) { - Ok(x) => x, - Err(_) => { - // Shared object file does not export this function -- try the shims next. - return Ok(false); - } - } - }; - - // Get the function arguments, and convert them to `libffi`-compatible form. - assert!(args.len() == external_fct_defn.inputs_types.len()); - let mut libffi_args = Vec::::with_capacity(args.len()); - for (cur_arg, arg_type) in args.iter().zip(external_fct_defn.inputs_types.iter()) { - libffi_args.push(Self::extract_scalar(this.read_scalar(cur_arg)?, arg_type, this)?); - } - - // Convert them to `libffi::high::Arg` type. - let libffi_args = libffi_args - .iter() - .map(|cur_arg| cur_arg.arg_downcast()) - .collect::>>(); - - // Code pointer to C function. - let ptr = CodePtr(*func.deref() as *mut _); - // Call the functio and store output, depending on return type in the function signature. - self.call_external_c_and_store_return(external_fct_defn, dest, ptr, libffi_args)?; - Ok(true) - } -} - -#[derive(Debug)] -/// Signature of an external C function. -pub struct ExternalCFuncDeclRep<'hir> { - /// Function name. - pub link_name: Symbol, - /// Argument types. - pub inputs_types: &'hir [Ty<'hir>], - /// Return type. - pub output_type: &'hir FnRetTy<'hir>, -} - -#[derive(Debug, Clone)] -/// Enum of supported arguments to external C functions. -pub enum CArg { - /// Invalid argument (unsupported type). - Invalid, - /// 8-bit signed integer. - Int8(i8), - /// 16-bit signed integer. - Int16(i16), - /// 32-bit signed integer. - Int32(i32), - /// 64-bit signed integer. - Int64(i64), - /// isize. - ISize(isize), - /// 8-bit unsigned integer. - UInt8(u8), - /// 16-bit unsigned integer. - UInt16(u16), - /// 32-bit unsigned integer. - UInt32(u32), - /// 64-bit unsigned integer. - UInt64(u64), - /// usize. - USize(usize), -} - -impl<'a> CArg { - /// Convert a `CArg` to a `libffi` argument type. - pub fn arg_downcast(&'a self) -> libffi::high::Arg<'a> { - match self { - CArg::Int8(i) => arg(i), - CArg::Int16(i) => arg(i), - CArg::Int32(i) => arg(i), - CArg::Int64(i) => arg(i), - CArg::ISize(i) => arg(i), - CArg::UInt8(i) => arg(i), - CArg::UInt16(i) => arg(i), - CArg::UInt32(i) => arg(i), - CArg::UInt64(i) => arg(i), - CArg::USize(i) => arg(i), - _ => { - // should be unreachable - panic!( - "Trying to call external function with unsupported type: should have already bailed" - ); - } - } - } -} - -impl<'hir> ExternalCFuncDeclRep<'hir> { - /// Convert `hir` node representation of a foreign function signature - /// to the signature we're storing. - pub fn from_hir_node(node: &Node<'hir>, link_name: Symbol) -> Option { - match node { - // Calls to foreign functions: - // `kind`: `Fn(&'hir FnDecl<'hir>, &'hir [Ident], &'hir Generics<'hir>)`. - // We care about the inputs (arguments) and output (return type). - Node::ForeignItem(&hir::ForeignItem { - ident: _, - kind: hir::ForeignItemKind::Fn(&hir::FnDecl { inputs, ref output, .. }, ..), - def_id: _, - span: _, - vis_span: _, - }) => Some(Self { link_name, inputs_types: inputs, output_type: output }), - _ => None, /* Not a call to a foreign function */ - } - } -} diff --git a/src/eval.rs b/src/eval.rs index ca19dd796a..cd0a7bdcad 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -127,9 +127,9 @@ pub struct MiriConfig { pub report_progress: Option, /// Whether Stacked Borrows retagging should recurse into fields of datatypes. pub retag_fields: bool, - /// The location of a shared object file to load when calling external C functions + /// The location of a shared object file to load when calling external functions /// TODO! consider allowing users to specify paths to multiple SO files, or to a directory - pub external_c_so_file: Option, + pub external_so_file: Option, } impl Default for MiriConfig { @@ -161,7 +161,7 @@ impl Default for MiriConfig { preemption_rate: 0.01, // 1% report_progress: None, retag_fields: false, - external_c_so_file: None, + external_so_file: None, } } } diff --git a/src/lib.rs b/src/lib.rs index 315648be6c..b3d408a6dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,7 +37,6 @@ extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; -mod c_ffi_support; mod concurrency; mod diagnostics; mod eval; @@ -70,8 +69,6 @@ pub use crate::shims::time::EvalContextExt as _; pub use crate::shims::tls::{EvalContextExt as _, TlsData}; pub use crate::shims::EvalContextExt as _; -pub use crate::c_ffi_support::EvalContextExt as CFFIEvalContextExt; - pub use crate::concurrency::data_race::{ AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as DataRaceEvalContextExt, diff --git a/src/machine.rs b/src/machine.rs index 8404e29b53..bf4f997dcd 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -340,7 +340,7 @@ pub struct Evaluator<'mir, 'tcx> { pub(crate) since_progress_report: u32, /// Handle of the optional C shared object file - pub external_c_so_lib: Option, + pub external_so_lib: Option<(libloading::Library, std::path::PathBuf)>, } impl<'mir, 'tcx> Evaluator<'mir, 'tcx> { @@ -400,13 +400,17 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> { preemption_rate: config.preemption_rate, report_progress: config.report_progress, since_progress_report: 0, - external_c_so_lib: config.external_c_so_file.as_ref().map(|lib_file_path| { - // Note: it is the user's responsibility to provide a correct SO file - // and if this condition is met, then this library loading is a safe operation. - unsafe { - libloading::Library::new(lib_file_path) - .expect("Failed to read specified shared object file") - } + external_so_lib: config.external_so_file.as_ref().map(|lib_file_path| { + // Note: it is the user's responsibility to provide a correct SO file. + // WATCH OUT: If an invalid/incorrect SO file is specified, this can cause + // undefined behaviour in Miri itself! + ( + unsafe { + libloading::Library::new(lib_file_path) + .expect("Failed to read specified shared object file") + }, + lib_file_path.clone(), + ) }), } } diff --git a/src/shims/ffi_support.rs b/src/shims/ffi_support.rs new file mode 100644 index 0000000000..d5544e87b0 --- /dev/null +++ b/src/shims/ffi_support.rs @@ -0,0 +1,283 @@ +use libffi::{high::call::*, low::CodePtr}; +use std::ops::Deref; + +use rustc_middle::ty::{IntTy, Ty, TyKind, UintTy}; +use rustc_span::Symbol; +use rustc_target::abi::HasDataLayout; + +use crate::*; + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} + +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { + /// Extract the scalar value from the result of reading a scalar from the machine, + /// and convert it to a `CArg`. + fn scalar_to_carg( + k: ScalarMaybeUninit, + arg_type: &Ty<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, CArg> { + match arg_type.kind() { + // If the primitive provided can be converted to a type matching the hir type pattern + // then create a `CArg` of this primitive value with the corresponding `CArg` constructor. + // the ints + TyKind::Int(IntTy::I8) => { + return Ok(CArg::Int8(k.to_i8()?)); + } + TyKind::Int(IntTy::I16) => { + return Ok(CArg::Int16(k.to_i16()?)); + } + TyKind::Int(IntTy::I32) => { + return Ok(CArg::Int32(k.to_i32()?)); + } + TyKind::Int(IntTy::I64) => { + return Ok(CArg::Int64(k.to_i64()?)); + } + TyKind::Int(IntTy::Isize) => { + return Ok(CArg::ISize(k.to_machine_isize(cx)?.try_into().unwrap())); + } + // the uints + TyKind::Uint(UintTy::U8) => { + return Ok(CArg::UInt8(k.to_u8()?)); + } + TyKind::Uint(UintTy::U16) => { + return Ok(CArg::UInt16(k.to_u16()?)); + } + TyKind::Uint(UintTy::U32) => { + return Ok(CArg::UInt32(k.to_u32()?)); + } + TyKind::Uint(UintTy::U64) => { + return Ok(CArg::UInt64(k.to_u64()?)); + } + TyKind::Uint(UintTy::Usize) => { + return Ok(CArg::USize(k.to_machine_usize(cx)?.try_into().unwrap())); + } + _ => {} + } + // If no primitives were returned then we have an unsupported type. + throw_unsup_format!( + "unsupported scalar argument type to external C function: {:?}", + arg_type + ); + } + + /// Call external C function and + /// store output, depending on return type in the function signature. + fn call_external_c_and_store_return<'a>( + &mut self, + external_fct_defn: ExternalCFuncDeclRep<'tcx>, + dest: &PlaceTy<'tcx, Tag>, + ptr: CodePtr, + libffi_args: Vec>, + ) -> InterpResult<'tcx, ()> { + let this = self.eval_context_mut(); + + // Unsafe because of the call to external C code. + // Because this is calling a C function it is not necessarily sound, + // but there is no way around this and we've checked as much as we can. + unsafe { + // If the return type of a function is a primitive integer type, + // then call the function (`ptr`) with arguments `libffi_args`, store the return value as the specified + // primitive integer type, and then write this value out to the miri memory as an integer. + match external_fct_defn.output_type.kind() { + // ints + TyKind::Int(IntTy::I8) => { + let x = call::(ptr, libffi_args.as_slice()); + this.write_int(x, dest)?; + return Ok(()); + } + TyKind::Int(IntTy::I16) => { + let x = call::(ptr, libffi_args.as_slice()); + this.write_int(x, dest)?; + return Ok(()); + } + TyKind::Int(IntTy::I32) => { + let x = call::(ptr, libffi_args.as_slice()); + this.write_int(x, dest)?; + return Ok(()); + } + TyKind::Int(IntTy::I64) => { + let x = call::(ptr, libffi_args.as_slice()); + this.write_int(x, dest)?; + return Ok(()); + } + TyKind::Int(IntTy::Isize) => { + let x = call::(ptr, libffi_args.as_slice()); + // `isize` doesn't `impl Into`, so convert manually. + // Convert to `i64` since this covers both 32- and 64-bit machines. + this.write_int(i64::try_from(x).unwrap(), dest)?; + return Ok(()); + } + // uints + TyKind::Uint(UintTy::U8) => { + let x = call::(ptr, libffi_args.as_slice()); + this.write_int(x, dest)?; + return Ok(()); + } + TyKind::Uint(UintTy::U16) => { + let x = call::(ptr, libffi_args.as_slice()); + this.write_int(x, dest)?; + return Ok(()); + } + TyKind::Uint(UintTy::U32) => { + let x = call::(ptr, libffi_args.as_slice()); + this.write_int(x, dest)?; + return Ok(()); + } + TyKind::Uint(UintTy::U64) => { + let x = call::(ptr, libffi_args.as_slice()); + this.write_int(x, dest)?; + return Ok(()); + } + TyKind::Uint(UintTy::Usize) => { + let x = call::(ptr, libffi_args.as_slice()); + // `usize` doesn't `impl Into`, so convert manually. + // Convert to `u64` since this covers both 32- and 64-bit machines. + this.write_int(u64::try_from(x).unwrap(), dest)?; + return Ok(()); + } + _ => {} + } + // Functions with no declared return type (i.e., the default return) + // have the output_type `Tuple([])`. + if let TyKind::Tuple(t_list) = external_fct_defn.output_type.kind() && t_list.len() == 0{ + call::<()>(ptr, libffi_args.as_slice()); + return Ok(()); + } + // TODO ellen! deal with all the other return types + throw_unsup_format!( + "unsupported return type to external C function: {:?}", + external_fct_defn.link_name + ); + } + } + + /// Call specified external C function, with supplied arguments. + /// Need to convert all the arguments from their hir representations to + /// a form compatible with C (through `libffi` call). + /// Then, convert return from the C call into a corresponding form that + /// can be stored in Miri internal memory. + fn call_and_add_external_c_fct_to_context( + &mut self, + external_fct_defn: ExternalCFuncDeclRep<'tcx>, + dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Tag>], + ) -> InterpResult<'tcx, bool> { + let this = self.eval_context_mut(); + let link_name = external_fct_defn.link_name; + let (lib, lib_path) = this.machine.external_so_lib.as_ref().unwrap(); + + // Load the C function from the library. + // Because this is getting the C function from the shared object file + // it is not necessarily a sound operation, but there is no way around + // this and we've checked as much as we can. + let func: libloading::Symbol<'_, unsafe extern "C" fn()> = unsafe { + match lib.get(link_name.as_str().as_bytes()) { + Ok(x) => x, + Err(_) => { + // Shared object file does not export this function -- try the shims next. + return Ok(false); + } + } + }; + + // FIXME: this is a hack! + // The `libloading` crate will automatically load system libraries like `libc`. + // So, in order to check if the function was actually found in the specified + // `machine.external_so_lib` we need to check its `dli_fname` and compare it to + // the specified SO file path. + // This code is a reimplementation of the mechanism for getting `dli_fname` in `libloading`, + // from: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#411 + // using the `libc` crate where this interface is public. + let mut info = std::mem::MaybeUninit::::uninit(); + unsafe { + if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 { + if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap() + != lib_path.to_str().unwrap() + { + return Ok(false); + } + } + } + + // Get the function arguments, and convert them to `libffi`-compatible form. + if args.len() != external_fct_defn.inputs_types.len() { + throw_ub_format!( + "calling function {:?} with {} arguments; expected {}", + link_name, + args.len(), + external_fct_defn.inputs_types.len() + ); + } + let mut libffi_args = Vec::::with_capacity(args.len()); + for (cur_arg, arg_type) in args.iter().zip(external_fct_defn.inputs_types.iter()) { + libffi_args.push(Self::scalar_to_carg(this.read_scalar(cur_arg)?, arg_type, this)?); + } + + // Convert them to `libffi::high::Arg` type. + let libffi_args = libffi_args + .iter() + .map(|cur_arg| cur_arg.arg_downcast()) + .collect::>>(); + + // Code pointer to C function. + let ptr = CodePtr(*func.deref() as *mut _); + // Call the functio and store output, depending on return type in the function signature. + self.call_external_c_and_store_return(external_fct_defn, dest, ptr, libffi_args)?; + Ok(true) + } +} + +#[derive(Debug)] +/// Signature of an external C function. +pub struct ExternalCFuncDeclRep<'tcx> { + /// Function name. + pub link_name: Symbol, + /// Argument types. + pub inputs_types: &'tcx [Ty<'tcx>], + /// Return type. + pub output_type: Ty<'tcx>, +} + +#[derive(Debug, Clone)] +/// Enum of supported arguments to external C functions. +pub enum CArg { + /// 8-bit signed integer. + Int8(i8), + /// 16-bit signed integer. + Int16(i16), + /// 32-bit signed integer. + Int32(i32), + /// 64-bit signed integer. + Int64(i64), + /// isize. + ISize(isize), + /// 8-bit unsigned integer. + UInt8(u8), + /// 16-bit unsigned integer. + UInt16(u16), + /// 32-bit unsigned integer. + UInt32(u32), + /// 64-bit unsigned integer. + UInt64(u64), + /// usize. + USize(usize), +} + +impl<'a> CArg { + /// Convert a `CArg` to a `libffi` argument type. + pub fn arg_downcast(&'a self) -> libffi::high::Arg<'a> { + match self { + CArg::Int8(i) => arg(i), + CArg::Int16(i) => arg(i), + CArg::Int32(i) => arg(i), + CArg::Int64(i) => arg(i), + CArg::ISize(i) => arg(i), + CArg::UInt8(i) => arg(i), + CArg::UInt16(i) => arg(i), + CArg::UInt32(i) => arg(i), + CArg::UInt64(i) => arg(i), + CArg::USize(i) => arg(i), + } + } +} diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index e9ba806062..b691fb111e 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -22,8 +22,9 @@ use rustc_target::{ }; use super::backtrace::EvalContextExt as _; -use crate::c_ffi_support::ExternalCFuncDeclRep; use crate::helpers::{convert::Truncate, target_os_is_unix}; +use crate::shims::ffi_support::EvalContextExt as _; +use crate::shims::ffi_support::ExternalCFuncDeclRep; use crate::*; /// Returned by `emulate_foreign_item_by_name`. @@ -34,8 +35,6 @@ pub enum EmulateByNameResult<'mir, 'tcx> { AlreadyJumped, /// A MIR body has been found for the function. MirBody(&'mir mir::Body<'tcx>, ty::Instance<'tcx>), - /// Executed an external C call. - ExecutedExternalCCall, /// The item is not supported. NotSupported, } @@ -302,7 +301,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Second: functions that return immediately. match this.emulate_foreign_item_by_name(def_id, link_name, abi, args, dest)? { - EmulateByNameResult::NeedsJumping | EmulateByNameResult::ExecutedExternalCCall => { + EmulateByNameResult::NeedsJumping => { trace!("{:?}", this.dump_place(**dest)); this.go_to_block(ret); } @@ -313,9 +312,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return Ok(Some(body)); } - this.handle_unsupported( - format!("can't call foreign function: {}; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile", - link_name))?; + this.handle_unsupported(format!("can't call foreign function: {}", link_name))?; return Ok(None); } } @@ -368,25 +365,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // First deal with any external C functions in linked .so file // (if any SO file is specified). - if this.machine.external_c_so_lib.as_ref().is_some() { - if let Some(local_id) = def_id.as_local() { - if let Some(extern_c_fct_rep) = ExternalCFuncDeclRep::from_hir_node( - &tcx.hir().get(tcx.hir().local_def_id_to_hir_id(local_id)), - link_name, - ) { - // An Ok(false) here means that the function being called was not exported - // by the specified SO file; we should continue and check if it corresponds to - // a provided shim. - - // TODO ellen! For now, continue to try the shims if there was an error in calling - // the external function. - // As discussed: https://github.com/rust-lang/miri/pull/2363#discussion_r922392879 - if let Ok(true) = - this.call_and_add_external_c_fct_to_context(extern_c_fct_rep, dest, args) - { - return Ok(EmulateByNameResult::ExecutedExternalCCall); - } - } + if this.machine.external_so_lib.as_ref().is_some() { + let fn_sig = &tcx.fn_sig(def_id); + let extern_c_fct_rep = ExternalCFuncDeclRep { + link_name, + inputs_types: fn_sig.inputs().skip_binder(), + output_type: fn_sig.output().skip_binder(), + }; + // An Ok(false) here means that the function being called was not exported + // by the specified SO file; we should continue and check if it corresponds to + // a provided shim. + if this.call_and_add_external_c_fct_to_context(extern_c_fct_rep, dest, args)? { + return Ok(EmulateByNameResult::NeedsJumping); } } diff --git a/src/shims/mod.rs b/src/shims/mod.rs index 2423ffaf5f..9bd1f81bdb 100644 --- a/src/shims/mod.rs +++ b/src/shims/mod.rs @@ -1,4 +1,5 @@ mod backtrace; +pub mod ffi_support; pub mod foreign_items; pub mod intrinsics; pub mod unix; diff --git a/tests/compiletest.rs b/tests/compiletest.rs index ec49e80ca9..d1850da60b 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -2,6 +2,7 @@ use colored::*; use regex::Regex; use std::env; use std::path::PathBuf; +use std::process::Command; use ui_test::{color_eyre::Result, Config, Mode, OutputConflictHandling}; fn miri_path() -> PathBuf { @@ -39,6 +40,25 @@ fn run_tests(mode: Mode, path: &str, target: Option) -> Result<()> { flags.push(target.clone()); } + // If we're on linux, then build the shared object file for testing external C function calls. + if env::consts::OS == "linux" { + let cc = option_env!("CC").unwrap_or("cc"); + Command::new(cc) + .args([ + "-shared", + "-o", + "tests/extern-so/libtestlib.so", + "tests/extern-so/test.c", + // Only add the functions specified in libcode.version to the shared object file. + // This is to avoid automatically adding `malloc`, etc. + // Source: https://anadoxin.org/blog/control-over-symbol-exports-in-gcc.html/ + "-fPIC", + "-Wl,--version-script=tests/extern-so/libcode.version", + ]) + .output() + .expect("failed to generate shared object file for testing external C function calls"); + } + let skip_ui_checks = env::var_os("MIRI_SKIP_UI_CHECKS").is_some(); let output_conflict_handling = match (env::var_os("MIRI_BLESS").is_some(), skip_ui_checks) { diff --git a/tests/extern-so/libcode.version b/tests/extern-so/libcode.version new file mode 100644 index 0000000000..0f04b9aaeb --- /dev/null +++ b/tests/extern-so/libcode.version @@ -0,0 +1,9 @@ +CODEABI_1.0 { + global: *add_one_int*; + *printer*; + *test_stack_spill*; + *get_unsigned_int*; + *add_int16*; + *add_short_to_long*; + local: *; +}; diff --git a/tests/extern-so/libtestlib.so b/tests/extern-so/libtestlib.so new file mode 100755 index 0000000000000000000000000000000000000000..4045953132e39609a4128e50011f8cb767edc997 GIT binary patch literal 15704 zcmeHOeQX>@6`woD?YT|jOIpArX|hBqxInf^qogh+&H3Y;b+MDy*n%RW&DnRhkKRXg zx0jEqQ9>g?mK7-ofuho?E(nMs{y=;dsfaWwDewn!B|uQ9WC4*}DS!(FY%n?An|Z(U zdVMYk3I5u#Zr;4#d%v04cXM~M-Z!U`z1`7BM4>dO2h^gbR6-0bd*H!Xn`)x^4z)@9 zu39uLYt1wo2vWt3&SPkk=)fU=Ad`Ru&afWOGX1E4d|Y^te%?()ncZ%9vwN4ywy4n) zBEe&odCE3+3Xdrwr)50In3#Ev{6fD$xlQ~a*_X>h*Ss{h;}&bnWv_u1BzO-KPtQ~A z|4tz6{W9K4afwSjAmwuPwcSpGG|+4RfkAcj_djwD7r#*)obS7M>c07xuAE>049|h> zJTKC zWUgCy{#G4Jmaz48a30^PgTHqLyqoZ?b^K2ewqC7KPpmUWowruG~>CILODBH$fUJ9Q;D#1kM5*AR-{>ZMVBTqs&d((VY^(k z_i&55B0;+9JlK_N@91%N+B?+#-ky$5_a6HmdpBKSgw|C=*HPvFV~hDaIREn|UY{I~ zZ%a&g4MfR)degfa*~W$A%7s~9C5g{PWka@!SMQXz()WgZU*PjiZ-y>&0sNA5U?q$l z{?MoQ862MpqbW7x!}&9VGV88H2A6)~4br^v#0$~Kg2!s&` zBM?R)j6fIxihy(Gdd!(i#IVsr#ntbn}+e?07{a`9G`ucR$JE zt{cwytn$6jRe8^#b7t>W zZaU-Fyw%Rc-UmpTd4y&;lcwrw<8IO;hiL5D_{@Lp7#RnNGk$VmBDrwpQuNRjR_P*M zUCW)lG}A~P=L||t9XS_1Yu+#kg!3Bo~aG0szyCv@yM>Z^B z1i}b}5eOp?Mj(tp7=bVXVFbbmgb@fM5Jq5m1oUIPY+=+IDHZcpr;2QBxSyXb^K+~C zY_-aF`a4MS4gNgI3v7G6TKyr(%_L_?mPp3v;o3=(tt8KpbogN#6+CB)Jl3xw$K#QW zt8b2-BilBz@w2(fKUS+2NwqfKy|(4S=39@&POE(z?tk~LZTj&m^XYb9ohQE-`$=K$ zN9ol=b82R+eQo@c(atqDJxBwP%J#?U)kb#x3>Ef=7wu1x-6s10$r?X;P&+XHd3p_z zT|eUmzk8AYYqIx|T|euE{lKDqf$Rygb6>UoKV<9-g(ZwY7=bVXVFbbmgb@fM5Jn)3 zKp25A0{_zpNZumy6y-Ke=RWedt1-CbF|HB)dEvcXbmT*B6dm_w8$?H5q`vnd4EDd= zs217fup}oUckvgJ^r&-$$v!FeTiBTj-)nI}zHN(9b$+pKCJca_N7!-y$@!36-e!cF zl7WG@ijH|+5I;%+)dl@6RL%USBpLIcqGQ|}qQ|5^dakRl7jdTBL9KsN`17Lsb3SFr z|BpaEdtkIZfjrjzot^KqTDvks*;K*WxzpZd@7TWUE@=h0ewD9#Rh_(LUT0CtTYgB! z0f(NqYy5Noe}~{$xA4E08WGi^S`!9AoI5pso5DIYR;4~h4IUTyyTFfX|Mj&zU*Mmm zM!os3bX`yBe%7huJmEm8*Uv@riK?{R(cW`$2qwU(-1KXCwi{$bXQCs#_Klp~cKcL@a6h#T|s#%eS{RzC2HUAK~@#*grsc zz5YKU{RH#p9~C^9KYvW{U>^R*1)r4fF?=t5M(|+1{pSg&qRO*%qwy7~#CvjU**?>*Sj-s>Lh?*3qMz#VAs=uJ|GJnoww zFyJ!+34aFvvf}~%RR6%qg=J?8{HGA!>{tO#DCnaP|H1KwB{NiZ`9$7J4UzOpMvkFX zD0&%tv{12!D%o6mdp51KHkK-nDLZ|lKujaOlCd1kl*-v+VTs}jOKdK=mNK~%^QCnp z=P6sq)~2RCS|ruW9H&~pgLbKyPI)P1XU1d|jHRg)XohN*gJGaeBc$@#VVX_R$NmWNf;eLS7$5OF zDf|xc!+r}Ae$40p&jqtgt+`%2z=}V z=j8k+{K!QY=#Z}v%4-k&+J4J6{?jslTsL@#>!-<$UBJh=uj#uD-!h_BgAe&zviSKb zA+9@&v_ypf5Ar%0{Cq26JT2kFFUW`fCu*<{=8yej>k51c2k7c=)M8BBYdH9uYlvxp z@0VD6epd$_%Eb70hVSukJ@{VX&j;qdgulyZQy55$0af1~)Z@4C@k1^FLO@mj0umcG A#Q*>R literal 0 HcmV?d00001 diff --git a/tests/external_C/test.c b/tests/extern-so/test.c similarity index 90% rename from tests/external_C/test.c rename to tests/extern-so/test.c index f015ccc17e..68714f1743 100644 --- a/tests/external_C/test.c +++ b/tests/extern-so/test.c @@ -4,10 +4,6 @@ int add_one_int(int x) { return 2 + x; } -double add_int_get_float(int x) { - return 2.75 + x; -} - void printer() { printf("printing from C\n"); } diff --git a/tests/fail/alloc/no_global_allocator.stderr b/tests/fail/alloc/no_global_allocator.stderr index 7024756750..5c11df9539 100644 --- a/tests/fail/alloc/no_global_allocator.stderr +++ b/tests/fail/alloc/no_global_allocator.stderr @@ -1,8 +1,8 @@ -error: unsupported operation: can't call foreign function: __rust_alloc; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile +error: unsupported operation: can't call foreign function: __rust_alloc --> $DIR/no_global_allocator.rs:LL:CC | LL | __rust_alloc(1, 1); - | ^^^^^^^^^^^^^^^^^^ can't call foreign function: __rust_alloc; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile + | ^^^^^^^^^^^^^^^^^^ can't call foreign function: __rust_alloc | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support = note: backtrace: diff --git a/tests/fail/extern-so/function_not_in_SO.rs b/tests/fail/extern-so/function_not_in_SO.rs new file mode 100644 index 0000000000..c84f0d6c43 --- /dev/null +++ b/tests/fail/extern-so/function_not_in_SO.rs @@ -0,0 +1,13 @@ +//@only-target-linux +//@only-on-host +//@compile-flags: -Zmiri-extern-so-file=tests/extern-so/libtestlib.so + +extern "C" { + fn foo(); +} + +fn main() { + unsafe { + foo(); //~ ERROR: unsupported operation: can't call foreign function: foo + } +} diff --git a/tests/fail/external_C/function_not_in_SO.stderr b/tests/fail/extern-so/function_not_in_SO.stderr similarity index 59% rename from tests/fail/external_C/function_not_in_SO.stderr rename to tests/fail/extern-so/function_not_in_SO.stderr index 53a06d5db5..0aca1700b5 100644 --- a/tests/fail/external_C/function_not_in_SO.stderr +++ b/tests/fail/extern-so/function_not_in_SO.stderr @@ -1,8 +1,8 @@ -error: unsupported operation: can't call foreign function: foo; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile +error: unsupported operation: can't call foreign function: foo --> $DIR/function_not_in_SO.rs:LL:CC | LL | foo(); - | ^^^^^ can't call foreign function: foo; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile + | ^^^^^ can't call foreign function: foo | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support = note: backtrace: diff --git a/tests/fail/external_C/function_not_in_SO.rs b/tests/fail/external_C/function_not_in_SO.rs deleted file mode 100644 index 8e40502e0a..0000000000 --- a/tests/fail/external_C/function_not_in_SO.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@only-target-linux -//@only-on-host -//@compile-flags: -Zmiri-external_c_so_file=tests/external_C/libtestlib.so - -extern "C" { - fn foo(); -} - -fn main() { - unsafe { - foo(); //~ ERROR: unsupported operation: can't call foreign function: foo; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile - } -} diff --git a/tests/fail/unsupported_foreign_function.stderr b/tests/fail/unsupported_foreign_function.stderr index e74a83c623..3298f57dd5 100644 --- a/tests/fail/unsupported_foreign_function.stderr +++ b/tests/fail/unsupported_foreign_function.stderr @@ -1,8 +1,8 @@ -error: unsupported operation: can't call foreign function: foo; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile +error: unsupported operation: can't call foreign function: foo --> $DIR/unsupported_foreign_function.rs:LL:CC | LL | foo(); - | ^^^^^ can't call foreign function: foo; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile + | ^^^^^ can't call foreign function: foo | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support = note: backtrace: diff --git a/tests/fail/unsupported_signal.stderr b/tests/fail/unsupported_signal.stderr index f5006da001..622b1876e0 100644 --- a/tests/fail/unsupported_signal.stderr +++ b/tests/fail/unsupported_signal.stderr @@ -1,8 +1,8 @@ -error: unsupported operation: can't call foreign function: signal; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile +error: unsupported operation: can't call foreign function: signal --> $DIR/unsupported_signal.rs:LL:CC | LL | libc::signal(libc::SIGPIPE, libc::SIG_IGN); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't call foreign function: signal; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't call foreign function: signal | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support = note: backtrace: diff --git a/tests/panic/external_C/incorrect_SO_file.rs b/tests/panic/external_C/incorrect_SO_file.rs deleted file mode 100644 index 751278e112..0000000000 --- a/tests/panic/external_C/incorrect_SO_file.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@rustc-env: RUST_BACKTRACE=0 -//@only-target-linux -//@only-on-host -//@compile-flags: -Zmiri-external_c_so_file=tests/external_C/badpath.so -//@normalize-stderr-test: "note: rustc.*running on.*" -> "" - -extern "C" { - fn printer(); -} - -fn main() { - unsafe { - // calling a function from a shared object file that doesn't exist - printer(); - } -} diff --git a/tests/panic/external_C/incorrect_SO_file.stderr b/tests/panic/external_C/incorrect_SO_file.stderr deleted file mode 100644 index f4063b8e05..0000000000 --- a/tests/panic/external_C/incorrect_SO_file.stderr +++ /dev/null @@ -1,15 +0,0 @@ -thread 'main' panicked at '-Zmiri-external_c_so_file path tests/external_C/badpath.so does not exist', src/bin/miri.rs:LL:CC -note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace - -error: internal compiler error: unexpected panic - -note: the compiler unexpectedly panicked. this is a bug. - -note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md - - - -note: compiler flags: -Z ui-testing -Z miri-external_c_so_file=tests/external_C/badpath.so - -query stack during panic: -end of query stack diff --git a/tests/panic/panic/unsupported_foreign_function.stderr b/tests/panic/panic/unsupported_foreign_function.stderr index f7d1600b1f..9af3e48655 100644 --- a/tests/panic/panic/unsupported_foreign_function.stderr +++ b/tests/panic/panic/unsupported_foreign_function.stderr @@ -1,2 +1,2 @@ -thread 'main' panicked at 'unsupported Miri functionality: can't call foreign function: foo; try specifying a shared object file with the flag -Zmiri-external_c_so_file=path/to/SOfile', $DIR/unsupported_foreign_function.rs:LL:CC +thread 'main' panicked at 'unsupported Miri functionality: can't call foreign function: foo', $DIR/unsupported_foreign_function.rs:LL:CC note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/tests/pass/external_C/int_c_tests.rs b/tests/pass/extern-so/call_extern_c_fcts.rs similarity index 84% rename from tests/pass/external_C/int_c_tests.rs rename to tests/pass/extern-so/call_extern_c_fcts.rs index 73b01f7cc7..3575f99e88 100644 --- a/tests/pass/external_C/int_c_tests.rs +++ b/tests/pass/extern-so/call_extern_c_fcts.rs @@ -1,6 +1,6 @@ //@only-target-linux //@only-on-host -//@compile-flags: -Zmiri-external_c_so_file=tests/external_C/libtestlib.so +//@compile-flags: -Zmiri-extern-so-file=tests/extern-so/libtestlib.so extern "C" { fn add_one_int(x: i32) -> i32; @@ -21,6 +21,7 @@ extern "C" { ) -> i32; fn add_short_to_long(x: i16, y: i64) -> i64; fn get_unsigned_int() -> u32; + fn printer(); } fn main() { @@ -39,5 +40,9 @@ fn main() { // test function that returns -10 as an unsigned int assert_eq!(get_unsigned_int(), (-10i32) as u32); + + // test void function that prints from C -- call it twice + printer(); + printer(); } } diff --git a/tests/pass/external_C/print_from_c.stdout b/tests/pass/extern-so/call_extern_c_fcts.stdout similarity index 100% rename from tests/pass/external_C/print_from_c.stdout rename to tests/pass/extern-so/call_extern_c_fcts.stdout diff --git a/tests/pass/external_C/print_from_c.rs b/tests/pass/external_C/print_from_c.rs deleted file mode 100644 index 06be7a9ebf..0000000000 --- a/tests/pass/external_C/print_from_c.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@only-target-linux -//@only-on-host -//@compile-flags: -Zmiri-external_c_so_file=tests/external_C/libtestlib.so - -extern "C" { - fn printer(); -} - -fn main() { - unsafe { - // test void function that prints from C -- call it twice - printer(); - printer(); - } -} From 48c179d4b6115812ee624cf65a56d1f7da423ec1 Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Wed, 20 Jul 2022 18:12:56 +0000 Subject: [PATCH 03/23] cfg for linux, and remove test SO file --- .gitignore | 2 +- src/shims/ffi_support.rs | 2 +- tests/compiletest.rs | 2 +- tests/extern-so/libtestlib.so | Bin 15704 -> 0 bytes 4 files changed, 3 insertions(+), 3 deletions(-) delete mode 100755 tests/extern-so/libtestlib.so diff --git a/.gitignore b/.gitignore index 68b7fd40e8..1311e7062b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,4 @@ tex/*/out perf.data perf.data.old flamegraph.svg -tests/external_C/libtestlib.so +tests/extern-so/libtestlib.so diff --git a/src/shims/ffi_support.rs b/src/shims/ffi_support.rs index d5544e87b0..ec113db73c 100644 --- a/src/shims/ffi_support.rs +++ b/src/shims/ffi_support.rs @@ -196,7 +196,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx != lib_path.to_str().unwrap() { return Ok(false); - } + } } } diff --git a/tests/compiletest.rs b/tests/compiletest.rs index d1850da60b..539f485818 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -41,7 +41,7 @@ fn run_tests(mode: Mode, path: &str, target: Option) -> Result<()> { } // If we're on linux, then build the shared object file for testing external C function calls. - if env::consts::OS == "linux" { + if cfg!(target_os = "linux") { let cc = option_env!("CC").unwrap_or("cc"); Command::new(cc) .args([ diff --git a/tests/extern-so/libtestlib.so b/tests/extern-so/libtestlib.so deleted file mode 100755 index 4045953132e39609a4128e50011f8cb767edc997..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15704 zcmeHOeQX>@6`woD?YT|jOIpArX|hBqxInf^qogh+&H3Y;b+MDy*n%RW&DnRhkKRXg zx0jEqQ9>g?mK7-ofuho?E(nMs{y=;dsfaWwDewn!B|uQ9WC4*}DS!(FY%n?An|Z(U zdVMYk3I5u#Zr;4#d%v04cXM~M-Z!U`z1`7BM4>dO2h^gbR6-0bd*H!Xn`)x^4z)@9 zu39uLYt1wo2vWt3&SPkk=)fU=Ad`Ru&afWOGX1E4d|Y^te%?()ncZ%9vwN4ywy4n) zBEe&odCE3+3Xdrwr)50In3#Ev{6fD$xlQ~a*_X>h*Ss{h;}&bnWv_u1BzO-KPtQ~A z|4tz6{W9K4afwSjAmwuPwcSpGG|+4RfkAcj_djwD7r#*)obS7M>c07xuAE>049|h> zJTKC zWUgCy{#G4Jmaz48a30^PgTHqLyqoZ?b^K2ewqC7KPpmUWowruG~>CILODBH$fUJ9Q;D#1kM5*AR-{>ZMVBTqs&d((VY^(k z_i&55B0;+9JlK_N@91%N+B?+#-ky$5_a6HmdpBKSgw|C=*HPvFV~hDaIREn|UY{I~ zZ%a&g4MfR)degfa*~W$A%7s~9C5g{PWka@!SMQXz()WgZU*PjiZ-y>&0sNA5U?q$l z{?MoQ862MpqbW7x!}&9VGV88H2A6)~4br^v#0$~Kg2!s&` zBM?R)j6fIxihy(Gdd!(i#IVsr#ntbn}+e?07{a`9G`ucR$JE zt{cwytn$6jRe8^#b7t>W zZaU-Fyw%Rc-UmpTd4y&;lcwrw<8IO;hiL5D_{@Lp7#RnNGk$VmBDrwpQuNRjR_P*M zUCW)lG}A~P=L||t9XS_1Yu+#kg!3Bo~aG0szyCv@yM>Z^B z1i}b}5eOp?Mj(tp7=bVXVFbbmgb@fM5Jq5m1oUIPY+=+IDHZcpr;2QBxSyXb^K+~C zY_-aF`a4MS4gNgI3v7G6TKyr(%_L_?mPp3v;o3=(tt8KpbogN#6+CB)Jl3xw$K#QW zt8b2-BilBz@w2(fKUS+2NwqfKy|(4S=39@&POE(z?tk~LZTj&m^XYb9ohQE-`$=K$ zN9ol=b82R+eQo@c(atqDJxBwP%J#?U)kb#x3>Ef=7wu1x-6s10$r?X;P&+XHd3p_z zT|eUmzk8AYYqIx|T|euE{lKDqf$Rygb6>UoKV<9-g(ZwY7=bVXVFbbmgb@fM5Jn)3 zKp25A0{_zpNZumy6y-Ke=RWedt1-CbF|HB)dEvcXbmT*B6dm_w8$?H5q`vnd4EDd= zs217fup}oUckvgJ^r&-$$v!FeTiBTj-)nI}zHN(9b$+pKCJca_N7!-y$@!36-e!cF zl7WG@ijH|+5I;%+)dl@6RL%USBpLIcqGQ|}qQ|5^dakRl7jdTBL9KsN`17Lsb3SFr z|BpaEdtkIZfjrjzot^KqTDvks*;K*WxzpZd@7TWUE@=h0ewD9#Rh_(LUT0CtTYgB! z0f(NqYy5Noe}~{$xA4E08WGi^S`!9AoI5pso5DIYR;4~h4IUTyyTFfX|Mj&zU*Mmm zM!os3bX`yBe%7huJmEm8*Uv@riK?{R(cW`$2qwU(-1KXCwi{$bXQCs#_Klp~cKcL@a6h#T|s#%eS{RzC2HUAK~@#*grsc zz5YKU{RH#p9~C^9KYvW{U>^R*1)r4fF?=t5M(|+1{pSg&qRO*%qwy7~#CvjU**?>*Sj-s>Lh?*3qMz#VAs=uJ|GJnoww zFyJ!+34aFvvf}~%RR6%qg=J?8{HGA!>{tO#DCnaP|H1KwB{NiZ`9$7J4UzOpMvkFX zD0&%tv{12!D%o6mdp51KHkK-nDLZ|lKujaOlCd1kl*-v+VTs}jOKdK=mNK~%^QCnp z=P6sq)~2RCS|ruW9H&~pgLbKyPI)P1XU1d|jHRg)XohN*gJGaeBc$@#VVX_R$NmWNf;eLS7$5OF zDf|xc!+r}Ae$40p&jqtgt+`%2z=}V z=j8k+{K!QY=#Z}v%4-k&+J4J6{?jslTsL@#>!-<$UBJh=uj#uD-!h_BgAe&zviSKb zA+9@&v_ypf5Ar%0{Cq26JT2kFFUW`fCu*<{=8yej>k51c2k7c=)M8BBYdH9uYlvxp z@0VD6epd$_%Eb70hVSukJ@{VX&j;qdgulyZQy55$0af1~)Z@4C@k1^FLO@mj0umcG A#Q*>R From 7f85148c261166905bf3650afb0f1621ab9dbf4a Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Wed, 20 Jul 2022 21:01:49 +0000 Subject: [PATCH 04/23] dont need to track `fn_sig`: nice simplification, thanks @oli-obk! --- src/shims/ffi_support.rs | 56 +++++++++++++------------------------- src/shims/foreign_items.rs | 14 ++-------- 2 files changed, 21 insertions(+), 49 deletions(-) diff --git a/src/shims/ffi_support.rs b/src/shims/ffi_support.rs index ec113db73c..a0b7972dfe 100644 --- a/src/shims/ffi_support.rs +++ b/src/shims/ffi_support.rs @@ -65,7 +65,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// store output, depending on return type in the function signature. fn call_external_c_and_store_return<'a>( &mut self, - external_fct_defn: ExternalCFuncDeclRep<'tcx>, + link_name: Symbol, dest: &PlaceTy<'tcx, Tag>, ptr: CodePtr, libffi_args: Vec>, @@ -79,7 +79,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // If the return type of a function is a primitive integer type, // then call the function (`ptr`) with arguments `libffi_args`, store the return value as the specified // primitive integer type, and then write this value out to the miri memory as an integer. - match external_fct_defn.output_type.kind() { + match dest.layout.ty.kind() { // ints TyKind::Int(IntTy::I8) => { let x = call::(ptr, libffi_args.as_slice()); @@ -136,19 +136,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_int(u64::try_from(x).unwrap(), dest)?; return Ok(()); } + // Functions with no declared return type (i.e., the default return) + // have the output_type `Tuple([])`. + TyKind::Tuple(t_list) => + if t_list.len() == 0 { + call::<()>(ptr, libffi_args.as_slice()); + return Ok(()); + }, _ => {} } - // Functions with no declared return type (i.e., the default return) - // have the output_type `Tuple([])`. - if let TyKind::Tuple(t_list) = external_fct_defn.output_type.kind() && t_list.len() == 0{ - call::<()>(ptr, libffi_args.as_slice()); - return Ok(()); - } // TODO ellen! deal with all the other return types - throw_unsup_format!( - "unsupported return type to external C function: {:?}", - external_fct_defn.link_name - ); + throw_unsup_format!("unsupported return type to external C function: {:?}", link_name); } } @@ -159,12 +157,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// can be stored in Miri internal memory. fn call_and_add_external_c_fct_to_context( &mut self, - external_fct_defn: ExternalCFuncDeclRep<'tcx>, + link_name: Symbol, dest: &PlaceTy<'tcx, Tag>, args: &[OpTy<'tcx, Tag>], ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); - let link_name = external_fct_defn.link_name; let (lib, lib_path) = this.machine.external_so_lib.as_ref().unwrap(); // Load the C function from the library. @@ -196,22 +193,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx != lib_path.to_str().unwrap() { return Ok(false); - } + } } } // Get the function arguments, and convert them to `libffi`-compatible form. - if args.len() != external_fct_defn.inputs_types.len() { - throw_ub_format!( - "calling function {:?} with {} arguments; expected {}", - link_name, - args.len(), - external_fct_defn.inputs_types.len() - ); - } let mut libffi_args = Vec::::with_capacity(args.len()); - for (cur_arg, arg_type) in args.iter().zip(external_fct_defn.inputs_types.iter()) { - libffi_args.push(Self::scalar_to_carg(this.read_scalar(cur_arg)?, arg_type, this)?); + for cur_arg in args.iter() { + libffi_args.push(Self::scalar_to_carg( + this.read_scalar(cur_arg)?, + &cur_arg.layout.ty, + this, + )?); } // Convert them to `libffi::high::Arg` type. @@ -223,22 +216,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Code pointer to C function. let ptr = CodePtr(*func.deref() as *mut _); // Call the functio and store output, depending on return type in the function signature. - self.call_external_c_and_store_return(external_fct_defn, dest, ptr, libffi_args)?; + self.call_external_c_and_store_return(link_name, dest, ptr, libffi_args)?; Ok(true) } } -#[derive(Debug)] -/// Signature of an external C function. -pub struct ExternalCFuncDeclRep<'tcx> { - /// Function name. - pub link_name: Symbol, - /// Argument types. - pub inputs_types: &'tcx [Ty<'tcx>], - /// Return type. - pub output_type: Ty<'tcx>, -} - #[derive(Debug, Clone)] /// Enum of supported arguments to external C functions. pub enum CArg { diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index b691fb111e..278b21abfd 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -24,7 +24,6 @@ use rustc_target::{ use super::backtrace::EvalContextExt as _; use crate::helpers::{convert::Truncate, target_os_is_unix}; use crate::shims::ffi_support::EvalContextExt as _; -use crate::shims::ffi_support::ExternalCFuncDeclRep; use crate::*; /// Returned by `emulate_foreign_item_by_name`. @@ -300,7 +299,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx }; // Second: functions that return immediately. - match this.emulate_foreign_item_by_name(def_id, link_name, abi, args, dest)? { + match this.emulate_foreign_item_by_name(link_name, abi, args, dest)? { EmulateByNameResult::NeedsJumping => { trace!("{:?}", this.dump_place(**dest)); this.go_to_block(ret); @@ -353,7 +352,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// Emulates calling a foreign item using its name. fn emulate_foreign_item_by_name( &mut self, - def_id: DefId, link_name: Symbol, abi: Abi, args: &[OpTy<'tcx, Tag>], @@ -361,21 +359,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> { let this = self.eval_context_mut(); - let tcx = this.tcx.tcx; - // First deal with any external C functions in linked .so file // (if any SO file is specified). if this.machine.external_so_lib.as_ref().is_some() { - let fn_sig = &tcx.fn_sig(def_id); - let extern_c_fct_rep = ExternalCFuncDeclRep { - link_name, - inputs_types: fn_sig.inputs().skip_binder(), - output_type: fn_sig.output().skip_binder(), - }; // An Ok(false) here means that the function being called was not exported // by the specified SO file; we should continue and check if it corresponds to // a provided shim. - if this.call_and_add_external_c_fct_to_context(extern_c_fct_rep, dest, args)? { + if this.call_and_add_external_c_fct_to_context(link_name, dest, args)? { return Ok(EmulateByNameResult::NeedsJumping); } } From 0b5879051ed0038f488f0b25b273a78104a8454f Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Wed, 20 Jul 2022 22:13:35 +0000 Subject: [PATCH 05/23] update README; moving libloading load and hack into its own function --- README.md | 8 ++++-- src/shims/ffi_support.rs | 60 +++++++++++++++++++++++++--------------- 2 files changed, 43 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index b31cf97b91..a76de4e833 100644 --- a/README.md +++ b/README.md @@ -287,9 +287,11 @@ environment variable. We first document the most relevant and most commonly used `-Zmiri-disable-isolation` is also set. * `-Zmiri-extern-so-file=` is an experimental flag for providing support for FFI calls. - **WARNING**: If an invalid/incorrect SO file is specified, this can cause undefined behaviour in Miri itself! - This is [work in progress](https://github.com/rust-lang/miri/pull/2363); - current support is for functions with integer arguments/returns. + **WARNING**: If an invalid/incorrect `.so` file is specified, this can cause undefined behaviour in Miri itself! + And of course, Miri cannot do any checks on the actions taken by the external code. + This is **work in progress**; currently, only integer arguments and return values are + supported (and no, pointer/integer casts to work around this limitation will not work; + they will fail horribly). Follow [the discussion on supporting other types](https://github.com/rust-lang/miri/issues/2365). * `-Zmiri-env-forward=` forwards the `var` environment variable to the interpreted program. Can be used multiple times to forward several variables. This takes precedence over diff --git a/src/shims/ffi_support.rs b/src/shims/ffi_support.rs index a0b7972dfe..bd7641da19 100644 --- a/src/shims/ffi_support.rs +++ b/src/shims/ffi_support.rs @@ -150,36 +150,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - /// Call specified external C function, with supplied arguments. - /// Need to convert all the arguments from their hir representations to - /// a form compatible with C (through `libffi` call). - /// Then, convert return from the C call into a corresponding form that - /// can be stored in Miri internal memory. - fn call_and_add_external_c_fct_to_context( - &mut self, - link_name: Symbol, - dest: &PlaceTy<'tcx, Tag>, - args: &[OpTy<'tcx, Tag>], - ) -> InterpResult<'tcx, bool> { + /// Get the pointer to the function of the specified name in the shared object file, + /// if it exists. The function must be in the shared object file specified: we do *not* + /// return pointers to functions in dependencies of the library. + fn get_func_ptr_explicitly_from_lib(&mut self, link_name: Symbol) -> Option { let this = self.eval_context_mut(); + // Try getting the function from the shared library. let (lib, lib_path) = this.machine.external_so_lib.as_ref().unwrap(); - - // Load the C function from the library. - // Because this is getting the C function from the shared object file - // it is not necessarily a sound operation, but there is no way around - // this and we've checked as much as we can. let func: libloading::Symbol<'_, unsafe extern "C" fn()> = unsafe { match lib.get(link_name.as_str().as_bytes()) { Ok(x) => x, Err(_) => { - // Shared object file does not export this function -- try the shims next. - return Ok(false); + return None; } } }; // FIXME: this is a hack! // The `libloading` crate will automatically load system libraries like `libc`. + // On linux `libloading` is based on `dlsym`: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#202 + // and `dlsym`(https://linux.die.net/man/3/dlsym) looks through the dependency tree of the + // library if it can't find the symbol in the library itself. // So, in order to check if the function was actually found in the specified // `machine.external_so_lib` we need to check its `dli_fname` and compare it to // the specified SO file path. @@ -192,10 +183,35 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap() != lib_path.to_str().unwrap() { - return Ok(false); + return None; } } } + // Return a pointer to the function. + Some(CodePtr(*func.deref() as *mut _)) + } + + /// Call specified external C function, with supplied arguments. + /// Need to convert all the arguments from their hir representations to + /// a form compatible with C (through `libffi` call). + /// Then, convert return from the C call into a corresponding form that + /// can be stored in Miri internal memory. + fn call_and_add_external_c_fct_to_context( + &mut self, + link_name: Symbol, + dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Tag>], + ) -> InterpResult<'tcx, bool> { + // Get the pointer to the function in the shared object file if it exists. + let code_ptr = match self.get_func_ptr_explicitly_from_lib(link_name) { + Some(ptr) => ptr, + None => { + // Shared object file does not export this function -- try the shims next. + return Ok(false); + } + }; + + let this = self.eval_context_mut(); // Get the function arguments, and convert them to `libffi`-compatible form. let mut libffi_args = Vec::::with_capacity(args.len()); @@ -214,9 +230,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx .collect::>>(); // Code pointer to C function. - let ptr = CodePtr(*func.deref() as *mut _); - // Call the functio and store output, depending on return type in the function signature. - self.call_external_c_and_store_return(link_name, dest, ptr, libffi_args)?; + // let ptr = CodePtr(*func.deref() as *mut _); + // Call the function and store output, depending on return type in the function signature. + self.call_external_c_and_store_return(link_name, dest, code_ptr, libffi_args)?; Ok(true) } } From 7c97b73336a5aeb9d720527ac6094a2a743d3052 Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Thu, 21 Jul 2022 17:27:42 +0000 Subject: [PATCH 06/23] fix outdated doc --- src/shims/ffi_support.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shims/ffi_support.rs b/src/shims/ffi_support.rs index bd7641da19..caeecae220 100644 --- a/src/shims/ffi_support.rs +++ b/src/shims/ffi_support.rs @@ -18,7 +18,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx cx: &impl HasDataLayout, ) -> InterpResult<'tcx, CArg> { match arg_type.kind() { - // If the primitive provided can be converted to a type matching the hir type pattern + // If the primitive provided can be converted to a type matching the type pattern // then create a `CArg` of this primitive value with the corresponding `CArg` constructor. // the ints TyKind::Int(IntTy::I8) => { From 62926b7db2f7a742d5f590b128b4fc09dc36a43d Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Thu, 21 Jul 2022 21:31:36 +0000 Subject: [PATCH 07/23] upstream master changes; and no libc on windows --- .gitignore | 1 + CONTRIBUTING.md | 17 + Cargo.lock | 35 ++ Cargo.toml | 5 +- README.md | 14 +- cargo-miri/bin.rs | 233 ++++++---- miri | 33 +- rust-version | 2 +- src/bin/miri.rs | 47 +- src/concurrency/data_race.rs | 130 +++--- src/concurrency/weak_memory.rs | 50 ++- src/diagnostics.rs | 20 +- src/eval.rs | 38 +- src/helpers.rs | 107 +++-- src/intptrcast.rs | 31 +- src/lib.rs | 9 +- src/machine.rs | 202 +++++---- src/operator.rs | 12 +- src/shims/backtrace.rs | 18 +- src/shims/dlsym.rs | 4 +- src/shims/env.rs | 49 +- src/shims/ffi_support.rs | 16 +- src/shims/foreign_items.rs | 20 +- src/shims/intrinsics/atomic.rs | 34 +- src/shims/intrinsics/mod.rs | 16 +- src/shims/intrinsics/simd.rs | 20 +- src/shims/mod.rs | 10 +- src/shims/os_str.rs | 23 +- src/shims/panic.rs | 14 +- src/shims/time.rs | 23 +- src/shims/tls.rs | 12 +- src/shims/unix/dlsym.rs | 4 +- src/shims/unix/foreign_items.rs | 4 +- src/shims/unix/freebsd/dlsym.rs | 4 +- src/shims/unix/freebsd/foreign_items.rs | 4 +- src/shims/unix/fs.rs | 112 +++-- src/shims/unix/linux/dlsym.rs | 4 +- src/shims/unix/linux/foreign_items.rs | 12 +- src/shims/unix/linux/sync.rs | 6 +- src/shims/unix/macos/dlsym.rs | 4 +- src/shims/unix/macos/foreign_items.rs | 4 +- src/shims/unix/sync.rs | 180 +++++--- src/shims/unix/thread.rs | 20 +- src/shims/windows/dlsym.rs | 4 +- src/shims/windows/foreign_items.rs | 4 +- src/shims/windows/sync.rs | 20 +- src/stacked_borrows/diagnostics.rs | 15 +- src/stacked_borrows/mod.rs | 128 +++--- src/stacked_borrows/stack.rs | 54 ++- src/sync.rs | 2 +- src/thread.rs | 34 +- test-cargo-miri/Cargo.lock | 127 +----- test-cargo-miri/Cargo.toml | 5 +- test-cargo-miri/cdylib/Cargo.toml | 2 +- test-cargo-miri/run-test.py | 30 +- test-cargo-miri/src/main.rs | 11 +- test-cargo-miri/test.bin-target.stdout.ref | 2 +- test-cargo-miri/test.cross-target.stdout.ref | 6 +- test-cargo-miri/test.default.stdout.ref | 6 +- .../test.filter.cross-target.stdout.ref | 4 +- test-cargo-miri/test.filter.stdout.ref | 4 +- test-cargo-miri/test.test-target.stdout.ref | 10 +- test-cargo-miri/tests/test.rs | 56 +-- test_dependencies/Cargo.lock | 387 ++++++++++++++++ test_dependencies/Cargo.toml | 20 + test_dependencies/src/main.rs | 1 + tests/compiletest.rs | 64 ++- .../libc_pthread_create_main_terminate.rs | 2 - .../concurrency/libc_pthread_join_detached.rs | 2 - .../concurrency/libc_pthread_join_joined.rs | 2 - .../concurrency/libc_pthread_join_main.rs | 2 - .../concurrency/libc_pthread_join_multiple.rs | 2 - .../concurrency/libc_pthread_join_self.rs | 2 - tests/fail/concurrency/too_few_args.rs | 2 - tests/fail/concurrency/too_few_args.stderr | 2 +- tests/fail/concurrency/too_many_args.rs | 2 - tests/fail/concurrency/too_many_args.stderr | 2 +- tests/fail/concurrency/unwind_top_of_stack.rs | 2 - tests/fail/crates/tokio_mvp.rs | 7 + tests/fail/crates/tokio_mvp.stderr | 19 + tests/fail/erroneous_const.stderr | 2 +- tests/fail/erroneous_const2.stderr | 2 +- tests/fail/extern_static_wrong_size.rs | 10 + tests/fail/extern_static_wrong_size.stderr | 14 + tests/fail/fs/close_stdout.rs | 2 - tests/fail/fs/isolated_stdin.rs | 2 - tests/fail/fs/read_from_stdout.rs | 2 - .../fs/unix_open_missing_required_mode.rs | 2 - tests/fail/fs/write_to_stdin.rs | 2 - tests/fail/panic/double_panic.stderr | 2 +- tests/fail/panic/panic_abort1.stderr | 2 +- tests/fail/panic/panic_abort2.stderr | 2 +- tests/fail/panic/panic_abort3.stderr | 2 +- tests/fail/panic/panic_abort4.stderr | 2 +- .../sync/libc_pthread_cond_double_destroy.rs | 1 - .../libc_pthread_condattr_double_destroy.rs | 1 - .../sync/libc_pthread_mutex_NULL_deadlock.rs | 2 - .../fail/sync/libc_pthread_mutex_deadlock.rs | 2 - .../libc_pthread_mutex_default_deadlock.rs | 2 - .../sync/libc_pthread_mutex_destroy_locked.rs | 2 - .../sync/libc_pthread_mutex_double_destroy.rs | 1 - .../libc_pthread_mutex_normal_deadlock.rs | 2 - ...bc_pthread_mutex_normal_unlock_unlocked.rs | 2 - .../sync/libc_pthread_mutex_wrong_owner.rs | 2 - .../libc_pthread_mutexattr_double_destroy.rs | 1 - ...libc_pthread_rwlock_destroy_read_locked.rs | 2 - ...ibc_pthread_rwlock_destroy_write_locked.rs | 2 - .../libc_pthread_rwlock_double_destroy.rs | 1 - ...wlock_read_write_deadlock_single_thread.rs | 2 - .../libc_pthread_rwlock_read_wrong_owner.rs | 2 - .../libc_pthread_rwlock_unlock_unlocked.rs | 2 - ...libc_pthread_rwlock_write_read_deadlock.rs | 2 - ...wlock_write_read_deadlock_single_thread.rs | 2 - ...ibc_pthread_rwlock_write_write_deadlock.rs | 2 - ...lock_write_write_deadlock_single_thread.rs | 2 - .../libc_pthread_rwlock_write_wrong_owner.rs | 2 - tests/fail/unsupported_signal.rs | 2 - .../invalid_enum_tag_256variants_uninit.rs | 272 ------------ ...invalid_enum_tag_256variants_uninit.stderr | 16 - tests/panic/panic/unsupported_syscall.rs | 2 - tests/pass/calloc.rs | 2 - tests/pass/concurrency/libc_pthread_cond.rs | 2 - tests/pass/concurrency/linux-futex.rs | 1 - .../concurrency/tls_pthread_drop_order.rs | 1 - tests/pass/crates/page_size.rs | 6 + tests/pass/crates/random.rs | 22 + tests/pass/dyn-upcast.rs | 418 ++++++++++++++++++ tests/pass/foreign-fn-linkname.rs | 2 - tests/pass/fs.rs | 2 - tests/pass/fs_with_isolation.rs | 2 - tests/pass/libc.rs | 2 - .../pass/linux-getrandom-without-isolation.rs | 1 - tests/pass/linux-getrandom.rs | 1 - tests/pass/malloc.rs | 2 - tests/pass/move-uninit-primval.rs | 7 +- tests/pass/move-uninit-primval.stderr | 1 - tests/pass/regions-mock-trans.rs | 2 - tests/pass/stacked-borrows/issue-miri-2389.rs | 17 + .../stacked-borrows/issue-miri-2389.stderr | 15 + tests/pass/uninit_number_ignored.rs | 8 - tests/pass/uninit_number_ignored.stderr | 1 - ui_test/Cargo.lock | 35 ++ ui_test/Cargo.toml | 2 +- ui_test/README.md | 15 +- ui_test/src/dependencies.rs | 137 ++++++ ui_test/src/lib.rs | 110 +++-- ui_test/src/tests.rs | 3 + 147 files changed, 2420 insertions(+), 1423 deletions(-) create mode 100644 test_dependencies/Cargo.lock create mode 100644 test_dependencies/Cargo.toml create mode 100644 test_dependencies/src/main.rs create mode 100644 tests/fail/crates/tokio_mvp.rs create mode 100644 tests/fail/crates/tokio_mvp.stderr create mode 100644 tests/fail/extern_static_wrong_size.rs create mode 100644 tests/fail/extern_static_wrong_size.stderr delete mode 100644 tests/fail/validity/invalid_enum_tag_256variants_uninit.rs delete mode 100644 tests/fail/validity/invalid_enum_tag_256variants_uninit.stderr create mode 100644 tests/pass/crates/page_size.rs create mode 100644 tests/pass/crates/random.rs create mode 100644 tests/pass/dyn-upcast.rs delete mode 100644 tests/pass/move-uninit-primval.stderr create mode 100644 tests/pass/stacked-borrows/issue-miri-2389.rs create mode 100644 tests/pass/stacked-borrows/issue-miri-2389.stderr delete mode 100644 tests/pass/uninit_number_ignored.rs delete mode 100644 tests/pass/uninit_number_ignored.stderr create mode 100644 ui_test/src/dependencies.rs diff --git a/.gitignore b/.gitignore index 1311e7062b..924a93e807 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ perf.data perf.data.old flamegraph.svg tests/extern-so/libtestlib.so +.auto-* diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a57ef09e7d..42f77b5cbc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,6 +28,11 @@ install that exact version of rustc as a toolchain: This will set up a rustup toolchain called `miri` and set it as an override for the current directory. +You can also create a `.auto-everything` file (contents don't matter, can be empty), which +will cause any `./miri` command to automatically call `rustup-toolchain`, `clippy` and `rustfmt` +for you. If you don't want all of these to happen, you can add individual `.auto-toolchain`, +`.auto-clippy` and `.auto-fmt` files respectively. + [`rustup-toolchain-install-master`]: https://github.com/kennytm/rustup-toolchain-install-master ## Building and testing Miri @@ -163,11 +168,15 @@ to `.vscode/settings.json` in your local Miri clone: "./cargo-miri/Cargo.toml" ], "rust-analyzer.checkOnSave.overrideCommand": [ + "env", + "MIRI_AUTO_OPS=no", "./miri", "check", "--message-format=json" ], "rust-analyzer.buildScripts.overrideCommand": [ + "env", + "MIRI_AUTO_OPS=no", "./miri", "check", "--message-format=json", @@ -244,6 +253,14 @@ rustup toolchain link stage2 build/x86_64-unknown-linux-gnu/stage2 rustup override set stage2 ``` +Note: When you are working with a locally built rustc or any other toolchain that +is not the same as the one in `rust-version`, you should not have `.auto-everything` or +`.auto-toolchain` as that will keep resetting your toolchain. + +``` +rm -f .auto-everything .auto-toolchain +``` + Important: You need to delete the Miri cache when you change the stdlib; otherwise the old, chached version will be used. On Linux, the cache is located at `~/.cache/miri`, and on Windows, it is located at `%LOCALAPPDATA%\rust-lang\miri\cache`; the exact diff --git a/Cargo.lock b/Cargo.lock index 8ce82d9df8..8f03cdbdf0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,6 +79,37 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "camino" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "869119e97797867fd90f5e22af7d0bd274bd4635ebb9eb68c04f3f513ae6c412" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3abb7553d5b9b8421c6de7cb02606ff15e0c6eea7d8eadd75ef013fd636bec36" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", +] + [[package]] name = "cc" version = "1.0.73" @@ -634,6 +665,9 @@ name = "semver" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd" +dependencies = [ + "serde", +] [[package]] name = "serde" @@ -762,6 +796,7 @@ dependencies = [ name = "ui_test" version = "0.1.0" dependencies = [ + "cargo_metadata", "color-eyre", "colored", "crossbeam", diff --git a/Cargo.toml b/Cargo.toml index e89c79f1b5..47237a2bd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,5 +58,6 @@ harness = false default = ["stack-cache"] stack-cache = [] -[profile.dev] -opt-level = 2 # because it's too slow otherwise +# Be aware that this file is inside a workspace when used via the +# submodule in the rustc repo. That means there are many cargo features +# we cannot use, such as profiles. diff --git a/README.md b/README.md index a76de4e833..8452cb6ed4 100644 --- a/README.md +++ b/README.md @@ -337,17 +337,6 @@ The remaining flags are for advanced use only, and more likely to change or be r Some of these are **unsound**, which means they can lead to Miri failing to detect cases of undefined behavior in a program. -* `-Zmiri-allow-uninit-numbers` disables the check to ensure that number types (integer and float - types) always hold initialized data. (They must still be initialized when any actual operation, - such as arithmetic, is performed.) Using this flag is **unsound** and - [deprecated](https://github.com/rust-lang/miri/issues/2187). This has no effect when - `-Zmiri-disable-validation` is present. -* `-Zmiri-allow-ptr-int-transmute` makes Miri more accepting of transmutation between pointers and - integers via `mem::transmute` or union/pointer type punning. This has two effects: it disables the - check against integers storing a pointer (i.e., data with provenance), thus allowing - pointer-to-integer transmutation, and it treats integer-to-pointer transmutation as equivalent to - a cast. Implies `-Zmiri-permissive-provenance`. Using this flag is **unsound** and - [deprecated](https://github.com/rust-lang/miri/issues/2188). * `-Zmiri-disable-abi-check` disables checking [function ABI]. Using this flag is **unsound**. * `-Zmiri-disable-alignment-check` disables checking pointer alignment, so you @@ -412,6 +401,9 @@ Some native rustc `-Z` flags are also very relevant for Miri: Moreover, Miri recognizes some environment variables: +* `MIRI_AUTO_OPS` indicates whether the automatic execution of rustfmt, clippy and rustup-toolchain + should be skipped. If it is set to any value, they are skipped. This is used for avoiding + infinite recursion in `./miri` and to allow automated IDE actions to avoid the auto ops. * `MIRI_LOG`, `MIRI_BACKTRACE` control logging and backtrace printing during Miri executions, also [see "Testing the Miri driver" in `CONTRIBUTING.md`][testing-miri]. * `MIRIFLAGS` (recognized by `cargo miri` and the test suite) defines extra diff --git a/cargo-miri/bin.rs b/cargo-miri/bin.rs index 4c9f3aabaa..48beb7c935 100644 --- a/cargo-miri/bin.rs +++ b/cargo-miri/bin.rs @@ -1,7 +1,9 @@ +#![feature(let_else)] #![allow(clippy::useless_format, clippy::derive_partial_eq_without_eq)] mod version; +use std::collections::HashMap; use std::env; use std::ffi::{OsStr, OsString}; use std::fmt::Write as _; @@ -25,6 +27,7 @@ Usage: Subcommands: run, r Run binaries test, t Run tests + nextest Run tests with nextest (requires cargo-nextest installed) setup Only perform automatic setup, but without asking questions (for getting a proper libstd) The cargo options are exactly the same as for `cargo run` and `cargo test`, respectively. @@ -34,11 +37,12 @@ Examples: cargo miri test -- test-suite-filter "#; -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug)] enum MiriCommand { - Run, - Test, + /// Our own special 'setup' command. Setup, + /// A command to be forwarded to cargo. + Forward(String), } /// The information to run a crate with the given environment. @@ -112,10 +116,14 @@ fn show_error(msg: String) -> ! { std::process::exit(1) } -// Determines whether a `--flag` is present. +/// Determines whether a `--flag` is present. fn has_arg_flag(name: &str) -> bool { - let mut args = std::env::args().take_while(|val| val != "--"); - args.any(|val| val == name) + num_arg_flag(name) > 0 +} + +/// Determines how many times a `--flag` is present. +fn num_arg_flag(name: &str) -> usize { + std::env::args().take_while(|val| val != "--").filter(|val| val == name).count() } /// Yields all values of command line flag `name` as `Ok(arg)`, and all other arguments except @@ -204,7 +212,11 @@ fn find_miri() -> PathBuf { return path.into(); } let mut path = std::env::current_exe().expect("current executable path invalid"); - path.set_file_name("miri"); + if cfg!(windows) { + path.set_file_name("miri.exe"); + } else { + path.set_file_name("miri"); + } path } @@ -319,12 +331,30 @@ fn ask_to_run(mut cmd: Command, ask: bool, text: &str) { } } +/// Writes the given content to the given file *cross-process atomically*, in the sense that another +/// process concurrently reading that file will see either the old content or the new content, but +/// not some intermediate (e.g., empty) state. +/// +/// We assume no other parts of this same process are trying to read or write that file. +fn write_to_file(filename: &Path, content: &str) { + // Create a temporary file with the desired contents. + let mut temp_filename = filename.as_os_str().to_os_string(); + temp_filename.push(&format!(".{}", std::process::id())); + let mut temp_file = File::create(&temp_filename).unwrap(); + temp_file.write_all(content.as_bytes()).unwrap(); + drop(temp_file); + + // Move file to the desired location. + fs::rename(temp_filename, filename).unwrap(); +} + /// Performs the setup required to make `cargo miri` work: Getting a custom-built libstd. Then sets /// `MIRI_SYSROOT`. Skipped if `MIRI_SYSROOT` is already set, in which case we expect the user has /// done all this already. -fn setup(subcommand: MiriCommand) { +fn setup(subcommand: &MiriCommand) { + let only_setup = matches!(subcommand, MiriCommand::Setup); if std::env::var_os("MIRI_SYSROOT").is_some() { - if subcommand == MiriCommand::Setup { + if only_setup { println!("WARNING: MIRI_SYSROOT already set, not doing anything.") } return; @@ -332,7 +362,7 @@ fn setup(subcommand: MiriCommand) { // Subcommands other than `setup` will do a setup if necessary, but // interactively confirm first. - let ask_user = subcommand != MiriCommand::Setup; + let ask_user = !only_setup; // First, we need xargo. if xargo_version().map_or(true, |v| v < XARGO_MIN_VERSION) { @@ -357,12 +387,15 @@ fn setup(subcommand: MiriCommand) { } None => { // Check for `rust-src` rustup component. - let sysroot = miri() - .args(&["--print", "sysroot"]) - .output() - .expect("failed to determine sysroot") - .stdout; - let sysroot = std::str::from_utf8(&sysroot).unwrap(); + let output = + miri().args(&["--print", "sysroot"]).output().expect("failed to determine sysroot"); + if !output.status.success() { + show_error(format!( + "Failed to determine sysroot; Miri said:\n{}", + String::from_utf8_lossy(&output.stderr).trim_end() + )); + } + let sysroot = std::str::from_utf8(&output.stdout).unwrap(); let sysroot = Path::new(sysroot.trim_end_matches('\n')); // Check for `$SYSROOT/lib/rustlib/src/rust/library`; test if that contains `std/Cargo.toml`. let rustup_src = @@ -398,28 +431,25 @@ fn setup(subcommand: MiriCommand) { if !dir.exists() { fs::create_dir_all(&dir).unwrap(); } - let mut xargo_toml = File::create(dir.join("Xargo.toml")).unwrap(); - if std::env::var_os("MIRI_NO_STD").is_none() { - // The interesting bit: Xargo.toml (only needs content if we actually need std) - xargo_toml - .write_all( - br#" + // The interesting bit: Xargo.toml (only needs content if we actually need std) + let xargo_toml = if std::env::var_os("MIRI_NO_STD").is_some() { + "" + } else { + r#" [dependencies.std] default_features = false # We support unwinding, so enable that panic runtime. features = ["panic_unwind", "backtrace"] [dependencies.test] -"#, - ) - .unwrap(); - } +"# + }; + write_to_file(&dir.join("Xargo.toml"), xargo_toml); // The boring bits: a dummy project for xargo. // FIXME: With xargo-check, can we avoid doing this? - File::create(dir.join("Cargo.toml")) - .unwrap() - .write_all( - br#" + write_to_file( + &dir.join("Cargo.toml"), + r#" [package] name = "miri-xargo" description = "A dummy project for building libstd with xargo." @@ -428,9 +458,8 @@ version = "0.0.0" [lib] path = "lib.rs" "#, - ) - .unwrap(); - File::create(dir.join("lib.rs")).unwrap().write_all(b"#![no_std]").unwrap(); + ); + write_to_file(&dir.join("lib.rs"), "#![no_std]"); // Determine architectures. // We always need to set a target so rustc bootstrap can tell apart host from target crates. @@ -479,11 +508,11 @@ path = "lib.rs" let sysroot = if target == &host { dir.join("HOST") } else { PathBuf::from(dir) }; std::env::set_var("MIRI_SYSROOT", &sysroot); // pass the env var to the processes we spawn, which will turn it into "--sysroot" flags // Figure out what to print. - let print_sysroot = subcommand == MiriCommand::Setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path + let print_sysroot = only_setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path if print_sysroot { // Print just the sysroot and nothing else; this way we do not need any escaping. println!("{}", sysroot.display()); - } else if subcommand == MiriCommand::Setup { + } else if only_setup { println!("A libstd for Miri is now available in `{}`.", sysroot.display()); } } @@ -557,20 +586,21 @@ fn phase_cargo_miri(mut args: env::Args) { // Require a subcommand before any flags. // We cannot know which of those flags take arguments and which do not, // so we cannot detect subcommands later. - let subcommand = match args.next().as_deref() { - Some("test" | "t") => MiriCommand::Test, - Some("run" | "r") => MiriCommand::Run, - Some("setup") => MiriCommand::Setup, - // Invalid command. + let Some(subcommand) = args.next() else { + show_error(format!("`cargo miri` needs to be called with a subcommand (`run`, `test`)")); + }; + let subcommand = match &*subcommand { + "setup" => MiriCommand::Setup, + "test" | "t" | "run" | "r" | "nextest" => MiriCommand::Forward(subcommand), _ => show_error(format!( - "`cargo miri` supports the following subcommands: `run`, `test`, and `setup`." + "`cargo miri` supports the following subcommands: `run`, `test`, `nextest`, and `setup`." )), }; - let verbose = has_arg_flag("-v"); + let verbose = num_arg_flag("-v"); // We always setup. - setup(subcommand); + setup(&subcommand); // Invoke actual cargo for the job, but with different flags. // We re-use `cargo test` and `cargo run`, which makes target and binary handling very easy but @@ -580,31 +610,15 @@ fn phase_cargo_miri(mut args: env::Args) { // harder. let cargo_miri_path = std::env::current_exe().expect("current executable path invalid"); let cargo_cmd = match subcommand { - MiriCommand::Test => "test", - MiriCommand::Run => "run", + MiriCommand::Forward(s) => s, MiriCommand::Setup => return, // `cargo miri setup` stops here. }; + let metadata = get_cargo_metadata(); let mut cmd = cargo(); cmd.arg(cargo_cmd); - // Make sure we know the build target, and cargo does, too. - // This is needed to make the `CARGO_TARGET_*_RUNNER` env var do something, - // and it later helps us detect which crates are proc-macro/build-script - // (host crates) and which crates are needed for the program itself. - let host = version_info().host; - let target = get_arg_flag_value("--target"); - let target = if let Some(ref target) = target { - target - } else { - // No target given. Pick default and tell cargo about it. - cmd.arg("--target"); - cmd.arg(&host); - &host - }; - - let mut target_dir = None; - // Forward all arguments before `--` other than `--target-dir` and its value to Cargo. + let mut target_dir = None; for arg in ArgSplitFlagValue::new(&mut args, "--target-dir") { match arg { Ok(value) => { @@ -618,16 +632,27 @@ fn phase_cargo_miri(mut args: env::Args) { } } } - - let metadata = get_cargo_metadata(); - // Detect the target directory if it's not specified via `--target-dir`. let target_dir = target_dir.get_or_insert_with(|| metadata.target_directory.clone()); - // Set `--target-dir` to `miri` inside the original target directory. target_dir.push("miri"); cmd.arg("--target-dir").arg(target_dir); + // Make sure we know the build target, and cargo does, too. + // This is needed to make the `CARGO_TARGET_*_RUNNER` env var do something, + // and it later helps us detect which crates are proc-macro/build-script + // (host crates) and which crates are needed for the program itself. + let host = version_info().host; + let target = get_arg_flag_value("--target"); + let target = if let Some(ref target) = target { + target + } else { + // No target given. Pick default and tell cargo about it. + cmd.arg("--target"); + cmd.arg(&host); + &host + }; + // Forward all further arguments after `--` to cargo. cmd.arg("--").args(args); @@ -640,14 +665,20 @@ fn phase_cargo_miri(mut args: env::Args) { ); } cmd.env("RUSTC_WRAPPER", &cargo_miri_path); - // Having both `RUSTC_WRAPPER` and `RUSTC` set does some odd things, so let's avoid that. - // See . + // We are going to invoke `MIRI` for everything, not `RUSTC`. if env::var_os("RUSTC").is_some() && env::var_os("MIRI").is_none() { println!( "WARNING: Ignoring `RUSTC` environment variable; set `MIRI` if you want to control the binary used as the driver." ); } - cmd.env_remove("RUSTC"); + // We'd prefer to just clear this env var, but cargo does not always honor `RUSTC_WRAPPER` + // (https://github.com/rust-lang/cargo/issues/10885). There is no good way to single out these invocations; + // some build scripts use the RUSTC env var as well. So we set it directly to the `miri` driver and + // hope that all they do is ask for the version number -- things could quickly go downhill from here. + // In `main`, we need the value of `RUSTC` to distinguish RUSTC_WRAPPER invocations from rustdoc + // or TARGET_RUNNER invocations, so we canonicalize it here to make it exceedingly unlikely that + // there would be a collision. + cmd.env("RUSTC", &fs::canonicalize(find_miri()).unwrap()); let runner_env_name = |triple: &str| format!("CARGO_TARGET_{}_RUNNER", triple.to_uppercase().replace('-', "_")); @@ -665,7 +696,7 @@ fn phase_cargo_miri(mut args: env::Args) { cmd.env("MIRI_LOCAL_CRATES", local_crates(&metadata)); // Run cargo. - if verbose { + if verbose > 0 { eprintln!("[cargo-miri miri] RUSTC_WRAPPER={:?}", cargo_miri_path); eprintln!("[cargo-miri miri] {}={:?}", target_runner_env_name, cargo_miri_path); if *target != host { @@ -673,7 +704,7 @@ fn phase_cargo_miri(mut args: env::Args) { } eprintln!("[cargo-miri miri] RUSTDOC={:?}", cargo_miri_path); eprintln!("[cargo-miri miri] {:?}", cmd); - cmd.env("MIRI_VERBOSE", ""); // This makes the other phases verbose. + cmd.env("MIRI_VERBOSE", verbose.to_string()); // This makes the other phases verbose. } exec(cmd) } @@ -732,7 +763,8 @@ fn phase_rustc(mut args: env::Args, phase: RustcPhase) { } } - let verbose = std::env::var_os("MIRI_VERBOSE").is_some(); + let verbose = std::env::var("MIRI_VERBOSE") + .map_or(0, |verbose| verbose.parse().expect("verbosity flag must be an integer")); let target_crate = is_target_crate(); let print = get_arg_flag_value("--print").is_some() || has_arg_flag("-vV"); // whether this is cargo/xargo invoking rustc to get some infos @@ -741,13 +773,13 @@ fn phase_rustc(mut args: env::Args, phase: RustcPhase) { // https://github.com/rust-lang/miri/issues/1724#issuecomment-787115693 // As we store a JSON file instead of building the crate here, an empty file is fine. let dep_info_name = out_filename("", ".d"); - if verbose { + if verbose > 0 { eprintln!("[cargo-miri rustc] writing stub dep-info to `{}`", dep_info_name.display()); } File::create(dep_info_name).expect("failed to create fake .d file"); let filename = out_filename("", ""); - if verbose { + if verbose > 0 { eprintln!("[cargo-miri rustc] writing run info to `{}`", filename.display()); } info.store(&filename); @@ -790,12 +822,12 @@ fn phase_rustc(mut args: env::Args, phase: RustcPhase) { cmd.args(&env.args); cmd.env("MIRI_BE_RUSTC", "target"); - if verbose { + if verbose > 0 { eprintln!( - "[cargo-miri rustc] captured input:\n{}", + "[cargo-miri rustc inside rustdoc] captured input:\n{}", std::str::from_utf8(&env.stdin).unwrap() ); - eprintln!("[cargo-miri rustc] {:?}", cmd); + eprintln!("[cargo-miri rustc inside rustdoc] going to run:\n{:?}", cmd); } exec_with_pipe(cmd, &env.stdin); @@ -857,6 +889,15 @@ fn phase_rustc(mut args: env::Args, phase: RustcPhase) { cmd.arg("-C").arg("panic=abort"); } } else { + // For host crates (but not when we are printing), we might still have to set the sysroot. + if !print { + // When we're running `cargo-miri` from `x.py` we need to pass the sysroot explicitly as rustc + // can't figure out the sysroot on its own unless it's from rustup. + if let Some(sysroot) = std::env::var_os("SYSROOT") { + cmd.arg("--sysroot").arg(sysroot); + } + } + // For host crates or when we are printing, just forward everything. cmd.args(args); } @@ -868,8 +909,17 @@ fn phase_rustc(mut args: env::Args, phase: RustcPhase) { cmd.env("MIRI_BE_RUSTC", if target_crate { "target" } else { "host" }); // Run it. - if verbose { - eprintln!("[cargo-miri rustc] {:?}", cmd); + if verbose > 0 { + eprintln!( + "[cargo-miri rustc] target_crate={target_crate} runnable_crate={runnable_crate} print={print}" + ); + eprintln!("[cargo-miri rustc] going to run:"); + if verbose > 1 { + for (key, value) in env_vars_from_cmd(&cmd) { + eprintln!("{key}={value:?} \\"); + } + } + eprintln!("{:?}", cmd); } exec(cmd); @@ -888,6 +938,23 @@ fn phase_rustc(mut args: env::Args, phase: RustcPhase) { } } +fn env_vars_from_cmd(cmd: &Command) -> Vec<(String, String)> { + let mut envs = HashMap::new(); + for (key, value) in std::env::vars() { + envs.insert(key, value); + } + for (key, value) in cmd.get_envs() { + if let Some(value) = value { + envs.insert(key.to_str().unwrap().into(), value.to_str().unwrap().to_owned()); + } else { + envs.remove(key.to_str().unwrap()); + } + } + let mut envs: Vec<_> = envs.into_iter().collect(); + envs.sort(); + envs +} + #[derive(Debug, Copy, Clone, PartialEq)] enum RunnerPhase { /// `cargo` is running a binary @@ -1119,8 +1186,14 @@ fn main() { match args.next().as_deref() { Some("miri") => phase_cargo_miri(args), - Some("rustc") => phase_rustc(args, RustcPhase::Build), Some(arg) => { + // If the first arg is equal to the RUSTC variable (which should be set at this point), + // then we need to behave as rustc. This is the somewhat counter-intuitive behavior of + // having both RUSTC and RUSTC_WRAPPER set (see + // https://github.com/rust-lang/cargo/issues/10886). + if arg == env::var_os("RUSTC").unwrap() { + return phase_rustc(args, RustcPhase::Build); + } // We have to distinguish the "runner" and "rustdoc" cases. // As runner, the first argument is the binary (a file that should exist, with an absolute path); // as rustdoc, the first argument is a flag (`--something`). diff --git a/miri b/miri index 0964f2a893..463e4607ba 100755 --- a/miri +++ b/miri @@ -48,19 +48,34 @@ Pass extra flags to all cargo invocations. EOF ) -## Preparation +## We need to know where we are. # macOS does not have a useful readlink/realpath so we have to use Python instead... MIRIDIR=$(python3 -c 'import os, sys; print(os.path.dirname(os.path.realpath(sys.argv[1])))' "$0") -TOOLCHAIN=$(cd "$MIRIDIR"; rustup show active-toolchain | head -n 1 | cut -d ' ' -f 1) -# if $CC is not set, use gcc as default -if [[ -z $CC ]]; then - CC=cc +## Run the auto-things. +if [ -z "$MIRI_AUTO_OPS" ]; then + export MIRI_AUTO_OPS=42 + + # Run this first, so that the toolchain doesn't change after + # other code has run. + if [ -f "$MIRIDIR/.auto-everything" ] || [ -f "$MIRIDIR/.auto-toolchain" ] ; then + (cd "$MIRIDIR" && ./rustup-toolchain) + fi + + if [ -f "$MIRIDIR/.auto-everything" ] || [ -f "$MIRIDIR/.auto-fmt" ] ; then + $0 fmt + fi + + if [ -f "$MIRIDIR/.auto-everything" ] || [ -f "$MIRIDIR/.auto-clippy" ] ; then + $0 clippy -- -D warnings + fi fi -# Determine command. +## Determine command and toolchain. COMMAND="$1" [ $# -gt 0 ] && shift +# Doing this *after* auto-toolchain logic above, since that might change the toolchain. +TOOLCHAIN=$(cd "$MIRIDIR"; rustup show active-toolchain | head -n 1 | cut -d ' ' -f 1) ## Handle some commands early, since they should *not* alter the environment. case "$COMMAND" in @@ -101,10 +116,14 @@ fi # Prepare flags for cargo and rustc. CARGO="cargo +$TOOLCHAIN" +# Share target dir between `miri` and `cargo-miri`. if [ -z "$CARGO_TARGET_DIR" ]; then - # Share target dir between `miri` and `cargo-miri`. export CARGO_TARGET_DIR="$MIRIDIR/target" fi +# We configure dev builds to not be unusably slow. +if [ -z "$CARGO_PROFILE_DEV_OPT_LEVEL" ]; then + export CARGO_PROFILE_DEV_OPT_LEVEL=2 +fi # We set the rpath so that Miri finds the private rustc libraries it needs. export RUSTFLAGS="-C link-args=-Wl,-rpath,$LIBDIR $RUSTFLAGS" diff --git a/rust-version b/rust-version index 362bcc35ea..f456ded7f4 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -880416180b0a9ee1141c07d4d17667edb77daebd +a7468c60f8dbf5feb23ad840b174d7e57113a846 diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 4526c3b89a..7778380087 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -1,5 +1,5 @@ #![feature(rustc_private, stmt_expr_attributes)] -#![allow(clippy::manual_range_contains)] +#![allow(clippy::manual_range_contains, clippy::useless_format)] extern crate rustc_data_structures; extern crate rustc_driver; @@ -143,6 +143,11 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { } } +fn show_error(msg: String) -> ! { + eprintln!("fatal error: {}", msg); + std::process::exit(1) +} + fn init_early_loggers() { // Note that our `extern crate log` is *not* the same as rustc's; as a result, we have to // initialize them both, and we always initialize `miri`'s first. @@ -214,13 +219,27 @@ fn compile_time_sysroot() -> Option { let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); Some(match (home, toolchain) { - (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain), - _ => - option_env!("RUST_SYSROOT") - .expect( + (Some(home), Some(toolchain)) => { + // Check that at runtime, we are still in this toolchain (if there is any toolchain). + if let Some(toolchain_runtime) = + env::var_os("RUSTUP_TOOLCHAIN").or_else(|| env::var_os("MULTIRUST_TOOLCHAIN")) + { + if toolchain_runtime != toolchain { + show_error(format!( + "This Miri got built with local toolchain `{toolchain}`, but now is being run under a different toolchain. \n\ + Make sure to run Miri in the toolchain it got built with, e.g. via `cargo +{toolchain} miri`." + )); + } + } + format!("{}/toolchains/{}", home, toolchain) + } + _ => option_env!("RUST_SYSROOT") + .unwrap_or_else(|| { + show_error(format!( "To build Miri without rustup, set the `RUST_SYSROOT` env var at build time", - ) - .to_owned(), + )) + }) + .to_owned(), }) } @@ -328,19 +347,6 @@ fn main() { "WARNING: the flag `-Zmiri-check-number-validity` no longer has any effect \ since it is now enabled by default" ); - } else if arg == "-Zmiri-allow-uninit-numbers" { - eprintln!( - "WARNING: `-Zmiri-allow-uninit-numbers` is deprecated and planned to be removed. \ - Please let us know at if you rely on this flag." - ); - miri_config.allow_uninit_numbers = true; - } else if arg == "-Zmiri-allow-ptr-int-transmute" { - eprintln!( - "WARNING: `-Zmiri-allow-ptr-int-transmute` is deprecated and planned to be removed. \ - Please let us know at if you rely on this flag." - ); - miri_config.allow_ptr_int_transmute = true; - miri_config.provenance_mode = ProvenanceMode::Permissive; } else if arg == "-Zmiri-disable-abi-check" { miri_config.check_abi = false; } else if arg == "-Zmiri-disable-isolation" { @@ -378,7 +384,6 @@ fn main() { eprintln!("WARNING: `-Zmiri-tag-raw-pointers` has no effect; it is enabled by default"); } else if arg == "-Zmiri-strict-provenance" { miri_config.provenance_mode = ProvenanceMode::Strict; - miri_config.allow_ptr_int_transmute = false; } else if arg == "-Zmiri-permissive-provenance" { miri_config.provenance_mode = ProvenanceMode::Permissive; } else if arg == "-Zmiri-mute-stdout-stderr" { diff --git a/src/concurrency/data_race.rs b/src/concurrency/data_race.rs index 4b402b51fc..90b3822510 100644 --- a/src/concurrency/data_race.rs +++ b/src/concurrency/data_race.rs @@ -436,53 +436,14 @@ impl MemoryCellClocks { /// Evaluation context extensions. impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { - /// Temporarily allow data-races to occur. This should only be used in - /// one of these cases: - /// - One of the appropriate `validate_atomic` functions will be called to - /// to treat a memory access as atomic. - /// - The memory being accessed should be treated as internal state, that - /// cannot be accessed by the interpreted program. - /// - Execution of the interpreted program execution has halted. - #[inline] - fn allow_data_races_ref(&self, op: impl FnOnce(&MiriEvalContext<'mir, 'tcx>) -> R) -> R { - let this = self.eval_context_ref(); - if let Some(data_race) = &this.machine.data_race { - data_race.ongoing_action_data_race_free.set(true); - } - let result = op(this); - if let Some(data_race) = &this.machine.data_race { - data_race.ongoing_action_data_race_free.set(false); - } - result - } - - /// Same as `allow_data_races_ref`, this temporarily disables any data-race detection and - /// so should only be used for atomic operations or internal state that the program cannot - /// access. - #[inline] - fn allow_data_races_mut( - &mut self, - op: impl FnOnce(&mut MiriEvalContext<'mir, 'tcx>) -> R, - ) -> R { - let this = self.eval_context_mut(); - if let Some(data_race) = &this.machine.data_race { - data_race.ongoing_action_data_race_free.set(true); - } - let result = op(this); - if let Some(data_race) = &this.machine.data_race { - data_race.ongoing_action_data_race_free.set(false); - } - result - } - /// Atomic variant of read_scalar_at_offset. fn read_scalar_at_offset_atomic( &self, - op: &OpTy<'tcx, Tag>, + op: &OpTy<'tcx, Provenance>, offset: u64, layout: TyAndLayout<'tcx>, atomic: AtomicReadOrd, - ) -> InterpResult<'tcx, ScalarMaybeUninit> { + ) -> InterpResult<'tcx, ScalarMaybeUninit> { let this = self.eval_context_ref(); let value_place = this.deref_operand_and_offset(op, offset, layout)?; this.read_scalar_atomic(&value_place, atomic) @@ -491,9 +452,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { /// Atomic variant of write_scalar_at_offset. fn write_scalar_at_offset_atomic( &mut self, - op: &OpTy<'tcx, Tag>, + op: &OpTy<'tcx, Provenance>, offset: u64, - value: impl Into>, + value: impl Into>, layout: TyAndLayout<'tcx>, atomic: AtomicWriteOrd, ) -> InterpResult<'tcx> { @@ -505,9 +466,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { /// Perform an atomic read operation at the memory location. fn read_scalar_atomic( &self, - place: &MPlaceTy<'tcx, Tag>, + place: &MPlaceTy<'tcx, Provenance>, atomic: AtomicReadOrd, - ) -> InterpResult<'tcx, ScalarMaybeUninit> { + ) -> InterpResult<'tcx, ScalarMaybeUninit> { let this = self.eval_context_ref(); // This will read from the last store in the modification order of this location. In case // weak memory emulation is enabled, this may not be the store we will pick to actually read from and return. @@ -524,8 +485,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { /// Perform an atomic write operation at the memory location. fn write_scalar_atomic( &mut self, - val: ScalarMaybeUninit, - dest: &MPlaceTy<'tcx, Tag>, + val: ScalarMaybeUninit, + dest: &MPlaceTy<'tcx, Provenance>, atomic: AtomicWriteOrd, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -543,12 +504,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { /// Perform an atomic operation on a memory location. fn atomic_op_immediate( &mut self, - place: &MPlaceTy<'tcx, Tag>, - rhs: &ImmTy<'tcx, Tag>, + place: &MPlaceTy<'tcx, Provenance>, + rhs: &ImmTy<'tcx, Provenance>, op: mir::BinOp, neg: bool, atomic: AtomicRwOrd, - ) -> InterpResult<'tcx, ImmTy<'tcx, Tag>> { + ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { let this = self.eval_context_mut(); this.validate_overlapping_atomic(place)?; @@ -574,10 +535,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { /// scalar value, the old value is returned. fn atomic_exchange_scalar( &mut self, - place: &MPlaceTy<'tcx, Tag>, - new: ScalarMaybeUninit, + place: &MPlaceTy<'tcx, Provenance>, + new: ScalarMaybeUninit, atomic: AtomicRwOrd, - ) -> InterpResult<'tcx, ScalarMaybeUninit> { + ) -> InterpResult<'tcx, ScalarMaybeUninit> { let this = self.eval_context_mut(); this.validate_overlapping_atomic(place)?; @@ -594,11 +555,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { /// scalar value, the old value is returned. fn atomic_min_max_scalar( &mut self, - place: &MPlaceTy<'tcx, Tag>, - rhs: ImmTy<'tcx, Tag>, + place: &MPlaceTy<'tcx, Provenance>, + rhs: ImmTy<'tcx, Provenance>, min: bool, atomic: AtomicRwOrd, - ) -> InterpResult<'tcx, ImmTy<'tcx, Tag>> { + ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { let this = self.eval_context_mut(); this.validate_overlapping_atomic(place)?; @@ -634,13 +595,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { /// identical. fn atomic_compare_exchange_scalar( &mut self, - place: &MPlaceTy<'tcx, Tag>, - expect_old: &ImmTy<'tcx, Tag>, - new: ScalarMaybeUninit, + place: &MPlaceTy<'tcx, Provenance>, + expect_old: &ImmTy<'tcx, Provenance>, + new: ScalarMaybeUninit, success: AtomicRwOrd, fail: AtomicReadOrd, can_fail_spuriously: bool, - ) -> InterpResult<'tcx, Immediate> { + ) -> InterpResult<'tcx, Immediate> { use rand::Rng as _; let this = self.eval_context_mut(); @@ -690,7 +651,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { /// associated memory-place and on the current thread. fn validate_atomic_load( &self, - place: &MPlaceTy<'tcx, Tag>, + place: &MPlaceTy<'tcx, Provenance>, atomic: AtomicReadOrd, ) -> InterpResult<'tcx> { let this = self.eval_context_ref(); @@ -713,7 +674,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { /// associated memory-place and on the current thread. fn validate_atomic_store( &mut self, - place: &MPlaceTy<'tcx, Tag>, + place: &MPlaceTy<'tcx, Provenance>, atomic: AtomicWriteOrd, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -736,7 +697,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { /// at the associated memory place and on the current thread. fn validate_atomic_rmw( &mut self, - place: &MPlaceTy<'tcx, Tag>, + place: &MPlaceTy<'tcx, Provenance>, atomic: AtomicRwOrd, ) -> InterpResult<'tcx> { use AtomicRwOrd::*; @@ -1044,10 +1005,49 @@ impl VClockAlloc { impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for MiriEvalContext<'mir, 'tcx> {} trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { + /// Temporarily allow data-races to occur. This should only be used in + /// one of these cases: + /// - One of the appropriate `validate_atomic` functions will be called to + /// to treat a memory access as atomic. + /// - The memory being accessed should be treated as internal state, that + /// cannot be accessed by the interpreted program. + /// - Execution of the interpreted program execution has halted. + #[inline] + fn allow_data_races_ref(&self, op: impl FnOnce(&MiriEvalContext<'mir, 'tcx>) -> R) -> R { + let this = self.eval_context_ref(); + if let Some(data_race) = &this.machine.data_race { + data_race.ongoing_action_data_race_free.set(true); + } + let result = op(this); + if let Some(data_race) = &this.machine.data_race { + data_race.ongoing_action_data_race_free.set(false); + } + result + } + + /// Same as `allow_data_races_ref`, this temporarily disables any data-race detection and + /// so should only be used for atomic operations or internal state that the program cannot + /// access. + #[inline] + fn allow_data_races_mut( + &mut self, + op: impl FnOnce(&mut MiriEvalContext<'mir, 'tcx>) -> R, + ) -> R { + let this = self.eval_context_mut(); + if let Some(data_race) = &this.machine.data_race { + data_race.ongoing_action_data_race_free.set(true); + } + let result = op(this); + if let Some(data_race) = &this.machine.data_race { + data_race.ongoing_action_data_race_free.set(false); + } + result + } + /// Generic atomic operation implementation fn validate_atomic_op( &self, - place: &MPlaceTy<'tcx, Tag>, + place: &MPlaceTy<'tcx, Provenance>, atomic: A, description: &str, mut op: impl FnMut( @@ -1061,7 +1061,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> { if let Some(data_race) = &this.machine.data_race { if data_race.race_detecting() { let size = place.layout.size; - let (alloc_id, base_offset, _tag) = this.ptr_get_alloc_id(place.ptr)?; + let (alloc_id, base_offset, _prov) = this.ptr_get_alloc_id(place.ptr)?; // Load and log the atomic operation. // Note that atomic loads are possible even from read-only allocations, so `get_alloc_extra_mut` is not an option. let alloc_meta = this.get_alloc_extra(alloc_id)?.data_race.as_ref().unwrap(); diff --git a/src/concurrency/weak_memory.rs b/src/concurrency/weak_memory.rs index f7cc9c4732..137a9f43d4 100644 --- a/src/concurrency/weak_memory.rs +++ b/src/concurrency/weak_memory.rs @@ -83,7 +83,8 @@ use rustc_const_eval::interpret::{ use rustc_data_structures::fx::FxHashMap; use crate::{ - AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, Tag, ThreadManager, VClock, VTimestamp, VectorIdx, + AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, Provenance, ThreadManager, VClock, VTimestamp, + VectorIdx, }; use super::{ @@ -127,7 +128,7 @@ struct StoreElement { // FIXME: this means the store is either fully initialized or fully uninitialized; // we will have to change this if we want to support atomics on // partially initialized data. - val: ScalarMaybeUninit, + val: ScalarMaybeUninit, /// Timestamp of first loads from this store element by each thread /// Behind a RefCell to keep load op take &self @@ -174,7 +175,7 @@ impl StoreBufferAlloc { fn get_or_create_store_buffer<'tcx>( &self, range: AllocRange, - init: ScalarMaybeUninit, + init: ScalarMaybeUninit, ) -> InterpResult<'tcx, Ref<'_, StoreBuffer>> { let access_type = self.store_buffers.borrow().access_type(range); let pos = match access_type { @@ -199,7 +200,7 @@ impl StoreBufferAlloc { fn get_or_create_store_buffer_mut<'tcx>( &mut self, range: AllocRange, - init: ScalarMaybeUninit, + init: ScalarMaybeUninit, ) -> InterpResult<'tcx, &mut StoreBuffer> { let buffers = self.store_buffers.get_mut(); let access_type = buffers.access_type(range); @@ -220,7 +221,7 @@ impl StoreBufferAlloc { } impl<'mir, 'tcx: 'mir> StoreBuffer { - fn new(init: ScalarMaybeUninit) -> Self { + fn new(init: ScalarMaybeUninit) -> Self { let mut buffer = VecDeque::new(); buffer.reserve(STORE_BUFFER_LIMIT); let mut ret = Self { buffer }; @@ -253,7 +254,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer { is_seqcst: bool, rng: &mut (impl rand::Rng + ?Sized), validate: impl FnOnce() -> InterpResult<'tcx>, - ) -> InterpResult<'tcx, ScalarMaybeUninit> { + ) -> InterpResult<'tcx, ScalarMaybeUninit> { // Having a live borrow to store_buffer while calling validate_atomic_load is fine // because the race detector doesn't touch store_buffer @@ -278,7 +279,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer { fn buffered_write( &mut self, - val: ScalarMaybeUninit, + val: ScalarMaybeUninit, global: &DataRaceState, thread_mgr: &ThreadManager<'_, '_>, is_seqcst: bool, @@ -366,7 +367,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer { /// ATOMIC STORE IMPL in the paper (except we don't need the location's vector clock) fn store_impl( &mut self, - val: ScalarMaybeUninit, + val: ScalarMaybeUninit, index: VectorIdx, thread_clock: &VClock, is_seqcst: bool, @@ -408,7 +409,11 @@ impl StoreElement { /// buffer regardless of subsequent loads by the same thread; if the earliest load of another /// thread doesn't happen before the current one, then no subsequent load by the other thread /// can happen before the current one. - fn load_impl(&self, index: VectorIdx, clocks: &ThreadClockSet) -> ScalarMaybeUninit { + fn load_impl( + &self, + index: VectorIdx, + clocks: &ThreadClockSet, + ) -> ScalarMaybeUninit { let _ = self.loads.borrow_mut().try_insert(index, clocks.clock[index]); self.val } @@ -421,7 +426,10 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: // If weak memory emulation is enabled, check if this atomic op imperfectly overlaps with a previous // atomic read or write. If it does, then we require it to be ordered (non-racy) with all previous atomic // accesses on all the bytes in range - fn validate_overlapping_atomic(&self, place: &MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { + fn validate_overlapping_atomic( + &self, + place: &MPlaceTy<'tcx, Provenance>, + ) -> InterpResult<'tcx> { let this = self.eval_context_ref(); let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr)?; if let crate::AllocExtra { @@ -448,10 +456,10 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: fn buffered_atomic_rmw( &mut self, - new_val: ScalarMaybeUninit, - place: &MPlaceTy<'tcx, Tag>, + new_val: ScalarMaybeUninit, + place: &MPlaceTy<'tcx, Provenance>, atomic: AtomicRwOrd, - init: ScalarMaybeUninit, + init: ScalarMaybeUninit, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr)?; @@ -474,11 +482,11 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: fn buffered_atomic_read( &self, - place: &MPlaceTy<'tcx, Tag>, + place: &MPlaceTy<'tcx, Provenance>, atomic: AtomicReadOrd, - latest_in_mo: ScalarMaybeUninit, + latest_in_mo: ScalarMaybeUninit, validate: impl FnOnce() -> InterpResult<'tcx>, - ) -> InterpResult<'tcx, ScalarMaybeUninit> { + ) -> InterpResult<'tcx, ScalarMaybeUninit> { let this = self.eval_context_ref(); if let Some(global) = &this.machine.data_race { let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr)?; @@ -510,10 +518,10 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: fn buffered_atomic_write( &mut self, - val: ScalarMaybeUninit, - dest: &MPlaceTy<'tcx, Tag>, + val: ScalarMaybeUninit, + dest: &MPlaceTy<'tcx, Provenance>, atomic: AtomicWriteOrd, - init: ScalarMaybeUninit, + init: ScalarMaybeUninit, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(dest.ptr)?; @@ -555,9 +563,9 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: /// to perform load_impl on the latest store element fn perform_read_on_buffered_latest( &self, - place: &MPlaceTy<'tcx, Tag>, + place: &MPlaceTy<'tcx, Provenance>, atomic: AtomicReadOrd, - init: ScalarMaybeUninit, + init: ScalarMaybeUninit, ) -> InterpResult<'tcx> { let this = self.eval_context_ref(); diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 4dfa7bd07c..6a692059be 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -65,7 +65,7 @@ pub enum NonHaltingDiagnostic { CreatedPointerTag(NonZeroU64, Option<(AllocId, AllocRange)>), /// This `Item` was popped from the borrow stack, either due to an access with the given tag or /// a deallocation when the second argument is `None`. - PoppedPointerTag(Item, Option<(SbTagExtra, AccessKind)>), + PoppedPointerTag(Item, Option<(ProvenanceExtra, AccessKind)>), CreatedCallId(CallId), CreatedAlloc(AllocId, Size, Align, MemoryKind), FreedAlloc(AllocId), @@ -505,4 +505,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } }); } + + /// We had a panic in Miri itself, try to print something useful. + fn handle_ice(&self) { + eprintln!(); + eprintln!( + "Miri caused an ICE during evaluation. Here's the interpreter backtrace at the time of the panic:" + ); + let this = self.eval_context_ref(); + let stacktrace = this.generate_stacktrace(); + report_msg( + this, + DiagLevel::Note, + "the place in the program where the ICE was triggered", + vec![], + vec![], + &stacktrace, + ); + } } diff --git a/src/eval.rs b/src/eval.rs index cd0a7bdcad..69c43b2913 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1,8 +1,11 @@ //! Main evaluator loop and setting up the initial stack frame. +use std::collections::HashSet; use std::ffi::OsStr; use std::iter; +use std::panic::{self, AssertUnwindSafe}; use std::path::PathBuf; +use std::thread; use log::info; @@ -16,8 +19,6 @@ use rustc_target::spec::abi::Abi; use rustc_session::config::EntryFnType; -use std::collections::HashSet; - use crate::*; #[derive(Copy, Clone, Debug, PartialEq)] @@ -78,10 +79,6 @@ pub struct MiriConfig { pub stacked_borrows: bool, /// Controls alignment checking. pub check_alignment: AlignmentCheck, - /// Controls integer and float validity initialization checking. - pub allow_uninit_numbers: bool, - /// Controls how we treat ptr2int and int2ptr transmutes. - pub allow_ptr_int_transmute: bool, /// Controls function [ABI](Abi) checking. pub check_abi: bool, /// Action for an op requiring communication with the host. @@ -138,8 +135,6 @@ impl Default for MiriConfig { validate: true, stacked_borrows: true, check_alignment: AlignmentCheck::Int, - allow_uninit_numbers: false, - allow_ptr_int_transmute: false, check_abi: true, isolated_op: IsolatedOp::Reject(RejectOpWith::Abort), ignore_leaks: false, @@ -175,7 +170,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( entry_id: DefId, entry_type: EntryFnType, config: &MiriConfig, -) -> InterpResult<'tcx, (InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>, MPlaceTy<'tcx, Tag>)> { +) -> InterpResult<'tcx, (InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>, MPlaceTy<'tcx, Provenance>)> { let param_env = ty::ParamEnv::reveal_all(); let layout_cx = LayoutCx { tcx, param_env }; let mut ecx = InterpCx::new( @@ -212,7 +207,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( // Third argument (`argv`): created from `config.args`. let argv = { // Put each argument in memory, collect pointers. - let mut argvs = Vec::>::new(); + let mut argvs = Vec::>::new(); for arg in config.args.iter() { // Make space for `0` terminator. let size = u64::try_from(arg.len()).unwrap().checked_add(1).unwrap(); @@ -337,7 +332,7 @@ pub fn eval_entry<'tcx>( }; // Perform the main execution. - let res: InterpResult<'_, i64> = (|| { + let res: thread::Result> = panic::catch_unwind(AssertUnwindSafe(|| { // Main loop. loop { let info = ecx.preprocess_diagnostics(); @@ -367,15 +362,18 @@ pub fn eval_entry<'tcx>( } let return_code = ecx.read_scalar(&ret_place.into())?.to_machine_isize(&ecx)?; Ok(return_code) - })(); - - // Machine cleanup. - // Execution of the program has halted so any memory access we do here - // cannot produce a real data race. If we do not do something to disable - // data race detection here, some uncommon combination of errors will - // cause a data race to be detected: - // https://github.com/rust-lang/miri/issues/2020 - ecx.allow_data_races_mut(|ecx| EnvVars::cleanup(ecx).unwrap()); + })); + let res = res.unwrap_or_else(|panic_payload| { + ecx.handle_ice(); + panic::resume_unwind(panic_payload) + }); + + // Machine cleanup. Only do this if all threads have terminated; threads that are still running + // might cause data races (https://github.com/rust-lang/miri/issues/2020) or Stacked Borrows + // errors (https://github.com/rust-lang/miri/issues/2396) if we deallocate here. + if ecx.have_all_terminated() { + EnvVars::cleanup(&mut ecx).unwrap(); + } // Process the result. match res { diff --git a/src/helpers.rs b/src/helpers.rs index b650e2ddf2..01fc8e0df3 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -84,7 +84,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// Evaluates the scalar at the specified path. Returns Some(val) /// if the path could be resolved, and None otherwise - fn eval_path_scalar(&self, path: &[&str]) -> InterpResult<'tcx, Scalar> { + fn eval_path_scalar(&self, path: &[&str]) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_ref(); let instance = this.resolve_path(path); let cid = GlobalId { instance, promoted: None }; @@ -94,7 +94,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } /// Helper function to get a `libc` constant as a `Scalar`. - fn eval_libc(&self, name: &str) -> InterpResult<'tcx, Scalar> { + fn eval_libc(&self, name: &str) -> InterpResult<'tcx, Scalar> { self.eval_path_scalar(&["libc", name]) } @@ -105,7 +105,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } /// Helper function to get a `windows` constant as a `Scalar`. - fn eval_windows(&self, module: &str, name: &str) -> InterpResult<'tcx, Scalar> { + fn eval_windows(&self, module: &str, name: &str) -> InterpResult<'tcx, Scalar> { self.eval_context_ref().eval_path_scalar(&["std", "sys", "windows", module, name]) } @@ -134,9 +134,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// Project to the given *named* field of the mplace (which must be a struct or union type). fn mplace_field_named( &self, - mplace: &MPlaceTy<'tcx, Tag>, + mplace: &MPlaceTy<'tcx, Provenance>, name: &str, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, Tag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> { let this = self.eval_context_ref(); let adt = mplace.layout.ty.ty_adt_def().unwrap(); for (idx, field) in adt.non_enum_variant().fields.iter().enumerate() { @@ -150,7 +150,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// Write an int of the appropriate size to `dest`. The target type may be signed or unsigned, /// we try to do the right thing anyway. `i128` can fit all integer types except for `u128` so /// this method is fine for almost all integer types. - fn write_int(&mut self, i: impl Into, dest: &PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { + fn write_int( + &mut self, + i: impl Into, + dest: &PlaceTy<'tcx, Provenance>, + ) -> InterpResult<'tcx> { assert!(dest.layout.abi.is_scalar(), "write_int on non-scalar type {}", dest.layout.ty); let val = if dest.layout.abi.is_signed() { Scalar::from_int(i, dest.layout.size) @@ -164,7 +168,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn write_int_fields( &mut self, values: &[i128], - dest: &MPlaceTy<'tcx, Tag>, + dest: &MPlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); for (idx, &val) in values.iter().enumerate() { @@ -178,7 +182,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn write_int_fields_named( &mut self, values: &[(&str, i128)], - dest: &MPlaceTy<'tcx, Tag>, + dest: &MPlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); for &(name, val) in values.iter() { @@ -189,24 +193,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } /// Write a 0 of the appropriate size to `dest`. - fn write_null(&mut self, dest: &PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { + fn write_null(&mut self, dest: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { self.write_int(0, dest) } /// Test if this pointer equals 0. - fn ptr_is_null(&self, ptr: Pointer>) -> InterpResult<'tcx, bool> { + fn ptr_is_null(&self, ptr: Pointer>) -> InterpResult<'tcx, bool> { Ok(ptr.addr().bytes() == 0) } /// Get the `Place` for a local - fn local_place(&mut self, local: mir::Local) -> InterpResult<'tcx, PlaceTy<'tcx, Tag>> { + fn local_place(&mut self, local: mir::Local) -> InterpResult<'tcx, PlaceTy<'tcx, Provenance>> { let this = self.eval_context_mut(); let place = mir::Place { local, projection: List::empty() }; this.eval_place(place) } /// Generate some random bytes, and write them to `dest`. - fn gen_random(&mut self, ptr: Pointer>, len: u64) -> InterpResult<'tcx> { + fn gen_random(&mut self, ptr: Pointer>, len: u64) -> InterpResult<'tcx> { // Some programs pass in a null pointer and a length of 0 // to their platform's random-generation function (e.g. getrandom()) // on Linux. For compatibility with these programs, we don't perform @@ -240,8 +244,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, f: ty::Instance<'tcx>, caller_abi: Abi, - args: &[Immediate], - dest: Option<&PlaceTy<'tcx, Tag>>, + args: &[Immediate], + dest: Option<&PlaceTy<'tcx, Provenance>>, stack_pop: StackPopCleanup, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -258,7 +262,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Push frame. let mir = this.load_mir(f.def, None)?; let dest = match dest { - Some(dest) => *dest, + Some(dest) => dest.clone(), None => MPlaceTy::fake_alloc_zst(this.layout_of(mir.return_ty())?).into(), }; this.push_stack_frame(f, mir, &dest, stack_pop)?; @@ -285,7 +289,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// The range is relative to `place`. fn visit_freeze_sensitive( &self, - place: &MPlaceTy<'tcx, Tag>, + place: &MPlaceTy<'tcx, Provenance>, size: Size, mut action: impl FnMut(AllocRange, bool) -> InterpResult<'tcx>, ) -> InterpResult<'tcx> { @@ -304,7 +308,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let mut cur_addr = start_addr; // Called when we detected an `UnsafeCell` at the given offset and size. // Calls `action` and advances `cur_ptr`. - let mut unsafe_cell_action = |unsafe_cell_ptr: &Pointer>, + let mut unsafe_cell_action = |unsafe_cell_ptr: &Pointer>, unsafe_cell_size: Size| { // We assume that we are given the fields in increasing offset order, // and nothing else changes. @@ -359,7 +363,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// whether we are inside an `UnsafeCell` or not. struct UnsafeCellVisitor<'ecx, 'mir, 'tcx, F> where - F: FnMut(&MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>, + F: FnMut(&MPlaceTy<'tcx, Provenance>) -> InterpResult<'tcx>, { ecx: &'ecx MiriEvalContext<'mir, 'tcx>, unsafe_cell_action: F, @@ -368,9 +372,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx impl<'ecx, 'mir, 'tcx: 'mir, F> ValueVisitor<'mir, 'tcx, Evaluator<'mir, 'tcx>> for UnsafeCellVisitor<'ecx, 'mir, 'tcx, F> where - F: FnMut(&MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>, + F: FnMut(&MPlaceTy<'tcx, Provenance>) -> InterpResult<'tcx>, { - type V = MPlaceTy<'tcx, Tag>; + type V = MPlaceTy<'tcx, Provenance>; #[inline(always)] fn ecx(&self) -> &MiriEvalContext<'mir, 'tcx> { @@ -378,7 +382,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } // Hook to detect `UnsafeCell`. - fn visit_value(&mut self, v: &MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { + fn visit_value(&mut self, v: &MPlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty); let is_unsafe_cell = match v.layout.ty.kind() { ty::Adt(adt, _) => @@ -421,8 +425,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Make sure we visit aggregrates in increasing offset order. fn visit_aggregate( &mut self, - place: &MPlaceTy<'tcx, Tag>, - fields: impl Iterator>>, + place: &MPlaceTy<'tcx, Provenance>, + fields: impl Iterator>>, ) -> InterpResult<'tcx> { match place.layout.fields { FieldsShape::Array { .. } => { @@ -432,8 +436,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } FieldsShape::Arbitrary { .. } => { // Gather the subplaces and sort them before visiting. - let mut places = - fields.collect::>>>()?; + let mut places = fields + .collect::>>>()?; // we just compare offsets, the abs. value never matters places.sort_by_key(|place| place.ptr.addr()); self.walk_aggregate(place, places.into_iter().map(Ok)) @@ -447,7 +451,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn visit_union( &mut self, - _v: &MPlaceTy<'tcx, Tag>, + _v: &MPlaceTy<'tcx, Provenance>, _fields: NonZeroUsize, ) -> InterpResult<'tcx> { bug!("we should have already handled unions in `visit_value`") @@ -511,7 +515,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// Get last error variable as a place, lazily allocating thread-local storage for it if /// necessary. - fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx, Tag>> { + fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> { let this = self.eval_context_mut(); if let Some(errno_place) = this.active_thread_ref().last_error { Ok(errno_place) @@ -526,14 +530,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } /// Sets the last error variable. - fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> { + fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let errno_place = this.last_error_place()?; this.write_scalar(scalar, &errno_place.into()) } /// Gets the last error variable. - fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { + fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let errno_place = this.last_error_place()?; this.read_scalar(&errno_place.into())?.check_init() @@ -541,7 +545,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// This function tries to produce the most similar OS error from the `std::io::ErrorKind` /// as a platform-specific errnum. - fn io_error_to_errnum(&self, err_kind: std::io::ErrorKind) -> InterpResult<'tcx, Scalar> { + fn io_error_to_errnum( + &self, + err_kind: std::io::ErrorKind, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_ref(); let target = &this.tcx.sess.target; if target.families.iter().any(|f| f == "unix") { @@ -575,7 +582,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } /// The inverse of `io_error_to_errnum`. - fn errnum_to_io_error(&self, errnum: Scalar) -> InterpResult<'tcx, std::io::ErrorKind> { + fn errnum_to_io_error( + &self, + errnum: Scalar, + ) -> InterpResult<'tcx, std::io::ErrorKind> { let this = self.eval_context_ref(); let target = &this.tcx.sess.target; if target.families.iter().any(|f| f == "unix") { @@ -621,10 +631,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// Calculates the MPlaceTy given the offset and layout of an access on an operand fn deref_operand_and_offset( &self, - op: &OpTy<'tcx, Tag>, + op: &OpTy<'tcx, Provenance>, offset: u64, layout: TyAndLayout<'tcx>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, Tag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> { let this = self.eval_context_ref(); let op_place = this.deref_operand(op)?; let offset = Size::from_bytes(offset); @@ -637,10 +647,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn read_scalar_at_offset( &self, - op: &OpTy<'tcx, Tag>, + op: &OpTy<'tcx, Provenance>, offset: u64, layout: TyAndLayout<'tcx>, - ) -> InterpResult<'tcx, ScalarMaybeUninit> { + ) -> InterpResult<'tcx, ScalarMaybeUninit> { let this = self.eval_context_ref(); let value_place = this.deref_operand_and_offset(op, offset, layout)?; this.read_scalar(&value_place.into()) @@ -648,9 +658,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn write_scalar_at_offset( &mut self, - op: &OpTy<'tcx, Tag>, + op: &OpTy<'tcx, Provenance>, offset: u64, - value: impl Into>, + value: impl Into>, layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); @@ -661,7 +671,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// Parse a `timespec` struct and return it as a `std::time::Duration`. It returns `None` /// if the value in the `timespec` struct is invalid. Some libc functions will return /// `EINVAL` in this case. - fn read_timespec(&mut self, tp: &MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx, Option> { + fn read_timespec( + &mut self, + tp: &MPlaceTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Option> { let this = self.eval_context_mut(); let seconds_place = this.mplace_field(tp, 0)?; let seconds_scalar = this.read_scalar(&seconds_place.into())?; @@ -683,7 +696,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx }) } - fn read_c_str<'a>(&'a self, ptr: Pointer>) -> InterpResult<'tcx, &'a [u8]> + fn read_c_str<'a>(&'a self, ptr: Pointer>) -> InterpResult<'tcx, &'a [u8]> where 'tcx: 'a, 'mir: 'a, @@ -709,7 +722,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.read_bytes_ptr(ptr, len) } - fn read_wide_str(&self, mut ptr: Pointer>) -> InterpResult<'tcx, Vec> { + fn read_wide_str(&self, mut ptr: Pointer>) -> InterpResult<'tcx, Vec> { let this = self.eval_context_ref(); let size2 = Size::from_bytes(2); let align2 = Align::from_bytes(2).unwrap(); @@ -801,17 +814,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx abi: Abi, exp_abi: Abi, link_name: Symbol, - args: &'a [OpTy<'tcx, Tag>], - ) -> InterpResult<'tcx, &'a [OpTy<'tcx, Tag>; N]> + args: &'a [OpTy<'tcx, Provenance>], + ) -> InterpResult<'tcx, &'a [OpTy<'tcx, Provenance>; N]> where - &'a [OpTy<'tcx, Tag>; N]: TryFrom<&'a [OpTy<'tcx, Tag>]>, + &'a [OpTy<'tcx, Provenance>; N]: TryFrom<&'a [OpTy<'tcx, Provenance>]>, { self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?; check_arg_count(args) } /// Mark a machine allocation that was just created as immutable. - fn mark_immutable(&mut self, mplace: &MemPlace) { + fn mark_immutable(&mut self, mplace: &MemPlace) { let this = self.eval_context_mut(); // This got just allocated, so there definitely is a pointer here. let provenance = mplace.ptr.into_pointer_or_addr().unwrap().provenance; @@ -866,10 +879,10 @@ impl<'a, 'mir, 'tcx> CurrentSpan<'a, 'mir, 'tcx> { /// Check that the number of args is what we expect. pub fn check_arg_count<'a, 'tcx, const N: usize>( - args: &'a [OpTy<'tcx, Tag>], -) -> InterpResult<'tcx, &'a [OpTy<'tcx, Tag>; N]> + args: &'a [OpTy<'tcx, Provenance>], +) -> InterpResult<'tcx, &'a [OpTy<'tcx, Provenance>; N]> where - &'a [OpTy<'tcx, Tag>; N]: TryFrom<&'a [OpTy<'tcx, Tag>]>, + &'a [OpTy<'tcx, Provenance>; N]: TryFrom<&'a [OpTy<'tcx, Provenance>]>, { if let Ok(ops) = args.try_into() { return Ok(ops); diff --git a/src/intptrcast.rs b/src/intptrcast.rs index e569960f68..442c201e8e 100644 --- a/src/intptrcast.rs +++ b/src/intptrcast.rs @@ -94,38 +94,37 @@ impl<'mir, 'tcx> GlobalStateInner { None } - pub fn expose_ptr(ecx: &mut MiriEvalContext<'mir, 'tcx>, alloc_id: AllocId, sb: SbTag) { + pub fn expose_ptr( + ecx: &mut MiriEvalContext<'mir, 'tcx>, + alloc_id: AllocId, + sb: SbTag, + ) -> InterpResult<'tcx> { let global_state = ecx.machine.intptrcast.get_mut(); // In strict mode, we don't need this, so we can save some cycles by not tracking it. if global_state.provenance_mode != ProvenanceMode::Strict { trace!("Exposing allocation id {alloc_id:?}"); global_state.exposed.insert(alloc_id); if ecx.machine.stacked_borrows.is_some() { - ecx.expose_tag(alloc_id, sb); + ecx.expose_tag(alloc_id, sb)?; } } + Ok(()) } pub fn ptr_from_addr_transmute( - ecx: &MiriEvalContext<'mir, 'tcx>, + _ecx: &MiriEvalContext<'mir, 'tcx>, addr: u64, - ) -> Pointer> { + ) -> Pointer> { trace!("Transmuting {:#x} to a pointer", addr); - let provenance = if ecx.machine.allow_ptr_int_transmute { - // When we allow transmutes, treat them like casts: generating a wildcard pointer. - Some(Tag::Wildcard) - } else { - // Usually, we consider transmuted pointers to be "invalid" (`None` provenance). - None - }; - Pointer::new(provenance, Size::from_bytes(addr)) + // We consider transmuted pointers to be "invalid" (`None` provenance). + Pointer::new(None, Size::from_bytes(addr)) } pub fn ptr_from_addr_cast( ecx: &MiriEvalContext<'mir, 'tcx>, addr: u64, - ) -> InterpResult<'tcx, Pointer>> { + ) -> InterpResult<'tcx, Pointer>> { trace!("Casting {:#x} to a pointer", addr); let global_state = ecx.machine.intptrcast.borrow(); @@ -152,7 +151,7 @@ impl<'mir, 'tcx> GlobalStateInner { } // This is how wildcard pointers are born. - Ok(Pointer::new(Some(Tag::Wildcard), Size::from_bytes(addr))) + Ok(Pointer::new(Some(Provenance::Wildcard), Size::from_bytes(addr))) } fn alloc_base_addr(ecx: &MiriEvalContext<'mir, 'tcx>, alloc_id: AllocId) -> u64 { @@ -214,11 +213,11 @@ impl<'mir, 'tcx> GlobalStateInner { /// access is going. pub fn abs_ptr_to_rel( ecx: &MiriEvalContext<'mir, 'tcx>, - ptr: Pointer, + ptr: Pointer, ) -> Option<(AllocId, Size)> { let (tag, addr) = ptr.into_parts(); // addr is absolute (Tag provenance) - let alloc_id = if let Tag::Concrete { alloc_id, .. } = tag { + let alloc_id = if let Provenance::Concrete { alloc_id, .. } = tag { alloc_id } else { // A wildcard pointer. diff --git a/src/lib.rs b/src/lib.rs index b3d408a6dc..35351664ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,7 +57,7 @@ mod vector_clock; // Make all those symbols available in the same place as our own. pub use rustc_const_eval::interpret::*; // Resolve ambiguity. -pub use rustc_const_eval::interpret::{self, AllocMap, PlaceTy}; +pub use rustc_const_eval::interpret::{self, AllocMap, PlaceTy, Provenance as _}; pub use crate::shims::dlsym::{Dlsym, EvalContextExt as _}; pub use crate::shims::env::{EnvVars, EvalContextExt as _}; @@ -83,15 +83,14 @@ pub use crate::eval::{ pub use crate::helpers::{CurrentSpan, EvalContextExt as HelpersEvalContextExt}; pub use crate::intptrcast::ProvenanceMode; pub use crate::machine::{ - AllocExtra, Evaluator, FrameData, MiriEvalContext, MiriEvalContextExt, MiriMemoryKind, Tag, - NUM_CPUS, PAGE_SIZE, STACK_ADDR, STACK_SIZE, + AllocExtra, Evaluator, FrameData, MiriEvalContext, MiriEvalContextExt, MiriMemoryKind, + Provenance, ProvenanceExtra, NUM_CPUS, PAGE_SIZE, STACK_ADDR, STACK_SIZE, }; pub use crate::mono_hash_map::MonoHashMap; pub use crate::operator::EvalContextExt as OperatorEvalContextExt; pub use crate::range_map::RangeMap; pub use crate::stacked_borrows::{ - CallId, EvalContextExt as StackedBorEvalContextExt, Item, Permission, SbTag, SbTagExtra, Stack, - Stacks, + CallId, EvalContextExt as StackedBorEvalContextExt, Item, Permission, SbTag, Stack, Stacks, }; pub use crate::sync::{CondvarId, EvalContextExt as SyncEvalContextExt, MutexId, RwLockId}; pub use crate::thread::{ diff --git a/src/machine.rs b/src/machine.rs index bf4f997dcd..6e524f3a4a 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -126,9 +126,9 @@ impl fmt::Display for MiriMemoryKind { } } -/// Pointer provenance (tag). +/// Pointer provenance. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Tag { +pub enum Provenance { Concrete { alloc_id: AllocId, /// Stacked Borrows tag. @@ -137,27 +137,34 @@ pub enum Tag { Wildcard, } +/// The "extra" information a pointer has over a regular AllocId. +#[derive(Copy, Clone)] +pub enum ProvenanceExtra { + Concrete(SbTag), + Wildcard, +} + #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(Pointer, 24); +static_assert_size!(Pointer, 24); // FIXME: this would with in 24bytes but layout optimizations are not smart enough // #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -//static_assert_size!(Pointer>, 24); +//static_assert_size!(Pointer>, 24); #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(ScalarMaybeUninit, 32); +static_assert_size!(ScalarMaybeUninit, 32); -impl Provenance for Tag { - /// We use absolute addresses in the `offset` of a `Pointer`. +impl interpret::Provenance for Provenance { + /// We use absolute addresses in the `offset` of a `Pointer`. const OFFSET_IS_ADDR: bool = true; /// We cannot err on partial overwrites, it happens too often in practice (due to unions). const ERR_ON_PARTIAL_PTR_OVERWRITE: bool = false; fn fmt(ptr: &Pointer, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let (tag, addr) = ptr.into_parts(); // address is absolute + let (prov, addr) = ptr.into_parts(); // address is absolute write!(f, "{:#x}", addr.bytes())?; - match tag { - Tag::Concrete { alloc_id, sb } => { + match prov { + Provenance::Concrete { alloc_id, sb } => { // Forward `alternate` flag to `alloc_id` printing. if f.alternate() { write!(f, "[{:#?}]", alloc_id)?; @@ -167,7 +174,7 @@ impl Provenance for Tag { // Print Stacked Borrows tag. write!(f, "{:?}", sb)?; } - Tag::Wildcard => { + Provenance::Wildcard => { write!(f, "[wildcard]")?; } } @@ -177,8 +184,26 @@ impl Provenance for Tag { fn get_alloc_id(self) -> Option { match self { - Tag::Concrete { alloc_id, .. } => Some(alloc_id), - Tag::Wildcard => None, + Provenance::Concrete { alloc_id, .. } => Some(alloc_id), + Provenance::Wildcard => None, + } + } +} + +impl fmt::Debug for ProvenanceExtra { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ProvenanceExtra::Concrete(pid) => write!(f, "{pid:?}"), + ProvenanceExtra::Wildcard => write!(f, ""), + } + } +} + +impl ProvenanceExtra { + pub fn and_then(self, f: impl FnOnce(SbTag) -> Option) -> Option { + match self { + ProvenanceExtra::Concrete(pid) => f(pid), + ProvenanceExtra::Wildcard => None, } } } @@ -244,9 +269,9 @@ pub struct Evaluator<'mir, 'tcx> { /// Program arguments (`Option` because we can only initialize them after creating the ecx). /// These are *pointers* to argc/argv because macOS. /// We also need the full command line as one string because of Windows. - pub(crate) argc: Option>, - pub(crate) argv: Option>, - pub(crate) cmd_line: Option>, + pub(crate) argc: Option>, + pub(crate) argv: Option>, + pub(crate) cmd_line: Option>, /// TLS state. pub(crate) tls: TlsData<'tcx>, @@ -259,13 +284,6 @@ pub struct Evaluator<'mir, 'tcx> { /// Whether to enforce the validity invariant. pub(crate) validate: bool, - /// Whether to allow uninitialized numbers (integers and floats). - pub(crate) allow_uninit_numbers: bool, - - /// Whether to allow ptr2int transmutes, and whether to allow *dereferencing* the result of an - /// int2ptr transmute. - pub(crate) allow_ptr_int_transmute: bool, - /// Whether to enforce [ABI](Abi) of function calls. pub(crate) enforce_abi: bool, @@ -309,7 +327,7 @@ pub struct Evaluator<'mir, 'tcx> { pub(crate) local_crates: Vec, /// Mapping extern static names to their base pointer. - extern_statics: FxHashMap>, + extern_statics: FxHashMap>, /// The random number generator used for resolving non-determinism. /// Needs to be queried by ptr_to_int, hence needs interior mutability. @@ -375,8 +393,6 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> { tls: TlsData::default(), isolated_op: config.isolated_op, validate: config.validate, - allow_uninit_numbers: config.allow_uninit_numbers, - allow_ptr_int_transmute: config.allow_ptr_int_transmute, enforce_abi: config.check_abi, file_handler: FileHandler::new(config.mute_stdout_stderr), dir_handler: Default::default(), @@ -427,7 +443,7 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> { fn add_extern_static( this: &mut MiriEvalContext<'mir, 'tcx>, name: &str, - ptr: Pointer>, + ptr: Pointer>, ) { // This got just allocated, so there definitely is a pointer here. let ptr = ptr.into_pointer_or_addr().unwrap(); @@ -515,11 +531,13 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { type FrameExtra = FrameData<'tcx>; type AllocExtra = AllocExtra; - type PointerTag = Tag; - type TagExtra = SbTagExtra; + type Provenance = Provenance; + type ProvenanceExtra = ProvenanceExtra; - type MemoryMap = - MonoHashMap, Allocation)>; + type MemoryMap = MonoHashMap< + AllocId, + (MemoryKind, Allocation), + >; const GLOBAL_KIND: Option = Some(MiriMemoryKind::Global); @@ -541,13 +559,13 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { } #[inline(always)] - fn enforce_number_init(ecx: &MiriEvalContext<'mir, 'tcx>) -> bool { - !ecx.machine.allow_uninit_numbers + fn enforce_number_init(_ecx: &MiriEvalContext<'mir, 'tcx>) -> bool { + true } #[inline(always)] - fn enforce_number_no_provenance(ecx: &MiriEvalContext<'mir, 'tcx>) -> bool { - !ecx.machine.allow_ptr_int_transmute + fn enforce_number_no_provenance(_ecx: &MiriEvalContext<'mir, 'tcx>) -> bool { + true } #[inline(always)] @@ -565,8 +583,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { ecx: &mut MiriEvalContext<'mir, 'tcx>, instance: ty::Instance<'tcx>, abi: Abi, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ret: Option, unwind: StackPopUnwind, ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> { @@ -578,8 +596,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { ecx: &mut MiriEvalContext<'mir, 'tcx>, fn_val: Dlsym, abi: Abi, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ret: Option, _unwind: StackPopUnwind, ) -> InterpResult<'tcx> { @@ -590,8 +608,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { fn call_intrinsic( ecx: &mut MiriEvalContext<'mir, 'tcx>, instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ret: Option, unwind: StackPopUnwind, ) -> InterpResult<'tcx> { @@ -616,41 +634,64 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { fn binary_ptr_op( ecx: &MiriEvalContext<'mir, 'tcx>, bin_op: mir::BinOp, - left: &ImmTy<'tcx, Tag>, - right: &ImmTy<'tcx, Tag>, - ) -> InterpResult<'tcx, (Scalar, bool, ty::Ty<'tcx>)> { + left: &ImmTy<'tcx, Provenance>, + right: &ImmTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, (Scalar, bool, ty::Ty<'tcx>)> { ecx.binary_ptr_op(bin_op, left, right) } fn thread_local_static_base_pointer( ecx: &mut MiriEvalContext<'mir, 'tcx>, def_id: DefId, - ) -> InterpResult<'tcx, Pointer> { + ) -> InterpResult<'tcx, Pointer> { ecx.get_or_create_thread_local_alloc(def_id) } fn extern_static_base_pointer( ecx: &MiriEvalContext<'mir, 'tcx>, def_id: DefId, - ) -> InterpResult<'tcx, Pointer> { + ) -> InterpResult<'tcx, Pointer> { let link_name = ecx.item_link_name(def_id); if let Some(&ptr) = ecx.machine.extern_statics.get(&link_name) { + // Various parts of the engine rely on `get_alloc_info` for size and alignment + // information. That uses the type information of this static. + // Make sure it matches the Miri allocation for this. + let Provenance::Concrete { alloc_id, .. } = ptr.provenance else { + panic!("extern_statics cannot contain wildcards") + }; + let (shim_size, shim_align, _kind) = ecx.get_alloc_info(alloc_id); + let extern_decl_layout = + ecx.tcx.layout_of(ty::ParamEnv::empty().and(ecx.tcx.type_of(def_id))).unwrap(); + if extern_decl_layout.size != shim_size || extern_decl_layout.align.abi != shim_align { + throw_unsup_format!( + "`extern` static `{name}` from crate `{krate}` has been declared \ + with a size of {decl_size} bytes and alignment of {decl_align} bytes, \ + but Miri emulates it via an extern static shim \ + with a size of {shim_size} bytes and alignment of {shim_align} bytes", + name = ecx.tcx.def_path_str(def_id), + krate = ecx.tcx.crate_name(def_id.krate), + decl_size = extern_decl_layout.size.bytes(), + decl_align = extern_decl_layout.align.abi.bytes(), + shim_size = shim_size.bytes(), + shim_align = shim_align.bytes(), + ) + } Ok(ptr) } else { throw_unsup_format!( - "`extern` static `{}` from crate `{}` is not supported by Miri", - ecx.tcx.def_path_str(def_id), - ecx.tcx.crate_name(def_id.krate), + "`extern` static `{name}` from crate `{krate}` is not supported by Miri", + name = ecx.tcx.def_path_str(def_id), + krate = ecx.tcx.crate_name(def_id.krate), ) } } - fn init_allocation_extra<'b>( + fn adjust_allocation<'b>( ecx: &MiriEvalContext<'mir, 'tcx>, id: AllocId, alloc: Cow<'b, Allocation>, kind: Option>, - ) -> InterpResult<'tcx, Cow<'b, Allocation>> { + ) -> InterpResult<'tcx, Cow<'b, Allocation>> { let kind = kind.expect("we set our STATIC_KIND so this cannot be None"); if ecx.machine.tracked_alloc_ids.contains(&id) { register_diagnostic(NonHaltingDiagnostic::CreatedAlloc( @@ -688,7 +729,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { } else { None }; - let alloc: Allocation = alloc.convert_tag_add_extra( + let alloc: Allocation = alloc.adjust_from_tcx( &ecx.tcx, AllocExtra { stacked_borrows: stacks.map(RefCell::new), @@ -700,19 +741,19 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { Ok(Cow::Owned(alloc)) } - fn tag_alloc_base_pointer( + fn adjust_alloc_base_pointer( ecx: &MiriEvalContext<'mir, 'tcx>, ptr: Pointer, - ) -> Pointer { + ) -> Pointer { if cfg!(debug_assertions) { // The machine promises to never call us on thread-local or extern statics. let alloc_id = ptr.provenance; match ecx.tcx.get_global_alloc(alloc_id) { Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_thread_local_static(def_id) => { - panic!("tag_alloc_base_pointer called on thread-local static") + panic!("adjust_alloc_base_pointer called on thread-local static") } Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_foreign_item(def_id) => { - panic!("tag_alloc_base_pointer called on extern static") + panic!("adjust_alloc_base_pointer called on extern static") } _ => {} } @@ -725,7 +766,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { SbTag::default() }; Pointer::new( - Tag::Concrete { alloc_id: ptr.provenance, sb: sb_tag }, + Provenance::Concrete { alloc_id: ptr.provenance, sb: sb_tag }, Size::from_bytes(absolute_addr), ) } @@ -734,7 +775,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { fn ptr_from_addr_cast( ecx: &MiriEvalContext<'mir, 'tcx>, addr: u64, - ) -> InterpResult<'tcx, Pointer>> { + ) -> InterpResult<'tcx, Pointer>> { intptrcast::GlobalStateInner::ptr_from_addr_cast(ecx, addr) } @@ -742,38 +783,37 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { fn ptr_from_addr_transmute( ecx: &MiriEvalContext<'mir, 'tcx>, addr: u64, - ) -> Pointer> { + ) -> Pointer> { intptrcast::GlobalStateInner::ptr_from_addr_transmute(ecx, addr) } fn expose_ptr( ecx: &mut InterpCx<'mir, 'tcx, Self>, - ptr: Pointer, + ptr: Pointer, ) -> InterpResult<'tcx> { match ptr.provenance { - Tag::Concrete { alloc_id, sb } => { - intptrcast::GlobalStateInner::expose_ptr(ecx, alloc_id, sb); - } - Tag::Wildcard => { + Provenance::Concrete { alloc_id, sb } => + intptrcast::GlobalStateInner::expose_ptr(ecx, alloc_id, sb), + Provenance::Wildcard => { // No need to do anything for wildcard pointers as // their provenances have already been previously exposed. + Ok(()) } } - Ok(()) } /// Convert a pointer with provenance into an allocation-offset pair, /// or a `None` with an absolute address if that conversion is not possible. fn ptr_get_alloc( ecx: &MiriEvalContext<'mir, 'tcx>, - ptr: Pointer, - ) -> Option<(AllocId, Size, Self::TagExtra)> { + ptr: Pointer, + ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> { let rel = intptrcast::GlobalStateInner::abs_ptr_to_rel(ecx, ptr); rel.map(|(alloc_id, size)| { let sb = match ptr.provenance { - Tag::Concrete { sb, .. } => SbTagExtra::Concrete(sb), - Tag::Wildcard => SbTagExtra::Wildcard, + Provenance::Concrete { sb, .. } => ProvenanceExtra::Concrete(sb), + Provenance::Wildcard => ProvenanceExtra::Wildcard, }; (alloc_id, size, sb) }) @@ -784,7 +824,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { _tcx: TyCtxt<'tcx>, machine: &Self, alloc_extra: &AllocExtra, - (alloc_id, tag): (AllocId, Self::TagExtra), + (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra), range: AllocRange, ) -> InterpResult<'tcx> { if let Some(data_race) = &alloc_extra.data_race { @@ -798,7 +838,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { if let Some(stacked_borrows) = &alloc_extra.stacked_borrows { stacked_borrows.borrow_mut().memory_read( alloc_id, - tag, + prov_extra, range, machine.stacked_borrows.as_ref().unwrap(), machine.current_span(), @@ -816,7 +856,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { _tcx: TyCtxt<'tcx>, machine: &mut Self, alloc_extra: &mut AllocExtra, - (alloc_id, tag): (AllocId, Self::TagExtra), + (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra), range: AllocRange, ) -> InterpResult<'tcx> { if let Some(data_race) = &mut alloc_extra.data_race { @@ -830,7 +870,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows { stacked_borrows.get_mut().memory_written( alloc_id, - tag, + prov_extra, range, machine.stacked_borrows.as_ref().unwrap(), machine.current_span(), @@ -848,7 +888,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { _tcx: TyCtxt<'tcx>, machine: &mut Self, alloc_extra: &mut AllocExtra, - (alloc_id, tag): (AllocId, Self::TagExtra), + (alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra), range: AllocRange, ) -> InterpResult<'tcx> { if machine.tracked_alloc_ids.contains(&alloc_id) { @@ -865,7 +905,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows { stacked_borrows.get_mut().memory_deallocated( alloc_id, - tag, + prove_extra, range, machine.stacked_borrows.as_ref().unwrap(), &machine.threads, @@ -879,7 +919,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { fn retag( ecx: &mut InterpCx<'mir, 'tcx, Self>, kind: mir::RetagKind, - place: &PlaceTy<'tcx, Tag>, + place: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { if ecx.machine.stacked_borrows.is_some() { ecx.retag(kind, place) } else { Ok(()) } } @@ -887,8 +927,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { #[inline(always)] fn init_frame_extra( ecx: &mut InterpCx<'mir, 'tcx, Self>, - frame: Frame<'mir, 'tcx, Tag>, - ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Tag, FrameData<'tcx>>> { + frame: Frame<'mir, 'tcx, Provenance>, + ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>> { // Start recording our event before doing anything else let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() { let fn_name = frame.instance.to_string(); @@ -916,13 +956,13 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { fn stack<'a>( ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] { + ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] { ecx.active_thread_stack() } fn stack_mut<'a>( ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec> { + ) -> &'a mut Vec> { ecx.active_thread_stack_mut() } @@ -949,7 +989,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { #[inline(always)] fn after_stack_pop( ecx: &mut InterpCx<'mir, 'tcx, Self>, - mut frame: Frame<'mir, 'tcx, Tag, FrameData<'tcx>>, + mut frame: Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>, unwinding: bool, ) -> InterpResult<'tcx, StackPopJump> { let timing = frame.extra.timing.take(); diff --git a/src/operator.rs b/src/operator.rs index 2c77830a6d..758e747d27 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -9,18 +9,18 @@ pub trait EvalContextExt<'tcx> { fn binary_ptr_op( &self, bin_op: mir::BinOp, - left: &ImmTy<'tcx, Tag>, - right: &ImmTy<'tcx, Tag>, - ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)>; + left: &ImmTy<'tcx, Provenance>, + right: &ImmTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)>; } impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> { fn binary_ptr_op( &self, bin_op: mir::BinOp, - left: &ImmTy<'tcx, Tag>, - right: &ImmTy<'tcx, Tag>, - ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> { + left: &ImmTy<'tcx, Provenance>, + right: &ImmTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> { use rustc_middle::mir::BinOp::*; trace!("ptr_op: {:?} {:?} {:?}", *left, bin_op, *right); diff --git a/src/shims/backtrace.rs b/src/shims/backtrace.rs index 5b39f2a48a..c5aab255aa 100644 --- a/src/shims/backtrace.rs +++ b/src/shims/backtrace.rs @@ -11,8 +11,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, abi: Abi, link_name: Symbol, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let [flags] = this.check_shim(abi, Abi::Rust, link_name, args)?; @@ -31,8 +31,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, abi: Abi, link_name: Symbol, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let tcx = this.tcx; @@ -117,13 +117,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn resolve_frame_pointer( &mut self, - ptr: &OpTy<'tcx, Tag>, + ptr: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, (Instance<'tcx>, Loc, String, String)> { let this = self.eval_context_mut(); let ptr = this.read_pointer(ptr)?; // Take apart the pointer, we need its pieces. - let (alloc_id, offset, _tag) = this.ptr_get_alloc_id(ptr)?; + let (alloc_id, offset, _prov) = this.ptr_get_alloc_id(ptr)?; let fn_instance = if let Some(GlobalAlloc::Function(instance)) = this.tcx.get_global_alloc(alloc_id) { @@ -145,8 +145,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, abi: Abi, link_name: Symbol, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let [ptr, flags] = this.check_shim(abi, Abi::Rust, link_name, args)?; @@ -228,7 +228,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, abi: Abi, link_name: Symbol, - args: &[OpTy<'tcx, Tag>], + args: &[OpTy<'tcx, Provenance>], ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); diff --git a/src/shims/dlsym.rs b/src/shims/dlsym.rs index 499e9f8a20..5cb3c99e3d 100644 --- a/src/shims/dlsym.rs +++ b/src/shims/dlsym.rs @@ -33,8 +33,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, dlsym: Dlsym, abi: Abi, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ret: Option, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); diff --git a/src/shims/env.rs b/src/shims/env.rs index f4aaeea2c1..f0818e71b6 100644 --- a/src/shims/env.rs +++ b/src/shims/env.rs @@ -30,10 +30,10 @@ fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 { pub struct EnvVars<'tcx> { /// Stores pointers to the environment variables. These variables must be stored as /// null-terminated target strings (c_str or wide_str) with the `"{name}={value}"` format. - map: FxHashMap>>, + map: FxHashMap>>, /// Place where the `environ` static is stored. Lazily initialized, but then never changes. - pub(crate) environ: Option>, + pub(crate) environ: Option>, } impl<'tcx> EnvVars<'tcx> { @@ -92,7 +92,7 @@ fn alloc_env_var_as_c_str<'mir, 'tcx>( name: &OsStr, value: &OsStr, ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>, -) -> InterpResult<'tcx, Pointer>> { +) -> InterpResult<'tcx, Pointer>> { let mut name_osstring = name.to_os_string(); name_osstring.push("="); name_osstring.push(value); @@ -103,7 +103,7 @@ fn alloc_env_var_as_wide_str<'mir, 'tcx>( name: &OsStr, value: &OsStr, ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>, -) -> InterpResult<'tcx, Pointer>> { +) -> InterpResult<'tcx, Pointer>> { let mut name_osstring = name.to_os_string(); name_osstring.push("="); name_osstring.push(value); @@ -112,7 +112,10 @@ fn alloc_env_var_as_wide_str<'mir, 'tcx>( impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { - fn getenv(&mut self, name_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, Pointer>> { + fn getenv( + &mut self, + name_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("getenv"); @@ -133,9 +136,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx #[allow(non_snake_case)] fn GetEnvironmentVariableW( &mut self, - name_op: &OpTy<'tcx, Tag>, // LPCWSTR - buf_op: &OpTy<'tcx, Tag>, // LPWSTR - size_op: &OpTy<'tcx, Tag>, // DWORD + name_op: &OpTy<'tcx, Provenance>, // LPCWSTR + buf_op: &OpTy<'tcx, Provenance>, // LPWSTR + size_op: &OpTy<'tcx, Provenance>, // DWORD ) -> InterpResult<'tcx, u32> { // ^ Returns DWORD (u32 on Windows) @@ -168,7 +171,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } #[allow(non_snake_case)] - fn GetEnvironmentStringsW(&mut self) -> InterpResult<'tcx, Pointer>> { + fn GetEnvironmentStringsW(&mut self) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); this.assert_target_os("windows", "GetEnvironmentStringsW"); @@ -191,7 +194,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx #[allow(non_snake_case)] fn FreeEnvironmentStringsW( &mut self, - env_block_op: &OpTy<'tcx, Tag>, + env_block_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); this.assert_target_os("windows", "FreeEnvironmentStringsW"); @@ -204,8 +207,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn setenv( &mut self, - name_op: &OpTy<'tcx, Tag>, - value_op: &OpTy<'tcx, Tag>, + name_op: &OpTy<'tcx, Provenance>, + value_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("setenv"); @@ -239,8 +242,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx #[allow(non_snake_case)] fn SetEnvironmentVariableW( &mut self, - name_op: &OpTy<'tcx, Tag>, // LPCWSTR - value_op: &OpTy<'tcx, Tag>, // LPCWSTR + name_op: &OpTy<'tcx, Provenance>, // LPCWSTR + value_op: &OpTy<'tcx, Provenance>, // LPCWSTR ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); this.assert_target_os("windows", "SetEnvironmentVariableW"); @@ -276,7 +279,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn unsetenv(&mut self, name_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn unsetenv(&mut self, name_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("unsetenv"); @@ -304,9 +307,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn getcwd( &mut self, - buf_op: &OpTy<'tcx, Tag>, - size_op: &OpTy<'tcx, Tag>, - ) -> InterpResult<'tcx, Pointer>> { + buf_op: &OpTy<'tcx, Provenance>, + size_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("getcwd"); @@ -337,8 +340,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx #[allow(non_snake_case)] fn GetCurrentDirectoryW( &mut self, - size_op: &OpTy<'tcx, Tag>, // DWORD - buf_op: &OpTy<'tcx, Tag>, // LPTSTR + size_op: &OpTy<'tcx, Provenance>, // DWORD + buf_op: &OpTy<'tcx, Provenance>, // LPTSTR ) -> InterpResult<'tcx, u32> { let this = self.eval_context_mut(); this.assert_target_os("windows", "GetCurrentDirectoryW"); @@ -361,7 +364,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(0) } - fn chdir(&mut self, path_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn chdir(&mut self, path_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); this.assert_target_os_is_unix("chdir"); @@ -386,7 +389,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx #[allow(non_snake_case)] fn SetCurrentDirectoryW( &mut self, - path_op: &OpTy<'tcx, Tag>, // LPCTSTR + path_op: &OpTy<'tcx, Provenance>, // LPCTSTR ) -> InterpResult<'tcx, i32> { // ^ Returns BOOL (i32 on Windows) @@ -428,7 +431,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } // Collect all the pointers to each variable in a vector. - let mut vars: Vec>> = + let mut vars: Vec>> = this.machine.env_vars.map.values().copied().collect(); // Add the trailing null pointer. vars.push(Pointer::null()); diff --git a/src/shims/ffi_support.rs b/src/shims/ffi_support.rs index caeecae220..f5510c3569 100644 --- a/src/shims/ffi_support.rs +++ b/src/shims/ffi_support.rs @@ -13,7 +13,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// Extract the scalar value from the result of reading a scalar from the machine, /// and convert it to a `CArg`. fn scalar_to_carg( - k: ScalarMaybeUninit, + k: ScalarMaybeUninit, arg_type: &Ty<'tcx>, cx: &impl HasDataLayout, ) -> InterpResult<'tcx, CArg> { @@ -66,7 +66,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn call_external_c_and_store_return<'a>( &mut self, link_name: Symbol, - dest: &PlaceTy<'tcx, Tag>, + dest: &PlaceTy<'tcx, Provenance>, ptr: CodePtr, libffi_args: Vec>, ) -> InterpResult<'tcx, ()> { @@ -156,7 +156,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn get_func_ptr_explicitly_from_lib(&mut self, link_name: Symbol) -> Option { let this = self.eval_context_mut(); // Try getting the function from the shared library. - let (lib, lib_path) = this.machine.external_so_lib.as_ref().unwrap(); + // On windows `_lib_path` will be unused, hence the name starting with `_`. + let (lib, _lib_path) = this.machine.external_so_lib.as_ref().unwrap(); let func: libloading::Symbol<'_, unsafe extern "C" fn()> = unsafe { match lib.get(link_name.as_str().as_bytes()) { Ok(x) => x, @@ -177,11 +178,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // This code is a reimplementation of the mechanism for getting `dli_fname` in `libloading`, // from: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#411 // using the `libc` crate where this interface is public. + // No `libc::dladdr` on windows. + #[cfg(unix)] let mut info = std::mem::MaybeUninit::::uninit(); + #[cfg(unix)] unsafe { if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 { if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap() - != lib_path.to_str().unwrap() + != _lib_path.to_str().unwrap() { return None; } @@ -199,8 +203,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn call_and_add_external_c_fct_to_context( &mut self, link_name: Symbol, - dest: &PlaceTy<'tcx, Tag>, - args: &[OpTy<'tcx, Tag>], + dest: &PlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx, Provenance>], ) -> InterpResult<'tcx, bool> { // Get the pointer to the function in the shared object file if it exists. let code_ptr = match self.get_func_ptr_explicitly_from_lib(link_name) { diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 278b21abfd..e3200e51a6 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -75,7 +75,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx size: u64, zero_init: bool, kind: MiriMemoryKind, - ) -> InterpResult<'tcx, Pointer>> { + ) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); if size == 0 { Ok(Pointer::null()) @@ -90,7 +90,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn free(&mut self, ptr: Pointer>, kind: MiriMemoryKind) -> InterpResult<'tcx> { + fn free( + &mut self, + ptr: Pointer>, + kind: MiriMemoryKind, + ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); if !this.ptr_is_null(ptr)? { this.deallocate_ptr(ptr, None, kind.into())?; @@ -100,10 +104,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn realloc( &mut self, - old_ptr: Pointer>, + old_ptr: Pointer>, new_size: u64, kind: MiriMemoryKind, - ) -> InterpResult<'tcx, Pointer>> { + ) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); let new_align = this.min_align(new_size, kind); if this.ptr_is_null(old_ptr)? { @@ -232,8 +236,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, def_id: DefId, abi: Abi, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ret: Option, unwind: StackPopUnwind, ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> { @@ -354,8 +358,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> { let this = self.eval_context_mut(); diff --git a/src/shims/intrinsics/atomic.rs b/src/shims/intrinsics/atomic.rs index 78e13a498c..8e0bb746e3 100644 --- a/src/shims/intrinsics/atomic.rs +++ b/src/shims/intrinsics/atomic.rs @@ -18,8 +18,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn emulate_atomic_intrinsic( &mut self, intrinsic_name: &str, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -121,8 +121,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn atomic_load( &mut self, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, atomic: AtomicReadOrd, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -150,7 +150,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn atomic_store( &mut self, - args: &[OpTy<'tcx, Tag>], + args: &[OpTy<'tcx, Provenance>], atomic: AtomicWriteOrd, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -177,7 +177,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn compiler_fence( &mut self, - args: &[OpTy<'tcx, Tag>], + args: &[OpTy<'tcx, Provenance>], atomic: AtomicFenceOrd, ) -> InterpResult<'tcx> { let [] = check_arg_count(args)?; @@ -188,7 +188,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn atomic_fence( &mut self, - args: &[OpTy<'tcx, Tag>], + args: &[OpTy<'tcx, Provenance>], atomic: AtomicFenceOrd, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -199,8 +199,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn atomic_op( &mut self, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, atomic_op: AtomicOp, atomic: AtomicRwOrd, ) -> InterpResult<'tcx> { @@ -252,8 +252,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn atomic_exchange( &mut self, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, atomic: AtomicRwOrd, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -280,8 +280,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn atomic_compare_exchange_impl( &mut self, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, success: AtomicRwOrd, fail: AtomicReadOrd, can_fail_spuriously: bool, @@ -320,8 +320,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn atomic_compare_exchange( &mut self, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, success: AtomicRwOrd, fail: AtomicReadOrd, ) -> InterpResult<'tcx> { @@ -330,8 +330,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn atomic_compare_exchange_weak( &mut self, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, success: AtomicRwOrd, fail: AtomicReadOrd, ) -> InterpResult<'tcx> { diff --git a/src/shims/intrinsics/mod.rs b/src/shims/intrinsics/mod.rs index 9ffa40f333..c07ed8b294 100644 --- a/src/shims/intrinsics/mod.rs +++ b/src/shims/intrinsics/mod.rs @@ -20,8 +20,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn call_intrinsic( &mut self, instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ret: Option, _unwind: StackPopUnwind, ) -> InterpResult<'tcx> { @@ -58,8 +58,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn emulate_intrinsic_by_name( &mut self, intrinsic_name: &str, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -224,7 +224,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "frem_fast" => mir::BinOp::Rem, _ => bug!(), }; - let float_finite = |x: ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> { + let float_finite = |x: &ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> { Ok(match x.layout.ty.kind() { ty::Float(FloatTy::F32) => x.to_scalar()?.to_f32()?.is_finite(), ty::Float(FloatTy::F64) => x.to_scalar()?.to_f64()?.is_finite(), @@ -234,7 +234,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ), }) }; - match (float_finite(a)?, float_finite(b)?) { + match (float_finite(&a)?, float_finite(&b)?) { (false, false) => throw_ub_format!( "`{intrinsic_name}` intrinsic called with non-finite value as both parameters", ), @@ -375,9 +375,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &self, f: F, dest_ty: ty::Ty<'tcx>, - ) -> InterpResult<'tcx, Scalar> + ) -> InterpResult<'tcx, Scalar> where - F: Float + Into>, + F: Float + Into>, { let this = self.eval_context_ref(); diff --git a/src/shims/intrinsics/simd.rs b/src/shims/intrinsics/simd.rs index fe5250ed08..96e97d7935 100644 --- a/src/shims/intrinsics/simd.rs +++ b/src/shims/intrinsics/simd.rs @@ -12,8 +12,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn emulate_simd_intrinsic( &mut self, intrinsic_name: &str, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); match intrinsic_name { @@ -557,13 +557,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } -fn bool_to_simd_element(b: bool, size: Size) -> Scalar { +fn bool_to_simd_element(b: bool, size: Size) -> Scalar { // SIMD uses all-1 as pattern for "true" let val = if b { -1 } else { 0 }; Scalar::from_int(val, size) } -fn simd_element_to_bool(elem: ImmTy<'_, Tag>) -> InterpResult<'_, bool> { +fn simd_element_to_bool(elem: ImmTy<'_, Provenance>) -> InterpResult<'_, bool> { let val = elem.to_scalar()?.to_int(elem.layout.size)?; Ok(match val { 0 => false, @@ -581,9 +581,9 @@ fn simd_bitmask_index(idx: u64, vec_len: u64, endianess: Endian) -> u64 { } fn fmax_op<'tcx>( - left: &ImmTy<'tcx, Tag>, - right: &ImmTy<'tcx, Tag>, -) -> InterpResult<'tcx, Scalar> { + left: &ImmTy<'tcx, Provenance>, + right: &ImmTy<'tcx, Provenance>, +) -> InterpResult<'tcx, Scalar> { assert_eq!(left.layout.ty, right.layout.ty); let ty::Float(float_ty) = left.layout.ty.kind() else { bug!("fmax operand is not a float") @@ -597,9 +597,9 @@ fn fmax_op<'tcx>( } fn fmin_op<'tcx>( - left: &ImmTy<'tcx, Tag>, - right: &ImmTy<'tcx, Tag>, -) -> InterpResult<'tcx, Scalar> { + left: &ImmTy<'tcx, Provenance>, + right: &ImmTy<'tcx, Provenance>, +) -> InterpResult<'tcx, Scalar> { assert_eq!(left.layout.ty, right.layout.ty); let ty::Float(float_ty) = left.layout.ty.kind() else { bug!("fmin operand is not a float") diff --git a/src/shims/mod.rs b/src/shims/mod.rs index 9bd1f81bdb..e223cc848c 100644 --- a/src/shims/mod.rs +++ b/src/shims/mod.rs @@ -28,8 +28,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, instance: ty::Instance<'tcx>, abi: Abi, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ret: Option, unwind: StackPopUnwind, ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> { @@ -63,9 +63,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// the actual MIR of `align_offset`. fn align_offset( &mut self, - ptr_op: &OpTy<'tcx, Tag>, - align_op: &OpTy<'tcx, Tag>, - dest: &PlaceTy<'tcx, Tag>, + ptr_op: &OpTy<'tcx, Provenance>, + align_op: &OpTy<'tcx, Provenance>, + dest: &PlaceTy<'tcx, Provenance>, ret: Option, unwind: StackPopUnwind, ) -> InterpResult<'tcx, bool> { diff --git a/src/shims/os_str.rs b/src/shims/os_str.rs index d6669b21a7..71824bee34 100644 --- a/src/shims/os_str.rs +++ b/src/shims/os_str.rs @@ -52,7 +52,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// the Unix APIs usually handle. fn read_os_str_from_c_str<'a>( &'a self, - ptr: Pointer>, + ptr: Pointer>, ) -> InterpResult<'tcx, &'a OsStr> where 'tcx: 'a, @@ -67,7 +67,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// which is what the Windows APIs usually handle. fn read_os_str_from_wide_str<'a>( &'a self, - ptr: Pointer>, + ptr: Pointer>, ) -> InterpResult<'tcx, OsString> where 'tcx: 'a, @@ -96,7 +96,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn write_os_str_to_c_str( &mut self, os_str: &OsStr, - ptr: Pointer>, + ptr: Pointer>, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { let bytes = os_str_to_bytes(os_str)?; @@ -119,7 +119,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn write_os_str_to_wide_str( &mut self, os_str: &OsStr, - ptr: Pointer>, + ptr: Pointer>, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { #[cfg(windows)] @@ -165,7 +165,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, os_str: &OsStr, memkind: MemoryKind, - ) -> InterpResult<'tcx, Pointer>> { + ) -> InterpResult<'tcx, Pointer>> { let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator. let this = self.eval_context_mut(); @@ -180,7 +180,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, os_str: &OsStr, memkind: MemoryKind, - ) -> InterpResult<'tcx, Pointer>> { + ) -> InterpResult<'tcx, Pointer>> { let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0x0000` terminator. let this = self.eval_context_mut(); @@ -193,7 +193,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// Read a null-terminated sequence of bytes, and perform path separator conversion if needed. fn read_path_from_c_str<'a>( &'a self, - ptr: Pointer>, + ptr: Pointer>, ) -> InterpResult<'tcx, Cow<'a, Path>> where 'tcx: 'a, @@ -209,7 +209,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } /// Read a null-terminated sequence of `u16`s, and perform path separator conversion if needed. - fn read_path_from_wide_str(&self, ptr: Pointer>) -> InterpResult<'tcx, PathBuf> { + fn read_path_from_wide_str( + &self, + ptr: Pointer>, + ) -> InterpResult<'tcx, PathBuf> { let this = self.eval_context_ref(); let os_str = this.read_os_str_from_wide_str(ptr)?; @@ -224,7 +227,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn write_path_to_c_str( &mut self, path: &Path, - ptr: Pointer>, + ptr: Pointer>, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { let this = self.eval_context_mut(); @@ -238,7 +241,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn write_path_to_wide_str( &mut self, path: &Path, - ptr: Pointer>, + ptr: Pointer>, size: u64, ) -> InterpResult<'tcx, (bool, u64)> { let this = self.eval_context_mut(); diff --git a/src/shims/panic.rs b/src/shims/panic.rs index c356dd8667..362b929eab 100644 --- a/src/shims/panic.rs +++ b/src/shims/panic.rs @@ -26,11 +26,11 @@ use helpers::check_arg_count; #[derive(Debug)] pub struct CatchUnwindData<'tcx> { /// The `catch_fn` callback to call in case of a panic. - catch_fn: Scalar, + catch_fn: Scalar, /// The `data` argument for that callback. - data: Scalar, + data: Scalar, /// The return place from the original call to `try`. - dest: PlaceTy<'tcx, Tag>, + dest: PlaceTy<'tcx, Provenance>, /// The return block from the original call to `try`. ret: mir::BasicBlock, } @@ -43,7 +43,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, abi: Abi, link_name: Symbol, - args: &[OpTy<'tcx, Tag>], + args: &[OpTy<'tcx, Provenance>], unwind: StackPopUnwind, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -65,8 +65,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// Handles the `try` intrinsic, the underlying implementation of `std::panicking::try`. fn handle_try( &mut self, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ret: mir::BasicBlock, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -108,7 +108,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // when we pop this frame. if this.tcx.sess.panic_strategy() == PanicStrategy::Unwind { this.frame_mut().extra.catch_unwind = - Some(CatchUnwindData { catch_fn, data, dest: *dest, ret }); + Some(CatchUnwindData { catch_fn, data, dest: dest.clone(), ret }); } Ok(()) diff --git a/src/shims/time.rs b/src/shims/time.rs index 0fd5cebd23..a2cbd84bc2 100644 --- a/src/shims/time.rs +++ b/src/shims/time.rs @@ -13,8 +13,8 @@ impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mi pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { fn clock_gettime( &mut self, - clk_id_op: &OpTy<'tcx, Tag>, - tp_op: &OpTy<'tcx, Tag>, + clk_id_op: &OpTy<'tcx, Provenance>, + tp_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { // This clock support is deliberately minimal because a lot of clock types have fiddly // properties (is it possible for Miri to be suspended independently of the host?). If you @@ -59,8 +59,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn gettimeofday( &mut self, - tv_op: &OpTy<'tcx, Tag>, - tz_op: &OpTy<'tcx, Tag>, + tv_op: &OpTy<'tcx, Provenance>, + tz_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -85,7 +85,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } #[allow(non_snake_case)] - fn GetSystemTimeAsFileTime(&mut self, LPFILETIME_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx> { + fn GetSystemTimeAsFileTime( + &mut self, + LPFILETIME_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); this.assert_target_os("windows", "GetSystemTimeAsFileTime"); @@ -115,7 +118,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx #[allow(non_snake_case)] fn QueryPerformanceCounter( &mut self, - lpPerformanceCount_op: &OpTy<'tcx, Tag>, + lpPerformanceCount_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -138,7 +141,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx #[allow(non_snake_case)] fn QueryPerformanceFrequency( &mut self, - lpFrequency_op: &OpTy<'tcx, Tag>, + lpFrequency_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -172,7 +175,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx }) } - fn mach_timebase_info(&mut self, info_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn mach_timebase_info(&mut self, info_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); this.assert_target_os("macos", "mach_timebase_info"); @@ -190,8 +193,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn nanosleep( &mut self, - req_op: &OpTy<'tcx, Tag>, - _rem: &OpTy<'tcx, Tag>, + req_op: &OpTy<'tcx, Provenance>, + _rem: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { // Signal handlers are not supported, so rem will never be written to. diff --git a/src/shims/tls.rs b/src/shims/tls.rs index 5a72c872b0..13af447f76 100644 --- a/src/shims/tls.rs +++ b/src/shims/tls.rs @@ -19,7 +19,7 @@ pub type TlsKey = u128; pub struct TlsEntry<'tcx> { /// The data for this key. None is used to represent NULL. /// (We normalize this early to avoid having to do a NULL-ptr-test each time we access the data.) - data: BTreeMap>, + data: BTreeMap>, dtor: Option>, } @@ -41,7 +41,7 @@ pub struct TlsData<'tcx> { /// A single per thread destructor of the thread local storage (that's how /// things work on macOS) with a data argument. - macos_thread_dtors: BTreeMap, Scalar)>, + macos_thread_dtors: BTreeMap, Scalar)>, /// State for currently running TLS dtors. If this map contains a key for a /// specific thread, it means that we are in the "destruct" phase, during @@ -94,7 +94,7 @@ impl<'tcx> TlsData<'tcx> { key: TlsKey, thread_id: ThreadId, cx: &impl HasDataLayout, - ) -> InterpResult<'tcx, Scalar> { + ) -> InterpResult<'tcx, Scalar> { match self.keys.get(&key) { Some(TlsEntry { data, .. }) => { let value = data.get(&thread_id).copied(); @@ -109,7 +109,7 @@ impl<'tcx> TlsData<'tcx> { &mut self, key: TlsKey, thread_id: ThreadId, - new_data: Scalar, + new_data: Scalar, cx: &impl HasDataLayout, ) -> InterpResult<'tcx> { match self.keys.get_mut(&key) { @@ -140,7 +140,7 @@ impl<'tcx> TlsData<'tcx> { &mut self, thread: ThreadId, dtor: ty::Instance<'tcx>, - data: Scalar, + data: Scalar, ) -> InterpResult<'tcx> { if self.dtors_running.contains_key(&thread) { // UB, according to libstd docs. @@ -179,7 +179,7 @@ impl<'tcx> TlsData<'tcx> { &mut self, key: Option, thread_id: ThreadId, - ) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)> { + ) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)> { use std::ops::Bound::*; let thread_local = &mut self.keys; diff --git a/src/shims/unix/dlsym.rs b/src/shims/unix/dlsym.rs index 2c563a9551..a806c16e28 100644 --- a/src/shims/unix/dlsym.rs +++ b/src/shims/unix/dlsym.rs @@ -32,8 +32,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, dlsym: Dlsym, abi: Abi, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ret: Option, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index cf34b4baec..2a051fb775 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -19,8 +19,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> { let this = self.eval_context_mut(); diff --git a/src/shims/unix/freebsd/dlsym.rs b/src/shims/unix/freebsd/dlsym.rs index 18347d274e..74c322c666 100644 --- a/src/shims/unix/freebsd/dlsym.rs +++ b/src/shims/unix/freebsd/dlsym.rs @@ -19,8 +19,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn call_dlsym( &mut self, dlsym: Dlsym, - _args: &[OpTy<'tcx, Tag>], - _dest: &PlaceTy<'tcx, Tag>, + _args: &[OpTy<'tcx, Provenance>], + _dest: &PlaceTy<'tcx, Provenance>, ret: Option, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); diff --git a/src/shims/unix/freebsd/foreign_items.rs b/src/shims/unix/freebsd/foreign_items.rs index 2350e5a12c..658711526d 100644 --- a/src/shims/unix/freebsd/foreign_items.rs +++ b/src/shims/unix/freebsd/foreign_items.rs @@ -11,8 +11,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> { let this = self.eval_context_mut(); match link_name.as_str() { diff --git a/src/shims/unix/fs.rs b/src/shims/unix/fs.rs index 1420279247..50a2053078 100644 --- a/src/shims/unix/fs.rs +++ b/src/shims/unix/fs.rs @@ -386,7 +386,7 @@ trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, ' fn macos_stat_write_buf( &mut self, metadata: FileMetadata, - buf_op: &OpTy<'tcx, Tag>, + buf_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -493,7 +493,7 @@ pub struct OpenDir { /// The directory reader on the host. read_dir: ReadDir, /// The most recent entry returned by readdir() - entry: Pointer>, + entry: Pointer>, } impl OpenDir { @@ -556,7 +556,7 @@ fn maybe_sync_file( impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { - fn open(&mut self, args: &[OpTy<'tcx, Tag>]) -> InterpResult<'tcx, i32> { + fn open(&mut self, args: &[OpTy<'tcx, Provenance>]) -> InterpResult<'tcx, i32> { if args.len() < 2 { throw_ub_format!( "incorrect number of arguments for `open`: got {}, expected at least 2", @@ -667,7 +667,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.try_unwrap_io_result(fd) } - fn fcntl(&mut self, args: &[OpTy<'tcx, Tag>]) -> InterpResult<'tcx, i32> { + fn fcntl(&mut self, args: &[OpTy<'tcx, Provenance>]) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); if args.len() < 2 { @@ -741,7 +741,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn close(&mut self, fd_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn close(&mut self, fd_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let fd = this.read_scalar(fd_op)?.to_i32()?; @@ -754,7 +754,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn read(&mut self, fd: i32, buf: Pointer>, count: u64) -> InterpResult<'tcx, i64> { + fn read( + &mut self, + fd: i32, + buf: Pointer>, + count: u64, + ) -> InterpResult<'tcx, i64> { let this = self.eval_context_mut(); // Isolation check is done via `FileDescriptor` trait. @@ -802,7 +807,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn write(&mut self, fd: i32, buf: Pointer>, count: u64) -> InterpResult<'tcx, i64> { + fn write( + &mut self, + fd: i32, + buf: Pointer>, + count: u64, + ) -> InterpResult<'tcx, i64> { let this = self.eval_context_mut(); // Isolation check is done via `FileDescriptor` trait. @@ -832,9 +842,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn lseek64( &mut self, - fd_op: &OpTy<'tcx, Tag>, - offset_op: &OpTy<'tcx, Tag>, - whence_op: &OpTy<'tcx, Tag>, + fd_op: &OpTy<'tcx, Provenance>, + offset_op: &OpTy<'tcx, Provenance>, + whence_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i64> { let this = self.eval_context_mut(); @@ -867,7 +877,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn unlink(&mut self, path_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn unlink(&mut self, path_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; @@ -885,8 +895,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn symlink( &mut self, - target_op: &OpTy<'tcx, Tag>, - linkpath_op: &OpTy<'tcx, Tag>, + target_op: &OpTy<'tcx, Provenance>, + linkpath_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { #[cfg(unix)] fn create_link(src: &Path, dst: &Path) -> std::io::Result<()> { @@ -916,8 +926,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn macos_stat( &mut self, - path_op: &OpTy<'tcx, Tag>, - buf_op: &OpTy<'tcx, Tag>, + path_op: &OpTy<'tcx, Provenance>, + buf_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); this.assert_target_os("macos", "stat"); @@ -945,8 +955,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // `lstat` is used to get symlink metadata. fn macos_lstat( &mut self, - path_op: &OpTy<'tcx, Tag>, - buf_op: &OpTy<'tcx, Tag>, + path_op: &OpTy<'tcx, Provenance>, + buf_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); this.assert_target_os("macos", "lstat"); @@ -972,8 +982,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn macos_fstat( &mut self, - fd_op: &OpTy<'tcx, Tag>, - buf_op: &OpTy<'tcx, Tag>, + fd_op: &OpTy<'tcx, Provenance>, + buf_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -997,11 +1007,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn linux_statx( &mut self, - dirfd_op: &OpTy<'tcx, Tag>, // Should be an `int` - pathname_op: &OpTy<'tcx, Tag>, // Should be a `const char *` - flags_op: &OpTy<'tcx, Tag>, // Should be an `int` - mask_op: &OpTy<'tcx, Tag>, // Should be an `unsigned int` - statxbuf_op: &OpTy<'tcx, Tag>, // Should be a `struct statx *` + dirfd_op: &OpTy<'tcx, Provenance>, // Should be an `int` + pathname_op: &OpTy<'tcx, Provenance>, // Should be a `const char *` + flags_op: &OpTy<'tcx, Provenance>, // Should be an `int` + mask_op: &OpTy<'tcx, Provenance>, // Should be an `unsigned int` + statxbuf_op: &OpTy<'tcx, Provenance>, // Should be a `struct statx *` ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -1188,8 +1198,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn rename( &mut self, - oldpath_op: &OpTy<'tcx, Tag>, - newpath_op: &OpTy<'tcx, Tag>, + oldpath_op: &OpTy<'tcx, Provenance>, + newpath_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -1219,8 +1229,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn mkdir( &mut self, - path_op: &OpTy<'tcx, Tag>, - mode_op: &OpTy<'tcx, Tag>, + path_op: &OpTy<'tcx, Provenance>, + mode_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -1256,7 +1266,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.try_unwrap_io_result(result) } - fn rmdir(&mut self, path_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn rmdir(&mut self, path_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; @@ -1273,7 +1283,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.try_unwrap_io_result(result) } - fn opendir(&mut self, name_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, Scalar> { + fn opendir( + &mut self, + name_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let name = this.read_path_from_c_str(this.read_pointer(name_op)?)?; @@ -1304,7 +1317,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn linux_readdir64(&mut self, dirp_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, Scalar> { + fn linux_readdir64( + &mut self, + dirp_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); this.assert_target_os("linux", "readdir64"); @@ -1393,9 +1409,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn macos_readdir_r( &mut self, - dirp_op: &OpTy<'tcx, Tag>, - entry_op: &OpTy<'tcx, Tag>, - result_op: &OpTy<'tcx, Tag>, + dirp_op: &OpTy<'tcx, Provenance>, + entry_op: &OpTy<'tcx, Provenance>, + result_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -1490,7 +1506,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn closedir(&mut self, dirp_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn closedir(&mut self, dirp_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?; @@ -1513,8 +1529,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn ftruncate64( &mut self, - fd_op: &OpTy<'tcx, Tag>, - length_op: &OpTy<'tcx, Tag>, + fd_op: &OpTy<'tcx, Provenance>, + length_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -1551,7 +1567,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn fsync(&mut self, fd_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn fsync(&mut self, fd_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { // On macOS, `fsync` (unlike `fcntl(F_FULLFSYNC)`) does not wait for the // underlying disk to finish writing. In the interest of host compatibility, // we conservatively implement this with `sync_all`, which @@ -1578,7 +1594,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn fdatasync(&mut self, fd_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn fdatasync(&mut self, fd_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let fd = this.read_scalar(fd_op)?.to_i32()?; @@ -1602,10 +1618,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn sync_file_range( &mut self, - fd_op: &OpTy<'tcx, Tag>, - offset_op: &OpTy<'tcx, Tag>, - nbytes_op: &OpTy<'tcx, Tag>, - flags_op: &OpTy<'tcx, Tag>, + fd_op: &OpTy<'tcx, Provenance>, + offset_op: &OpTy<'tcx, Provenance>, + nbytes_op: &OpTy<'tcx, Provenance>, + flags_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -1647,9 +1663,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn readlink( &mut self, - pathname_op: &OpTy<'tcx, Tag>, - buf_op: &OpTy<'tcx, Tag>, - bufsize_op: &OpTy<'tcx, Tag>, + pathname_op: &OpTy<'tcx, Provenance>, + buf_op: &OpTy<'tcx, Provenance>, + bufsize_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i64> { let this = self.eval_context_mut(); @@ -1691,7 +1707,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } #[cfg_attr(not(unix), allow(unused))] - fn isatty(&mut self, miri_fd: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn isatty(&mut self, miri_fd: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); #[cfg(unix)] if matches!(this.machine.isolated_op, IsolatedOp::Allow) { @@ -1740,7 +1756,7 @@ fn extract_sec_and_nsec<'tcx>( /// Stores a file's metadata in order to avoid code duplication in the different metadata related /// shims. struct FileMetadata { - mode: Scalar, + mode: Scalar, size: u64, created: Option<(u64, u32)>, accessed: Option<(u64, u32)>, diff --git a/src/shims/unix/linux/dlsym.rs b/src/shims/unix/linux/dlsym.rs index 01bf17db9f..44d51c4a0b 100644 --- a/src/shims/unix/linux/dlsym.rs +++ b/src/shims/unix/linux/dlsym.rs @@ -23,8 +23,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn call_dlsym( &mut self, dlsym: Dlsym, - _args: &[OpTy<'tcx, Tag>], - _dest: &PlaceTy<'tcx, Tag>, + _args: &[OpTy<'tcx, Provenance>], + _dest: &PlaceTy<'tcx, Provenance>, ret: Option, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); diff --git a/src/shims/unix/linux/foreign_items.rs b/src/shims/unix/linux/foreign_items.rs index efd4e2a8c0..bae3780b46 100644 --- a/src/shims/unix/linux/foreign_items.rs +++ b/src/shims/unix/linux/foreign_items.rs @@ -14,8 +14,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> { let this = self.eval_context_mut(); @@ -165,10 +165,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Shims the linux `getrandom` syscall. fn getrandom<'tcx>( this: &mut MiriEvalContext<'_, 'tcx>, - ptr: &OpTy<'tcx, Tag>, - len: &OpTy<'tcx, Tag>, - flags: &OpTy<'tcx, Tag>, - dest: &PlaceTy<'tcx, Tag>, + ptr: &OpTy<'tcx, Provenance>, + len: &OpTy<'tcx, Provenance>, + flags: &OpTy<'tcx, Provenance>, + dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { let ptr = this.read_pointer(ptr)?; let len = this.read_scalar(len)?.to_machine_usize(this)?; diff --git a/src/shims/unix/linux/sync.rs b/src/shims/unix/linux/sync.rs index a81fdb5e99..a11aa8ed84 100644 --- a/src/shims/unix/linux/sync.rs +++ b/src/shims/unix/linux/sync.rs @@ -7,8 +7,8 @@ use std::time::{Instant, SystemTime}; /// `args` is the arguments *after* the syscall number. pub fn futex<'tcx>( this: &mut MiriEvalContext<'_, 'tcx>, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { // The amount of arguments used depends on the type of futex operation. // The full futex syscall takes six arguments (excluding the syscall @@ -188,8 +188,8 @@ pub fn futex<'tcx>( this.write_scalar(Scalar::from_machine_isize(0, this), dest)?; // Register a timeout callback if a timeout was specified. // This callback will override the return value when the timeout triggers. - let dest = *dest; if let Some(timeout_time) = timeout_time { + let dest = dest.clone(); this.register_timeout_callback( thread, timeout_time, diff --git a/src/shims/unix/macos/dlsym.rs b/src/shims/unix/macos/dlsym.rs index 2e97b7918e..f21f1ae9af 100644 --- a/src/shims/unix/macos/dlsym.rs +++ b/src/shims/unix/macos/dlsym.rs @@ -27,8 +27,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn call_dlsym( &mut self, dlsym: Dlsym, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ret: Option, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); diff --git a/src/shims/unix/macos/foreign_items.rs b/src/shims/unix/macos/foreign_items.rs index 58dd40cda3..21c7762c3c 100644 --- a/src/shims/unix/macos/foreign_items.rs +++ b/src/shims/unix/macos/foreign_items.rs @@ -12,8 +12,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> { let this = self.eval_context_mut(); diff --git a/src/shims/unix/sync.rs b/src/shims/unix/sync.rs index ae63907c2c..18226c2b8c 100644 --- a/src/shims/unix/sync.rs +++ b/src/shims/unix/sync.rs @@ -21,14 +21,14 @@ const PTHREAD_MUTEX_NORMAL_FLAG: i32 = 0x8000000; fn is_mutex_kind_default<'mir, 'tcx: 'mir>( ecx: &mut MiriEvalContext<'mir, 'tcx>, - kind: Scalar, + kind: Scalar, ) -> InterpResult<'tcx, bool> { Ok(kind == ecx.eval_libc("PTHREAD_MUTEX_DEFAULT")?) } fn is_mutex_kind_normal<'mir, 'tcx: 'mir>( ecx: &mut MiriEvalContext<'mir, 'tcx>, - kind: Scalar, + kind: Scalar, ) -> InterpResult<'tcx, bool> { let kind = kind.to_i32()?; let mutex_normal_kind = ecx.eval_libc("PTHREAD_MUTEX_NORMAL")?.to_i32()?; @@ -37,15 +37,15 @@ fn is_mutex_kind_normal<'mir, 'tcx: 'mir>( fn mutexattr_get_kind<'mir, 'tcx: 'mir>( ecx: &MiriEvalContext<'mir, 'tcx>, - attr_op: &OpTy<'tcx, Tag>, -) -> InterpResult<'tcx, ScalarMaybeUninit> { + attr_op: &OpTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ScalarMaybeUninit> { ecx.read_scalar_at_offset(attr_op, 0, ecx.machine.layouts.i32) } fn mutexattr_set_kind<'mir, 'tcx: 'mir>( ecx: &mut MiriEvalContext<'mir, 'tcx>, - attr_op: &OpTy<'tcx, Tag>, - kind: impl Into>, + attr_op: &OpTy<'tcx, Provenance>, + kind: impl Into>, ) -> InterpResult<'tcx, ()> { ecx.write_scalar_at_offset(attr_op, 0, kind, layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.i32)) } @@ -61,8 +61,8 @@ fn mutexattr_set_kind<'mir, 'tcx: 'mir>( fn mutex_get_kind<'mir, 'tcx: 'mir>( ecx: &MiriEvalContext<'mir, 'tcx>, - mutex_op: &OpTy<'tcx, Tag>, -) -> InterpResult<'tcx, ScalarMaybeUninit> { + mutex_op: &OpTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ScalarMaybeUninit> { let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 }; ecx.read_scalar_at_offset_atomic( mutex_op, @@ -74,8 +74,8 @@ fn mutex_get_kind<'mir, 'tcx: 'mir>( fn mutex_set_kind<'mir, 'tcx: 'mir>( ecx: &mut MiriEvalContext<'mir, 'tcx>, - mutex_op: &OpTy<'tcx, Tag>, - kind: impl Into>, + mutex_op: &OpTy<'tcx, Provenance>, + kind: impl Into>, ) -> InterpResult<'tcx, ()> { let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 }; ecx.write_scalar_at_offset_atomic( @@ -89,15 +89,15 @@ fn mutex_set_kind<'mir, 'tcx: 'mir>( fn mutex_get_id<'mir, 'tcx: 'mir>( ecx: &MiriEvalContext<'mir, 'tcx>, - mutex_op: &OpTy<'tcx, Tag>, -) -> InterpResult<'tcx, ScalarMaybeUninit> { + mutex_op: &OpTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ScalarMaybeUninit> { ecx.read_scalar_at_offset_atomic(mutex_op, 4, ecx.machine.layouts.u32, AtomicReadOrd::Relaxed) } fn mutex_set_id<'mir, 'tcx: 'mir>( ecx: &mut MiriEvalContext<'mir, 'tcx>, - mutex_op: &OpTy<'tcx, Tag>, - id: impl Into>, + mutex_op: &OpTy<'tcx, Provenance>, + id: impl Into>, ) -> InterpResult<'tcx, ()> { ecx.write_scalar_at_offset_atomic( mutex_op, @@ -110,7 +110,7 @@ fn mutex_set_id<'mir, 'tcx: 'mir>( fn mutex_get_or_create_id<'mir, 'tcx: 'mir>( ecx: &mut MiriEvalContext<'mir, 'tcx>, - mutex_op: &OpTy<'tcx, Tag>, + mutex_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, MutexId> { let value_place = ecx.deref_operand_and_offset(mutex_op, 4, ecx.machine.layouts.u32)?; @@ -145,15 +145,15 @@ fn mutex_get_or_create_id<'mir, 'tcx: 'mir>( fn rwlock_get_id<'mir, 'tcx: 'mir>( ecx: &MiriEvalContext<'mir, 'tcx>, - rwlock_op: &OpTy<'tcx, Tag>, -) -> InterpResult<'tcx, ScalarMaybeUninit> { + rwlock_op: &OpTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ScalarMaybeUninit> { ecx.read_scalar_at_offset_atomic(rwlock_op, 4, ecx.machine.layouts.u32, AtomicReadOrd::Relaxed) } fn rwlock_set_id<'mir, 'tcx: 'mir>( ecx: &mut MiriEvalContext<'mir, 'tcx>, - rwlock_op: &OpTy<'tcx, Tag>, - id: impl Into>, + rwlock_op: &OpTy<'tcx, Provenance>, + id: impl Into>, ) -> InterpResult<'tcx, ()> { ecx.write_scalar_at_offset_atomic( rwlock_op, @@ -166,7 +166,7 @@ fn rwlock_set_id<'mir, 'tcx: 'mir>( fn rwlock_get_or_create_id<'mir, 'tcx: 'mir>( ecx: &mut MiriEvalContext<'mir, 'tcx>, - rwlock_op: &OpTy<'tcx, Tag>, + rwlock_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, RwLockId> { let value_place = ecx.deref_operand_and_offset(rwlock_op, 4, ecx.machine.layouts.u32)?; @@ -200,15 +200,15 @@ fn rwlock_get_or_create_id<'mir, 'tcx: 'mir>( fn condattr_get_clock_id<'mir, 'tcx: 'mir>( ecx: &MiriEvalContext<'mir, 'tcx>, - attr_op: &OpTy<'tcx, Tag>, -) -> InterpResult<'tcx, ScalarMaybeUninit> { + attr_op: &OpTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ScalarMaybeUninit> { ecx.read_scalar_at_offset(attr_op, 0, ecx.machine.layouts.i32) } fn condattr_set_clock_id<'mir, 'tcx: 'mir>( ecx: &mut MiriEvalContext<'mir, 'tcx>, - attr_op: &OpTy<'tcx, Tag>, - clock_id: impl Into>, + attr_op: &OpTy<'tcx, Provenance>, + clock_id: impl Into>, ) -> InterpResult<'tcx, ()> { ecx.write_scalar_at_offset( attr_op, @@ -229,15 +229,15 @@ fn condattr_set_clock_id<'mir, 'tcx: 'mir>( fn cond_get_id<'mir, 'tcx: 'mir>( ecx: &MiriEvalContext<'mir, 'tcx>, - cond_op: &OpTy<'tcx, Tag>, -) -> InterpResult<'tcx, ScalarMaybeUninit> { + cond_op: &OpTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ScalarMaybeUninit> { ecx.read_scalar_at_offset_atomic(cond_op, 4, ecx.machine.layouts.u32, AtomicReadOrd::Relaxed) } fn cond_set_id<'mir, 'tcx: 'mir>( ecx: &mut MiriEvalContext<'mir, 'tcx>, - cond_op: &OpTy<'tcx, Tag>, - id: impl Into>, + cond_op: &OpTy<'tcx, Provenance>, + id: impl Into>, ) -> InterpResult<'tcx, ()> { ecx.write_scalar_at_offset_atomic( cond_op, @@ -250,7 +250,7 @@ fn cond_set_id<'mir, 'tcx: 'mir>( fn cond_get_or_create_id<'mir, 'tcx: 'mir>( ecx: &mut MiriEvalContext<'mir, 'tcx>, - cond_op: &OpTy<'tcx, Tag>, + cond_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, CondvarId> { let value_place = ecx.deref_operand_and_offset(cond_op, 4, ecx.machine.layouts.u32)?; @@ -278,15 +278,15 @@ fn cond_get_or_create_id<'mir, 'tcx: 'mir>( fn cond_get_clock_id<'mir, 'tcx: 'mir>( ecx: &MiriEvalContext<'mir, 'tcx>, - cond_op: &OpTy<'tcx, Tag>, -) -> InterpResult<'tcx, ScalarMaybeUninit> { + cond_op: &OpTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ScalarMaybeUninit> { ecx.read_scalar_at_offset(cond_op, 8, ecx.machine.layouts.i32) } fn cond_set_clock_id<'mir, 'tcx: 'mir>( ecx: &mut MiriEvalContext<'mir, 'tcx>, - cond_op: &OpTy<'tcx, Tag>, - clock_id: impl Into>, + cond_op: &OpTy<'tcx, Provenance>, + clock_id: impl Into>, ) -> InterpResult<'tcx, ()> { ecx.write_scalar_at_offset( cond_op, @@ -347,7 +347,10 @@ fn release_cond_mutex_and_block<'mir, 'tcx: 'mir>( impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { - fn pthread_mutexattr_init(&mut self, attr_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_mutexattr_init( + &mut self, + attr_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let default_kind = this.eval_libc("PTHREAD_MUTEX_DEFAULT")?; @@ -358,8 +361,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn pthread_mutexattr_settype( &mut self, - attr_op: &OpTy<'tcx, Tag>, - kind_op: &OpTy<'tcx, Tag>, + attr_op: &OpTy<'tcx, Provenance>, + kind_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -397,7 +400,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(0) } - fn pthread_mutexattr_destroy(&mut self, attr_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_mutexattr_destroy( + &mut self, + attr_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); // Destroying an uninit pthread_mutexattr is UB, so check to make sure it's not uninit. @@ -423,8 +429,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn pthread_mutex_init( &mut self, - mutex_op: &OpTy<'tcx, Tag>, - attr_op: &OpTy<'tcx, Tag>, + mutex_op: &OpTy<'tcx, Provenance>, + attr_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -443,7 +449,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(0) } - fn pthread_mutex_lock(&mut self, mutex_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_mutex_lock(&mut self, mutex_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let kind = mutex_get_kind(this, mutex_op)?.check_init()?; @@ -480,7 +486,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_mutex_trylock( + &mut self, + mutex_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let kind = mutex_get_kind(this, mutex_op)?.check_init()?; @@ -513,7 +522,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_mutex_unlock( + &mut self, + mutex_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let kind = mutex_get_kind(this, mutex_op)?.check_init()?; @@ -545,7 +557,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn pthread_mutex_destroy(&mut self, mutex_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_mutex_destroy( + &mut self, + mutex_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = mutex_get_or_create_id(this, mutex_op)?; @@ -566,7 +581,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(0) } - fn pthread_rwlock_rdlock(&mut self, rwlock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_rwlock_rdlock( + &mut self, + rwlock_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = rwlock_get_or_create_id(this, rwlock_op)?; @@ -581,7 +599,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn pthread_rwlock_tryrdlock(&mut self, rwlock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_rwlock_tryrdlock( + &mut self, + rwlock_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = rwlock_get_or_create_id(this, rwlock_op)?; @@ -595,7 +616,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn pthread_rwlock_wrlock(&mut self, rwlock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_rwlock_wrlock( + &mut self, + rwlock_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = rwlock_get_or_create_id(this, rwlock_op)?; @@ -622,7 +646,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(0) } - fn pthread_rwlock_trywrlock(&mut self, rwlock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_rwlock_trywrlock( + &mut self, + rwlock_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = rwlock_get_or_create_id(this, rwlock_op)?; @@ -636,7 +663,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn pthread_rwlock_unlock(&mut self, rwlock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_rwlock_unlock( + &mut self, + rwlock_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = rwlock_get_or_create_id(this, rwlock_op)?; @@ -652,7 +682,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - fn pthread_rwlock_destroy(&mut self, rwlock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_rwlock_destroy( + &mut self, + rwlock_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = rwlock_get_or_create_id(this, rwlock_op)?; @@ -671,7 +704,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(0) } - fn pthread_condattr_init(&mut self, attr_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_condattr_init( + &mut self, + attr_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); // The default value of the clock attribute shall refer to the system @@ -685,8 +721,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn pthread_condattr_setclock( &mut self, - attr_op: &OpTy<'tcx, Tag>, - clock_id_op: &OpTy<'tcx, Tag>, + attr_op: &OpTy<'tcx, Provenance>, + clock_id_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -705,8 +741,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn pthread_condattr_getclock( &mut self, - attr_op: &OpTy<'tcx, Tag>, - clk_id_op: &OpTy<'tcx, Tag>, + attr_op: &OpTy<'tcx, Provenance>, + clk_id_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -716,7 +752,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(0) } - fn pthread_condattr_destroy(&mut self, attr_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_condattr_destroy( + &mut self, + attr_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); // Destroying an uninit pthread_condattr is UB, so check to make sure it's not uninit. @@ -730,8 +769,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn pthread_cond_init( &mut self, - cond_op: &OpTy<'tcx, Tag>, - attr_op: &OpTy<'tcx, Tag>, + cond_op: &OpTy<'tcx, Provenance>, + attr_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -750,7 +789,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(0) } - fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = cond_get_or_create_id(this, cond_op)?; if let Some((thread, mutex)) = this.condvar_signal(id) { @@ -760,7 +799,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(0) } - fn pthread_cond_broadcast(&mut self, cond_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_cond_broadcast( + &mut self, + cond_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = cond_get_or_create_id(this, cond_op)?; @@ -773,8 +815,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn pthread_cond_wait( &mut self, - cond_op: &OpTy<'tcx, Tag>, - mutex_op: &OpTy<'tcx, Tag>, + cond_op: &OpTy<'tcx, Provenance>, + mutex_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -790,10 +832,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn pthread_cond_timedwait( &mut self, - cond_op: &OpTy<'tcx, Tag>, - mutex_op: &OpTy<'tcx, Tag>, - abstime_op: &OpTy<'tcx, Tag>, - dest: &PlaceTy<'tcx, Tag>, + cond_op: &OpTy<'tcx, Provenance>, + mutex_op: &OpTy<'tcx, Provenance>, + abstime_op: &OpTy<'tcx, Provenance>, + dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -828,9 +870,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // We return success for now and override it in the timeout callback. this.write_scalar(Scalar::from_i32(0), dest)?; - let dest = *dest; - // Register the timeout callback. + let dest = dest.clone(); this.register_timeout_callback( active_thread, timeout_time, @@ -853,7 +894,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(()) } - fn pthread_cond_destroy(&mut self, cond_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_cond_destroy( + &mut self, + cond_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = cond_get_or_create_id(this, cond_op)?; diff --git a/src/shims/unix/thread.rs b/src/shims/unix/thread.rs index 8dc5f81354..1a8531e880 100644 --- a/src/shims/unix/thread.rs +++ b/src/shims/unix/thread.rs @@ -6,10 +6,10 @@ impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tc pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { fn pthread_create( &mut self, - thread: &OpTy<'tcx, Tag>, - _attr: &OpTy<'tcx, Tag>, - start_routine: &OpTy<'tcx, Tag>, - arg: &OpTy<'tcx, Tag>, + thread: &OpTy<'tcx, Provenance>, + _attr: &OpTy<'tcx, Provenance>, + start_routine: &OpTy<'tcx, Provenance>, + arg: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -59,8 +59,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn pthread_join( &mut self, - thread: &OpTy<'tcx, Tag>, - retval: &OpTy<'tcx, Tag>, + thread: &OpTy<'tcx, Provenance>, + retval: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); @@ -75,7 +75,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(0) } - fn pthread_detach(&mut self, thread: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> { + fn pthread_detach(&mut self, thread: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let thread_id = this.read_scalar(thread)?.to_machine_usize(this)?; @@ -84,14 +84,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(0) } - fn pthread_self(&mut self, dest: &PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { + fn pthread_self(&mut self, dest: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let thread_id = this.get_active_thread(); this.write_scalar(Scalar::from_uint(thread_id.to_u32(), dest.layout.size), dest) } - fn prctl(&mut self, args: &[OpTy<'tcx, Tag>]) -> InterpResult<'tcx, i32> { + fn prctl(&mut self, args: &[OpTy<'tcx, Provenance>]) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); this.assert_target_os("linux", "prctl"); @@ -138,7 +138,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(0) } - fn pthread_setname_np(&mut self, name: Pointer>) -> InterpResult<'tcx> { + fn pthread_setname_np(&mut self, name: Pointer>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); this.assert_target_os("macos", "pthread_setname_np"); diff --git a/src/shims/windows/dlsym.rs b/src/shims/windows/dlsym.rs index 60ef11b796..ee4f392277 100644 --- a/src/shims/windows/dlsym.rs +++ b/src/shims/windows/dlsym.rs @@ -31,8 +31,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, dlsym: Dlsym, abi: Abi, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ret: Option, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index 6563434241..3f4b8b1400 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -15,8 +15,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx &mut self, link_name: Symbol, abi: Abi, - args: &[OpTy<'tcx, Tag>], - dest: &PlaceTy<'tcx, Tag>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> { let this = self.eval_context_mut(); diff --git a/src/shims/windows/sync.rs b/src/shims/windows/sync.rs index 35603f7f38..878b9b94a6 100644 --- a/src/shims/windows/sync.rs +++ b/src/shims/windows/sync.rs @@ -5,7 +5,7 @@ use crate::*; fn srwlock_get_or_create_id<'mir, 'tcx: 'mir>( ecx: &mut MiriEvalContext<'mir, 'tcx>, - lock_op: &OpTy<'tcx, Tag>, + lock_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, RwLockId> { let value_place = ecx.deref_operand_and_offset(lock_op, 0, ecx.machine.layouts.u32)?; @@ -34,7 +34,7 @@ fn srwlock_get_or_create_id<'mir, 'tcx: 'mir>( impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { #[allow(non_snake_case)] - fn AcquireSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx> { + fn AcquireSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let id = srwlock_get_or_create_id(this, lock_op)?; let active_thread = this.get_active_thread(); @@ -56,7 +56,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } #[allow(non_snake_case)] - fn TryAcquireSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, u8> { + fn TryAcquireSRWLockExclusive( + &mut self, + lock_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, u8> { let this = self.eval_context_mut(); let id = srwlock_get_or_create_id(this, lock_op)?; let active_thread = this.get_active_thread(); @@ -71,7 +74,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } #[allow(non_snake_case)] - fn ReleaseSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx> { + fn ReleaseSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let id = srwlock_get_or_create_id(this, lock_op)?; let active_thread = this.get_active_thread(); @@ -87,7 +90,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } #[allow(non_snake_case)] - fn AcquireSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx> { + fn AcquireSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let id = srwlock_get_or_create_id(this, lock_op)?; let active_thread = this.get_active_thread(); @@ -102,7 +105,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } #[allow(non_snake_case)] - fn TryAcquireSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, u8> { + fn TryAcquireSRWLockShared( + &mut self, + lock_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, u8> { let this = self.eval_context_mut(); let id = srwlock_get_or_create_id(this, lock_op)?; let active_thread = this.get_active_thread(); @@ -116,7 +122,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } #[allow(non_snake_case)] - fn ReleaseSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx> { + fn ReleaseSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let id = srwlock_get_or_create_id(this, lock_op)?; let active_thread = this.get_active_thread(); diff --git a/src/stacked_borrows/diagnostics.rs b/src/stacked_borrows/diagnostics.rs index 133164f390..6521f07721 100644 --- a/src/stacked_borrows/diagnostics.rs +++ b/src/stacked_borrows/diagnostics.rs @@ -5,11 +5,8 @@ use rustc_span::{Span, SpanData}; use rustc_target::abi::Size; use crate::helpers::CurrentSpan; -use crate::stacked_borrows::{err_sb_ub, AccessKind, Permission}; -use crate::Item; -use crate::SbTag; -use crate::SbTagExtra; -use crate::Stack; +use crate::stacked_borrows::{err_sb_ub, AccessKind}; +use crate::*; use rustc_middle::mir::interpret::InterpError; @@ -132,7 +129,7 @@ impl AllocHistory { /// Report a descriptive error when `new` could not be granted from `derived_from`. pub fn grant_error<'tcx>( &self, - derived_from: SbTagExtra, + derived_from: ProvenanceExtra, new: Item, alloc_id: AllocId, alloc_range: AllocRange, @@ -155,7 +152,7 @@ impl AllocHistory { pub fn access_error<'tcx>( &self, access: AccessKind, - tag: SbTagExtra, + tag: ProvenanceExtra, alloc_id: AllocId, alloc_range: AllocRange, error_offset: Size, @@ -181,8 +178,8 @@ fn operation_summary( format!("this error occurs as part of {operation} at {alloc_id:?}{alloc_range:?}") } -fn error_cause(stack: &Stack, tag: SbTagExtra) -> &'static str { - if let SbTagExtra::Concrete(tag) = tag { +fn error_cause(stack: &Stack, prov_extra: ProvenanceExtra) -> &'static str { + if let ProvenanceExtra::Concrete(tag) = prov_extra { if (0..stack.len()) .map(|i| stack.get(i).unwrap()) .any(|item| item.tag() == tag && item.perm() != Permission::Disabled) diff --git a/src/stacked_borrows/mod.rs b/src/stacked_borrows/mod.rs index 9040d03632..38a73929a0 100644 --- a/src/stacked_borrows/mod.rs +++ b/src/stacked_borrows/mod.rs @@ -55,32 +55,6 @@ impl fmt::Debug for SbTag { } } -/// The "extra" information an SB pointer has over a regular AllocId. -/// Newtype for `Option`. -#[derive(Copy, Clone)] -pub enum SbTagExtra { - Concrete(SbTag), - Wildcard, -} - -impl fmt::Debug for SbTagExtra { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - SbTagExtra::Concrete(pid) => write!(f, "{pid:?}"), - SbTagExtra::Wildcard => write!(f, ""), - } - } -} - -impl SbTagExtra { - fn and_then(self, f: impl FnOnce(SbTag) -> Option) -> Option { - match self { - SbTagExtra::Concrete(pid) => f(pid), - SbTagExtra::Wildcard => None, - } - } -} - #[derive(Debug)] pub struct FrameExtra { /// The ID of the call this frame corresponds to. @@ -311,7 +285,7 @@ impl<'tcx> Stack { /// currently checking. fn item_popped( item: &Item, - provoking_access: Option<(SbTagExtra, AllocRange, Size, AccessKind)>, // just for debug printing and error messages + provoking_access: Option<(ProvenanceExtra, AllocRange, Size, AccessKind)>, // just for debug printing and error messages global: &GlobalStateInner, alloc_history: &mut AllocHistory, threads: &ThreadManager<'_, 'tcx>, @@ -322,7 +296,7 @@ impl<'tcx> Stack { #[inline(never)] // cold path fn check_tracked( item: &Item, - provoking_access: &Option<(SbTagExtra, AllocRange, Size, AccessKind)>, + provoking_access: &Option<(ProvenanceExtra, AllocRange, Size, AccessKind)>, global: &GlobalStateInner, ) { if global.tracked_pointer_tags.contains(&item.tag()) { @@ -357,7 +331,7 @@ impl<'tcx> Stack { #[inline(never)] // cold path fn protector_error<'tcx>( item: &Item, - provoking_access: &Option<(SbTagExtra, AllocRange, Size, AccessKind)>, + provoking_access: &Option<(ProvenanceExtra, AllocRange, Size, AccessKind)>, alloc_history: &mut AllocHistory, threads: &ThreadManager<'_, 'tcx>, ) -> InterpErrorInfo<'tcx> { @@ -410,7 +384,7 @@ impl<'tcx> Stack { fn access( &mut self, access: AccessKind, - tag: SbTagExtra, + tag: ProvenanceExtra, (alloc_id, alloc_range, offset): (AllocId, AllocRange, Size), // just for debug printing and error messages global: &mut GlobalStateInner, current_span: &mut CurrentSpan<'_, '_, 'tcx>, @@ -482,7 +456,7 @@ impl<'tcx> Stack { } // If this was an approximate action, we now collapse everything into an unknown. - if granting_idx.is_none() || matches!(tag, SbTagExtra::Wildcard) { + if granting_idx.is_none() || matches!(tag, ProvenanceExtra::Wildcard) { // Compute the upper bound of the items that remain. // (This is why we did all the work above: to reduce the items we have to consider here.) let mut max = NonZeroU64::new(1).unwrap(); @@ -512,7 +486,7 @@ impl<'tcx> Stack { /// active protectors at all because we will remove all items. fn dealloc( &mut self, - tag: SbTagExtra, + tag: ProvenanceExtra, (alloc_id, _alloc_range, _offset): (AllocId, AllocRange, Size), // just for debug printing and error messages global: &GlobalStateInner, alloc_history: &mut AllocHistory, @@ -546,7 +520,7 @@ impl<'tcx> Stack { /// `range` that we are currently checking. fn grant( &mut self, - derived_from: SbTagExtra, + derived_from: ProvenanceExtra, new: Item, (alloc_id, alloc_range, offset): (AllocId, AllocRange, Size), // just for debug printing and error messages global: &mut GlobalStateInner, @@ -575,7 +549,7 @@ impl<'tcx> Stack { "this case only makes sense for stack-like accesses" ); - let (Some(granting_idx), SbTagExtra::Concrete(_)) = (granting_idx, derived_from) else { + let (Some(granting_idx), ProvenanceExtra::Concrete(_)) = (granting_idx, derived_from) else { // The parent is a wildcard pointer or matched the unknown bottom. // This is approximate. Nobody knows what happened, so forget everything. // The new thing is SRW anyway, so we cannot push it "on top of the unkown part" @@ -686,7 +660,7 @@ impl Stacks { pub fn memory_read<'tcx>( &mut self, alloc_id: AllocId, - tag: SbTagExtra, + tag: ProvenanceExtra, range: AllocRange, state: &GlobalState, mut current_span: CurrentSpan<'_, '_, 'tcx>, @@ -717,7 +691,7 @@ impl Stacks { pub fn memory_written<'tcx>( &mut self, alloc_id: AllocId, - tag: SbTagExtra, + tag: ProvenanceExtra, range: AllocRange, state: &GlobalState, mut current_span: CurrentSpan<'_, '_, 'tcx>, @@ -748,7 +722,7 @@ impl Stacks { pub fn memory_deallocated<'tcx>( &mut self, alloc_id: AllocId, - tag: SbTagExtra, + tag: ProvenanceExtra, range: AllocRange, state: &GlobalState, threads: &ThreadManager<'_, 'tcx>, @@ -770,7 +744,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// happened. fn reborrow( &mut self, - place: &MPlaceTy<'tcx, Tag>, + place: &MPlaceTy<'tcx, Provenance>, size: Size, kind: RefKind, new_tag: SbTag, @@ -782,7 +756,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // It is crucial that this gets called on all code paths, to ensure we track tag creation. let log_creation = |this: &MiriEvalContext<'mir, 'tcx>, current_span: &mut CurrentSpan<'_, 'mir, 'tcx>, - loc: Option<(AllocId, Size, SbTagExtra)>| // alloc_id, base_offset, orig_tag + loc: Option<(AllocId, Size, ProvenanceExtra)>| // alloc_id, base_offset, orig_tag -> InterpResult<'tcx> { let global = this.machine.stacked_borrows.as_ref().unwrap().borrow(); if global.tracked_pointer_tags.contains(&new_tag) { @@ -798,25 +772,36 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx }; // The SB history tracking needs a parent tag, so skip if we come from a wildcard. - let SbTagExtra::Concrete(orig_tag) = orig_tag else { + let ProvenanceExtra::Concrete(orig_tag) = orig_tag else { // FIXME: should we log this? return Ok(()) }; - let extra = this.get_alloc_extra(alloc_id)?; - let mut stacked_borrows = extra - .stacked_borrows - .as_ref() - .expect("we should have Stacked Borrows data") - .borrow_mut(); - stacked_borrows.history.log_creation( - Some(orig_tag), - new_tag, - alloc_range(base_offset, size), - current_span, - ); - if protect { - stacked_borrows.history.log_protector(orig_tag, new_tag, current_span); + let (_size, _align, kind) = this.get_alloc_info(alloc_id); + match kind { + AllocKind::LiveData => { + // This should have alloc_extra data, but `get_alloc_extra` can still fail + // if converting this alloc_id from a global to a local one + // uncovers a non-supported `extern static`. + let extra = this.get_alloc_extra(alloc_id)?; + let mut stacked_borrows = extra + .stacked_borrows + .as_ref() + .expect("we should have Stacked Borrows data") + .borrow_mut(); + stacked_borrows.history.log_creation( + Some(orig_tag), + new_tag, + alloc_range(base_offset, size), + current_span, + ); + if protect { + stacked_borrows.history.log_protector(orig_tag, new_tag, current_span); + } + } + AllocKind::Function | AllocKind::Dead => { + // No stacked borrows on these allocations. + } } Ok(()) }; @@ -972,10 +957,10 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// `mutbl` can be `None` to make this a raw pointer. fn retag_reference( &mut self, - val: &ImmTy<'tcx, Tag>, + val: &ImmTy<'tcx, Provenance>, kind: RefKind, protect: bool, - ) -> InterpResult<'tcx, ImmTy<'tcx, Tag>> { + ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { let this = self.eval_context_mut(); // We want a place for where the ptr *points to*, so we get one. let place = this.ref_to_mplace(val)?; @@ -985,7 +970,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // See https://github.com/rust-lang/unsafe-code-guidelines/issues/276. let size = match size { Some(size) => size, - None => return Ok(*val), + None => return Ok(val.clone()), }; // Compute new borrow. @@ -1001,12 +986,12 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Some(alloc_id) => { // If `reborrow` could figure out the AllocId of this ptr, hard-code it into the new one. // Even if we started out with a wildcard, this newly retagged pointer is tied to that allocation. - Tag::Concrete { alloc_id, sb: new_tag } + Provenance::Concrete { alloc_id, sb: new_tag } } None => { // Looks like this has to stay a wildcard pointer. - assert!(matches!(prov, Tag::Wildcard)); - Tag::Wildcard + assert!(matches!(prov, Provenance::Wildcard)); + Provenance::Wildcard } } }) @@ -1019,7 +1004,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { - fn retag(&mut self, kind: RetagKind, place: &PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { + fn retag(&mut self, kind: RetagKind, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let retag_fields = this.machine.stacked_borrows.as_mut().unwrap().get_mut().retag_fields; let mut visitor = RetagVisitor { ecx: this, kind, retag_fields }; @@ -1057,7 +1042,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx #[inline(always)] // yes this helps in our benchmarks fn retag_place( &mut self, - place: &PlaceTy<'tcx, Tag>, + place: &PlaceTy<'tcx, Provenance>, ref_kind: RefKind, protector: bool, ) -> InterpResult<'tcx> { @@ -1070,14 +1055,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx impl<'ecx, 'mir, 'tcx> MutValueVisitor<'mir, 'tcx, Evaluator<'mir, 'tcx>> for RetagVisitor<'ecx, 'mir, 'tcx> { - type V = PlaceTy<'tcx, Tag>; + type V = PlaceTy<'tcx, Provenance>; #[inline(always)] fn ecx(&mut self) -> &mut MiriEvalContext<'mir, 'tcx> { self.ecx } - fn visit_box(&mut self, place: &PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { + fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { // Boxes do not get a protector: protectors reflect that references outlive the call // they were passed in to; that's just not the case for boxes. self.retag_place( @@ -1087,7 +1072,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) } - fn visit_value(&mut self, place: &PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { + fn visit_value(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { if let Some((ref_kind, protector)) = qualify(place.layout.ty, self.kind) { self.retag_place(place, ref_kind, protector)?; } else if matches!(place.layout.ty.kind(), ty::RawPtr(..)) { @@ -1116,13 +1101,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx /// explicit. Also see . fn retag_return_place(&mut self) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let return_place = this.frame_mut().return_place; + let return_place = &this.frame().return_place; if return_place.layout.is_zst() { // There may not be any memory here, nothing to do. return Ok(()); } // We need this to be in-memory to use tagged pointers. - let return_place = this.force_allocation(&return_place)?; + let return_place = this.force_allocation(&return_place.clone())?; // We have to turn the place into a pointer to use the existing code. // (The pointer type does not matter, so we use a raw pointer.) @@ -1142,7 +1127,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } /// Mark the given tag as exposed. It was found on a pointer with the given AllocId. - fn expose_tag(&mut self, alloc_id: AllocId, tag: SbTag) { + fn expose_tag(&mut self, alloc_id: AllocId, tag: SbTag) -> InterpResult<'tcx> { let this = self.eval_context_mut(); // Function pointers and dead objects don't have an alloc_extra so we ignore them. @@ -1151,8 +1136,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let (_size, _align, kind) = this.get_alloc_info(alloc_id); match kind { AllocKind::LiveData => { - // This should have alloc_extra data. - let alloc_extra = this.get_alloc_extra(alloc_id).unwrap(); + // This should have alloc_extra data, but `get_alloc_extra` can still fail + // if converting this alloc_id from a global to a local one + // uncovers a non-supported `extern static`. + let alloc_extra = this.get_alloc_extra(alloc_id)?; trace!("Stacked Borrows tag {tag:?} exposed in {alloc_id:?}"); alloc_extra.stacked_borrows.as_ref().unwrap().borrow_mut().exposed_tags.insert(tag); } @@ -1160,5 +1147,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // No stacked borrows on these allocations. } } + Ok(()) } } diff --git a/src/stacked_borrows/stack.rs b/src/stacked_borrows/stack.rs index bc7ffd4fae..c0d4d0d291 100644 --- a/src/stacked_borrows/stack.rs +++ b/src/stacked_borrows/stack.rs @@ -1,8 +1,11 @@ -use crate::stacked_borrows::{AccessKind, Item, Permission, SbTag, SbTagExtra}; -use rustc_data_structures::fx::FxHashSet; #[cfg(feature = "stack-cache")] use std::ops::Range; +use rustc_data_structures::fx::FxHashSet; + +use crate::stacked_borrows::{AccessKind, Item, Permission, SbTag}; +use crate::ProvenanceExtra; + /// Exactly what cache size we should use is a difficult tradeoff. There will always be some /// workload which has a `SbTag` working set which exceeds the size of the cache, and ends up /// falling back to linear searches of the borrow stack very often. @@ -92,6 +95,7 @@ impl<'tcx> Stack { } } + // Check that all Unique items fall within unique_range. for (idx, item) in self.borrows.iter().enumerate() { if item.perm() == Permission::Unique { assert!( @@ -102,6 +106,19 @@ impl<'tcx> Stack { ); } } + + // Check that the unique_range is a valid index into the borrow stack. + // This asserts that the unique_range's start <= end. + let uniques = &self.borrows[self.unique_range.clone()]; + + // Check that the start of the unique_range is precise. + if let Some(first_unique) = uniques.first() { + assert_eq!(first_unique.perm(), Permission::Unique); + } + // We cannot assert that the unique range is exact on the upper end. + // When we pop items within the unique range, setting the end of the range precisely + // requires doing a linear search of the borrow stack, which is exactly the kind of + // operation that all this caching exists to avoid. } /// Find the item granting the given kind of access to the given tag, and return where @@ -112,13 +129,13 @@ impl<'tcx> Stack { pub(super) fn find_granting( &mut self, access: AccessKind, - tag: SbTagExtra, + tag: ProvenanceExtra, exposed_tags: &FxHashSet, ) -> Result, ()> { #[cfg(debug_assertions)] self.verify_cache_consistency(); - let SbTagExtra::Concrete(tag) = tag else { + let ProvenanceExtra::Concrete(tag) = tag else { // Handle the wildcard case. // Go search the stack for an exposed tag. if let Some(idx) = @@ -227,9 +244,14 @@ impl<'tcx> Stack { self.unique_range.end += 1; } if new.perm() == Permission::Unique { - // Make sure the possibly-unique range contains the new borrow - self.unique_range.start = self.unique_range.start.min(new_idx); - self.unique_range.end = self.unique_range.end.max(new_idx + 1); + // If this is the only Unique, set the range to contain just the new item. + if self.unique_range.is_empty() { + self.unique_range = new_idx..new_idx + 1; + } else { + // We already have other Unique items, expand the range to include the new item + self.unique_range.start = self.unique_range.start.min(new_idx); + self.unique_range.end = self.unique_range.end.max(new_idx + 1); + } } // The above insert changes the meaning of every index in the cache >= new_idx, so now @@ -282,6 +304,10 @@ impl<'tcx> Stack { // cache when it has been cleared and not yet refilled. self.borrows.clear(); self.unknown_bottom = Some(tag); + #[cfg(feature = "stack-cache")] + { + self.unique_range = 0..0; + } } /// Find all `Unique` elements in this borrow stack above `granting_idx`, pass a copy of them @@ -298,7 +324,7 @@ impl<'tcx> Stack { if disable_start <= unique_range.end { let lower = unique_range.start.max(disable_start); - let upper = (unique_range.end + 1).min(self.borrows.len()); + let upper = self.unique_range.end; for item in &mut self.borrows[lower..upper] { if item.perm() == Permission::Unique { log::trace!("access: disabling item {:?}", item); @@ -315,14 +341,14 @@ impl<'tcx> Stack { } #[cfg(feature = "stack-cache")] - if disable_start < self.unique_range.start { + if disable_start <= self.unique_range.start { // We disabled all Unique items self.unique_range.start = 0; self.unique_range.end = 0; } else { - // Truncate the range to disable_start. This is + 2 because we are only removing - // elements after disable_start, and this range does not include the end. - self.unique_range.end = self.unique_range.end.min(disable_start + 1); + // Truncate the range to only include items up to the index that we started disabling + // at. + self.unique_range.end = self.unique_range.end.min(disable_start); } #[cfg(debug_assertions)] @@ -369,12 +395,12 @@ impl<'tcx> Stack { self.cache.items[i] = base_tag; } - if start < self.unique_range.start.saturating_sub(1) { + if start <= self.unique_range.start { // We removed all the Unique items self.unique_range = 0..0; } else { // Ensure the range doesn't extend past the new top of the stack - self.unique_range.end = self.unique_range.end.min(start + 1); + self.unique_range.end = self.unique_range.end.min(start); } } else { self.unique_range = 0..0; diff --git a/src/sync.rs b/src/sync.rs index 0eebe4f654..5571bbd8f2 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -42,7 +42,7 @@ macro_rules! declare_id { } impl $name { - pub fn to_u32_scalar<'tcx>(&self) -> Scalar { + pub fn to_u32_scalar<'tcx>(&self) -> Scalar { Scalar::from_u32(self.0.get()) } } diff --git a/src/thread.rs b/src/thread.rs index 96135d093d..683694f482 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -70,7 +70,7 @@ impl From for ThreadId { } impl ThreadId { - pub fn to_u32_scalar(&self) -> Scalar { + pub fn to_u32_scalar(&self) -> Scalar { Scalar::from_u32(self.0) } } @@ -112,7 +112,7 @@ pub struct Thread<'mir, 'tcx> { thread_name: Option>, /// The virtual call stack. - stack: Vec>>, + stack: Vec>>, /// The join status. join_status: ThreadJoinStatus, @@ -120,10 +120,10 @@ pub struct Thread<'mir, 'tcx> { /// The temporary used for storing the argument of /// the call to `miri_start_panic` (the panic payload) when unwinding. /// This is pointer-sized, and matches the `Payload` type in `src/libpanic_unwind/miri.rs`. - pub(crate) panic_payload: Option>, + pub(crate) panic_payload: Option>, /// Last OS error location in memory. It is a 32-bit integer. - pub(crate) last_error: Option>, + pub(crate) last_error: Option>, } impl<'mir, 'tcx> Thread<'mir, 'tcx> { @@ -227,7 +227,7 @@ pub struct ThreadManager<'mir, 'tcx> { pub(crate) sync: SynchronizationState, /// A mapping from a thread-local static to an allocation id of a thread /// specific allocation. - thread_local_alloc_ids: RefCell>>, + thread_local_alloc_ids: RefCell>>, /// A flag that indicates that we should change the active thread. yield_active_thread: bool, /// Callbacks that are called once the specified time passes. @@ -256,7 +256,7 @@ impl<'mir, 'tcx> Default for ThreadManager<'mir, 'tcx> { impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { /// Check if we have an allocation for the given thread local static for the /// active thread. - fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option> { + fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option> { self.thread_local_alloc_ids.borrow().get(&(def_id, self.active_thread)).cloned() } @@ -264,7 +264,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { /// static for the active thread. /// /// Panics if a thread local is initialized twice for the same thread. - fn set_thread_local_alloc(&self, def_id: DefId, ptr: Pointer) { + fn set_thread_local_alloc(&self, def_id: DefId, ptr: Pointer) { self.thread_local_alloc_ids .borrow_mut() .try_insert((def_id, self.active_thread), ptr) @@ -272,16 +272,20 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { } /// Borrow the stack of the active thread. - pub fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Tag, FrameData<'tcx>>] { + pub fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>] { &self.threads[self.active_thread].stack } /// Mutably borrow the stack of the active thread. - fn active_thread_stack_mut(&mut self) -> &mut Vec>> { + fn active_thread_stack_mut( + &mut self, + ) -> &mut Vec>> { &mut self.threads[self.active_thread].stack } - pub fn all_stacks(&self) -> impl Iterator>]> { + pub fn all_stacks( + &self, + ) -> impl Iterator>]> { self.threads.iter().map(|t| &t.stack[..]) } @@ -468,7 +472,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { fn thread_terminated( &mut self, mut data_race: Option<&mut data_race::GlobalState>, - ) -> Vec> { + ) -> Vec> { let mut free_tls_statics = Vec::new(); { let mut thread_local_statics = self.thread_local_alloc_ids.borrow_mut(); @@ -589,7 +593,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn get_or_create_thread_local_alloc( &mut self, def_id: DefId, - ) -> InterpResult<'tcx, Pointer> { + ) -> InterpResult<'tcx, Pointer> { let this = self.eval_context_mut(); let tcx = this.tcx; if let Some(old_alloc) = this.machine.threads.get_thread_local_alloc_id(def_id) { @@ -686,13 +690,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } #[inline] - fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Tag, FrameData<'tcx>>] { + fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>] { let this = self.eval_context_ref(); this.machine.threads.active_thread_stack() } #[inline] - fn active_thread_stack_mut(&mut self) -> &mut Vec>> { + fn active_thread_stack_mut( + &mut self, + ) -> &mut Vec>> { let this = self.eval_context_mut(); this.machine.threads.active_thread_stack_mut() } diff --git a/test-cargo-miri/Cargo.lock b/test-cargo-miri/Cargo.lock index d34db9f14d..3f61fb3d54 100644 --- a/test-cargo-miri/Cargo.lock +++ b/test-cargo-miri/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "byteorder" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" + [[package]] name = "byteorder" version = "1.4.3" @@ -12,18 +18,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" name = "cargo-miri-test" version = "0.1.0" dependencies = [ - "byteorder", + "byteorder 0.5.3", + "byteorder 1.4.3", "cdylib", "exported_symbol", - "getrandom 0.1.16", - "getrandom 0.2.7", "issue_1567", "issue_1691", "issue_1705", "issue_1760", "issue_rust_86261", - "page_size", - "rand", "serde_derive", ] @@ -31,15 +34,9 @@ dependencies = [ name = "cdylib" version = "0.1.0" dependencies = [ - "byteorder", + "byteorder 1.4.3", ] -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - [[package]] name = "exported_symbol" version = "0.1.0" @@ -51,28 +48,6 @@ dependencies = [ name = "exported_symbol_dep" version = "0.1.0" -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - [[package]] name = "hermit-abi" version = "0.1.19" @@ -86,7 +61,7 @@ dependencies = [ name = "issue_1567" version = "0.1.0" dependencies = [ - "byteorder", + "byteorder 1.4.3", ] [[package]] @@ -97,7 +72,7 @@ version = "0.1.0" name = "issue_1705" version = "0.1.0" dependencies = [ - "byteorder", + "byteorder 1.4.3", ] [[package]] @@ -124,22 +99,6 @@ dependencies = [ "libc", ] -[[package]] -name = "page_size" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebde548fbbf1ea81a99b128872779c437752fb99f217c45245e1a61dcd9edcd" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - [[package]] name = "proc-macro2" version = "1.0.40" @@ -158,36 +117,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom 0.2.7", -] - [[package]] name = "serde_derive" version = "1.0.137" @@ -222,37 +151,3 @@ name = "unicode-ident" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/test-cargo-miri/Cargo.toml b/test-cargo-miri/Cargo.toml index 2193d354d5..51967c54e1 100644 --- a/test-cargo-miri/Cargo.toml +++ b/test-cargo-miri/Cargo.toml @@ -18,11 +18,8 @@ issue_1760 = { path = "issue-1760" } issue_rust_86261 = { path = "issue-rust-86261" } [dev-dependencies] -rand = { version = "0.8", features = ["small_rng"] } -getrandom_1 = { package = "getrandom", version = "0.1" } -getrandom_2 = { package = "getrandom", version = "0.2" } +byteorder_2 = { package = "byteorder", version = "0.5" } # to test dev-dependencies behave as expected, with renaming serde_derive = "1.0" # not actually used, but exercises some unique code path (`--extern` .so file) -page_size = "0.4.1" [lib] test = false # test that this is respected (will show in the output) diff --git a/test-cargo-miri/cdylib/Cargo.toml b/test-cargo-miri/cdylib/Cargo.toml index 4e5b5601a5..527602e0a8 100644 --- a/test-cargo-miri/cdylib/Cargo.toml +++ b/test-cargo-miri/cdylib/Cargo.toml @@ -9,4 +9,4 @@ edition = "2018" crate-type = ["cdylib"] [dependencies] -byteorder = "1.0" +byteorder = "1.0" # to test dependencies of sub-crates diff --git a/test-cargo-miri/run-test.py b/test-cargo-miri/run-test.py index 09e7df39b1..ab43c72511 100755 --- a/test-cargo-miri/run-test.py +++ b/test-cargo-miri/run-test.py @@ -5,7 +5,7 @@ and the working directory to contain the cargo-miri-test project. ''' -import sys, subprocess, os, re +import sys, subprocess, os, re, difflib CGREEN = '\33[32m' CBOLD = '\33[1m' @@ -27,6 +27,17 @@ def normalize_stdout(str): str = str.replace("src\\", "src/") # normalize paths across platforms return re.sub("finished in \d+\.\d\ds", "finished in $TIME", str) +def check_output(actual, path, name): + expected = open(path).read() + if expected == actual: + return True + print(f"{path} did not match reference!") + print(f"--- BEGIN diff {name} ---") + for text in difflib.unified_diff(expected.split("\n"), actual.split("\n")): + print(text) + print(f"--- END diff {name} ---") + return False + def test(name, cmd, stdout_ref, stderr_ref, stdin=b'', env={}): print("Testing {}...".format(name)) ## Call `cargo miri`, capture all output @@ -42,17 +53,14 @@ def test(name, cmd, stdout_ref, stderr_ref, stdin=b'', env={}): (stdout, stderr) = p.communicate(input=stdin) stdout = stdout.decode("UTF-8") stderr = stderr.decode("UTF-8") - if p.returncode == 0 and normalize_stdout(stdout) == open(stdout_ref).read() and stderr == open(stderr_ref).read(): + stdout = normalize_stdout(stdout) + + stdout_matches = check_output(stdout, stdout_ref, "stdout") + stderr_matches = check_output(stderr, stderr_ref, "stderr") + + if p.returncode == 0 and stdout_matches and stderr_matches: # All good! return - # Show output - print("Test stdout or stderr did not match reference!") - print("--- BEGIN test stdout ---") - print(stdout, end="") - print("--- END test stdout ---") - print("--- BEGIN test stderr ---") - print(stderr, end="") - print("--- END test stderr ---") fail("exit code was {}".format(p.returncode)) def test_no_rebuild(name, cmd, env={}): @@ -126,7 +134,7 @@ def test_cargo_miri_test(): env={'MIRIFLAGS': "-Zmiri-permissive-provenance -Zmiri-disable-isolation"}, ) test("`cargo miri test` (with filter)", - cargo_miri("test") + ["--", "--format=pretty", "le1"], + cargo_miri("test") + ["--", "--format=pretty", "pl"], filter_ref, "test.stderr-empty.ref", ) test("`cargo miri test` (test target)", diff --git a/test-cargo-miri/src/main.rs b/test-cargo-miri/src/main.rs index 5807d0765f..41c52b7017 100644 --- a/test-cargo-miri/src/main.rs +++ b/test-cargo-miri/src/main.rs @@ -45,17 +45,12 @@ fn main() { #[cfg(test)] mod test { - use rand::{Rng, SeedableRng}; + use byteorder_2::{BigEndian, ByteOrder}; // Make sure in-crate tests with dev-dependencies work #[test] - fn rng() { - let mut rng = rand::rngs::StdRng::seed_from_u64(0xcafebeef); - let x: u32 = rng.gen(); - let y: usize = rng.gen(); - let z: u128 = rng.gen(); - assert_ne!(x as usize, y); - assert_ne!(y as u128, z); + fn dev_dependency() { + let _n = ::read_u64(&[1, 2, 3, 4, 5, 6, 7, 8]); } #[test] diff --git a/test-cargo-miri/test.bin-target.stdout.ref b/test-cargo-miri/test.bin-target.stdout.ref index 62cfd1d37a..5264530160 100644 --- a/test-cargo-miri/test.bin-target.stdout.ref +++ b/test-cargo-miri/test.bin-target.stdout.ref @@ -1,7 +1,7 @@ running 2 tests +test test::dev_dependency ... ok test test::exported_symbol ... ok -test test::rng ... ok test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out diff --git a/test-cargo-miri/test.cross-target.stdout.ref b/test-cargo-miri/test.cross-target.stdout.ref index 3673e5549d..8c543e479f 100644 --- a/test-cargo-miri/test.cross-target.stdout.ref +++ b/test-cargo-miri/test.cross-target.stdout.ref @@ -5,7 +5,7 @@ test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out imported main -running 8 tests -..i..... -test result: ok. 7 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out +running 6 tests +...i.. +test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out diff --git a/test-cargo-miri/test.default.stdout.ref b/test-cargo-miri/test.default.stdout.ref index a59108efb3..9a17f3d61b 100644 --- a/test-cargo-miri/test.default.stdout.ref +++ b/test-cargo-miri/test.default.stdout.ref @@ -5,9 +5,9 @@ test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out imported main -running 8 tests -..i..... -test result: ok. 7 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out +running 6 tests +...i.. +test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out running 4 tests diff --git a/test-cargo-miri/test.filter.cross-target.stdout.ref b/test-cargo-miri/test.filter.cross-target.stdout.ref index 9fb7670d06..bb0282d6c9 100644 --- a/test-cargo-miri/test.filter.cross-target.stdout.ref +++ b/test-cargo-miri/test.filter.cross-target.stdout.ref @@ -6,7 +6,7 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out imported main running 1 test -test simple1 ... ok +test simple ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 7 filtered out +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out diff --git a/test-cargo-miri/test.filter.stdout.ref b/test-cargo-miri/test.filter.stdout.ref index 4b598960a0..c618956656 100644 --- a/test-cargo-miri/test.filter.stdout.ref +++ b/test-cargo-miri/test.filter.stdout.ref @@ -6,9 +6,9 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out imported main running 1 test -test simple1 ... ok +test simple ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 7 filtered out +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out running 0 tests diff --git a/test-cargo-miri/test.test-target.stdout.ref b/test-cargo-miri/test.test-target.stdout.ref index ca069b702e..dd59b32b78 100644 --- a/test-cargo-miri/test.test-target.stdout.ref +++ b/test-cargo-miri/test.test-target.stdout.ref @@ -1,13 +1,11 @@ -running 8 tests +running 6 tests test cargo_env ... ok +test deps ... ok test do_panic - should panic ... ok test does_not_work_on_miri ... ignored -test entropy_rng ... ok test fail_index_check - should panic ... ok -test page_size ... ok -test simple1 ... ok -test simple2 ... ok +test simple ... ok -test result: ok. 7 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out +test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out diff --git a/test-cargo-miri/tests/test.rs b/test-cargo-miri/tests/test.rs index 8a938ef3c2..9ed2152893 100644 --- a/test-cargo-miri/tests/test.rs +++ b/test-cargo-miri/tests/test.rs @@ -1,45 +1,30 @@ -use rand::{rngs::SmallRng, Rng, SeedableRng}; - -// Having more than 1 test does seem to make a difference -// (i.e., this calls ptr::swap which having just one test does not). #[test] -fn simple1() { +fn simple() { assert_eq!(4, 4); } -#[test] -fn simple2() { - assert_ne!(42, 24); -} - // A test that won't work on miri (tests disabling tests). #[test] #[cfg_attr(miri, ignore)] fn does_not_work_on_miri() { - let x = 0u8; - assert!(&x as *const _ as usize % 4 < 4); + // Only do this where inline assembly is stable. + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + unsafe { + std::arch::asm!("foo"); + } } -// We also use this to test some external crates, that we cannot depend on in the compiletest suite. - +// Make sure integration tests can access both dependencies and dev-dependencies #[test] -fn entropy_rng() { - // Test `getrandom` directly (in multiple different versions). - let mut data = vec![0; 16]; - getrandom_1::getrandom(&mut data).unwrap(); - getrandom_2::getrandom(&mut data).unwrap(); - - // Try seeding with "real" entropy. - let mut rng = SmallRng::from_entropy(); - let _val = rng.gen::(); - let _val = rng.gen::(); - let _val = rng.gen::(); - - // Also try per-thread RNG. - let mut rng = rand::thread_rng(); - let _val = rng.gen::(); - let _val = rng.gen::(); - let _val = rng.gen::(); +fn deps() { + { + use byteorder::{BigEndian, ByteOrder}; + let _n = ::read_u64(&[1, 2, 3, 4, 5, 6, 7, 8]); + } + { + use byteorder_2::{BigEndian, ByteOrder}; + let _n = ::read_u64(&[1, 2, 3, 4, 5, 6, 7, 8]); + } } #[test] @@ -55,17 +40,10 @@ fn do_panic() // In large, friendly letters :) panic!("Explicit panic from test!"); } +// A different way of raising a panic #[test] #[allow(unconditional_panic)] #[should_panic(expected = "the len is 0 but the index is 42")] fn fail_index_check() { [][42] } - -#[test] -fn page_size() { - let page_size = page_size::get(); - - // In particular, this checks that it is not 0. - assert!(page_size.is_power_of_two(), "page size not a power of two: {}", page_size); -} diff --git a/test_dependencies/Cargo.lock b/test_dependencies/Cargo.lock new file mode 100644 index 0000000000..a492deba4c --- /dev/null +++ b/test_dependencies/Cargo.lock @@ -0,0 +1,387 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "miri-test-deps" +version = "0.1.0" +dependencies = [ + "getrandom 0.1.16", + "getrandom 0.2.7", + "libc", + "page_size", + "rand", + "tokio", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + +[[package]] +name = "page_size" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebde548fbbf1ea81a99b128872779c437752fb99f217c45245e1a61dcd9edcd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro2" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom 0.2.7", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tokio" +version = "1.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" diff --git a/test_dependencies/Cargo.toml b/test_dependencies/Cargo.toml new file mode 100644 index 0000000000..0bf43aefeb --- /dev/null +++ b/test_dependencies/Cargo.toml @@ -0,0 +1,20 @@ +[package] +authors = ["Miri Team"] +description = "dependencies that unit tests can have" +license = "MIT OR Apache-2.0" +name = "miri-test-deps" +repository = "https://github.com/rust-lang/miri" +version = "0.1.0" +edition = "2021" + +[dependencies] +# all dependencies (and their transitive ones) listed here can be used in `tests/`. +tokio = { version = "1.0", features = ["full"] } +libc = "0.2" +page_size = "0.4.1" + +getrandom_1 = { package = "getrandom", version = "0.1" } +getrandom_2 = { package = "getrandom", version = "0.2" } +rand = { version = "0.8", features = ["small_rng"] } + +[workspace] diff --git a/test_dependencies/src/main.rs b/test_dependencies/src/main.rs new file mode 100644 index 0000000000..f328e4d9d0 --- /dev/null +++ b/test_dependencies/src/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 539f485818..3493c3a524 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -1,9 +1,8 @@ use colored::*; use regex::Regex; -use std::env; -use std::path::PathBuf; -use std::process::Command; -use ui_test::{color_eyre::Result, Config, Mode, OutputConflictHandling}; +use std::path::{Path, PathBuf}; +use std::{env, ffi::OsString, process::Command}; +use ui_test::{color_eyre::Result, Config, DependencyBuilder, Mode, OutputConflictHandling}; fn miri_path() -> PathBuf { PathBuf::from(option_env!("MIRI").unwrap_or(env!("CARGO_BIN_EXE_miri"))) @@ -13,31 +12,31 @@ fn run_tests(mode: Mode, path: &str, target: Option) -> Result<()> { let in_rustc_test_suite = option_env!("RUSTC_STAGE").is_some(); // Add some flags we always want. - let mut flags = Vec::new(); - flags.push("--edition".to_owned()); - flags.push("2018".to_owned()); + let mut flags: Vec = Vec::new(); + flags.push("--edition".into()); + flags.push("2018".into()); if in_rustc_test_suite { // Less aggressive warnings to make the rustc toolstate management less painful. // (We often get warnings when e.g. a feature gets stabilized or some lint gets added/improved.) - flags.push("-Astable-features".to_owned()); - flags.push("-Aunused".to_owned()); + flags.push("-Astable-features".into()); + flags.push("-Aunused".into()); } else { - flags.push("-Dwarnings".to_owned()); - flags.push("-Dunused".to_owned()); + flags.push("-Dwarnings".into()); + flags.push("-Dunused".into()); } - if let Ok(sysroot) = env::var("MIRI_SYSROOT") { - flags.push("--sysroot".to_string()); + if let Some(sysroot) = env::var_os("MIRI_SYSROOT") { + flags.push("--sysroot".into()); flags.push(sysroot); } if let Ok(extra_flags) = env::var("MIRIFLAGS") { for flag in extra_flags.split_whitespace() { - flags.push(flag.to_string()); + flags.push(flag.into()); } } - flags.push("-Zui-testing".to_string()); + flags.push("-Zui-testing".into()); if let Some(target) = &target { - flags.push("--target".to_string()); - flags.push(target.clone()); + flags.push("--target".into()); + flags.push(target.into()); } // If we're on linux, then build the shared object file for testing external C function calls. @@ -68,8 +67,19 @@ fn run_tests(mode: Mode, path: &str, target: Option) -> Result<()> { (true, true) => panic!("cannot use MIRI_BLESS and MIRI_SKIP_UI_CHECKS at the same time"), }; - // Pass on all arguments as filters. - let path_filter = std::env::args().skip(1); + // Pass on all unknown arguments as filters. + let mut quiet = false; + let path_filter = std::env::args().skip(1).filter(|arg| { + match &**arg { + "--quiet" => { + quiet = true; + false + } + _ => true, + } + }); + + let use_std = env::var_os("MIRI_NO_STD").is_none(); let config = Config { args: flags, @@ -81,6 +91,20 @@ fn run_tests(mode: Mode, path: &str, target: Option) -> Result<()> { path_filter: path_filter.collect(), program: miri_path(), output_conflict_handling, + dependencies_crate_manifest_path: use_std + .then(|| Path::new("test_dependencies").join("Cargo.toml")), + dependency_builder: Some(DependencyBuilder { + program: std::env::var_os("CARGO").unwrap().into(), + args: vec![ + "run".into(), + "--manifest-path".into(), + "cargo-miri/Cargo.toml".into(), + "--".into(), + "miri".into(), + ], + envs: vec![], + }), + quiet, }; ui_test::run_tests(config) } @@ -127,6 +151,8 @@ regexes! { "[^ `]*/(rust[^/]*|checkout)/library/" => "RUSTLIB/", // erase platform file paths "sys/[a-z]+/" => "sys/PLATFORM/", + // erase paths into the crate registry + r"[^ ]*/\.cargo/registry/.*/(.*\.rs)" => "CARGO_REGISTRY/$1", } fn ui(mode: Mode, path: &str) -> Result<()> { diff --git a/tests/fail/concurrency/libc_pthread_create_main_terminate.rs b/tests/fail/concurrency/libc_pthread_create_main_terminate.rs index 169a021215..9da9fbf202 100644 --- a/tests/fail/concurrency/libc_pthread_create_main_terminate.rs +++ b/tests/fail/concurrency/libc_pthread_create_main_terminate.rs @@ -5,8 +5,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::{mem, ptr}; extern "C" fn thread_start(_null: *mut libc::c_void) -> *mut libc::c_void { diff --git a/tests/fail/concurrency/libc_pthread_join_detached.rs b/tests/fail/concurrency/libc_pthread_join_detached.rs index 6f0d45e2dc..e81978fc99 100644 --- a/tests/fail/concurrency/libc_pthread_join_detached.rs +++ b/tests/fail/concurrency/libc_pthread_join_detached.rs @@ -4,8 +4,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::{mem, ptr}; extern "C" fn thread_start(_null: *mut libc::c_void) -> *mut libc::c_void { diff --git a/tests/fail/concurrency/libc_pthread_join_joined.rs b/tests/fail/concurrency/libc_pthread_join_joined.rs index 77f59fabca..11e00429c6 100644 --- a/tests/fail/concurrency/libc_pthread_join_joined.rs +++ b/tests/fail/concurrency/libc_pthread_join_joined.rs @@ -4,8 +4,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::{mem, ptr}; extern "C" fn thread_start(_null: *mut libc::c_void) -> *mut libc::c_void { diff --git a/tests/fail/concurrency/libc_pthread_join_main.rs b/tests/fail/concurrency/libc_pthread_join_main.rs index aff28bcc9b..f029f08772 100644 --- a/tests/fail/concurrency/libc_pthread_join_main.rs +++ b/tests/fail/concurrency/libc_pthread_join_main.rs @@ -4,8 +4,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::{ptr, thread}; fn main() { diff --git a/tests/fail/concurrency/libc_pthread_join_multiple.rs b/tests/fail/concurrency/libc_pthread_join_multiple.rs index d4d54d3a23..017036ab01 100644 --- a/tests/fail/concurrency/libc_pthread_join_multiple.rs +++ b/tests/fail/concurrency/libc_pthread_join_multiple.rs @@ -4,8 +4,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::thread; use std::{mem, ptr}; diff --git a/tests/fail/concurrency/libc_pthread_join_self.rs b/tests/fail/concurrency/libc_pthread_join_self.rs index b911b2db3a..ae61488931 100644 --- a/tests/fail/concurrency/libc_pthread_join_self.rs +++ b/tests/fail/concurrency/libc_pthread_join_self.rs @@ -6,8 +6,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::{ptr, thread}; fn main() { diff --git a/tests/fail/concurrency/too_few_args.rs b/tests/fail/concurrency/too_few_args.rs index 11e97ca290..4760fbb6b0 100644 --- a/tests/fail/concurrency/too_few_args.rs +++ b/tests/fail/concurrency/too_few_args.rs @@ -4,8 +4,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::{mem, ptr}; extern "C" fn thread_start() -> *mut libc::c_void { diff --git a/tests/fail/concurrency/too_few_args.stderr b/tests/fail/concurrency/too_few_args.stderr index 093a307685..c1eb4d8cb6 100644 --- a/tests/fail/concurrency/too_few_args.stderr +++ b/tests/fail/concurrency/too_few_args.stderr @@ -8,7 +8,7 @@ LL | panic!() = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: backtrace: = note: inside `thread_start` at RUSTLIB/std/src/panic.rs:LL:CC - = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/tests/fail/concurrency/too_many_args.rs b/tests/fail/concurrency/too_many_args.rs index dd44207a62..6abe767dc8 100644 --- a/tests/fail/concurrency/too_many_args.rs +++ b/tests/fail/concurrency/too_many_args.rs @@ -4,8 +4,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::{mem, ptr}; extern "C" fn thread_start(_null: *mut libc::c_void, _x: i32) -> *mut libc::c_void { diff --git a/tests/fail/concurrency/too_many_args.stderr b/tests/fail/concurrency/too_many_args.stderr index 87e22ec1dc..42a96ae626 100644 --- a/tests/fail/concurrency/too_many_args.stderr +++ b/tests/fail/concurrency/too_many_args.stderr @@ -8,7 +8,7 @@ LL | panic!() = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: backtrace: = note: inside `thread_start` at RUSTLIB/std/src/panic.rs:LL:CC - = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/tests/fail/concurrency/unwind_top_of_stack.rs b/tests/fail/concurrency/unwind_top_of_stack.rs index 179ff9c146..bd49401e61 100644 --- a/tests/fail/concurrency/unwind_top_of_stack.rs +++ b/tests/fail/concurrency/unwind_top_of_stack.rs @@ -5,8 +5,6 @@ #![feature(rustc_private, c_unwind)] -extern crate libc; - use std::{mem, ptr}; extern "C-unwind" fn thread_start(_null: *mut libc::c_void) -> *mut libc::c_void { diff --git a/tests/fail/crates/tokio_mvp.rs b/tests/fail/crates/tokio_mvp.rs new file mode 100644 index 0000000000..7cb42c09a9 --- /dev/null +++ b/tests/fail/crates/tokio_mvp.rs @@ -0,0 +1,7 @@ +//@compile-flags: -Zmiri-disable-isolation +//@error-pattern: can't call foreign function: epoll_create1 +//@normalize-stderr-test: " = note: inside .*\n" -> "" +//@only-target-linux: the errors differ too much between platforms + +#[tokio::main] +async fn main() {} diff --git a/tests/fail/crates/tokio_mvp.stderr b/tests/fail/crates/tokio_mvp.stderr new file mode 100644 index 0000000000..cff948f364 --- /dev/null +++ b/tests/fail/crates/tokio_mvp.stderr @@ -0,0 +1,19 @@ +error: unsupported operation: can't call foreign function: epoll_create1 + --> CARGO_REGISTRY/epoll.rs:LL:CC + | +LL | syscall!(epoll_create1(flag)).map(|ep| Selector { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't call foreign function: epoll_create1 + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = note: backtrace: +note: inside `main` at $DIR/tokio_mvp.rs:LL:CC + --> $DIR/tokio_mvp.rs:LL:CC + | +LL | #[tokio::main] + | ^^^^^^^^^^^^^^ + = note: this error originates in the macro `syscall` which comes from the expansion of the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/erroneous_const.stderr b/tests/fail/erroneous_const.stderr index d4b8f25e03..8138d69f40 100644 --- a/tests/fail/erroneous_const.stderr +++ b/tests/fail/erroneous_const.stderr @@ -4,7 +4,7 @@ error[E0080]: evaluation of `PrintName::::VOID` failed LL | const VOID: ! = panic!(); | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/erroneous_const.rs:LL:CC | - = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: post-monomorphization error: referenced constant has errors --> $DIR/erroneous_const.rs:LL:CC diff --git a/tests/fail/erroneous_const2.stderr b/tests/fail/erroneous_const2.stderr index 75e3d9bcd2..05ed8ea1c1 100644 --- a/tests/fail/erroneous_const2.stderr +++ b/tests/fail/erroneous_const2.stderr @@ -22,7 +22,7 @@ LL | println!("{}", FOO); | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #71800 - = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: post-monomorphization error: referenced constant has errors --> $DIR/erroneous_const2.rs:LL:CC diff --git a/tests/fail/extern_static_wrong_size.rs b/tests/fail/extern_static_wrong_size.rs new file mode 100644 index 0000000000..17061f0e5c --- /dev/null +++ b/tests/fail/extern_static_wrong_size.rs @@ -0,0 +1,10 @@ +//@ only-target-linux: we need a specific extern supported on this target +//@normalize-stderr-test: "[48] bytes" -> "N bytes" + +extern "C" { + static mut environ: i8; +} + +fn main() { + let _val = unsafe { environ }; //~ ERROR: /has been declared with a size of 1 bytes and alignment of 1 bytes, but Miri emulates it via an extern static shim with a size of [48] bytes and alignment of [48] bytes/ +} diff --git a/tests/fail/extern_static_wrong_size.stderr b/tests/fail/extern_static_wrong_size.stderr new file mode 100644 index 0000000000..fdeb7bb5f6 --- /dev/null +++ b/tests/fail/extern_static_wrong_size.stderr @@ -0,0 +1,14 @@ +error: unsupported operation: `extern` static `environ` from crate `extern_static_wrong_size` has been declared with a size of 1 bytes and alignment of 1 bytes, but Miri emulates it via an extern static shim with a size of N bytes and alignment of N bytes + --> $DIR/extern_static_wrong_size.rs:LL:CC + | +LL | let _val = unsafe { environ }; + | ^^^^^^^ `extern` static `environ` from crate `extern_static_wrong_size` has been declared with a size of 1 bytes and alignment of 1 bytes, but Miri emulates it via an extern static shim with a size of N bytes and alignment of N bytes + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = note: backtrace: + = note: inside `main` at $DIR/extern_static_wrong_size.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/tests/fail/fs/close_stdout.rs b/tests/fail/fs/close_stdout.rs index bc709fe36d..86a6239f5f 100644 --- a/tests/fail/fs/close_stdout.rs +++ b/tests/fail/fs/close_stdout.rs @@ -5,8 +5,6 @@ #![feature(rustc_private)] -extern crate libc; - fn main() { unsafe { libc::close(1); //~ ERROR: stdout cannot be closed diff --git a/tests/fail/fs/isolated_stdin.rs b/tests/fail/fs/isolated_stdin.rs index cd54de3bce..86b04a0383 100644 --- a/tests/fail/fs/isolated_stdin.rs +++ b/tests/fail/fs/isolated_stdin.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - fn main() -> std::io::Result<()> { let mut bytes = [0u8; 512]; unsafe { diff --git a/tests/fail/fs/read_from_stdout.rs b/tests/fail/fs/read_from_stdout.rs index 949fe88f43..0fd8ba2fc4 100644 --- a/tests/fail/fs/read_from_stdout.rs +++ b/tests/fail/fs/read_from_stdout.rs @@ -3,8 +3,6 @@ #![feature(rustc_private)] -extern crate libc; - fn main() -> std::io::Result<()> { let mut bytes = [0u8; 512]; unsafe { diff --git a/tests/fail/fs/unix_open_missing_required_mode.rs b/tests/fail/fs/unix_open_missing_required_mode.rs index 1f6beadcb8..4740dcebe9 100644 --- a/tests/fail/fs/unix_open_missing_required_mode.rs +++ b/tests/fail/fs/unix_open_missing_required_mode.rs @@ -3,8 +3,6 @@ #![feature(rustc_private)] -extern crate libc; - fn main() { test_file_open_missing_needed_mode(); } diff --git a/tests/fail/fs/write_to_stdin.rs b/tests/fail/fs/write_to_stdin.rs index 4ad7b648e1..0e9109fc6e 100644 --- a/tests/fail/fs/write_to_stdin.rs +++ b/tests/fail/fs/write_to_stdin.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - fn main() -> std::io::Result<()> { let bytes = b"hello"; unsafe { diff --git a/tests/fail/panic/double_panic.stderr b/tests/fail/panic/double_panic.stderr index c88dfd39e1..f1d2b4de97 100644 --- a/tests/fail/panic/double_panic.stderr +++ b/tests/fail/panic/double_panic.stderr @@ -86,7 +86,7 @@ note: inside `main` at $DIR/double_panic.rs:LL:CC | LL | } | ^ - = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/tests/fail/panic/panic_abort1.stderr b/tests/fail/panic/panic_abort1.stderr index 808fccaaec..7547199454 100644 --- a/tests/fail/panic/panic_abort1.stderr +++ b/tests/fail/panic/panic_abort1.stderr @@ -17,7 +17,7 @@ note: inside `main` at RUSTLIB/std/src/panic.rs:LL:CC | LL | std::panic!("panicking from libstd"); | ^ - = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `std::panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/tests/fail/panic/panic_abort2.stderr b/tests/fail/panic/panic_abort2.stderr index 9b86dc92f7..2fdf889d79 100644 --- a/tests/fail/panic/panic_abort2.stderr +++ b/tests/fail/panic/panic_abort2.stderr @@ -18,7 +18,7 @@ note: inside `main` at RUSTLIB/std/src/panic.rs:LL:CC | LL | std::panic!("{}-panicking from libstd", 42); | ^ - = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `std::panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/tests/fail/panic/panic_abort3.stderr b/tests/fail/panic/panic_abort3.stderr index 2bb50d55bf..8704b0d940 100644 --- a/tests/fail/panic/panic_abort3.stderr +++ b/tests/fail/panic/panic_abort3.stderr @@ -18,7 +18,7 @@ note: inside `main` at RUSTLIB/core/src/panic.rs:LL:CC | LL | core::panic!("panicking from libcore"); | ^ - = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/tests/fail/panic/panic_abort4.stderr b/tests/fail/panic/panic_abort4.stderr index 8ab5793016..1d75d72c03 100644 --- a/tests/fail/panic/panic_abort4.stderr +++ b/tests/fail/panic/panic_abort4.stderr @@ -18,7 +18,7 @@ note: inside `main` at RUSTLIB/core/src/panic.rs:LL:CC | LL | core::panic!("{}-panicking from libcore", 42); | ^ - = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/tests/fail/sync/libc_pthread_cond_double_destroy.rs b/tests/fail/sync/libc_pthread_cond_double_destroy.rs index d0a4ac46cb..90d5997f87 100644 --- a/tests/fail/sync/libc_pthread_cond_double_destroy.rs +++ b/tests/fail/sync/libc_pthread_cond_double_destroy.rs @@ -2,7 +2,6 @@ #![feature(rustc_private)] /// Test that destroying a pthread_cond twice fails, even without a check for number validity -extern crate libc; fn main() { unsafe { diff --git a/tests/fail/sync/libc_pthread_condattr_double_destroy.rs b/tests/fail/sync/libc_pthread_condattr_double_destroy.rs index c64b323813..028a924196 100644 --- a/tests/fail/sync/libc_pthread_condattr_double_destroy.rs +++ b/tests/fail/sync/libc_pthread_condattr_double_destroy.rs @@ -2,7 +2,6 @@ #![feature(rustc_private)] /// Test that destroying a pthread_condattr twice fails, even without a check for number validity -extern crate libc; fn main() { unsafe { diff --git a/tests/fail/sync/libc_pthread_mutex_NULL_deadlock.rs b/tests/fail/sync/libc_pthread_mutex_NULL_deadlock.rs index 8797e895d8..d87455877a 100644 --- a/tests/fail/sync/libc_pthread_mutex_NULL_deadlock.rs +++ b/tests/fail/sync/libc_pthread_mutex_NULL_deadlock.rs @@ -4,8 +4,6 @@ #![feature(rustc_private)] -extern crate libc; - fn main() { unsafe { let mut mutex: libc::pthread_mutex_t = std::mem::zeroed(); diff --git a/tests/fail/sync/libc_pthread_mutex_deadlock.rs b/tests/fail/sync/libc_pthread_mutex_deadlock.rs index 7da6e51600..f77f5c2e20 100644 --- a/tests/fail/sync/libc_pthread_mutex_deadlock.rs +++ b/tests/fail/sync/libc_pthread_mutex_deadlock.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::cell::UnsafeCell; use std::sync::Arc; use std::thread; diff --git a/tests/fail/sync/libc_pthread_mutex_default_deadlock.rs b/tests/fail/sync/libc_pthread_mutex_default_deadlock.rs index 70a85aa0f9..b28101e20b 100644 --- a/tests/fail/sync/libc_pthread_mutex_default_deadlock.rs +++ b/tests/fail/sync/libc_pthread_mutex_default_deadlock.rs @@ -4,8 +4,6 @@ #![feature(rustc_private)] -extern crate libc; - fn main() { unsafe { let mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed(); diff --git a/tests/fail/sync/libc_pthread_mutex_destroy_locked.rs b/tests/fail/sync/libc_pthread_mutex_destroy_locked.rs index fc69ace369..0f74446fa2 100644 --- a/tests/fail/sync/libc_pthread_mutex_destroy_locked.rs +++ b/tests/fail/sync/libc_pthread_mutex_destroy_locked.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - fn main() { unsafe { let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed(); diff --git a/tests/fail/sync/libc_pthread_mutex_double_destroy.rs b/tests/fail/sync/libc_pthread_mutex_double_destroy.rs index 9b539afc19..89022f3b56 100644 --- a/tests/fail/sync/libc_pthread_mutex_double_destroy.rs +++ b/tests/fail/sync/libc_pthread_mutex_double_destroy.rs @@ -2,7 +2,6 @@ #![feature(rustc_private)] /// Test that destroying a pthread_mutex twice fails, even without a check for number validity -extern crate libc; fn main() { unsafe { diff --git a/tests/fail/sync/libc_pthread_mutex_normal_deadlock.rs b/tests/fail/sync/libc_pthread_mutex_normal_deadlock.rs index 944e86e106..ab6d9c7739 100644 --- a/tests/fail/sync/libc_pthread_mutex_normal_deadlock.rs +++ b/tests/fail/sync/libc_pthread_mutex_normal_deadlock.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - fn main() { unsafe { let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed(); diff --git a/tests/fail/sync/libc_pthread_mutex_normal_unlock_unlocked.rs b/tests/fail/sync/libc_pthread_mutex_normal_unlock_unlocked.rs index c2bdce82b6..f259a4dee7 100644 --- a/tests/fail/sync/libc_pthread_mutex_normal_unlock_unlocked.rs +++ b/tests/fail/sync/libc_pthread_mutex_normal_unlock_unlocked.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - fn main() { unsafe { let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed(); diff --git a/tests/fail/sync/libc_pthread_mutex_wrong_owner.rs b/tests/fail/sync/libc_pthread_mutex_wrong_owner.rs index eea4db7115..b8e57f8f74 100644 --- a/tests/fail/sync/libc_pthread_mutex_wrong_owner.rs +++ b/tests/fail/sync/libc_pthread_mutex_wrong_owner.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::cell::UnsafeCell; use std::sync::Arc; use std::thread; diff --git a/tests/fail/sync/libc_pthread_mutexattr_double_destroy.rs b/tests/fail/sync/libc_pthread_mutexattr_double_destroy.rs index 620dbb94a7..ac6292570e 100644 --- a/tests/fail/sync/libc_pthread_mutexattr_double_destroy.rs +++ b/tests/fail/sync/libc_pthread_mutexattr_double_destroy.rs @@ -2,7 +2,6 @@ #![feature(rustc_private)] /// Test that destroying a pthread_mutexattr twice fails, even without a check for number validity -extern crate libc; fn main() { unsafe { diff --git a/tests/fail/sync/libc_pthread_rwlock_destroy_read_locked.rs b/tests/fail/sync/libc_pthread_rwlock_destroy_read_locked.rs index 67b77ff286..ae7c1bbde7 100644 --- a/tests/fail/sync/libc_pthread_rwlock_destroy_read_locked.rs +++ b/tests/fail/sync/libc_pthread_rwlock_destroy_read_locked.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); unsafe { diff --git a/tests/fail/sync/libc_pthread_rwlock_destroy_write_locked.rs b/tests/fail/sync/libc_pthread_rwlock_destroy_write_locked.rs index 5bc5fe3c6b..9642595ca4 100644 --- a/tests/fail/sync/libc_pthread_rwlock_destroy_write_locked.rs +++ b/tests/fail/sync/libc_pthread_rwlock_destroy_write_locked.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); unsafe { diff --git a/tests/fail/sync/libc_pthread_rwlock_double_destroy.rs b/tests/fail/sync/libc_pthread_rwlock_double_destroy.rs index 7e756c5bb8..81b1661ce8 100644 --- a/tests/fail/sync/libc_pthread_rwlock_double_destroy.rs +++ b/tests/fail/sync/libc_pthread_rwlock_double_destroy.rs @@ -2,7 +2,6 @@ #![feature(rustc_private)] /// Test that destroying a pthread_rwlock twice fails, even without a check for number validity -extern crate libc; fn main() { unsafe { diff --git a/tests/fail/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.rs b/tests/fail/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.rs index 76fceb315f..158dd8c1cd 100644 --- a/tests/fail/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.rs +++ b/tests/fail/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); unsafe { diff --git a/tests/fail/sync/libc_pthread_rwlock_read_wrong_owner.rs b/tests/fail/sync/libc_pthread_rwlock_read_wrong_owner.rs index e971fd8c30..23dda68c6a 100644 --- a/tests/fail/sync/libc_pthread_rwlock_read_wrong_owner.rs +++ b/tests/fail/sync/libc_pthread_rwlock_read_wrong_owner.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::cell::UnsafeCell; use std::sync::Arc; use std::thread; diff --git a/tests/fail/sync/libc_pthread_rwlock_unlock_unlocked.rs b/tests/fail/sync/libc_pthread_rwlock_unlock_unlocked.rs index 29cfd36caf..fdcf8e41d3 100644 --- a/tests/fail/sync/libc_pthread_rwlock_unlock_unlocked.rs +++ b/tests/fail/sync/libc_pthread_rwlock_unlock_unlocked.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); unsafe { diff --git a/tests/fail/sync/libc_pthread_rwlock_write_read_deadlock.rs b/tests/fail/sync/libc_pthread_rwlock_write_read_deadlock.rs index e9c5c17f3e..adbb95fc28 100644 --- a/tests/fail/sync/libc_pthread_rwlock_write_read_deadlock.rs +++ b/tests/fail/sync/libc_pthread_rwlock_write_read_deadlock.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::cell::UnsafeCell; use std::sync::Arc; use std::thread; diff --git a/tests/fail/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.rs b/tests/fail/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.rs index 5ed25344e7..a7d16caa7a 100644 --- a/tests/fail/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.rs +++ b/tests/fail/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); unsafe { diff --git a/tests/fail/sync/libc_pthread_rwlock_write_write_deadlock.rs b/tests/fail/sync/libc_pthread_rwlock_write_write_deadlock.rs index 3d15370e83..070373255d 100644 --- a/tests/fail/sync/libc_pthread_rwlock_write_write_deadlock.rs +++ b/tests/fail/sync/libc_pthread_rwlock_write_write_deadlock.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::cell::UnsafeCell; use std::sync::Arc; use std::thread; diff --git a/tests/fail/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.rs b/tests/fail/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.rs index 14361bee54..867c6272c4 100644 --- a/tests/fail/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.rs +++ b/tests/fail/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); unsafe { diff --git a/tests/fail/sync/libc_pthread_rwlock_write_wrong_owner.rs b/tests/fail/sync/libc_pthread_rwlock_write_wrong_owner.rs index 668ccb3eca..cff2a7a2e9 100644 --- a/tests/fail/sync/libc_pthread_rwlock_write_wrong_owner.rs +++ b/tests/fail/sync/libc_pthread_rwlock_write_wrong_owner.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::cell::UnsafeCell; use std::sync::Arc; use std::thread; diff --git a/tests/fail/unsupported_signal.rs b/tests/fail/unsupported_signal.rs index e9bd340fca..20ebcc9bc4 100644 --- a/tests/fail/unsupported_signal.rs +++ b/tests/fail/unsupported_signal.rs @@ -3,8 +3,6 @@ //@ignore-target-windows: No libc on Windows #![feature(rustc_private)] -extern crate libc; - fn main() { unsafe { libc::signal(libc::SIGPIPE, libc::SIG_IGN); diff --git a/tests/fail/validity/invalid_enum_tag_256variants_uninit.rs b/tests/fail/validity/invalid_enum_tag_256variants_uninit.rs deleted file mode 100644 index 708e0cafa9..0000000000 --- a/tests/fail/validity/invalid_enum_tag_256variants_uninit.rs +++ /dev/null @@ -1,272 +0,0 @@ -// Even when uninit numbers are allowed, this enum is not. -//@compile-flags: -Zmiri-allow-uninit-numbers -#![allow(unused, deprecated, invalid_value)] - -#[derive(Copy, Clone)] -enum A { - A0, - A1, - A2, - A3, - A4, - A5, - A6, - A7, - A8, - A9, - A10, - A11, - A12, - A13, - A14, - A15, - A16, - A17, - A18, - A19, - A20, - A21, - A22, - A23, - A24, - A25, - A26, - A27, - A28, - A29, - A30, - A31, - A32, - A33, - A34, - A35, - A36, - A37, - A38, - A39, - A40, - A41, - A42, - A43, - A44, - A45, - A46, - A47, - A48, - A49, - A50, - A51, - A52, - A53, - A54, - A55, - A56, - A57, - A58, - A59, - A60, - A61, - A62, - A63, - A64, - A65, - A66, - A67, - A68, - A69, - A70, - A71, - A72, - A73, - A74, - A75, - A76, - A77, - A78, - A79, - A80, - A81, - A82, - A83, - A84, - A85, - A86, - A87, - A88, - A89, - A90, - A91, - A92, - A93, - A94, - A95, - A96, - A97, - A98, - A99, - A100, - A101, - A102, - A103, - A104, - A105, - A106, - A107, - A108, - A109, - A110, - A111, - A112, - A113, - A114, - A115, - A116, - A117, - A118, - A119, - A120, - A121, - A122, - A123, - A124, - A125, - A126, - A127, - A128, - A129, - A130, - A131, - A132, - A133, - A134, - A135, - A136, - A137, - A138, - A139, - A140, - A141, - A142, - A143, - A144, - A145, - A146, - A147, - A148, - A149, - A150, - A151, - A152, - A153, - A154, - A155, - A156, - A157, - A158, - A159, - A160, - A161, - A162, - A163, - A164, - A165, - A166, - A167, - A168, - A169, - A170, - A171, - A172, - A173, - A174, - A175, - A176, - A177, - A178, - A179, - A180, - A181, - A182, - A183, - A184, - A185, - A186, - A187, - A188, - A189, - A190, - A191, - A192, - A193, - A194, - A195, - A196, - A197, - A198, - A199, - A200, - A201, - A202, - A203, - A204, - A205, - A206, - A207, - A208, - A209, - A210, - A211, - A212, - A213, - A214, - A215, - A216, - A217, - A218, - A219, - A220, - A221, - A222, - A223, - A224, - A225, - A226, - A227, - A228, - A229, - A230, - A231, - A232, - A233, - A234, - A235, - A236, - A237, - A238, - A239, - A240, - A241, - A242, - A243, - A244, - A245, - A246, - A247, - A248, - A249, - A250, - A251, - A252, - A253, - A254, - A255, -} - -union MyUninit { - init: (), - uninit: A, -} - -fn main() { - let _a = unsafe { MyUninit { init: () }.uninit }; //~ ERROR: constructing invalid value at .: encountered uninitialized bytes, but expected a valid enum tag -} diff --git a/tests/fail/validity/invalid_enum_tag_256variants_uninit.stderr b/tests/fail/validity/invalid_enum_tag_256variants_uninit.stderr deleted file mode 100644 index c3ebf462b6..0000000000 --- a/tests/fail/validity/invalid_enum_tag_256variants_uninit.stderr +++ /dev/null @@ -1,16 +0,0 @@ -WARNING: `-Zmiri-allow-uninit-numbers` is deprecated and planned to be removed. Please let us know at if you rely on this flag. -error: Undefined Behavior: constructing invalid value at .: encountered uninitialized bytes, but expected a valid enum tag - --> $DIR/invalid_enum_tag_256variants_uninit.rs:LL:CC - | -LL | let _a = unsafe { MyUninit { init: () }.uninit }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered uninitialized bytes, but expected a valid enum tag - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: backtrace: - = note: inside `main` at $DIR/invalid_enum_tag_256variants_uninit.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to previous error - diff --git a/tests/panic/panic/unsupported_syscall.rs b/tests/panic/panic/unsupported_syscall.rs index a689172814..27595bf107 100644 --- a/tests/panic/panic/unsupported_syscall.rs +++ b/tests/panic/panic/unsupported_syscall.rs @@ -3,8 +3,6 @@ //@compile-flags: -Zmiri-panic-on-unsupported #![feature(rustc_private)] -extern crate libc; - fn main() { unsafe { libc::syscall(0); diff --git a/tests/pass/calloc.rs b/tests/pass/calloc.rs index a9efb776b6..e155d53ce8 100644 --- a/tests/pass/calloc.rs +++ b/tests/pass/calloc.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - use core::slice; fn main() { diff --git a/tests/pass/concurrency/libc_pthread_cond.rs b/tests/pass/concurrency/libc_pthread_cond.rs index eb491486be..c5b0e86666 100644 --- a/tests/pass/concurrency/libc_pthread_cond.rs +++ b/tests/pass/concurrency/libc_pthread_cond.rs @@ -6,8 +6,6 @@ /// Test that conditional variable timeouts are working properly with both /// monotonic and system clocks. -extern crate libc; - use std::mem::MaybeUninit; use std::time::Instant; diff --git a/tests/pass/concurrency/linux-futex.rs b/tests/pass/concurrency/linux-futex.rs index f9c87e0723..43216481e7 100644 --- a/tests/pass/concurrency/linux-futex.rs +++ b/tests/pass/concurrency/linux-futex.rs @@ -2,7 +2,6 @@ //@compile-flags: -Zmiri-disable-isolation #![feature(rustc_private)] -extern crate libc; use std::mem::MaybeUninit; use std::ptr; diff --git a/tests/pass/concurrency/tls_pthread_drop_order.rs b/tests/pass/concurrency/tls_pthread_drop_order.rs index c9e8b9271c..1ccc57da25 100644 --- a/tests/pass/concurrency/tls_pthread_drop_order.rs +++ b/tests/pass/concurrency/tls_pthread_drop_order.rs @@ -1,7 +1,6 @@ //@ignore-target-windows: No libc on Windows #![feature(rustc_private)] -extern crate libc; use std::mem; use std::ptr; diff --git a/tests/pass/crates/page_size.rs b/tests/pass/crates/page_size.rs new file mode 100644 index 0000000000..cdcabf3333 --- /dev/null +++ b/tests/pass/crates/page_size.rs @@ -0,0 +1,6 @@ +fn main() { + let page_size = page_size::get(); + + // In particular, this checks that it is not 0. + assert!(page_size.is_power_of_two(), "page size not a power of two: {}", page_size); +} diff --git a/tests/pass/crates/random.rs b/tests/pass/crates/random.rs new file mode 100644 index 0000000000..808d1006d4 --- /dev/null +++ b/tests/pass/crates/random.rs @@ -0,0 +1,22 @@ +use rand::{rngs::SmallRng, Rng, SeedableRng}; +// mac-os `getrandom_1` does some pointer shenanigans +//@compile-flags: -Zmiri-permissive-provenance + +fn main() { + // Test `getrandom` directly (in multiple different versions). + let mut data = vec![0; 16]; + getrandom_1::getrandom(&mut data).unwrap(); + getrandom_2::getrandom(&mut data).unwrap(); + + // Try seeding with "real" entropy. + let mut rng = SmallRng::from_entropy(); + let _val = rng.gen::(); + let _val = rng.gen::(); + let _val = rng.gen::(); + + // Also try per-thread RNG. + let mut rng = rand::thread_rng(); + let _val = rng.gen::(); + let _val = rng.gen::(); + let _val = rng.gen::(); +} diff --git a/tests/pass/dyn-upcast.rs b/tests/pass/dyn-upcast.rs new file mode 100644 index 0000000000..030c2a7cbf --- /dev/null +++ b/tests/pass/dyn-upcast.rs @@ -0,0 +1,418 @@ +#![feature(trait_upcasting)] +#![allow(incomplete_features)] + +fn main() { + basic(); + diamond(); + struct_(); + replace_vptr(); +} + +fn basic() { + trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + fn a(&self) -> i32 { + 10 + } + + fn z(&self) -> i32 { + 11 + } + + fn y(&self) -> i32 { + 12 + } + } + + trait Bar: Foo { + fn b(&self) -> i32 { + 20 + } + + fn w(&self) -> i32 { + 21 + } + } + + trait Baz: Bar { + fn c(&self) -> i32 { + 30 + } + } + + impl Foo for i32 { + fn a(&self) -> i32 { + 100 + } + } + + impl Bar for i32 { + fn b(&self) -> i32 { + 200 + } + } + + impl Baz for i32 { + fn c(&self) -> i32 { + 300 + } + } + + let baz: &dyn Baz = &1; + let _: &dyn std::fmt::Debug = baz; + assert_eq!(*baz, 1); + assert_eq!(baz.a(), 100); + assert_eq!(baz.b(), 200); + assert_eq!(baz.c(), 300); + assert_eq!(baz.z(), 11); + assert_eq!(baz.y(), 12); + assert_eq!(baz.w(), 21); + + let bar: &dyn Bar = baz; + let _: &dyn std::fmt::Debug = bar; + assert_eq!(*bar, 1); + assert_eq!(bar.a(), 100); + assert_eq!(bar.b(), 200); + assert_eq!(bar.z(), 11); + assert_eq!(bar.y(), 12); + assert_eq!(bar.w(), 21); + + let foo: &dyn Foo = baz; + let _: &dyn std::fmt::Debug = foo; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + + let foo: &dyn Foo = bar; + let _: &dyn std::fmt::Debug = foo; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); +} + +fn diamond() { + trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + fn a(&self) -> i32 { + 10 + } + + fn z(&self) -> i32 { + 11 + } + + fn y(&self) -> i32 { + 12 + } + } + + trait Bar1: Foo { + fn b(&self) -> i32 { + 20 + } + + fn w(&self) -> i32 { + 21 + } + } + + trait Bar2: Foo { + fn c(&self) -> i32 { + 30 + } + + fn v(&self) -> i32 { + 31 + } + } + + trait Baz: Bar1 + Bar2 { + fn d(&self) -> i32 { + 40 + } + } + + impl Foo for i32 { + fn a(&self) -> i32 { + 100 + } + } + + impl Bar1 for i32 { + fn b(&self) -> i32 { + 200 + } + } + + impl Bar2 for i32 { + fn c(&self) -> i32 { + 300 + } + } + + impl Baz for i32 { + fn d(&self) -> i32 { + 400 + } + } + + let baz: &dyn Baz = &1; + let _: &dyn std::fmt::Debug = baz; + assert_eq!(*baz, 1); + assert_eq!(baz.a(), 100); + assert_eq!(baz.b(), 200); + assert_eq!(baz.c(), 300); + assert_eq!(baz.d(), 400); + assert_eq!(baz.z(), 11); + assert_eq!(baz.y(), 12); + assert_eq!(baz.w(), 21); + assert_eq!(baz.v(), 31); + + let bar1: &dyn Bar1 = baz; + let _: &dyn std::fmt::Debug = bar1; + assert_eq!(*bar1, 1); + assert_eq!(bar1.a(), 100); + assert_eq!(bar1.b(), 200); + assert_eq!(bar1.z(), 11); + assert_eq!(bar1.y(), 12); + assert_eq!(bar1.w(), 21); + + let bar2: &dyn Bar2 = baz; + let _: &dyn std::fmt::Debug = bar2; + assert_eq!(*bar2, 1); + assert_eq!(bar2.a(), 100); + assert_eq!(bar2.c(), 300); + assert_eq!(bar2.z(), 11); + assert_eq!(bar2.y(), 12); + assert_eq!(bar2.v(), 31); + + let foo: &dyn Foo = baz; + let _: &dyn std::fmt::Debug = foo; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + + let foo: &dyn Foo = bar1; + let _: &dyn std::fmt::Debug = foo; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + + let foo: &dyn Foo = bar2; + let _: &dyn std::fmt::Debug = foo; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); +} + +fn struct_() { + use std::rc::Rc; + use std::sync::Arc; + + trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + fn a(&self) -> i32 { + 10 + } + + fn z(&self) -> i32 { + 11 + } + + fn y(&self) -> i32 { + 12 + } + } + + trait Bar: Foo { + fn b(&self) -> i32 { + 20 + } + + fn w(&self) -> i32 { + 21 + } + } + + trait Baz: Bar { + fn c(&self) -> i32 { + 30 + } + } + + impl Foo for i32 { + fn a(&self) -> i32 { + 100 + } + } + + impl Bar for i32 { + fn b(&self) -> i32 { + 200 + } + } + + impl Baz for i32 { + fn c(&self) -> i32 { + 300 + } + } + + fn test_box() { + let v = Box::new(1); + + let baz: Box = v.clone(); + assert_eq!(*baz, 1); + assert_eq!(baz.a(), 100); + assert_eq!(baz.b(), 200); + assert_eq!(baz.c(), 300); + assert_eq!(baz.z(), 11); + assert_eq!(baz.y(), 12); + assert_eq!(baz.w(), 21); + + let baz: Box = v.clone(); + let bar: Box = baz; + assert_eq!(*bar, 1); + assert_eq!(bar.a(), 100); + assert_eq!(bar.b(), 200); + assert_eq!(bar.z(), 11); + assert_eq!(bar.y(), 12); + assert_eq!(bar.w(), 21); + + let baz: Box = v.clone(); + let foo: Box = baz; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + + let baz: Box = v.clone(); + let bar: Box = baz; + let foo: Box = bar; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + } + + fn test_rc() { + let v = Rc::new(1); + + let baz: Rc = v.clone(); + assert_eq!(*baz, 1); + assert_eq!(baz.a(), 100); + assert_eq!(baz.b(), 200); + assert_eq!(baz.c(), 300); + assert_eq!(baz.z(), 11); + assert_eq!(baz.y(), 12); + assert_eq!(baz.w(), 21); + + let baz: Rc = v.clone(); + let bar: Rc = baz; + assert_eq!(*bar, 1); + assert_eq!(bar.a(), 100); + assert_eq!(bar.b(), 200); + assert_eq!(bar.z(), 11); + assert_eq!(bar.y(), 12); + assert_eq!(bar.w(), 21); + + let baz: Rc = v.clone(); + let foo: Rc = baz; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + + let baz: Rc = v.clone(); + let bar: Rc = baz; + let foo: Rc = bar; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + } + + fn test_arc() { + let v = Arc::new(1); + + let baz: Arc = v.clone(); + assert_eq!(*baz, 1); + assert_eq!(baz.a(), 100); + assert_eq!(baz.b(), 200); + assert_eq!(baz.c(), 300); + assert_eq!(baz.z(), 11); + assert_eq!(baz.y(), 12); + assert_eq!(baz.w(), 21); + + let baz: Arc = v.clone(); + let bar: Arc = baz; + assert_eq!(*bar, 1); + assert_eq!(bar.a(), 100); + assert_eq!(bar.b(), 200); + assert_eq!(bar.z(), 11); + assert_eq!(bar.y(), 12); + assert_eq!(bar.w(), 21); + + let baz: Arc = v.clone(); + let foo: Arc = baz; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + + let baz: Arc = v.clone(); + let bar: Arc = baz; + let foo: Arc = bar; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + } + + test_box(); + test_rc(); + test_arc(); +} + +fn replace_vptr() { + trait A { + fn foo_a(&self); + } + + trait B { + fn foo_b(&self); + } + + trait C: A + B { + fn foo_c(&self); + } + + struct S(i32); + + impl A for S { + fn foo_a(&self) { + unreachable!(); + } + } + + impl B for S { + fn foo_b(&self) { + assert_eq!(42, self.0); + } + } + + impl C for S { + fn foo_c(&self) { + unreachable!(); + } + } + + fn invoke_inner(b: &dyn B) { + b.foo_b(); + } + + fn invoke_outer(c: &dyn C) { + invoke_inner(c); + } + + let s = S(42); + invoke_outer(&s); +} diff --git a/tests/pass/foreign-fn-linkname.rs b/tests/pass/foreign-fn-linkname.rs index 391b182fda..40aeb2ef63 100644 --- a/tests/pass/foreign-fn-linkname.rs +++ b/tests/pass/foreign-fn-linkname.rs @@ -1,8 +1,6 @@ //ignore-windows: Uses POSIX APIs #![feature(rustc_private)] -extern crate libc; - use std::ffi::CString; mod mlibc { diff --git a/tests/pass/fs.rs b/tests/pass/fs.rs index aa5cd83c69..9d59fedb20 100644 --- a/tests/pass/fs.rs +++ b/tests/pass/fs.rs @@ -4,8 +4,6 @@ #![feature(rustc_private)] #![feature(io_error_more)] -extern crate libc; - use std::ffi::CString; use std::fs::{ create_dir, read_dir, read_link, remove_dir, remove_dir_all, remove_file, rename, File, diff --git a/tests/pass/fs_with_isolation.rs b/tests/pass/fs_with_isolation.rs index 41ada94e27..f73e64ad17 100644 --- a/tests/pass/fs_with_isolation.rs +++ b/tests/pass/fs_with_isolation.rs @@ -4,8 +4,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::ffi::CString; use std::fs::{self, File}; use std::io::{Error, ErrorKind}; diff --git a/tests/pass/libc.rs b/tests/pass/libc.rs index 99b7c3f249..9b83ab45b0 100644 --- a/tests/pass/libc.rs +++ b/tests/pass/libc.rs @@ -5,8 +5,6 @@ use std::fs::{remove_file, File}; use std::os::unix::io::AsRawFd; -extern crate libc; - fn tmp() -> std::path::PathBuf { std::env::var("MIRI_TEMP") .map(std::path::PathBuf::from) diff --git a/tests/pass/linux-getrandom-without-isolation.rs b/tests/pass/linux-getrandom-without-isolation.rs index fea3bb3fdc..12b42552bd 100644 --- a/tests/pass/linux-getrandom-without-isolation.rs +++ b/tests/pass/linux-getrandom-without-isolation.rs @@ -1,7 +1,6 @@ //@only-target-linux //@compile-flags: -Zmiri-disable-isolation #![feature(rustc_private)] -extern crate libc; use std::ptr; diff --git a/tests/pass/linux-getrandom.rs b/tests/pass/linux-getrandom.rs index 1d0ab6ed74..e3309f480d 100644 --- a/tests/pass/linux-getrandom.rs +++ b/tests/pass/linux-getrandom.rs @@ -1,6 +1,5 @@ //@only-target-linux #![feature(rustc_private)] -extern crate libc; use std::ptr; diff --git a/tests/pass/malloc.rs b/tests/pass/malloc.rs index d20ceddbb8..9066e2af25 100644 --- a/tests/pass/malloc.rs +++ b/tests/pass/malloc.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - use core::{ptr, slice}; fn main() { diff --git a/tests/pass/move-uninit-primval.rs b/tests/pass/move-uninit-primval.rs index 220470b637..f5fd27fa0d 100644 --- a/tests/pass/move-uninit-primval.rs +++ b/tests/pass/move-uninit-primval.rs @@ -1,13 +1,14 @@ -//@compile-flags: -Zmiri-allow-uninit-numbers #![allow(deprecated)] +use std::mem; + struct Foo { - _inner: i32, + _inner: mem::MaybeUninit, } fn main() { unsafe { - let foo = Foo { _inner: std::mem::uninitialized() }; + let foo = Foo { _inner: mem::uninitialized() }; let _bar = foo; } } diff --git a/tests/pass/move-uninit-primval.stderr b/tests/pass/move-uninit-primval.stderr deleted file mode 100644 index d9f2331fe7..0000000000 --- a/tests/pass/move-uninit-primval.stderr +++ /dev/null @@ -1 +0,0 @@ -WARNING: `-Zmiri-allow-uninit-numbers` is deprecated and planned to be removed. Please let us know at if you rely on this flag. diff --git a/tests/pass/regions-mock-trans.rs b/tests/pass/regions-mock-trans.rs index 5dafc88756..7432ea9582 100644 --- a/tests/pass/regions-mock-trans.rs +++ b/tests/pass/regions-mock-trans.rs @@ -2,8 +2,6 @@ #![feature(rustc_private)] -extern crate libc; - use std::mem; struct Arena(()); diff --git a/tests/pass/stacked-borrows/issue-miri-2389.rs b/tests/pass/stacked-borrows/issue-miri-2389.rs new file mode 100644 index 0000000000..469122095e --- /dev/null +++ b/tests/pass/stacked-borrows/issue-miri-2389.rs @@ -0,0 +1,17 @@ +use std::cell::Cell; + +fn main() { + unsafe { + let root0 = Cell::new(42); + let wildcard = &root0 as *const Cell as usize as *const Cell; + // empty the stack to unknown (via SRW reborrow from wildcard) + let _ref0 = &*wildcard; + // Do a non-SRW reborrow from wildcard to start building up a stack again. + // Now new refs start being inserted at idx 0, pushing the unique_range up. + let _refn = &*&*&*&*&*(wildcard.cast::()); + // empty the stack again, but this time with unique_range.start sitting at some high index. + let _ref0 = &*wildcard; + // and do a read which tries to clear the uniques + wildcard.cast::().read(); + } +} diff --git a/tests/pass/stacked-borrows/issue-miri-2389.stderr b/tests/pass/stacked-borrows/issue-miri-2389.stderr new file mode 100644 index 0000000000..2ff931f231 --- /dev/null +++ b/tests/pass/stacked-borrows/issue-miri-2389.stderr @@ -0,0 +1,15 @@ +warning: integer-to-pointer cast + --> $DIR/issue-miri-2389.rs:LL:CC + | +LL | let wildcard = &root0 as *const Cell as usize as *const Cell; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast + | + = help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`, + = help: which means that Miri might miss pointer bugs in this program. + = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation. + = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead. + = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics. + = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning. + = note: backtrace: + = note: inside `main` at $DIR/issue-miri-2389.rs:LL:CC + diff --git a/tests/pass/uninit_number_ignored.rs b/tests/pass/uninit_number_ignored.rs deleted file mode 100644 index 44f6fa2679..0000000000 --- a/tests/pass/uninit_number_ignored.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@compile-flags: -Zmiri-allow-uninit-numbers -// This test is adapted from https://github.com/rust-lang/miri/issues/1340#issue-600900312. - -fn main() { - let _val1 = unsafe { std::mem::MaybeUninit::::uninit().assume_init() }; - let _val2 = unsafe { std::mem::MaybeUninit::::uninit().assume_init() }; - let _val3 = unsafe { std::mem::MaybeUninit::::uninit().assume_init() }; -} diff --git a/tests/pass/uninit_number_ignored.stderr b/tests/pass/uninit_number_ignored.stderr deleted file mode 100644 index d9f2331fe7..0000000000 --- a/tests/pass/uninit_number_ignored.stderr +++ /dev/null @@ -1 +0,0 @@ -WARNING: `-Zmiri-allow-uninit-numbers` is deprecated and planned to be removed. Please let us know at if you rely on this flag. diff --git a/ui_test/Cargo.lock b/ui_test/Cargo.lock index 2065cc34be..9addea9b19 100644 --- a/ui_test/Cargo.lock +++ b/ui_test/Cargo.lock @@ -67,6 +67,37 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "camino" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "869119e97797867fd90f5e22af7d0bd274bd4635ebb9eb68c04f3f513ae6c412" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3abb7553d5b9b8421c6de7cb02606ff15e0c6eea7d8eadd75ef013fd636bec36" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", +] + [[package]] name = "cc" version = "1.0.73" @@ -390,6 +421,9 @@ name = "semver" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd" +dependencies = [ + "serde", +] [[package]] name = "serde" @@ -497,6 +531,7 @@ dependencies = [ name = "ui_test" version = "0.1.0" dependencies = [ + "cargo_metadata", "color-eyre", "colored", "crossbeam", diff --git a/ui_test/Cargo.toml b/ui_test/Cargo.toml index cdc5e5db47..bb14eb7ecf 100644 --- a/ui_test/Cargo.toml +++ b/ui_test/Cargo.toml @@ -18,4 +18,4 @@ lazy_static = "1.4.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" color-eyre = { version = "0.6.1", default-features = false, features = ["capture-spantrace"] } - +cargo_metadata = "0.15" diff --git a/ui_test/README.md b/ui_test/README.md index 4ecebcc8dd..3db3361faa 100644 --- a/ui_test/README.md +++ b/ui_test/README.md @@ -23,12 +23,11 @@ In order to change how a single test is tested, you can add various `//@` commen Any other comments will be ignored, and all `//@` comments must be formatted precisely as their command specifies, or the test will fail without even being run. -* `//@ignore-XXX` avoids running the test on targets whose triple contains `XXX` - * `XXX` can also be one of `64bit`, `32bit` or `16bit` - * `XXX` can also be `on-host`, which will only run the test during cross compilation testing. -* `//@only-XXX` avoids running the test on targets whose triple **does not** contain `XXX` - * `XXX` can also be one of `64bit`, `32bit` or `16bit` - * `XXX` can also be `on-host`, which will not run the test during cross compilation testing +* `//@ignore-C` avoids running the test when condition `C` is met. + * `C` can be `target-XXX`, which checks whether the target triple contains `XXX`. + * `C` can also be one of `64bit`, `32bit` or `16bit`. + * `C` can also be `on-host`, which will only run the test during cross compilation testing. +* `//@only-C` **only** runs the test when condition `C` is met. The conditions are the same as with `ignore`. * `//@stderr-per-bitwidth` produces one stderr file per bitwidth, as they may differ significantly sometimes * `//@error-pattern: XXX` make sure the stderr output contains `XXX` * `//@revisions: XXX YYY` runs the test once for each space separated name in the list @@ -46,5 +45,7 @@ their command specifies, or the test will fail without even being run. ## Significant differences to compiletest-rs -* `ignore-*` and `only-*` opereate solely on the triple, instead of supporting things like `macos` +* `ignore-target-*` and `only-target-*` opereate solely on the triple, instead of supporting things like `macos` * only `//~` comments can be individualized per revision +* only supports `ui` tests +* tests are run in named order, so you can prefix slow tests with `0` in order to make them get run first diff --git a/ui_test/src/dependencies.rs b/ui_test/src/dependencies.rs new file mode 100644 index 0000000000..ab3a015659 --- /dev/null +++ b/ui_test/src/dependencies.rs @@ -0,0 +1,137 @@ +use color_eyre::eyre::{bail, Result}; +use std::{ + collections::{HashMap, HashSet}, + path::{Path, PathBuf}, + process::Command, +}; + +use crate::Config; + +#[derive(Default, Debug)] +pub struct Dependencies { + /// All paths that must be imported with `-L dependency=`. This is for + /// finding proc macros run on the host and dependencies for the target. + pub import_paths: Vec, + /// The name as chosen in the `Cargo.toml` and its corresponding rmeta file. + pub dependencies: Vec<(String, PathBuf)>, +} + +/// Compiles dependencies and returns the crate names and corresponding rmeta files. +pub fn build_dependencies(config: &Config) -> Result { + let manifest_path = match &config.dependencies_crate_manifest_path { + Some(path) => path, + None => return Ok(Default::default()), + }; + let (program, args, envs): (&Path, &[_], &[_]) = match &config.dependency_builder { + Some(db) => (&db.program, &db.args, &db.envs), + None => (Path::new("cargo"), &[], &[]), + }; + let mut build = Command::new(program); + build.args(args); + // HACK: we're using `cargo run` (or `cargo miri run`), because the latter does not + // support `cargo miri build` yet. + build.arg("run"); + + if let Some(target) = &config.target { + build.arg(format!("--target={target}")); + } + + // Reusable closure for setting up the environment both for artifact generation and `cargo_metadata` + let setup_command = |cmd: &mut Command| { + cmd.envs(envs.iter().map(|(k, v)| (k, v))); + cmd.arg("--manifest-path").arg(manifest_path); + }; + + setup_command(&mut build); + build + .arg("--target-dir=target/test_dependencies") + .arg("--message-format=json") + .arg("-Zunstable-options"); + + let output = build.output()?; + + if !output.status.success() { + let stdout = String::from_utf8(output.stdout)?; + let stderr = String::from_utf8(output.stderr)?; + bail!("failed to compile dependencies:\nstderr:\n{stderr}\n\nstdout:{stdout}"); + } + + // Collect all artifacts generated + let output = output.stdout; + let output = String::from_utf8(output)?; + let mut import_paths: HashSet = HashSet::new(); + let mut artifacts: HashMap<_, _> = output + .lines() + .filter_map(|line| { + let message = serde_json::from_str::(line).ok()?; + if let cargo_metadata::Message::CompilerArtifact(artifact) = message { + for filename in &artifact.filenames { + import_paths.insert(filename.parent().unwrap().into()); + } + let filename = artifact + .filenames + .into_iter() + .find(|filename| filename.extension() == Some("rmeta"))?; + Some((artifact.package_id, filename.into_std_path_buf())) + } else { + None + } + }) + .collect(); + + // Check which crates are mentioned in the crate itself + let mut metadata = cargo_metadata::MetadataCommand::new().cargo_command(); + setup_command(&mut metadata); + let output = metadata.output()?; + + if !output.status.success() { + let stdout = String::from_utf8(output.stdout)?; + let stderr = String::from_utf8(output.stderr)?; + bail!("failed to run cargo-metadata:\nstderr:\n{stderr}\n\nstdout:{stdout}"); + } + + let output = output.stdout; + let output = String::from_utf8(output)?; + + for line in output.lines() { + if !line.starts_with('{') { + continue; + } + let metadata: cargo_metadata::Metadata = serde_json::from_str(line)?; + // Only take artifacts that are defined in the Cargo.toml + + // First, find the root artifact + let root = metadata + .packages + .iter() + .find(|package| { + package.manifest_path.as_std_path().canonicalize().unwrap() + == manifest_path.canonicalize().unwrap() + }) + .unwrap(); + + // Then go over all of its dependencies + let dependencies = root + .dependencies + .iter() + .map(|package| { + // Get the id for the package matching the version requirement of the dep + let id = &metadata + .packages + .iter() + .find(|&dep| dep.name == package.name && package.req.matches(&dep.version)) + .expect("dependency does not exist") + .id; + // Return the name chosen in `Cargo.toml` and the path to the corresponding artifact + ( + package.rename.clone().unwrap_or_else(|| package.name.clone()), + artifacts.remove(id).expect("package without artifact"), + ) + }) + .collect(); + let import_paths = import_paths.into_iter().collect(); + return Ok(Dependencies { dependencies, import_paths }); + } + + bail!("no json found in cargo-metadata output") +} diff --git a/ui_test/src/lib.rs b/ui_test/src/lib.rs index 16f7be30f8..06a84cfbf3 100644 --- a/ui_test/src/lib.rs +++ b/ui_test/src/lib.rs @@ -1,6 +1,7 @@ #![allow(clippy::enum_variant_names, clippy::useless_format, clippy::too_many_arguments)] use std::collections::VecDeque; +use std::ffi::OsString; use std::fmt::Write; use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus}; @@ -14,8 +15,10 @@ use parser::{ErrorMatch, Pattern}; use regex::Regex; use rustc_stderr::{Level, Message}; +use crate::dependencies::build_dependencies; use crate::parser::{Comments, Condition}; +mod dependencies; mod parser; mod rustc_stderr; #[cfg(test)] @@ -24,7 +27,7 @@ mod tests; #[derive(Debug)] pub struct Config { /// Arguments passed to the binary that is executed. - pub args: Vec, + pub args: Vec, /// `None` to run on the host, otherwise a target triple pub target: Option, /// Filters applied to stderr output before processing it @@ -38,6 +41,20 @@ pub struct Config { pub output_conflict_handling: OutputConflictHandling, /// Only run tests with one of these strings in their path/name pub path_filter: Vec, + /// Path to a `Cargo.toml` that describes which dependencies the tests can access. + pub dependencies_crate_manifest_path: Option, + /// Can be used to override what command to run instead of `cargo` to build the + /// dependencies in `manifest_path` + pub dependency_builder: Option, + /// Print one character per test instead of one line + pub quiet: bool, +} + +#[derive(Debug)] +pub struct DependencyBuilder { + pub program: PathBuf, + pub args: Vec, + pub envs: Vec<(String, String)>, } #[derive(Debug)] @@ -53,12 +70,26 @@ pub enum OutputConflictHandling { pub type Filter = Vec<(Regex, &'static str)>; -pub fn run_tests(config: Config) -> Result<()> { +pub fn run_tests(mut config: Config) -> Result<()> { eprintln!(" Compiler flags: {:?}", config.args); // Get the triple with which to run the tests let target = config.target.clone().unwrap_or_else(|| config.get_host()); + let dependencies = build_dependencies(&config)?; + for (name, dependency) in dependencies.dependencies { + config.args.push("--extern".into()); + let mut dep = OsString::from(name); + dep.push("="); + dep.push(dependency); + config.args.push(dep); + } + for import_path in dependencies.import_paths { + config.args.push("-L".into()); + config.args.push(import_path.into()); + } + let config = config; + // A channel for files to process let (submit, receive) = crossbeam::channel::unbounded(); @@ -94,11 +125,50 @@ pub fn run_tests(config: Config) -> Result<()> { drop(submit); }); + // A channel for the messages emitted by the individual test threads. + let (finished_files_sender, finished_files_recv) = crossbeam::channel::unbounded(); + enum TestResult { + Ok, + Failed, + Ignored, + } + + s.spawn(|_| { + if config.quiet { + for (i, (_, result)) in finished_files_recv.into_iter().enumerate() { + // Humans start counting at 1 + let i = i + 1; + match result { + TestResult::Ok => eprint!("{}", ".".green()), + TestResult::Failed => eprint!("{}", "F".red().bold()), + TestResult::Ignored => eprint!("{}", "i".yellow()), + } + if i % 100 == 0 { + eprintln!(" {i}"); + } + } + } else { + for (msg, result) in finished_files_recv { + eprint!("{msg} ... "); + eprintln!( + "{}", + match result { + TestResult::Ok => "ok".green(), + TestResult::Failed => "FAILED".red().bold(), + TestResult::Ignored => "ignored (in-test comment)".yellow(), + } + ); + } + } + }); + let mut threads = vec![]; // Create N worker threads that receive files to test. for _ in 0..std::thread::available_parallelism().unwrap().get() { + let finished_files_sender = finished_files_sender.clone(); threads.push(s.spawn(|_| -> Result<()> { + let finished_files_sender = finished_files_sender; for path in &receive { if !config.path_filter.is_empty() { let path_display = path.display().to_string(); @@ -111,11 +181,8 @@ pub fn run_tests(config: Config) -> Result<()> { // Ignore file if only/ignore rules do (not) apply if !test_file_conditions(&comments, &target, &config) { ignored.fetch_add(1, Ordering::Relaxed); - eprintln!( - "{} ... {}", - path.display(), - "ignored (in-test comment)".yellow() - ); + finished_files_sender + .send((path.display().to_string(), TestResult::Ignored))?; continue; } // Run the test for all revisions @@ -130,12 +197,11 @@ pub fn run_tests(config: Config) -> Result<()> { if !revision.is_empty() { write!(msg, "(revision `{revision}`) ").unwrap(); } - write!(msg, "... ").unwrap(); if errors.is_empty() { - eprintln!("{msg}{}", "ok".green()); + finished_files_sender.send((msg, TestResult::Ok))?; succeeded.fetch_add(1, Ordering::Relaxed); } else { - eprintln!("{msg}{}", "FAILED".red().bold()); + finished_files_sender.send((msg, TestResult::Failed))?; failures.lock().unwrap().push(( path.clone(), m, @@ -149,6 +215,7 @@ pub fn run_tests(config: Config) -> Result<()> { Ok(()) })); } + for thread in threads { thread.join().unwrap()?; } @@ -164,15 +231,14 @@ pub fn run_tests(config: Config) -> Result<()> { if !failures.is_empty() { for (path, miri, revision, errors, stderr) in &failures { eprintln!(); - eprint!("{}", path.display().to_string().underline()); + eprint!("{}", path.display().to_string().underline().bold()); if !revision.is_empty() { eprint!(" (revision `{}`)", revision); } - eprint!(" {}", "FAILED".red()); + eprint!(" {}", "FAILED:".red().bold()); eprintln!(); eprintln!("command: {:?}", miri); eprintln!(); - let mut dump_stderr = true; for error in errors { match error { Error::ExitStatus(mode, exit_status) => eprintln!("{mode:?} got {exit_status}"), @@ -194,9 +260,6 @@ pub fn run_tests(config: Config) -> Result<()> { Error::PatternFoundInPassTest => eprintln!("{}", "error pattern found in success test".red()), Error::OutputDiffers { path, actual, expected } => { - if path.extension().unwrap() == "stderr" { - dump_stderr = false; - } eprintln!("actual output differed from expected {}", path.display()); eprintln!("{}", pretty_assertions::StrComparison::new(expected, actual)); eprintln!() @@ -223,14 +286,11 @@ pub fn run_tests(config: Config) -> Result<()> { } eprintln!(); } - // Unless we already dumped the stderr via an OutputDiffers diff, let's dump it here. - if dump_stderr { - eprintln!("actual stderr:"); - eprintln!("{}", stderr); - eprintln!(); - } + eprintln!("full stderr:"); + eprintln!("{}", stderr); + eprintln!(); } - eprintln!("{}", "failures:".red().underline()); + eprintln!("{}", "FAILURES:".red().underline().bold()); for (path, _miri, _revision, _errors, _stderr) in &failures { eprintln!(" {}", path.display()); } @@ -301,9 +361,7 @@ fn run_test( for arg in &comments.compile_flags { miri.arg(arg); } - for (k, v) in &comments.env_vars { - miri.env(k, v); - } + miri.envs(comments.env_vars.iter().map(|(k, v)| (k, v))); let output = miri.output().expect("could not execute miri"); let mut errors = config.mode.ok(output.status); let stderr = check_test_result( diff --git a/ui_test/src/tests.rs b/ui_test/src/tests.rs index 96c0f362b6..2032988ed3 100644 --- a/ui_test/src/tests.rs +++ b/ui_test/src/tests.rs @@ -16,6 +16,9 @@ fn config() -> Config { path_filter: vec![], program: PathBuf::from("cake"), output_conflict_handling: OutputConflictHandling::Error, + dependencies_crate_manifest_path: None, + dependency_builder: None, + quiet: false, } } From f825988d3cbc83f47b57c4d4a8dc9db9abde542e Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Thu, 21 Jul 2022 21:36:48 +0000 Subject: [PATCH 08/23] upstream --- tests/compiletest.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 7557f4add5..3493c3a524 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -1,11 +1,7 @@ use colored::*; use regex::Regex; use std::path::{Path, PathBuf}; -<<<<<<< HEAD use std::{env, ffi::OsString, process::Command}; -======= -use std::{env, ffi::OsString}; ->>>>>>> master use ui_test::{color_eyre::Result, Config, DependencyBuilder, Mode, OutputConflictHandling}; fn miri_path() -> PathBuf { From 7d0ad069a8d74f8793344f03979a2d3384e7b636 Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Thu, 21 Jul 2022 21:38:09 +0000 Subject: [PATCH 09/23] upstream --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9974abd175..924a93e807 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,5 @@ tex/*/out perf.data perf.data.old flamegraph.svg -<<<<<<< HEAD tests/extern-so/libtestlib.so .auto-* From 450ef91615f7a36161b087253b9c37bab95971d1 Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Tue, 26 Jul 2022 00:46:08 +0000 Subject: [PATCH 10/23] broken, but at least finding poitner to internal bytes in allocation. now need to make allocid the memory address --- ffi_tests/Cargo.lock | 16 +++++ ffi_tests/Cargo.toml | 9 +++ ffi_tests/build.rs | 7 +++ ffi_tests/src/libtestlib.so | Bin 0 -> 15704 bytes ffi_tests/src/main.rs | 48 +++++++++++++++ ffi_tests/src/test.c | 34 +++++++++++ src/machine.rs | 26 ++++++-- src/mono_hash_map.rs | 3 +- src/shims/ffi_support.rs | 119 +++++++++++++++++++++++++++++++++++- 9 files changed, 255 insertions(+), 7 deletions(-) create mode 100644 ffi_tests/Cargo.lock create mode 100644 ffi_tests/Cargo.toml create mode 100644 ffi_tests/build.rs create mode 100755 ffi_tests/src/libtestlib.so create mode 100644 ffi_tests/src/main.rs create mode 100644 ffi_tests/src/test.c diff --git a/ffi_tests/Cargo.lock b/ffi_tests/Cargo.lock new file mode 100644 index 0000000000..242c10eb6b --- /dev/null +++ b/ffi_tests/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "ffi-tests" +version = "0.1.0" +dependencies = [ + "cc", +] diff --git a/ffi_tests/Cargo.toml b/ffi_tests/Cargo.toml new file mode 100644 index 0000000000..ad069a227d --- /dev/null +++ b/ffi_tests/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ffi-tests" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +# +[build-dependencies] +cc = "1.0" diff --git a/ffi_tests/build.rs b/ffi_tests/build.rs new file mode 100644 index 0000000000..8de0fdbf8e --- /dev/null +++ b/ffi_tests/build.rs @@ -0,0 +1,7 @@ +extern crate cc; + +fn main() { + cc::Build::new() + .file("src/test.c") + .compile("libtest.a"); +} diff --git a/ffi_tests/src/libtestlib.so b/ffi_tests/src/libtestlib.so new file mode 100755 index 0000000000000000000000000000000000000000..f0918a499a1970605439a675e1ac690219724a73 GIT binary patch literal 15704 zcmeHOZ){sv6~A_uF5TMND_aR|Sl_00O1teXTh$WQb$w~#^mS_5EKNoyQN6y{&xx7; z$$swAY8@3FP!k0@VA3Qytq37DgwUh`nh?{{wRBYQA&h+)uquKTrBcP*G9W-zbI!fz z*v~J{BqYQa-i>n4J-_q!9ben``Q7{Z*si{CC?vQo5)X?yCxk(QeVbVWu`aO$`gXBe z>fTTn%^xi$h-p;u7^4M;#QW)FSm`lkIoip1nvx42eV>wN>b1iZqPnt+KHNwzeEY2&NydyBu@f4HyoBTNA0 zSQE6v{EQVLQxk(7FqHUGpbx!sajNw@({CQE<|^Oq{@Qu>o_ytk{)716|MUV?8?_r7 z=C3tEl6xNd6-{t^0UYzvn&=-{K>wZv^!ot6vx%P^%_UZR+_sXbtes9Bc0?jkaFSNOkjlD3_DM17xK_55 z5&0Z|PQh}WqAN0XI-QG)yjxJpPC@D=M$#f*a*G0$Fthj*RxzJSr$st764#44eY4oH zYv=YJ>jC`%eG^sCb4b=szpZu_69j^0op;%E+4= z`QPI8zqi4EGV;OstC0=;*RDpQEe7aajqE#}G$T(>L6@DPz>_Tz5&ICk3U-&Vo5e1QT^n{9b{*JtV%HU#-Vakv&cTLR z2NxT(paMY!f(ir`2r3X%AgDl4fuI6G1%e6$75J}KU|?uaTbH;=Y^V)tBYH%qjoC%* zpq(x`4@yn>ATO02)slr=M(b(FX0Z`ky=V(Q6`xy1E7dAKmwgxLD?p7a z)#^0RJwPi!OF*OW3&JU&oj`v9bO7iDpn3dd0UADQ#uhp}C_>}W(CVc(ww?glTEN=j ze;>p$fQseOzU6I?L~eev^{CjkYRiU=Yvp5Ew1=MsL>~0`SskjQ?PuZt4#;g3f$rte zFNJ%SEe?-B1SNk8;4zR#yz)n+SlXWi{4*ez&t%!&uFHQ0^8Fx}&tO@etjm8-{yV(> z<8}Ep!1G`)pAobDqjh;Z)FBCS`D~fx;XO(LeFiP4Kv0380zn0W3Ir7hDiBm4s6bGG zpaM-Q!22C}e2=*k@=T>nDikeUz(-=Xx`uAtaktX`bNF;-i_;N;hvo46S>mUB z_#K4vJZ1e)!5{>$Vwa&1t~>rt5rUoJJnxiLh|j?Qv&EoL~7>z z#xF|zHX#f~6#W;FF#ek3t3MtY(Hd1H_esE4Kt28Y(bq|QzQN`N@ST7+^S^Ha{U;aEop!nbhN= zzU|&}Ggcfo-WOqiej;a$rgI~9+Df>&Lea8I<078RU0|C8{ z@9ys3*#jt?1+ii#8JN9;0KwS-Hr=sn&-U(J)}Fq;y|E!{sC)aa7&yeZA9>)Qc4z<& z0~p>N|LRad1LQl$ppF(aXv{lJ;5~QXJ+gpd@eG1I3(@qvL>stKslNLr9?)4`p0? z1gKk3bc{#Yoa^YL*^)j|N~II`rV>JO@T=>X&=ZHUV5z8EP?CdAp_s~L8yE{@1t)Ez z0gdu$SLm{|It=yE9I&o44!wK<^+GOTySC7sF`5Kp32?%kGF7udS@0Ml*qKxu!slG{ z49Q79BgLZ7VLoPHnl+o?{~3ns2K?^j@9TxmPqA0}I%QVqF!~#Ox3c{K8Zb3TlfQTQ zdz~G0!2s71Y=4RdOy!O@kYikUOUtuNKx*p%1IFl^?Rove^eo7{4SjeUu1|37!1lae zV0t?#bN;Ny6wmqKT7-FCk1*B9-k(3gA^`)Gu|2PUnDY9E<8%FZ9h4>e?c|WxQ%v#t zu%SJC>&&a)IAB;4Uhmbuf9QLN*J+?48|Lb5MPR)48rd;5yvi`=G=Kb8L56ETo?pBU<8z9Bedd|I0k#p3J+J%P z@Ph#w>nX0zGyY2u;N0W*yuRXdpi`7TUpL$F>-Yt*MHRN^^I$Xg0SHZ#eZ6CQrtg5r zYcCAa?NpQqf%Ef!4o()_880CW`RM>pC{Y%=NH?s zy_fRbV;!XX$YCcXhWC?r56AiQ=MTO=ICmJ<+VPEQc!2|v#K#Ie_Seilxk(vZAv-31 G`+oy_kAoNh literal 0 HcmV?d00001 diff --git a/ffi_tests/src/main.rs b/ffi_tests/src/main.rs new file mode 100644 index 0000000000..01b0b8067e --- /dev/null +++ b/ffi_tests/src/main.rs @@ -0,0 +1,48 @@ +extern "C" { + fn get_num(x: i32) -> i32; + fn printer(); + fn get_dbl(x: i32) -> f64; + fn test_stack_spill(a:i32, b:i32, c:i32, d:i32, e:i32, f:i32, g:i32, h:i32, i:i32, j:i32, k:i32, l:i32) -> i32; + fn pointer_test() -> *mut i32; + fn ptr_printer(x: *mut i32); + fn ddref_print(x: *mut *mut i32); +} + +//extern "C" { pub fn get_num () -> :: std :: os :: raw :: c_int ; } + +fn main() { + //let x; + unsafe { + let mut y: i32 = 45; + let mut z = (&mut y) as *mut i32; + let mut z = &mut z; + println!("{:?}, {:?}, {:?}", z, *z, **z); + println!("as **i32 in rust: {:?}", (z as *mut *mut i32)); + println!("as **i32 in rust: {:?}", **(z as *mut *mut i32)); + ddref_print(z as *mut *mut i32); + /* + //println!("{}", get_num()); + printer(); + x = get_num(1); +// let y = get_dbl(x); + + println!("{}", x); + printer(); + let y = test_stack_spill(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); + println!("{}", y); + + let ptr = pointer_test(); + + ptr_printer(ptr); + println!("In Rust this pointer has value: {:?}", *ptr); + + *ptr = 10; + ptr_printer(ptr); + println!("In Rust this pointer has value: {:?}", *ptr); + + //let ptr2 = pointer_test(); + //println!("{:?}, {:?}", *ptr, *ptr2);*/ + } + //println!("x: {:?}", x); + //println!("rjeiworjweio"); +} diff --git a/ffi_tests/src/test.c b/ffi_tests/src/test.c new file mode 100644 index 0000000000..49d2b302fd --- /dev/null +++ b/ffi_tests/src/test.c @@ -0,0 +1,34 @@ +#include +#include + +void ddref_print(int **x) { + printf("PTR %d\n", x); + printf("*PTR %d\n", *x); + printf("**PTR %d\n", **x); +} + +int get_num(int x) { + return 2 + x; +} + +int* pointer_test() { + int *point = malloc(sizeof(int)); + *point=1; + return point; +} + +void ptr_printer(int *x) { + printf("pointer has value: %d\n", *x); +} + +double get_dbl(int x) { + return 2.75 + x; +} + +void printer() { + printf("printing from C\n"); +} + +int test_stack_spill(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l) { + return a+b+c+d+e+f+g+h+i+j+k+l; +} diff --git a/src/machine.rs b/src/machine.rs index 6e524f3a4a..27199f122b 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -135,6 +135,7 @@ pub enum Provenance { sb: SbTag, }, Wildcard, + CHasAccess, } /// The "extra" information a pointer has over a regular AllocId. @@ -145,12 +146,12 @@ pub enum ProvenanceExtra { } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(Pointer, 24); +static_assert_size!(Pointer, 32); // FIXME: this would with in 24bytes but layout optimizations are not smart enough // #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] //static_assert_size!(Pointer>, 24); #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(ScalarMaybeUninit, 32); +static_assert_size!(ScalarMaybeUninit, 40); impl interpret::Provenance for Provenance { /// We use absolute addresses in the `offset` of a `Pointer`. @@ -177,6 +178,9 @@ impl interpret::Provenance for Provenance { Provenance::Wildcard => { write!(f, "[wildcard]")?; } + Provenance::CHasAccess => { + write!(f, "[c has access]")?; + } } Ok(()) @@ -186,6 +190,7 @@ impl interpret::Provenance for Provenance { match self { Provenance::Concrete { alloc_id, .. } => Some(alloc_id), Provenance::Wildcard => None, + Provenance::CHasAccess => None, } } } @@ -219,6 +224,7 @@ pub struct AllocExtra { /// Weak memory emulation via the use of store buffers, /// this is only added if it is enabled. pub weak_memory: Option, + // pub real_pointer: u64, } /// Precomputed layouts of primitive types @@ -692,6 +698,16 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { alloc: Cow<'b, Allocation>, kind: Option>, ) -> InterpResult<'tcx, Cow<'b, Allocation>> { + let (size, _, _) = ecx.get_alloc_info(id); + let fake_range = AllocRange{ start: rustc_target::abi::Size::ZERO, size: size}; + let ree = ecx.memory.alloc_map().get(id).unwrap().1.get_bytes_with_uninit_and_ptr(ecx, fake_range).unwrap(); + + // let bytes_ptr = alloc.get_bytes( ecx, fake_range); + // unsafe { + // if bytes_ptr.is_ok() {//&& id.0 > std::num::NonZeroU64::new(1600).unwrap(){ + // // println!("{:?}, {:?}", id, *(bytes_ptr.unwrap().as_ptr())); + // } + // } let kind = kind.expect("we set our STATIC_KIND so this cannot be None"); if ecx.machine.tracked_alloc_ids.contains(&id) { register_diagnostic(NonHaltingDiagnostic::CreatedAlloc( @@ -729,12 +745,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { } else { None }; + // println!("{:?}", ree.as_ptr() as u64); let alloc: Allocation = alloc.adjust_from_tcx( &ecx.tcx, AllocExtra { stacked_borrows: stacks.map(RefCell::new), data_race: race_alloc, weak_memory: buffer_alloc, + // real_pointer: 0, }, |ptr| ecx.global_base_pointer(ptr), )?; @@ -794,7 +812,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { match ptr.provenance { Provenance::Concrete { alloc_id, sb } => intptrcast::GlobalStateInner::expose_ptr(ecx, alloc_id, sb), - Provenance::Wildcard => { + Provenance::Wildcard | Provenance::CHasAccess => { // No need to do anything for wildcard pointers as // their provenances have already been previously exposed. Ok(()) @@ -813,7 +831,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { rel.map(|(alloc_id, size)| { let sb = match ptr.provenance { Provenance::Concrete { sb, .. } => ProvenanceExtra::Concrete(sb), - Provenance::Wildcard => ProvenanceExtra::Wildcard, + Provenance::Wildcard | Provenance::CHasAccess => ProvenanceExtra::Wildcard, }; (alloc_id, size, sb) }) diff --git a/src/mono_hash_map.rs b/src/mono_hash_map.rs index 45057632df..2ed36ab043 100644 --- a/src/mono_hash_map.rs +++ b/src/mono_hash_map.rs @@ -37,7 +37,7 @@ impl Default for MonoHashMap { } } -impl AllocMap for MonoHashMap { +impl AllocMap for MonoHashMap { #[inline(always)] fn contains_key(&mut self, k: &Q) -> bool where @@ -48,6 +48,7 @@ impl AllocMap for MonoHashMap { #[inline(always)] fn insert(&mut self, k: K, v: V) -> Option { + println!("WHY: {:?}", v); self.0.get_mut().insert(k, Box::new(v)).map(|x| *x) } diff --git a/src/shims/ffi_support.rs b/src/shims/ffi_support.rs index f5510c3569..cc9f9196c0 100644 --- a/src/shims/ffi_support.rs +++ b/src/shims/ffi_support.rs @@ -1,21 +1,37 @@ use libffi::{high::call::*, low::CodePtr}; use std::ops::Deref; -use rustc_middle::ty::{IntTy, Ty, TyKind, UintTy}; +use rustc_middle::ty::{IntTy, Ty, TypeAndMut, TyKind, UintTy}; use rustc_span::Symbol; use rustc_target::abi::HasDataLayout; use crate::*; +// pub trait HasUnderlyingPointer { +// fn get_underlying_raw_ptr(&self) -> *const u8; +// } + +// impl HasUnderlyingPointer for Allocation { +// fn get_underlying_raw_ptr(&self) -> *const u8 { + +// } +// } + impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { + + // fn get_allocation_for_id(&self, id: AllocId) -> InterpResult<'tcx, &Allocation> { + // let this = self.eval_context_ref(); + // this.get_alloc_raw(id) + // } + /// Extract the scalar value from the result of reading a scalar from the machine, /// and convert it to a `CArg`. fn scalar_to_carg( k: ScalarMaybeUninit, arg_type: &Ty<'tcx>, - cx: &impl HasDataLayout, + cx: &MiriEvalContext<'mir, 'tcx>, ) -> InterpResult<'tcx, CArg> { match arg_type.kind() { // If the primitive provided can be converted to a type matching the type pattern @@ -52,6 +68,54 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx TyKind::Uint(UintTy::Usize) => { return Ok(CArg::USize(k.to_machine_usize(cx)?.try_into().unwrap())); } + // mut pointers + TyKind::RawPtr(TypeAndMut{ ty: some_ty, mutbl: rustc_hir::Mutability::Mut} ) => { + match some_ty.kind() { + TyKind::Int(IntTy::I32) => { + println!("REEEE i32 pointer ARG"); + }, + TyKind::RawPtr(TypeAndMut{ ty: some_ty, mutbl: rustc_hir::Mutability::Mut} ) => { + match some_ty.kind() { + TyKind::Int(IntTy::I32) => { + println!("RECURSION BRO: **i32 arg"); + match k { + ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, sz)) => { + let (alloc_id, _, _) = cx.ptr_get_alloc_id(ptr.into())?; + let (size, align, _) = cx.get_alloc_info(alloc_id); + let fake_range = AllocRange{ start: rustc_target::abi::Size::ZERO, size: size}; + println!("{:?}, {:?}", size, align); + let alloc = cx.get_ptr_alloc(ptr.into(), size, align)?.unwrap(); + let wtf = alloc.read_integer(alloc_range(rustc_target::abi::Size::ZERO, size)); + println!("UMM {:?}", wtf); + let ree = cx.memory.alloc_map().get(alloc_id).unwrap().1.get_bytes_with_uninit_and_ptr(cx, fake_range).unwrap(); + println!("{:?}", ree.as_ptr()); + let bytes = cx.read_bytes_ptr(ptr.into(), size); + println!("WHAT {:?}", bytes); + // println!("{:?}", ptr.into_parts().0); + // let ree = &ptr.into_parts().1.bytes(); + // println!("{:?}", self.memory); + // println!("{:?}", ptr.get_alloc_id()); + // unsafe { + // println!("{:?}", *(ree as *const u64)); + // println!("{:?}", *(ptr.into_parts().1.bytes() as *mut u64)); + // } + // println!("REE {:?}", intptrcast::GlobalStateInner::rel_ptr_to_addr(cx, ptr)); + // let the_int = s.assert_int(); + // println!("{:?}", the_int.try_to_u64().unwrap()); + let inner_carg = CArg::ConstPtrUInt8(unsafe{ree.as_ptr()}); + return Ok(CArg::RecMutPtrCarg(Box::new(inner_carg))); + // return Ok(CArg::USize(the_int.try_to_u64().unwrap().try_into().unwrap())); + // println!("{:?}", s.assert_int()) + }, + _ => {} + } + }, + _ => { } + } + } + _ => { } + } + } _ => {} } // If no primitives were returned then we have an unsupported type. @@ -136,6 +200,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_int(u64::try_from(x).unwrap(), dest)?; return Ok(()); } + // mut pointers + TyKind::RawPtr(TypeAndMut{ ty: some_ty, mutbl: rustc_hir::Mutability::Mut} ) => { + match some_ty.kind() { + TyKind::Int(IntTy::I32) => { + println!("REEEE "); + // let x = call::(ptr, libffi_args.as_slice()); + // this.write_int(x, dest)?; + return Ok(()); + }, + _ => { } + } + } // Functions with no declared return type (i.e., the default return) // have the output_type `Tuple([])`. TyKind::Tuple(t_list) => @@ -264,6 +340,27 @@ pub enum CArg { UInt64(u64), /// usize. USize(usize), + // mutable pointers + MutPtrInt8(*mut i8), + MutPtrInt16(*mut i16), + MutPtrInt32(*mut i32), + MutPtrInt64(*mut i64), + MutPtrUInt8(*mut u8), + MutPtrUInt16(*mut u16), + MutPtrUInt32(*mut u32), + MutPtrUInt64(*mut u64), + // const pointers + ConstPtrInt8(*const i8), + ConstPtrInt16(*const i16), + ConstPtrInt32(*const i32), + ConstPtrInt64(*const i64), + ConstPtrUInt8(*const u8), + ConstPtrUInt16(*const u16), + ConstPtrUInt32(*const u32), + ConstPtrUInt64(*const u64), + // recursive + RecConstPtrCarg(Box), + RecMutPtrCarg(Box), } impl<'a> CArg { @@ -280,6 +377,24 @@ impl<'a> CArg { CArg::UInt32(i) => arg(i), CArg::UInt64(i) => arg(i), CArg::USize(i) => arg(i), + CArg::MutPtrInt8(i) => arg(i), + CArg::MutPtrInt16(i) => arg(i), + CArg::MutPtrInt32(i) => arg(i), + CArg::MutPtrInt64(i) => arg(i), + CArg::MutPtrUInt8(i) => arg(i), + CArg::MutPtrUInt16(i) => arg(i), + CArg::MutPtrUInt32(i) => arg(i), + CArg::MutPtrUInt64(i) => arg(i), + CArg::ConstPtrInt8(i) => arg(i), + CArg::ConstPtrInt16(i) => arg(i), + CArg::ConstPtrInt32(i) => arg(i), + CArg::ConstPtrInt64(i) => arg(i), + CArg::ConstPtrUInt8(i) => arg(i), + CArg::ConstPtrUInt16(i) => arg(i), + CArg::ConstPtrUInt32(i) => arg(i), + CArg::ConstPtrUInt64(i) => arg(i), + CArg::RecConstPtrCarg(box_carg) => (*box_carg).arg_downcast(), + CArg::RecMutPtrCarg(box_carg) => (*box_carg).arg_downcast(), } } } From 1018b62c681898d1b06e55e74be3e1ec046f8827 Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Tue, 26 Jul 2022 01:16:52 +0000 Subject: [PATCH 11/23] store the pointer in the allocextra -- now just need to make it the allocid --- src/machine.rs | 19 ++++++------------- src/mono_hash_map.rs | 3 +-- src/shims/ffi_support.rs | 25 +++---------------------- 3 files changed, 10 insertions(+), 37 deletions(-) diff --git a/src/machine.rs b/src/machine.rs index 27199f122b..5f6f62c8f0 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -224,7 +224,7 @@ pub struct AllocExtra { /// Weak memory emulation via the use of store buffers, /// this is only added if it is enabled. pub weak_memory: Option, - // pub real_pointer: u64, + pub real_pointer: *const u8, } /// Precomputed layouts of primitive types @@ -698,16 +698,10 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { alloc: Cow<'b, Allocation>, kind: Option>, ) -> InterpResult<'tcx, Cow<'b, Allocation>> { - let (size, _, _) = ecx.get_alloc_info(id); - let fake_range = AllocRange{ start: rustc_target::abi::Size::ZERO, size: size}; - let ree = ecx.memory.alloc_map().get(id).unwrap().1.get_bytes_with_uninit_and_ptr(ecx, fake_range).unwrap(); - - // let bytes_ptr = alloc.get_bytes( ecx, fake_range); - // unsafe { - // if bytes_ptr.is_ok() {//&& id.0 > std::num::NonZeroU64::new(1600).unwrap(){ - // // println!("{:?}, {:?}", id, *(bytes_ptr.unwrap().as_ptr())); - // } - // } + let size = alloc.size(); + let alloc_range = AllocRange{ start: rustc_target::abi::Size::ZERO, size: size}; + let alloc_bytes_ptr = alloc.get_bytes_with_uninit_and_ptr(ecx, alloc_range).unwrap().as_ptr(); + let kind = kind.expect("we set our STATIC_KIND so this cannot be None"); if ecx.machine.tracked_alloc_ids.contains(&id) { register_diagnostic(NonHaltingDiagnostic::CreatedAlloc( @@ -745,14 +739,13 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { } else { None }; - // println!("{:?}", ree.as_ptr() as u64); let alloc: Allocation = alloc.adjust_from_tcx( &ecx.tcx, AllocExtra { stacked_borrows: stacks.map(RefCell::new), data_race: race_alloc, weak_memory: buffer_alloc, - // real_pointer: 0, + real_pointer: alloc_bytes_ptr, }, |ptr| ecx.global_base_pointer(ptr), )?; diff --git a/src/mono_hash_map.rs b/src/mono_hash_map.rs index 2ed36ab043..45057632df 100644 --- a/src/mono_hash_map.rs +++ b/src/mono_hash_map.rs @@ -37,7 +37,7 @@ impl Default for MonoHashMap { } } -impl AllocMap for MonoHashMap { +impl AllocMap for MonoHashMap { #[inline(always)] fn contains_key(&mut self, k: &Q) -> bool where @@ -48,7 +48,6 @@ impl AllocMap for MonoHashMap { #[inline(always)] fn insert(&mut self, k: K, v: V) -> Option { - println!("WHY: {:?}", v); self.0.get_mut().insert(k, Box::new(v)).map(|x| *x) } diff --git a/src/shims/ffi_support.rs b/src/shims/ffi_support.rs index cc9f9196c0..f8dc431b31 100644 --- a/src/shims/ffi_support.rs +++ b/src/shims/ffi_support.rs @@ -81,28 +81,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx match k { ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, sz)) => { let (alloc_id, _, _) = cx.ptr_get_alloc_id(ptr.into())?; - let (size, align, _) = cx.get_alloc_info(alloc_id); - let fake_range = AllocRange{ start: rustc_target::abi::Size::ZERO, size: size}; - println!("{:?}, {:?}", size, align); - let alloc = cx.get_ptr_alloc(ptr.into(), size, align)?.unwrap(); - let wtf = alloc.read_integer(alloc_range(rustc_target::abi::Size::ZERO, size)); - println!("UMM {:?}", wtf); - let ree = cx.memory.alloc_map().get(alloc_id).unwrap().1.get_bytes_with_uninit_and_ptr(cx, fake_range).unwrap(); - println!("{:?}", ree.as_ptr()); - let bytes = cx.read_bytes_ptr(ptr.into(), size); - println!("WHAT {:?}", bytes); - // println!("{:?}", ptr.into_parts().0); - // let ree = &ptr.into_parts().1.bytes(); - // println!("{:?}", self.memory); - // println!("{:?}", ptr.get_alloc_id()); - // unsafe { - // println!("{:?}", *(ree as *const u64)); - // println!("{:?}", *(ptr.into_parts().1.bytes() as *mut u64)); - // } - // println!("REE {:?}", intptrcast::GlobalStateInner::rel_ptr_to_addr(cx, ptr)); - // let the_int = s.assert_int(); - // println!("{:?}", the_int.try_to_u64().unwrap()); - let inner_carg = CArg::ConstPtrUInt8(unsafe{ree.as_ptr()}); + let ree = cx.memory.alloc_map().get(alloc_id).unwrap().1.extra.real_pointer;//get_bytes_with_uninit_and_ptr(cx, fake_range).unwrap(); + println!("{:?}", ree); + let inner_carg = CArg::ConstPtrUInt8(ree); return Ok(CArg::RecMutPtrCarg(Box::new(inner_carg))); // return Ok(CArg::USize(the_int.try_to_u64().unwrap().try_into().unwrap())); // println!("{:?}", s.assert_int()) From 33ef5610317fa1a214ea691bab0582c956c3f944 Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Tue, 2 Aug 2022 22:18:50 -0700 Subject: [PATCH 12/23] DO NOT MERGE - scratchwork This shows how the base address, if it can be acquired, can be used to call external C functions. There are numerous TODOs, including the big one of coming up with a way to access the bytes address without the huge hole punch I put in rustc. This also requires that the bytes addresses in rustc become properly aligned. Double dereference passes, as do most tests. Of the few that remain, some of them make questionable assumptions (which we might need some way to retain or put behind an option for sanitization) like that it is fundamentally possible for two allocations to end up next to each other - this won't happen when we're using a real heap. The simd tests failing are concerning, but can be looked into. --- src/intptrcast.rs | 43 ++++++++++------------ src/shims/ffi_support.rs | 34 ++++------------- tests/extern-so/libcode.version | 3 +- tests/extern-so/test.c | 4 ++ tests/pass/extern-so/call_extern_c_fcts.rs | 6 +++ ui_test/src/lib.rs | 4 +- 6 files changed, 42 insertions(+), 52 deletions(-) diff --git a/src/intptrcast.rs b/src/intptrcast.rs index 99fc086a22..1a07c3e086 100644 --- a/src/intptrcast.rs +++ b/src/intptrcast.rs @@ -37,9 +37,6 @@ pub struct GlobalStateInner { /// Whether an allocation has been exposed or not. This cannot be put /// into `AllocExtra` for the same reason as `base_addr`. exposed: FxHashSet, - /// This is used as a memory address when a new pointer is casted to an integer. It - /// is always larger than any address that was previously made part of a block. - next_base_addr: u64, /// The provenance to use for int2ptr casts provenance_mode: ProvenanceMode, } @@ -50,7 +47,6 @@ impl GlobalStateInner { int_to_ptr_map: Vec::default(), base_addr: FxHashMap::default(), exposed: FxHashSet::default(), - next_base_addr: STACK_ADDR, provenance_mode: config.provenance_mode, } } @@ -157,6 +153,19 @@ impl<'mir, 'tcx> GlobalStateInner { } fn alloc_base_addr(ecx: &MiriEvalContext<'mir, 'tcx>, alloc_id: AllocId) -> u64 { + // TODO avoid hole punch + // TODO avoid bytes hole punch + // TODO avoid leaked address hack + let base_addr: u64 = match ecx.get_alloc_raw(alloc_id) { + Ok(ref alloc) => { + let temp = alloc.bytes.as_ptr() as u64; + assert!(temp % 16 == 0); + temp + } + // Grabbing u128 for max alignment + Err(_) => Box::leak(Box::new(0u128)) as *const u128 as u64, + }; + // With our hack, base_addr should always be fully aligned let mut global_state = ecx.machine.intptrcast.borrow_mut(); let global_state = &mut *global_state; @@ -167,34 +176,22 @@ impl<'mir, 'tcx> GlobalStateInner { // it became dangling. Hence we allow dead allocations. let (size, align, _kind) = ecx.get_alloc_info(alloc_id); - // This allocation does not have a base address yet, pick one. - // Leave some space to the previous allocation, to give it some chance to be less aligned. - let slack = { - let mut rng = ecx.machine.rng.borrow_mut(); - // This means that `(global_state.next_base_addr + slack) % 16` is uniformly distributed. - rng.gen_range(0..16) - }; - // From next_base_addr + slack, round up to adjust for alignment. - let base_addr = global_state.next_base_addr.checked_add(slack).unwrap(); - let base_addr = Self::align_addr(base_addr, align.bytes()); + // This allocation does not have a base address yet, assign its bytes base. entry.insert(base_addr); trace!( - "Assigning base address {:#x} to allocation {:?} (size: {}, align: {}, slack: {})", + "Assigning base address {:#x} to allocation {:?} (size: {}, align: {})", base_addr, alloc_id, size.bytes(), align.bytes(), - slack, ); - // Remember next base address. If this allocation is zero-sized, leave a gap - // of at least 1 to avoid two allocations having the same base address. - // (The logic in `alloc_id_from_addr` assumes unique addresses, and different - // function/vtable pointers need to be distinguishable!) - global_state.next_base_addr = base_addr.checked_add(max(size.bytes(), 1)).unwrap(); - // Given that `next_base_addr` increases in each allocation, pushing the - // corresponding tuple keeps `int_to_ptr_map` sorted + // TODO replace int_to_ptr_map's data structure, since even if we binary search for + // the insert location, the insertion is still linear (due to copies) + // I've done it the dumb obviously correct way for now. + global_state.int_to_ptr_map.retain(|&(ref a,_)| a != &base_addr); global_state.int_to_ptr_map.push((base_addr, alloc_id)); + global_state.int_to_ptr_map.sort(); base_addr } diff --git a/src/shims/ffi_support.rs b/src/shims/ffi_support.rs index f8dc431b31..ab6e2a0dc8 100644 --- a/src/shims/ffi_support.rs +++ b/src/shims/ffi_support.rs @@ -68,33 +68,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx TyKind::Uint(UintTy::Usize) => { return Ok(CArg::USize(k.to_machine_usize(cx)?.try_into().unwrap())); } - // mut pointers - TyKind::RawPtr(TypeAndMut{ ty: some_ty, mutbl: rustc_hir::Mutability::Mut} ) => { - match some_ty.kind() { - TyKind::Int(IntTy::I32) => { - println!("REEEE i32 pointer ARG"); - }, - TyKind::RawPtr(TypeAndMut{ ty: some_ty, mutbl: rustc_hir::Mutability::Mut} ) => { - match some_ty.kind() { - TyKind::Int(IntTy::I32) => { - println!("RECURSION BRO: **i32 arg"); - match k { - ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, sz)) => { - let (alloc_id, _, _) = cx.ptr_get_alloc_id(ptr.into())?; - let ree = cx.memory.alloc_map().get(alloc_id).unwrap().1.extra.real_pointer;//get_bytes_with_uninit_and_ptr(cx, fake_range).unwrap(); - println!("{:?}", ree); - let inner_carg = CArg::ConstPtrUInt8(ree); - return Ok(CArg::RecMutPtrCarg(Box::new(inner_carg))); - // return Ok(CArg::USize(the_int.try_to_u64().unwrap().try_into().unwrap())); - // println!("{:?}", s.assert_int()) - }, - _ => {} - } - }, - _ => { } - } + // pointers + TyKind::RawPtr(TypeAndMut{..} ) => { + match k { + ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, sz)) => { + let qq = ptr.into_parts().1.bytes_usize(); + // TODO select correct types rather than yoloing + return Ok(CArg::RecConstPtrCarg(Box::new(CArg::ConstPtrInt32(qq as *const i32)))) } - _ => { } + _ => {} } } _ => {} diff --git a/tests/extern-so/libcode.version b/tests/extern-so/libcode.version index 0f04b9aaeb..d52bb11c51 100644 --- a/tests/extern-so/libcode.version +++ b/tests/extern-so/libcode.version @@ -1,5 +1,6 @@ CODEABI_1.0 { - global: *add_one_int*; + global: *double_deref*; + *add_one_int*; *printer*; *test_stack_spill*; *get_unsigned_int*; diff --git a/tests/extern-so/test.c b/tests/extern-so/test.c index 68714f1743..36c7ce1e91 100644 --- a/tests/extern-so/test.c +++ b/tests/extern-so/test.c @@ -1,5 +1,9 @@ #include +int double_deref(const int **p) { + return **p; +} + int add_one_int(int x) { return 2 + x; } diff --git a/tests/pass/extern-so/call_extern_c_fcts.rs b/tests/pass/extern-so/call_extern_c_fcts.rs index 3575f99e88..eaea261dd8 100644 --- a/tests/pass/extern-so/call_extern_c_fcts.rs +++ b/tests/pass/extern-so/call_extern_c_fcts.rs @@ -3,6 +3,7 @@ //@compile-flags: -Zmiri-extern-so-file=tests/extern-so/libtestlib.so extern "C" { + fn double_deref(x: *const *const i32) -> i32; fn add_one_int(x: i32) -> i32; fn add_int16(x: i16) -> i16; fn test_stack_spill( @@ -44,5 +45,10 @@ fn main() { // test void function that prints from C -- call it twice printer(); printer(); + + let base: i32 = 42; + let base_p: *const i32 = &base as *const i32; + let base_pp: *const *const i32 = &base_p as *const *const i32; + assert_eq!(double_deref(base_pp), 42); } } diff --git a/ui_test/src/lib.rs b/ui_test/src/lib.rs index bcc48b4b63..bfc3b7b358 100644 --- a/ui_test/src/lib.rs +++ b/ui_test/src/lib.rs @@ -624,8 +624,8 @@ pub enum Mode { impl Mode { fn ok(self, status: ExitStatus) -> Errors { - match (status.code().unwrap(), self) { - (1, Mode::Fail) | (101, Mode::Panic) | (0, Mode::Pass) => vec![], + match (status.code(), self) { + (Some(1), Mode::Fail) | (Some(101), Mode::Panic) | (Some(0), Mode::Pass) => vec![], _ => vec![Error::ExitStatus(self, status)], } } From b8a595d442c5af18964df99a03807a1903e93b52 Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Thu, 4 Aug 2022 23:18:27 +0000 Subject: [PATCH 13/23] one level up from full holepunch to alloc bytes --- src/intptrcast.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/intptrcast.rs b/src/intptrcast.rs index 1a07c3e086..08cb842373 100644 --- a/src/intptrcast.rs +++ b/src/intptrcast.rs @@ -158,9 +158,10 @@ impl<'mir, 'tcx> GlobalStateInner { // TODO avoid leaked address hack let base_addr: u64 = match ecx.get_alloc_raw(alloc_id) { Ok(ref alloc) => { - let temp = alloc.bytes.as_ptr() as u64; - assert!(temp % 16 == 0); - temp + let temp = alloc.get_bytes_addr(); + // TODO make this a check + assert!(temp.bytes() % 16 == 0); + temp.bytes() } // Grabbing u128 for max alignment Err(_) => Box::leak(Box::new(0u128)) as *const u128 as u64, From a87fdb34df32dbbb82b4cf45273976c88e07038a Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Thu, 4 Aug 2022 23:47:11 +0000 Subject: [PATCH 14/23] no more holepunch to alloc.bytes --- src/intptrcast.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/intptrcast.rs b/src/intptrcast.rs index 08cb842373..2a672bfa4a 100644 --- a/src/intptrcast.rs +++ b/src/intptrcast.rs @@ -153,15 +153,11 @@ impl<'mir, 'tcx> GlobalStateInner { } fn alloc_base_addr(ecx: &MiriEvalContext<'mir, 'tcx>, alloc_id: AllocId) -> u64 { - // TODO avoid hole punch - // TODO avoid bytes hole punch // TODO avoid leaked address hack - let base_addr: u64 = match ecx.get_alloc_raw(alloc_id) { - Ok(ref alloc) => { - let temp = alloc.get_bytes_addr(); - // TODO make this a check - assert!(temp.bytes() % 16 == 0); - temp.bytes() + let base_addr: u64 = match ecx.get_alloc_base_addr(alloc_id) { + Ok(addr) => { + assert!(addr.bytes() % 16 == 0); + addr.bytes() } // Grabbing u128 for max alignment Err(_) => Box::leak(Box::new(0u128)) as *const u128 as u64, From 94dacfd731ab07779c5700b58246a71b4fa84bae Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Fri, 5 Aug 2022 01:08:21 +0000 Subject: [PATCH 15/23] moving the intptrcast int_to_ptr_map from a vector to a btreemap --- src/intptrcast.rs | 51 ++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/intptrcast.rs b/src/intptrcast.rs index 2a672bfa4a..e3c049b2cc 100644 --- a/src/intptrcast.rs +++ b/src/intptrcast.rs @@ -1,6 +1,6 @@ use std::cell::RefCell; use std::cmp::max; -use std::collections::hash_map::Entry; +use std::collections::{hash_map::Entry, BTreeMap}; use log::trace; use rand::Rng; @@ -26,9 +26,9 @@ pub type GlobalState = RefCell; #[derive(Clone, Debug)] pub struct GlobalStateInner { - /// This is used as a map between the address of each allocation and its `AllocId`. - /// It is always sorted - int_to_ptr_map: Vec<(u64, AllocId)>, + /// This is a map between the address of each allocation and its `AllocId`. + /// Since it's a `BTreeMap`, it is always sorted, and provides efficient insertion. + int_to_ptr_map: BTreeMap, /// The base address for each allocation. We cannot put that into /// `AllocExtra` because function pointers also have a base address, and /// they do not have an `AllocExtra`. @@ -44,7 +44,7 @@ pub struct GlobalStateInner { impl GlobalStateInner { pub fn new(config: &MiriConfig) -> Self { GlobalStateInner { - int_to_ptr_map: Vec::default(), + int_to_ptr_map: BTreeMap::default(), base_addr: FxHashMap::default(), exposed: FxHashSet::default(), provenance_mode: config.provenance_mode, @@ -59,22 +59,26 @@ impl<'mir, 'tcx> GlobalStateInner { let global_state = ecx.machine.intptrcast.borrow(); assert!(global_state.provenance_mode != ProvenanceMode::Strict); - let pos = global_state.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr); - // Determine the in-bounds provenance for this pointer. // (This is only called on an actual access, so in-bounds is the only possible kind of provenance.) - let alloc_id = match pos { - Ok(pos) => Some(global_state.int_to_ptr_map[pos].1), - Err(0) => None, - Err(pos) => { - // This is the largest of the adresses smaller than `int`, - // i.e. the greatest lower bound (glb) - let (glb, alloc_id) = global_state.int_to_ptr_map[pos - 1]; - // This never overflows because `addr >= glb` - let offset = addr - glb; - // If the offset exceeds the size of the allocation, don't use this `alloc_id`. - let size = ecx.get_alloc_info(alloc_id).0; - if offset <= size.bytes() { Some(alloc_id) } else { None } + let alloc_id = match global_state.int_to_ptr_map.get(&addr) { + Some(&id) => Some(id), + None => { + // If the address is not in the map, we check the position it should be inserted at. + // This returns the max key in the map less than `addr`. + match global_state.int_to_ptr_map.range(..addr).next_back() { + // Should be inserted at the beginning. + None => None, + // This is the largest of the adresses smaller than `int`, + // i.e. the greatest lower bound (glb). + Some((glb, &alloc_id)) => { + // This never overflows because `addr >= glb` + let offset = addr - glb; + // If the offset exceeds the size of the allocation, don't use this `alloc_id`. + let size = ecx.get_alloc_info(alloc_id).0; + if offset <= size.bytes() { Some(alloc_id) } else { None } + } + } } }?; @@ -183,12 +187,9 @@ impl<'mir, 'tcx> GlobalStateInner { align.bytes(), ); - // TODO replace int_to_ptr_map's data structure, since even if we binary search for - // the insert location, the insertion is still linear (due to copies) - // I've done it the dumb obviously correct way for now. - global_state.int_to_ptr_map.retain(|&(ref a,_)| a != &base_addr); - global_state.int_to_ptr_map.push((base_addr, alloc_id)); - global_state.int_to_ptr_map.sort(); + // Map has no duplicates so no need to remove copies. + // Map is always sorted. + global_state.int_to_ptr_map.insert(base_addr, alloc_id); base_addr } From 89688bb2dd8451b8fe693ed4b03765258126ba79 Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Fri, 5 Aug 2022 02:08:38 +0000 Subject: [PATCH 16/23] no more yolo pointer typing --- src/shims/ffi_support.rs | 88 ++++++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 25 deletions(-) diff --git a/src/shims/ffi_support.rs b/src/shims/ffi_support.rs index ab6e2a0dc8..ab0014731f 100644 --- a/src/shims/ffi_support.rs +++ b/src/shims/ffi_support.rs @@ -7,25 +7,10 @@ use rustc_target::abi::HasDataLayout; use crate::*; -// pub trait HasUnderlyingPointer { -// fn get_underlying_raw_ptr(&self) -> *const u8; -// } - -// impl HasUnderlyingPointer for Allocation { -// fn get_underlying_raw_ptr(&self) -> *const u8 { - -// } -// } - impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { - // fn get_allocation_for_id(&self, id: AllocId) -> InterpResult<'tcx, &Allocation> { - // let this = self.eval_context_ref(); - // this.get_alloc_raw(id) - // } - /// Extract the scalar value from the result of reading a scalar from the machine, /// and convert it to a `CArg`. fn scalar_to_carg( @@ -69,16 +54,71 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return Ok(CArg::USize(k.to_machine_usize(cx)?.try_into().unwrap())); } // pointers - TyKind::RawPtr(TypeAndMut{..} ) => { + TyKind::RawPtr(TypeAndMut{ ty: some_ty, mutbl: some_mut } ) => { match k { - ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, sz)) => { + ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, _)) => { let qq = ptr.into_parts().1.bytes_usize(); - // TODO select correct types rather than yoloing - return Ok(CArg::RecConstPtrCarg(Box::new(CArg::ConstPtrInt32(qq as *const i32)))) + match (some_ty.kind(), some_mut) { + // int + (TyKind::Int(IntTy::I8), rustc_hir::Mutability::Mut) => { + return Ok(CArg::MutPtrInt8(qq as *mut i8)); + }, + (TyKind::Int(IntTy::I8), rustc_hir::Mutability::Not) => { + return Ok(CArg::ConstPtrInt8(qq as *const i8)); + }, + (TyKind::Int(IntTy::I16), rustc_hir::Mutability::Mut) => { + return Ok(CArg::MutPtrInt16(qq as *mut i16)); + }, + (TyKind::Int(IntTy::I16), rustc_hir::Mutability::Not) => { + return Ok(CArg::ConstPtrInt16(qq as *const i16)); + }, + (TyKind::Int(IntTy::I32), rustc_hir::Mutability::Mut) => { + return Ok(CArg::MutPtrInt32(qq as *mut i32)); + }, + (TyKind::Int(IntTy::I32), rustc_hir::Mutability::Not) => { + return Ok(CArg::ConstPtrInt32(qq as *const i32)); + }, + (TyKind::Int(IntTy::I64), rustc_hir::Mutability::Mut) => { + return Ok(CArg::MutPtrInt64(qq as *mut i64)); + }, + (TyKind::Int(IntTy::I64), rustc_hir::Mutability::Not) => { + return Ok(CArg::ConstPtrInt64(qq as *const i64)); + }, + // uints + (TyKind::Uint(UintTy::U8), rustc_hir::Mutability::Mut) => { + return Ok(CArg::MutPtrUInt8(qq as *mut u8)); + }, + (TyKind::Uint(UintTy::U8), rustc_hir::Mutability::Not) => { + return Ok(CArg::ConstPtrUInt8(qq as *const u8)); + }, + (TyKind::Uint(UintTy::U16), rustc_hir::Mutability::Mut) => { + return Ok(CArg::MutPtrUInt16(qq as *mut u16)); + }, + (TyKind::Uint(UintTy::U16), rustc_hir::Mutability::Not) => { + return Ok(CArg::ConstPtrUInt16(qq as *const u16)); + }, + (TyKind::Uint(UintTy::U32), rustc_hir::Mutability::Mut) => { + return Ok(CArg::MutPtrUInt32(qq as *mut u32)); + }, + (TyKind::Uint(UintTy::U32), rustc_hir::Mutability::Not) => { + return Ok(CArg::ConstPtrUInt32(qq as *const u32)); + }, + (TyKind::Uint(UintTy::U64), rustc_hir::Mutability::Mut) => { + return Ok(CArg::MutPtrUInt64(qq as *mut u64)); + }, + (TyKind::Uint(UintTy::U64), rustc_hir::Mutability::Not) => { + return Ok(CArg::ConstPtrUInt64(qq as *const u64)); + }, + // recursive case + (TyKind::RawPtr(..), _) => { + return Ok(CArg::RecCarg(Box::new(Self::scalar_to_carg(k, some_ty, cx)?))); + } + _ => {} + } } _ => {} } - } + }, _ => {} } // If no primitives were returned then we have an unsupported type. @@ -321,9 +361,8 @@ pub enum CArg { ConstPtrUInt16(*const u16), ConstPtrUInt32(*const u32), ConstPtrUInt64(*const u64), - // recursive - RecConstPtrCarg(Box), - RecMutPtrCarg(Box), + /// Recursive `CArg` (for nested pointers). + RecCarg(Box), } impl<'a> CArg { @@ -356,8 +395,7 @@ impl<'a> CArg { CArg::ConstPtrUInt16(i) => arg(i), CArg::ConstPtrUInt32(i) => arg(i), CArg::ConstPtrUInt64(i) => arg(i), - CArg::RecConstPtrCarg(box_carg) => (*box_carg).arg_downcast(), - CArg::RecMutPtrCarg(box_carg) => (*box_carg).arg_downcast(), + CArg::RecCarg(box_carg) => (*box_carg).arg_downcast(), } } } From 7d73854340e07ab14a8645eff06c00b334c8e078 Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Sat, 6 Aug 2022 20:55:58 +0000 Subject: [PATCH 17/23] commenting out alignment check fixes the alignment failing tests -- needs to be fixed in rustc, but the issue is the alignment check isnt formulated correctly wrt real addrs --- src/intptrcast.rs | 12 +++++++++++- src/machine.rs | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/intptrcast.rs b/src/intptrcast.rs index e3c049b2cc..a3954b7415 100644 --- a/src/intptrcast.rs +++ b/src/intptrcast.rs @@ -158,13 +158,17 @@ impl<'mir, 'tcx> GlobalStateInner { fn alloc_base_addr(ecx: &MiriEvalContext<'mir, 'tcx>, alloc_id: AllocId) -> u64 { // TODO avoid leaked address hack + let mut is_global = false; let base_addr: u64 = match ecx.get_alloc_base_addr(alloc_id) { Ok(addr) => { assert!(addr.bytes() % 16 == 0); addr.bytes() } // Grabbing u128 for max alignment - Err(_) => Box::leak(Box::new(0u128)) as *const u128 as u64, + Err(_) => { + is_global = true; + Box::leak(Box::new(0u128)) as *const u128 as u64 + } }; // With our hack, base_addr should always be fully aligned let mut global_state = ecx.machine.intptrcast.borrow_mut(); @@ -177,6 +181,12 @@ impl<'mir, 'tcx> GlobalStateInner { // it became dangling. Hence we allow dead allocations. let (size, align, _kind) = ecx.get_alloc_info(alloc_id); + // println!("REE: {:?}, {:?}, {:?}", align, base_addr % align.bytes(), is_global); + let what = Self::align_addr(base_addr, align.bytes()); + if (what != base_addr) { + // println!("REEE: {:?}, {:?}, {:?}", what, base_addr, alloc_id); + } + // This allocation does not have a base address yet, assign its bytes base. entry.insert(base_addr); trace!( diff --git a/src/machine.rs b/src/machine.rs index c2b56fbe14..6ea17a7dd6 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -723,6 +723,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { ) }); let buffer_alloc = ecx.machine.weak_memory.then(weak_memory::AllocExtra::new_allocation); + // println!("yup -- {:?}", id); let alloc: Allocation = alloc.adjust_from_tcx( &ecx.tcx, AllocExtra { From bca7fcc56d5589b6082f1ca685cf6aba4e5e45cb Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Tue, 9 Aug 2022 21:52:27 +0000 Subject: [PATCH 18/23] avoid infinite recursion into pointers in intptrcast `alloc_base_addr` (see: recursive_static test case) --- src/intptrcast.rs | 46 +++++++++++++++++++++++++++------------------- src/machine.rs | 7 ------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/intptrcast.rs b/src/intptrcast.rs index a3954b7415..9b7811663c 100644 --- a/src/intptrcast.rs +++ b/src/intptrcast.rs @@ -157,36 +157,44 @@ impl<'mir, 'tcx> GlobalStateInner { } fn alloc_base_addr(ecx: &MiriEvalContext<'mir, 'tcx>, alloc_id: AllocId) -> u64 { - // TODO avoid leaked address hack - let mut is_global = false; - let base_addr: u64 = match ecx.get_alloc_base_addr(alloc_id) { - Ok(addr) => { - assert!(addr.bytes() % 16 == 0); - addr.bytes() - } - // Grabbing u128 for max alignment + // With our hack, base_addr should always be fully aligned + let mut global_state = match ecx.machine.intptrcast.try_borrow_mut() { + Ok(gstate) => gstate, Err(_) => { - is_global = true; - Box::leak(Box::new(0u128)) as *const u128 as u64 + // we're recursing + let new_addr = Box::leak(Box::new(0u128)) as *const u128 as u64; + unsafe { + (*ecx.machine.intptrcast.as_ptr()).base_addr.insert(alloc_id, new_addr); + (*ecx.machine.intptrcast.as_ptr()).int_to_ptr_map.insert(new_addr, alloc_id); + } + trace!( + "Recursive case: Assigning base address {:#x} to allocation {:?}", + new_addr, + alloc_id, + ); + return new_addr; } }; - // With our hack, base_addr should always be fully aligned - let mut global_state = ecx.machine.intptrcast.borrow_mut(); let global_state = &mut *global_state; match global_state.base_addr.entry(alloc_id) { Entry::Occupied(entry) => *entry.get(), Entry::Vacant(entry) => { + let base_addr = match ecx.get_alloc_base_addr(alloc_id) { + Ok(addr) => { + assert!(addr.bytes() % 16 == 0); + addr.bytes() + } + // Grabbing u128 for max alignment + Err(_) => { + // TODO avoid leaked address hack + Box::leak(Box::new(0u128)) as *const u128 as u64 + } + }; // There is nothing wrong with a raw pointer being cast to an integer only after // it became dangling. Hence we allow dead allocations. let (size, align, _kind) = ecx.get_alloc_info(alloc_id); - // println!("REE: {:?}, {:?}, {:?}", align, base_addr % align.bytes(), is_global); - let what = Self::align_addr(base_addr, align.bytes()); - if (what != base_addr) { - // println!("REEE: {:?}, {:?}, {:?}", what, base_addr, alloc_id); - } - // This allocation does not have a base address yet, assign its bytes base. entry.insert(base_addr); trace!( @@ -200,7 +208,7 @@ impl<'mir, 'tcx> GlobalStateInner { // Map has no duplicates so no need to remove copies. // Map is always sorted. global_state.int_to_ptr_map.insert(base_addr, alloc_id); - + base_addr } } diff --git a/src/machine.rs b/src/machine.rs index 6ea17a7dd6..6268e4a846 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -224,7 +224,6 @@ pub struct AllocExtra { /// Weak memory emulation via the use of store buffers, /// this is only added if it is enabled. pub weak_memory: Option, - pub real_pointer: *const u8, } /// Precomputed layouts of primitive types @@ -690,10 +689,6 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { alloc: Cow<'b, Allocation>, kind: Option>, ) -> InterpResult<'tcx, Cow<'b, Allocation>> { - let size = alloc.size(); - let alloc_range = AllocRange{ start: rustc_target::abi::Size::ZERO, size: size}; - let alloc_bytes_ptr = alloc.get_bytes_with_uninit_and_ptr(ecx, alloc_range).unwrap().as_ptr(); - let kind = kind.expect("we set our STATIC_KIND so this cannot be None"); if ecx.machine.tracked_alloc_ids.contains(&id) { register_diagnostic(NonHaltingDiagnostic::CreatedAlloc( @@ -723,14 +718,12 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { ) }); let buffer_alloc = ecx.machine.weak_memory.then(weak_memory::AllocExtra::new_allocation); - // println!("yup -- {:?}", id); let alloc: Allocation = alloc.adjust_from_tcx( &ecx.tcx, AllocExtra { stacked_borrows: stacks.map(RefCell::new), data_race: race_alloc, weak_memory: buffer_alloc, - real_pointer: alloc_bytes_ptr, }, |ptr| ecx.global_base_pointer(ptr), )?; From 1c0168adf5e24143d5c9cd45fece8e251b1422f4 Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Fri, 19 Aug 2022 23:38:09 +0000 Subject: [PATCH 19/23] get rid of box leak hack --- src/intptrcast.rs | 54 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/src/intptrcast.rs b/src/intptrcast.rs index 9b7811663c..6cf54df1d4 100644 --- a/src/intptrcast.rs +++ b/src/intptrcast.rs @@ -7,7 +7,7 @@ use rand::Rng; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_span::Span; -use rustc_target::abi::{HasDataLayout, Size}; +use rustc_target::abi::{Size, Align}; use crate::*; @@ -37,6 +37,9 @@ pub struct GlobalStateInner { /// Whether an allocation has been exposed or not. This cannot be put /// into `AllocExtra` for the same reason as `base_addr`. exposed: FxHashSet, + /// This is used as a memory address when a new pointer is casted to an integer. It + /// is always larger than any address that was previously made part of a block. + next_base_addr: u64, /// The provenance to use for int2ptr casts provenance_mode: ProvenanceMode, } @@ -47,6 +50,7 @@ impl GlobalStateInner { int_to_ptr_map: BTreeMap::default(), base_addr: FxHashMap::default(), exposed: FxHashSet::default(), + next_base_addr: 1, provenance_mode: config.provenance_mode, } } @@ -156,17 +160,47 @@ impl<'mir, 'tcx> GlobalStateInner { Ok(Pointer::new(Some(Provenance::Wildcard), Size::from_bytes(addr))) } + fn get_next_fake_addr(ecx: &MiriEvalContext<'mir, 'tcx>, align: Align, size: Size, next_base_addr: u64) -> (u64, u64) { + // This allocation does not have a base address yet, pick one. + // Leave some space to the previous allocation, to give it some chance to be less aligned. + // It also doesn't correspond to a real array of bytes. + // HACK: we're not going to actually have pointers in the program that correspond to + // the really low addresses, so let's use these as placeholders for these allocations. + // This makes sure we won't overlap with any existing (real) addresses. + // An alternate hack, which we had before, was to create and leak a Box: + // `let new_addr = Box::leak(Box::new(0u128)) as *const u128 as u64;` + let slack = { + let mut rng = ecx.machine.rng.borrow_mut(); + // This means that `(global_state.next_base_addr + slack) % 16` is uniformly distributed. + rng.gen_range(0..16) + }; + // From next_base_addr + slack, round up to adjust for alignment. + let base_addr = next_base_addr.checked_add(slack).unwrap(); + let base_addr = Self::align_addr(base_addr, align.bytes()); + + // Remember next base address. If this allocation is zero-sized, leave a gap + // of at least 1 to avoid two allocations having the same base address. + // (The logic in `alloc_id_from_addr` assumes unique addresses, and different + // function/vtable pointers need to be distinguishable!) + let next_base_addr = base_addr.checked_add(max(size.bytes(), 1)).unwrap(); + (base_addr, next_base_addr) + } + fn alloc_base_addr(ecx: &MiriEvalContext<'mir, 'tcx>, alloc_id: AllocId) -> u64 { // With our hack, base_addr should always be fully aligned let mut global_state = match ecx.machine.intptrcast.try_borrow_mut() { Ok(gstate) => gstate, Err(_) => { // we're recursing - let new_addr = Box::leak(Box::new(0u128)) as *const u128 as u64; - unsafe { + let (size, align, _kind) = ecx.get_alloc_info(alloc_id); + let new_addr = unsafe { + let next_base_addr = (*ecx.machine.intptrcast.as_ptr()).next_base_addr; + let (new_addr, next_base_addr) = Self::get_next_fake_addr(ecx, align, size, next_base_addr); //Box::leak(Box::new(0u128)) as *const u128 as u64; (*ecx.machine.intptrcast.as_ptr()).base_addr.insert(alloc_id, new_addr); (*ecx.machine.intptrcast.as_ptr()).int_to_ptr_map.insert(new_addr, alloc_id); - } + (*ecx.machine.intptrcast.as_ptr()).next_base_addr = next_base_addr; + new_addr + }; trace!( "Recursive case: Assigning base address {:#x} to allocation {:?}", new_addr, @@ -180,6 +214,10 @@ impl<'mir, 'tcx> GlobalStateInner { match global_state.base_addr.entry(alloc_id) { Entry::Occupied(entry) => *entry.get(), Entry::Vacant(entry) => { + // There is nothing wrong with a raw pointer being cast to an integer only after + // it became dangling. Hence we allow dead allocations. + let (size, align, _kind) = ecx.get_alloc_info(alloc_id); + let base_addr = match ecx.get_alloc_base_addr(alloc_id) { Ok(addr) => { assert!(addr.bytes() % 16 == 0); @@ -188,12 +226,12 @@ impl<'mir, 'tcx> GlobalStateInner { // Grabbing u128 for max alignment Err(_) => { // TODO avoid leaked address hack - Box::leak(Box::new(0u128)) as *const u128 as u64 + // Box::leak(Box::new(0u128)) as *const u128 as u64 + let (new_addr, next_base_addr) = Self::get_next_fake_addr(ecx, align, size, global_state.next_base_addr); //Box::leak(Box::new(0u128)) as *const u128 as u64; + global_state.next_base_addr = next_base_addr; + new_addr } }; - // There is nothing wrong with a raw pointer being cast to an integer only after - // it became dangling. Hence we allow dead allocations. - let (size, align, _kind) = ecx.get_alloc_info(alloc_id); // This allocation does not have a base address yet, assign its bytes base. entry.insert(base_addr); From 73c978337d78e71b7a455ee8fa279a86389332c6 Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Fri, 19 Aug 2022 23:48:19 +0000 Subject: [PATCH 20/23] some cleanup --- ffi_tests/Cargo.lock | 16 ------------ ffi_tests/Cargo.toml | 9 ------- ffi_tests/build.rs | 7 ------ ffi_tests/src/libtestlib.so | Bin 15704 -> 0 bytes ffi_tests/src/main.rs | 48 ------------------------------------ ffi_tests/src/test.c | 34 ------------------------- src/intptrcast.rs | 2 +- src/machine.rs | 9 ++----- src/shims/ffi_support.rs | 1 - 9 files changed, 3 insertions(+), 123 deletions(-) delete mode 100644 ffi_tests/Cargo.lock delete mode 100644 ffi_tests/Cargo.toml delete mode 100644 ffi_tests/build.rs delete mode 100755 ffi_tests/src/libtestlib.so delete mode 100644 ffi_tests/src/main.rs delete mode 100644 ffi_tests/src/test.c diff --git a/ffi_tests/Cargo.lock b/ffi_tests/Cargo.lock deleted file mode 100644 index 242c10eb6b..0000000000 --- a/ffi_tests/Cargo.lock +++ /dev/null @@ -1,16 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "cc" -version = "1.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" - -[[package]] -name = "ffi-tests" -version = "0.1.0" -dependencies = [ - "cc", -] diff --git a/ffi_tests/Cargo.toml b/ffi_tests/Cargo.toml deleted file mode 100644 index ad069a227d..0000000000 --- a/ffi_tests/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "ffi-tests" -version = "0.1.0" -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -# -[build-dependencies] -cc = "1.0" diff --git a/ffi_tests/build.rs b/ffi_tests/build.rs deleted file mode 100644 index 8de0fdbf8e..0000000000 --- a/ffi_tests/build.rs +++ /dev/null @@ -1,7 +0,0 @@ -extern crate cc; - -fn main() { - cc::Build::new() - .file("src/test.c") - .compile("libtest.a"); -} diff --git a/ffi_tests/src/libtestlib.so b/ffi_tests/src/libtestlib.so deleted file mode 100755 index f0918a499a1970605439a675e1ac690219724a73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15704 zcmeHOZ){sv6~A_uF5TMND_aR|Sl_00O1teXTh$WQb$w~#^mS_5EKNoyQN6y{&xx7; z$$swAY8@3FP!k0@VA3Qytq37DgwUh`nh?{{wRBYQA&h+)uquKTrBcP*G9W-zbI!fz z*v~J{BqYQa-i>n4J-_q!9ben``Q7{Z*si{CC?vQo5)X?yCxk(QeVbVWu`aO$`gXBe z>fTTn%^xi$h-p;u7^4M;#QW)FSm`lkIoip1nvx42eV>wN>b1iZqPnt+KHNwzeEY2&NydyBu@f4HyoBTNA0 zSQE6v{EQVLQxk(7FqHUGpbx!sajNw@({CQE<|^Oq{@Qu>o_ytk{)716|MUV?8?_r7 z=C3tEl6xNd6-{t^0UYzvn&=-{K>wZv^!ot6vx%P^%_UZR+_sXbtes9Bc0?jkaFSNOkjlD3_DM17xK_55 z5&0Z|PQh}WqAN0XI-QG)yjxJpPC@D=M$#f*a*G0$Fthj*RxzJSr$st764#44eY4oH zYv=YJ>jC`%eG^sCb4b=szpZu_69j^0op;%E+4= z`QPI8zqi4EGV;OstC0=;*RDpQEe7aajqE#}G$T(>L6@DPz>_Tz5&ICk3U-&Vo5e1QT^n{9b{*JtV%HU#-Vakv&cTLR z2NxT(paMY!f(ir`2r3X%AgDl4fuI6G1%e6$75J}KU|?uaTbH;=Y^V)tBYH%qjoC%* zpq(x`4@yn>ATO02)slr=M(b(FX0Z`ky=V(Q6`xy1E7dAKmwgxLD?p7a z)#^0RJwPi!OF*OW3&JU&oj`v9bO7iDpn3dd0UADQ#uhp}C_>}W(CVc(ww?glTEN=j ze;>p$fQseOzU6I?L~eev^{CjkYRiU=Yvp5Ew1=MsL>~0`SskjQ?PuZt4#;g3f$rte zFNJ%SEe?-B1SNk8;4zR#yz)n+SlXWi{4*ez&t%!&uFHQ0^8Fx}&tO@etjm8-{yV(> z<8}Ep!1G`)pAobDqjh;Z)FBCS`D~fx;XO(LeFiP4Kv0380zn0W3Ir7hDiBm4s6bGG zpaM-Q!22C}e2=*k@=T>nDikeUz(-=Xx`uAtaktX`bNF;-i_;N;hvo46S>mUB z_#K4vJZ1e)!5{>$Vwa&1t~>rt5rUoJJnxiLh|j?Qv&EoL~7>z z#xF|zHX#f~6#W;FF#ek3t3MtY(Hd1H_esE4Kt28Y(bq|QzQN`N@ST7+^S^Ha{U;aEop!nbhN= zzU|&}Ggcfo-WOqiej;a$rgI~9+Df>&Lea8I<078RU0|C8{ z@9ys3*#jt?1+ii#8JN9;0KwS-Hr=sn&-U(J)}Fq;y|E!{sC)aa7&yeZA9>)Qc4z<& z0~p>N|LRad1LQl$ppF(aXv{lJ;5~QXJ+gpd@eG1I3(@qvL>stKslNLr9?)4`p0? z1gKk3bc{#Yoa^YL*^)j|N~II`rV>JO@T=>X&=ZHUV5z8EP?CdAp_s~L8yE{@1t)Ez z0gdu$SLm{|It=yE9I&o44!wK<^+GOTySC7sF`5Kp32?%kGF7udS@0Ml*qKxu!slG{ z49Q79BgLZ7VLoPHnl+o?{~3ns2K?^j@9TxmPqA0}I%QVqF!~#Ox3c{K8Zb3TlfQTQ zdz~G0!2s71Y=4RdOy!O@kYikUOUtuNKx*p%1IFl^?Rove^eo7{4SjeUu1|37!1lae zV0t?#bN;Ny6wmqKT7-FCk1*B9-k(3gA^`)Gu|2PUnDY9E<8%FZ9h4>e?c|WxQ%v#t zu%SJC>&&a)IAB;4Uhmbuf9QLN*J+?48|Lb5MPR)48rd;5yvi`=G=Kb8L56ETo?pBU<8z9Bedd|I0k#p3J+J%P z@Ph#w>nX0zGyY2u;N0W*yuRXdpi`7TUpL$F>-Yt*MHRN^^I$Xg0SHZ#eZ6CQrtg5r zYcCAa?NpQqf%Ef!4o()_880CW`RM>pC{Y%=NH?s zy_fRbV;!XX$YCcXhWC?r56AiQ=MTO=ICmJ<+VPEQc!2|v#K#Ie_Seilxk(vZAv-31 G`+oy_kAoNh diff --git a/ffi_tests/src/main.rs b/ffi_tests/src/main.rs deleted file mode 100644 index 01b0b8067e..0000000000 --- a/ffi_tests/src/main.rs +++ /dev/null @@ -1,48 +0,0 @@ -extern "C" { - fn get_num(x: i32) -> i32; - fn printer(); - fn get_dbl(x: i32) -> f64; - fn test_stack_spill(a:i32, b:i32, c:i32, d:i32, e:i32, f:i32, g:i32, h:i32, i:i32, j:i32, k:i32, l:i32) -> i32; - fn pointer_test() -> *mut i32; - fn ptr_printer(x: *mut i32); - fn ddref_print(x: *mut *mut i32); -} - -//extern "C" { pub fn get_num () -> :: std :: os :: raw :: c_int ; } - -fn main() { - //let x; - unsafe { - let mut y: i32 = 45; - let mut z = (&mut y) as *mut i32; - let mut z = &mut z; - println!("{:?}, {:?}, {:?}", z, *z, **z); - println!("as **i32 in rust: {:?}", (z as *mut *mut i32)); - println!("as **i32 in rust: {:?}", **(z as *mut *mut i32)); - ddref_print(z as *mut *mut i32); - /* - //println!("{}", get_num()); - printer(); - x = get_num(1); -// let y = get_dbl(x); - - println!("{}", x); - printer(); - let y = test_stack_spill(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); - println!("{}", y); - - let ptr = pointer_test(); - - ptr_printer(ptr); - println!("In Rust this pointer has value: {:?}", *ptr); - - *ptr = 10; - ptr_printer(ptr); - println!("In Rust this pointer has value: {:?}", *ptr); - - //let ptr2 = pointer_test(); - //println!("{:?}, {:?}", *ptr, *ptr2);*/ - } - //println!("x: {:?}", x); - //println!("rjeiworjweio"); -} diff --git a/ffi_tests/src/test.c b/ffi_tests/src/test.c deleted file mode 100644 index 49d2b302fd..0000000000 --- a/ffi_tests/src/test.c +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include - -void ddref_print(int **x) { - printf("PTR %d\n", x); - printf("*PTR %d\n", *x); - printf("**PTR %d\n", **x); -} - -int get_num(int x) { - return 2 + x; -} - -int* pointer_test() { - int *point = malloc(sizeof(int)); - *point=1; - return point; -} - -void ptr_printer(int *x) { - printf("pointer has value: %d\n", *x); -} - -double get_dbl(int x) { - return 2.75 + x; -} - -void printer() { - printf("printing from C\n"); -} - -int test_stack_spill(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l) { - return a+b+c+d+e+f+g+h+i+j+k+l; -} diff --git a/src/intptrcast.rs b/src/intptrcast.rs index 6cf54df1d4..0050f256c8 100644 --- a/src/intptrcast.rs +++ b/src/intptrcast.rs @@ -7,7 +7,7 @@ use rand::Rng; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_span::Span; -use rustc_target::abi::{Size, Align}; +use rustc_target::abi::{HasDataLayout, Size, Align}; use crate::*; diff --git a/src/machine.rs b/src/machine.rs index 6268e4a846..49d2faaf7f 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -135,7 +135,6 @@ pub enum Provenance { sb: SbTag, }, Wildcard, - CHasAccess, } /// The "extra" information a pointer has over a regular AllocId. @@ -178,9 +177,6 @@ impl interpret::Provenance for Provenance { Provenance::Wildcard => { write!(f, "[wildcard]")?; } - Provenance::CHasAccess => { - write!(f, "[c has access]")?; - } } Ok(()) @@ -190,7 +186,6 @@ impl interpret::Provenance for Provenance { match self { Provenance::Concrete { alloc_id, .. } => Some(alloc_id), Provenance::Wildcard => None, - Provenance::CHasAccess => None, } } } @@ -775,7 +770,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { match ptr.provenance { Provenance::Concrete { alloc_id, sb } => intptrcast::GlobalStateInner::expose_ptr(ecx, alloc_id, sb), - Provenance::Wildcard | Provenance::CHasAccess => { + Provenance::Wildcard => { // No need to do anything for wildcard pointers as // their provenances have already been previously exposed. Ok(()) @@ -794,7 +789,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { rel.map(|(alloc_id, size)| { let sb = match ptr.provenance { Provenance::Concrete { sb, .. } => ProvenanceExtra::Concrete(sb), - Provenance::Wildcard | Provenance::CHasAccess => ProvenanceExtra::Wildcard, + Provenance::Wildcard => ProvenanceExtra::Wildcard, }; (alloc_id, size, sb) }) diff --git a/src/shims/ffi_support.rs b/src/shims/ffi_support.rs index ab0014731f..6db3922a1b 100644 --- a/src/shims/ffi_support.rs +++ b/src/shims/ffi_support.rs @@ -3,7 +3,6 @@ use std::ops::Deref; use rustc_middle::ty::{IntTy, Ty, TypeAndMut, TyKind, UintTy}; use rustc_span::Symbol; -use rustc_target::abi::HasDataLayout; use crate::*; From e4519e25a1ef3ec55551fbd2b1a8f60c5186afa4 Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Sat, 20 Aug 2022 00:14:13 +0000 Subject: [PATCH 21/23] putting new allocation address stuff behind a flag, so it only runs in ffi mode --- src/intptrcast.rs | 62 ++++++++++++++++++++++++++--------------------- src/machine.rs | 4 +-- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/src/intptrcast.rs b/src/intptrcast.rs index 0050f256c8..1a30a87193 100644 --- a/src/intptrcast.rs +++ b/src/intptrcast.rs @@ -46,11 +46,17 @@ pub struct GlobalStateInner { impl GlobalStateInner { pub fn new(config: &MiriConfig) -> Self { + // + let next_base_addr = if config.external_so_file.is_some() { + 1 + } else { + STACK_ADDR + }; GlobalStateInner { int_to_ptr_map: BTreeMap::default(), base_addr: FxHashMap::default(), exposed: FxHashSet::default(), - next_base_addr: 1, + next_base_addr, provenance_mode: config.provenance_mode, } } @@ -160,6 +166,8 @@ impl<'mir, 'tcx> GlobalStateInner { Ok(Pointer::new(Some(Provenance::Wildcard), Size::from_bytes(addr))) } + // Create a fake address for a new allocation, of a particular size and alignment. + // Ensure this address doesn't overlap with existing or future-assigned memory. fn get_next_fake_addr(ecx: &MiriEvalContext<'mir, 'tcx>, align: Align, size: Size, next_base_addr: u64) -> (u64, u64) { // This allocation does not have a base address yet, pick one. // Leave some space to the previous allocation, to give it some chance to be less aligned. @@ -187,26 +195,30 @@ impl<'mir, 'tcx> GlobalStateInner { } fn alloc_base_addr(ecx: &MiriEvalContext<'mir, 'tcx>, alloc_id: AllocId) -> u64 { + let in_ffi_mode = ecx.machine.external_so_lib.is_some(); // With our hack, base_addr should always be fully aligned let mut global_state = match ecx.machine.intptrcast.try_borrow_mut() { Ok(gstate) => gstate, Err(_) => { - // we're recursing - let (size, align, _kind) = ecx.get_alloc_info(alloc_id); - let new_addr = unsafe { - let next_base_addr = (*ecx.machine.intptrcast.as_ptr()).next_base_addr; - let (new_addr, next_base_addr) = Self::get_next_fake_addr(ecx, align, size, next_base_addr); //Box::leak(Box::new(0u128)) as *const u128 as u64; - (*ecx.machine.intptrcast.as_ptr()).base_addr.insert(alloc_id, new_addr); - (*ecx.machine.intptrcast.as_ptr()).int_to_ptr_map.insert(new_addr, alloc_id); - (*ecx.machine.intptrcast.as_ptr()).next_base_addr = next_base_addr; - new_addr - }; - trace!( - "Recursive case: Assigning base address {:#x} to allocation {:?}", - new_addr, - alloc_id, - ); - return new_addr; + if in_ffi_mode { + // we're recursing + let (size, align, _kind) = ecx.get_alloc_info(alloc_id); + let new_addr = unsafe { + let next_base_addr = (*ecx.machine.intptrcast.as_ptr()).next_base_addr; + let (new_addr, next_base_addr) = Self::get_next_fake_addr(ecx, align, size, next_base_addr); //Box::leak(Box::new(0u128)) as *const u128 as u64; + (*ecx.machine.intptrcast.as_ptr()).base_addr.insert(alloc_id, new_addr); + (*ecx.machine.intptrcast.as_ptr()).int_to_ptr_map.insert(new_addr, alloc_id); + (*ecx.machine.intptrcast.as_ptr()).next_base_addr = next_base_addr; + new_addr + }; + trace!( + "Recursive case: Assigning base address {:#x} to allocation {:?}", + new_addr, + alloc_id, + ); + return new_addr; + } + panic!("Can't mutably borrow the `intptrcast` global state!"); } }; let global_state = &mut *global_state; @@ -218,19 +230,13 @@ impl<'mir, 'tcx> GlobalStateInner { // it became dangling. Hence we allow dead allocations. let (size, align, _kind) = ecx.get_alloc_info(alloc_id); - let base_addr = match ecx.get_alloc_base_addr(alloc_id) { - Ok(addr) => { + let base_addr = if in_ffi_mode && let Ok(addr) = ecx.get_alloc_base_addr(alloc_id) { assert!(addr.bytes() % 16 == 0); addr.bytes() - } - // Grabbing u128 for max alignment - Err(_) => { - // TODO avoid leaked address hack - // Box::leak(Box::new(0u128)) as *const u128 as u64 - let (new_addr, next_base_addr) = Self::get_next_fake_addr(ecx, align, size, global_state.next_base_addr); //Box::leak(Box::new(0u128)) as *const u128 as u64; - global_state.next_base_addr = next_base_addr; - new_addr - } + } else { + let (new_addr, next_base_addr) = Self::get_next_fake_addr(ecx, align, size, global_state.next_base_addr); //Box::leak(Box::new(0u128)) as *const u128 as u64; + global_state.next_base_addr = next_base_addr; + new_addr }; // This allocation does not have a base address yet, assign its bytes base. diff --git a/src/machine.rs b/src/machine.rs index 49d2faaf7f..d2e2930c78 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -145,12 +145,12 @@ pub enum ProvenanceExtra { } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(Pointer, 32); +static_assert_size!(Pointer, 24); // FIXME: this would with in 24bytes but layout optimizations are not smart enough // #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] //static_assert_size!(Pointer>, 24); #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(ScalarMaybeUninit, 40); +static_assert_size!(ScalarMaybeUninit, 32); impl interpret::Provenance for Provenance { /// We use absolute addresses in the `offset` of a `Pointer`. From 3ced463c436df7af889245f1cbf7580b789a5819 Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Sat, 20 Aug 2022 00:31:03 +0000 Subject: [PATCH 22/23] formatting --- src/intptrcast.rs | 30 ++++++++++++++---------- src/shims/ffi_support.rs | 49 ++++++++++++++++++++-------------------- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/src/intptrcast.rs b/src/intptrcast.rs index 1a30a87193..5c12c7e7e0 100644 --- a/src/intptrcast.rs +++ b/src/intptrcast.rs @@ -7,7 +7,7 @@ use rand::Rng; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_span::Span; -use rustc_target::abi::{HasDataLayout, Size, Align}; +use rustc_target::abi::{Align, HasDataLayout, Size}; use crate::*; @@ -46,12 +46,8 @@ pub struct GlobalStateInner { impl GlobalStateInner { pub fn new(config: &MiriConfig) -> Self { - // - let next_base_addr = if config.external_so_file.is_some() { - 1 - } else { - STACK_ADDR - }; + // + let next_base_addr = if config.external_so_file.is_some() { 1 } else { STACK_ADDR }; GlobalStateInner { int_to_ptr_map: BTreeMap::default(), base_addr: FxHashMap::default(), @@ -168,11 +164,16 @@ impl<'mir, 'tcx> GlobalStateInner { // Create a fake address for a new allocation, of a particular size and alignment. // Ensure this address doesn't overlap with existing or future-assigned memory. - fn get_next_fake_addr(ecx: &MiriEvalContext<'mir, 'tcx>, align: Align, size: Size, next_base_addr: u64) -> (u64, u64) { + fn get_next_fake_addr( + ecx: &MiriEvalContext<'mir, 'tcx>, + align: Align, + size: Size, + next_base_addr: u64, + ) -> (u64, u64) { // This allocation does not have a base address yet, pick one. // Leave some space to the previous allocation, to give it some chance to be less aligned. // It also doesn't correspond to a real array of bytes. - // HACK: we're not going to actually have pointers in the program that correspond to + // HACK: we're not going to actually have pointers in the program that correspond to // the really low addresses, so let's use these as placeholders for these allocations. // This makes sure we won't overlap with any existing (real) addresses. // An alternate hack, which we had before, was to create and leak a Box: @@ -204,10 +205,15 @@ impl<'mir, 'tcx> GlobalStateInner { // we're recursing let (size, align, _kind) = ecx.get_alloc_info(alloc_id); let new_addr = unsafe { + // can't borrow_mut to get the global state, so just refer to it + // via pointer instead let next_base_addr = (*ecx.machine.intptrcast.as_ptr()).next_base_addr; - let (new_addr, next_base_addr) = Self::get_next_fake_addr(ecx, align, size, next_base_addr); //Box::leak(Box::new(0u128)) as *const u128 as u64; + let (new_addr, next_base_addr) = + Self::get_next_fake_addr(ecx, align, size, next_base_addr); (*ecx.machine.intptrcast.as_ptr()).base_addr.insert(alloc_id, new_addr); - (*ecx.machine.intptrcast.as_ptr()).int_to_ptr_map.insert(new_addr, alloc_id); + (*ecx.machine.intptrcast.as_ptr()) + .int_to_ptr_map + .insert(new_addr, alloc_id); (*ecx.machine.intptrcast.as_ptr()).next_base_addr = next_base_addr; new_addr }; @@ -252,7 +258,7 @@ impl<'mir, 'tcx> GlobalStateInner { // Map has no duplicates so no need to remove copies. // Map is always sorted. global_state.int_to_ptr_map.insert(base_addr, alloc_id); - + base_addr } } diff --git a/src/shims/ffi_support.rs b/src/shims/ffi_support.rs index 6db3922a1b..c486f913fc 100644 --- a/src/shims/ffi_support.rs +++ b/src/shims/ffi_support.rs @@ -1,7 +1,7 @@ use libffi::{high::call::*, low::CodePtr}; use std::ops::Deref; -use rustc_middle::ty::{IntTy, Ty, TypeAndMut, TyKind, UintTy}; +use rustc_middle::ty::{IntTy, Ty, TyKind, TypeAndMut, UintTy}; use rustc_span::Symbol; use crate::*; @@ -9,7 +9,6 @@ use crate::*; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { - /// Extract the scalar value from the result of reading a scalar from the machine, /// and convert it to a `CArg`. fn scalar_to_carg( @@ -53,7 +52,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return Ok(CArg::USize(k.to_machine_usize(cx)?.try_into().unwrap())); } // pointers - TyKind::RawPtr(TypeAndMut{ ty: some_ty, mutbl: some_mut } ) => { + TyKind::RawPtr(TypeAndMut { ty: some_ty, mutbl: some_mut }) => { match k { ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, _)) => { let qq = ptr.into_parts().1.bytes_usize(); @@ -61,63 +60,65 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // int (TyKind::Int(IntTy::I8), rustc_hir::Mutability::Mut) => { return Ok(CArg::MutPtrInt8(qq as *mut i8)); - }, + } (TyKind::Int(IntTy::I8), rustc_hir::Mutability::Not) => { return Ok(CArg::ConstPtrInt8(qq as *const i8)); - }, + } (TyKind::Int(IntTy::I16), rustc_hir::Mutability::Mut) => { return Ok(CArg::MutPtrInt16(qq as *mut i16)); - }, + } (TyKind::Int(IntTy::I16), rustc_hir::Mutability::Not) => { return Ok(CArg::ConstPtrInt16(qq as *const i16)); - }, + } (TyKind::Int(IntTy::I32), rustc_hir::Mutability::Mut) => { return Ok(CArg::MutPtrInt32(qq as *mut i32)); - }, + } (TyKind::Int(IntTy::I32), rustc_hir::Mutability::Not) => { return Ok(CArg::ConstPtrInt32(qq as *const i32)); - }, + } (TyKind::Int(IntTy::I64), rustc_hir::Mutability::Mut) => { return Ok(CArg::MutPtrInt64(qq as *mut i64)); - }, + } (TyKind::Int(IntTy::I64), rustc_hir::Mutability::Not) => { return Ok(CArg::ConstPtrInt64(qq as *const i64)); - }, + } // uints (TyKind::Uint(UintTy::U8), rustc_hir::Mutability::Mut) => { return Ok(CArg::MutPtrUInt8(qq as *mut u8)); - }, + } (TyKind::Uint(UintTy::U8), rustc_hir::Mutability::Not) => { return Ok(CArg::ConstPtrUInt8(qq as *const u8)); - }, + } (TyKind::Uint(UintTy::U16), rustc_hir::Mutability::Mut) => { return Ok(CArg::MutPtrUInt16(qq as *mut u16)); - }, + } (TyKind::Uint(UintTy::U16), rustc_hir::Mutability::Not) => { return Ok(CArg::ConstPtrUInt16(qq as *const u16)); - }, + } (TyKind::Uint(UintTy::U32), rustc_hir::Mutability::Mut) => { return Ok(CArg::MutPtrUInt32(qq as *mut u32)); - }, + } (TyKind::Uint(UintTy::U32), rustc_hir::Mutability::Not) => { return Ok(CArg::ConstPtrUInt32(qq as *const u32)); - }, + } (TyKind::Uint(UintTy::U64), rustc_hir::Mutability::Mut) => { return Ok(CArg::MutPtrUInt64(qq as *mut u64)); - }, + } (TyKind::Uint(UintTy::U64), rustc_hir::Mutability::Not) => { return Ok(CArg::ConstPtrUInt64(qq as *const u64)); - }, + } // recursive case (TyKind::RawPtr(..), _) => { - return Ok(CArg::RecCarg(Box::new(Self::scalar_to_carg(k, some_ty, cx)?))); + return Ok(CArg::RecCarg(Box::new(Self::scalar_to_carg( + k, some_ty, cx, + )?))); } _ => {} } } _ => {} } - }, + } _ => {} } // If no primitives were returned then we have an unsupported type. @@ -203,15 +204,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx return Ok(()); } // mut pointers - TyKind::RawPtr(TypeAndMut{ ty: some_ty, mutbl: rustc_hir::Mutability::Mut} ) => { + TyKind::RawPtr(TypeAndMut { ty: some_ty, mutbl: rustc_hir::Mutability::Mut }) => { match some_ty.kind() { TyKind::Int(IntTy::I32) => { println!("REEEE "); // let x = call::(ptr, libffi_args.as_slice()); // this.write_int(x, dest)?; return Ok(()); - }, - _ => { } + } + _ => {} } } // Functions with no declared return type (i.e., the default return) From 8ed36ed34eac7518ce93a56f1b89ee4a59cf1a04 Mon Sep 17 00:00:00 2001 From: Ellen Arteca Date: Sat, 20 Aug 2022 00:38:22 +0000 Subject: [PATCH 23/23] adding some comments --- src/intptrcast.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/intptrcast.rs b/src/intptrcast.rs index 5c12c7e7e0..1c2b021809 100644 --- a/src/intptrcast.rs +++ b/src/intptrcast.rs @@ -46,7 +46,11 @@ pub struct GlobalStateInner { impl GlobalStateInner { pub fn new(config: &MiriConfig) -> Self { - // + // If we're in FFI mode, then the `next_base_addr` is only used to assign fake addresses + // to allocations that don't have associated arrays of bytes. + // CURRENT HACK: + // We start at 1 to avoid overlap with existing/future real memory the program has + // pointers to. let next_base_addr = if config.external_so_file.is_some() { 1 } else { STACK_ADDR }; GlobalStateInner { int_to_ptr_map: BTreeMap::default(), @@ -202,11 +206,12 @@ impl<'mir, 'tcx> GlobalStateInner { Ok(gstate) => gstate, Err(_) => { if in_ffi_mode { - // we're recursing + // We're recursing! let (size, align, _kind) = ecx.get_alloc_info(alloc_id); let new_addr = unsafe { - // can't borrow_mut to get the global state, so just refer to it - // via pointer instead + // Can't `borrow_mut` to get the global state, so just refer to it + // via pointer instead. + // This is unsafe. let next_base_addr = (*ecx.machine.intptrcast.as_ptr()).next_base_addr; let (new_addr, next_base_addr) = Self::get_next_fake_addr(ecx, align, size, next_base_addr); @@ -236,9 +241,10 @@ impl<'mir, 'tcx> GlobalStateInner { // it became dangling. Hence we allow dead allocations. let (size, align, _kind) = ecx.get_alloc_info(alloc_id); + // Short circuit -- only call `ecx.get_alloc_base_addr` if we're `in_ffi_mode`. let base_addr = if in_ffi_mode && let Ok(addr) = ecx.get_alloc_base_addr(alloc_id) { - assert!(addr.bytes() % 16 == 0); - addr.bytes() + assert!(addr.bytes() % 16 == 0); + addr.bytes() } else { let (new_addr, next_base_addr) = Self::get_next_fake_addr(ecx, align, size, global_state.next_base_addr); //Box::leak(Box::new(0u128)) as *const u128 as u64; global_state.next_base_addr = next_base_addr;