From 8089fce101f4ac2ed68c8df080c61102b0210429 Mon Sep 17 00:00:00 2001 From: DianQK Date: Wed, 26 Feb 2025 19:54:34 +0800 Subject: [PATCH] Don't infer attributes of virtual calls based on the function body --- compiler/rustc_middle/src/ty/instance.rs | 5 +- compiler/rustc_ty_utils/src/abi.rs | 63 +++++++++---------- .../virtual-call-attrs-issue-137646.rs | 18 ++++++ tests/ui/virtual-call-attrs-issue-137646.rs | 6 +- ...virtual-call-attrs-issue-137646.run.stderr | 20 ------ 5 files changed, 52 insertions(+), 60 deletions(-) create mode 100644 tests/codegen/virtual-call-attrs-issue-137646.rs delete mode 100644 tests/ui/virtual-call-attrs-issue-137646.run.stderr diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index e9c19331e4a..98ca71b86be 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -111,8 +111,9 @@ pub enum InstanceKind<'tcx> { /// Dynamic dispatch to `::fn`. /// - /// This `InstanceKind` does not have callable MIR. Calls to `Virtual` instances must be - /// codegen'd as virtual calls through the vtable. + /// This `InstanceKind` may have a callable MIR as the default implementation. + /// Calls to `Virtual` instances must be codegen'd as virtual calls through the vtable. + /// *This means we might not know exactly what is being called.* /// /// If this is reified to a `fn` pointer, a `ReifyShim` is used (see `ReifyShim` above for more /// details on that). diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 0ff82f0c256..4fa8c9fe15d 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -309,15 +309,11 @@ fn fn_abi_of_fn_ptr<'tcx>( query: ty::PseudoCanonicalInput<'tcx, (ty::PolyFnSig<'tcx>, &'tcx ty::List>)>, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { let ty::PseudoCanonicalInput { typing_env, value: (sig, extra_args) } = query; - - let cx = LayoutCx::new(tcx, typing_env); fn_abi_new_uncached( - &cx, + &LayoutCx::new(tcx, typing_env), tcx.instantiate_bound_regions_with_erased(sig), extra_args, None, - None, - false, ) } @@ -326,19 +322,11 @@ fn fn_abi_of_instance<'tcx>( query: ty::PseudoCanonicalInput<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)>, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { let ty::PseudoCanonicalInput { typing_env, value: (instance, extra_args) } = query; - - let sig = fn_sig_for_fn_abi(tcx, instance, typing_env); - - let caller_location = - instance.def.requires_caller_location(tcx).then(|| tcx.caller_location_ty()); - fn_abi_new_uncached( &LayoutCx::new(tcx, typing_env), - sig, + fn_sig_for_fn_abi(tcx, instance, typing_env), extra_args, - caller_location, - Some(instance.def_id()), - matches!(instance.def, ty::InstanceKind::Virtual(..)), + Some(instance), ) } @@ -547,19 +535,23 @@ fn fn_abi_sanity_check<'tcx>( fn_arg_sanity_check(cx, fn_abi, spec_abi, &fn_abi.ret); } -// FIXME(eddyb) perhaps group the signature/type-containing (or all of them?) -// arguments of this method, into a separate `struct`. -#[tracing::instrument(level = "debug", skip(cx, caller_location, fn_def_id, force_thin_self_ptr))] +#[tracing::instrument(level = "debug", skip(cx, instance))] fn fn_abi_new_uncached<'tcx>( cx: &LayoutCx<'tcx>, sig: ty::FnSig<'tcx>, extra_args: &[Ty<'tcx>], - caller_location: Option>, - fn_def_id: Option, - // FIXME(eddyb) replace this with something typed, like an `enum`. - force_thin_self_ptr: bool, + instance: Option>, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { let tcx = cx.tcx(); + let (caller_location, fn_def_id, is_virtual_call) = if let Some(instance) = instance { + ( + instance.def.requires_caller_location(tcx).then(|| tcx.caller_location_ty()), + Some(instance.def_id()), + matches!(instance.def, ty::InstanceKind::Virtual(..)), + ) + } else { + (None, None, false) + }; let sig = tcx.normalize_erasing_regions(cx.typing_env, sig); let conv = conv_from_spec_abi(cx.tcx(), sig.abi, sig.c_variadic); @@ -568,16 +560,11 @@ fn fn_abi_new_uncached<'tcx>( let extra_args = if sig.abi == ExternAbi::RustCall { assert!(!sig.c_variadic && extra_args.is_empty()); - if let Some(input) = sig.inputs().last() { - if let ty::Tuple(tupled_arguments) = input.kind() { - inputs = &sig.inputs()[0..sig.inputs().len() - 1]; - tupled_arguments - } else { - bug!( - "argument to function with \"rust-call\" ABI \ - is not a tuple" - ); - } + if let Some(input) = sig.inputs().last() + && let ty::Tuple(tupled_arguments) = input.kind() + { + inputs = &sig.inputs()[0..sig.inputs().len() - 1]; + tupled_arguments } else { bug!( "argument to function with \"rust-call\" ABI \ @@ -603,7 +590,7 @@ fn fn_abi_new_uncached<'tcx>( }); let layout = cx.layout_of(ty).map_err(|err| &*tcx.arena.alloc(FnAbiError::Layout(*err)))?; - let layout = if force_thin_self_ptr && arg_idx == Some(0) { + let layout = if is_virtual_call && arg_idx == Some(0) { // Don't pass the vtable, it's not an argument of the virtual fn. // Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait` // or `&/&mut dyn Trait` because this is special-cased elsewhere in codegen @@ -648,7 +635,15 @@ fn fn_abi_new_uncached<'tcx>( conv, can_unwind: fn_can_unwind(cx.tcx(), fn_def_id, sig.abi), }; - fn_abi_adjust_for_abi(cx, &mut fn_abi, sig.abi, fn_def_id); + fn_abi_adjust_for_abi( + cx, + &mut fn_abi, + sig.abi, + // If this is a virtual call, we cannot pass the `fn_def_id`, as it might call other + // functions from vtable. Internally, `deduced_param_attrs` attempts to infer attributes by + // visit the function body. + fn_def_id.filter(|_| !is_virtual_call), + ); debug!("fn_abi_new_uncached = {:?}", fn_abi); fn_abi_sanity_check(cx, &fn_abi, sig.abi); Ok(tcx.arena.alloc(fn_abi)) diff --git a/tests/codegen/virtual-call-attrs-issue-137646.rs b/tests/codegen/virtual-call-attrs-issue-137646.rs new file mode 100644 index 00000000000..72a25b75856 --- /dev/null +++ b/tests/codegen/virtual-call-attrs-issue-137646.rs @@ -0,0 +1,18 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/137646. +//! Since we don't know the exact implementation of the virtual call, +//! it might write to parameters, we can't infer the readonly attribute. +//@ compile-flags: -C opt-level=3 -C no-prepopulate-passes + +#![crate_type = "lib"] + +pub trait Trait { + fn m(&self, _: (i32, i32, i32)) {} +} + +#[no_mangle] +pub fn foo(trait_: &dyn Trait) { + // CHECK-LABEL: @foo( + // CHECK: call void + // CHECK-NOT: readonly + trait_.m((1, 1, 1)); +} diff --git a/tests/ui/virtual-call-attrs-issue-137646.rs b/tests/ui/virtual-call-attrs-issue-137646.rs index 055dc6ea4f2..e80bd5768a4 100644 --- a/tests/ui/virtual-call-attrs-issue-137646.rs +++ b/tests/ui/virtual-call-attrs-issue-137646.rs @@ -1,8 +1,7 @@ //! Regression test for https://github.com/rust-lang/rust/issues/137646. -//! FIXME: The parameter value at all calls to `check` should be `(1, 1, 1)`. +//! The parameter value at all calls to `check` should be `(1, 1, 1)`. //@ run-pass -//@ check-run-results use std::hint::black_box; @@ -35,8 +34,7 @@ pub fn run_2(trait_: &dyn Trait) { #[inline(never)] fn check(v: T) { - dbg!(v); - // assert_eq!(v, (1, 1, 1)); + assert_eq!(v, (1, 1, 1)); } fn main() { diff --git a/tests/ui/virtual-call-attrs-issue-137646.run.stderr b/tests/ui/virtual-call-attrs-issue-137646.run.stderr deleted file mode 100644 index 445975e1e54..00000000000 --- a/tests/ui/virtual-call-attrs-issue-137646.run.stderr +++ /dev/null @@ -1,20 +0,0 @@ -[$DIR/virtual-call-attrs-issue-137646.rs:38:5] v = ( - 498486832, - 22093, - 498491440, -) -[$DIR/virtual-call-attrs-issue-137646.rs:38:5] v = ( - 0, - 0, - 0, -) -[$DIR/virtual-call-attrs-issue-137646.rs:38:5] v = ( - 0, - 0, - 0, -) -[$DIR/virtual-call-attrs-issue-137646.rs:38:5] v = ( - 0, - 0, - 0, -)