diff --git a/Cargo.lock b/Cargo.lock index 5f81a5a84966a..508fe6e01dab4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4139,6 +4139,7 @@ dependencies = [ name = "rustc_monomorphize" version = "0.0.0" dependencies = [ + "rustc_abi", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 45a5ce0ca20e9..2839acb58e4ef 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -16,6 +16,7 @@ declare_lint_pass! { /// that are used by other parts of the compiler. HardwiredLints => [ // tidy-alphabetical-start + ABI_ERROR_DISABLED_VECTOR_TYPE, ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_ASSOCIATED_ITEMS, AMBIGUOUS_GLOB_IMPORTS, @@ -5078,3 +5079,76 @@ declare_lint! { }; crate_level_only } + +declare_lint! { + /// The `abi_error_disabled_vector_type` lint detects function definitions and calls + /// whose ABI depends on enabling certain target features that do not enable those features. + /// + /// ### Example + /// + /// ```rust,ignore (fails on non-x86_64) + /// #![allow(improper_ctypes_definitions)] // false positive + /// + /// pub extern "C" fn foo(_: std::arch::x86_64::__m256) { + /// todo!() + /// } + /// + /// #[target_feature(enable = "avx")] + /// pub unsafe extern "C" fn favx(_: std::arch::x86_64::__m256) { + /// todo!() + /// } + /// + /// # #[target_feature(enable = "avx")] + /// # unsafe fn helper() { + /// # foo(unsafe { std::mem::zeroed() }); + /// # } + /// # fn main() { + /// # let v = unsafe { std::mem::zeroed() }; + /// # unsafe { favx(v); } + /// # unsafe { helper(); } + /// # } + /// ``` + /// + /// ```text + /// warning: ABI error: this function call uses a avx vector type, which is not enabled in the caller + /// --> lint_example.rs:18:12 + /// | + /// | unsafe { favx(v); } + /// | ^^^^^^^ function called here + /// | + /// = 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 #116558 + /// = help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")]) + /// = note: `#[warn(abi_error_disabled_vector_type)]` on by default + /// + /// + /// warning: ABI error: this function definition uses a avx vector type, which is not enabled + /// --> lint_example.rs:3:1 + /// | + /// | pub extern "C" fn foo(_: std::arch::x86_64::__m256) { + /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + /// | + /// = 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 #116558 + /// = help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")]) + /// ``` + /// + /// + /// + /// ### Explanation + /// + /// The ABI of `foo` is somewhat surprising: since AVX may not be enabled when compiling it, + /// the parameter may be passed by stack and not by register. This then easily leads to + /// undefined behaviour if calling the function from a function for which AVX is enabled. + /// A similar (but complementary) problem is triggered by a caller that does *not* enable + /// the AVX feature calling `favx`. + /// + /// Note that this lint is very similar to the `-Wpsabi` warning in `gcc`/`clang`. + pub ABI_ERROR_DISABLED_VECTOR_TYPE, + Warn, + "this function call or definition uses a vector type which is not enabled", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, + reference: "issue #116558 ", + }; +} diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index c7f1b9fa78454..6c881fd7e06ba 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start +rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_monomorphize/messages.ftl b/compiler/rustc_monomorphize/messages.ftl index 7210701d4828c..9a4ec9138b446 100644 --- a/compiler/rustc_monomorphize/messages.ftl +++ b/compiler/rustc_monomorphize/messages.ftl @@ -1,3 +1,12 @@ +monomorphize_abi_error_disabled_vector_type_call = + ABI error: this function call uses a {$required_feature} vector type, which is not enabled in the caller + .label = function called here + .help = consider enabling it globally (-C target-feature=+{$required_feature}) or locally (#[target_feature(enable="{$required_feature}")]) +monomorphize_abi_error_disabled_vector_type_def = + ABI error: this function definition uses a {$required_feature} vector type, which is not enabled + .label = function defined here + .help = consider enabling it globally (-C target-feature=+{$required_feature}) or locally (#[target_feature(enable="{$required_feature}")]) + monomorphize_couldnt_dump_mono_stats = unexpected error occurred while dumping monomorphization stats: {$error} diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index b4d084d4dffc4..82de64cbce047 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -205,6 +205,7 @@ //! this is not implemented however: a mono item will be produced //! regardless of whether it is actually needed or not. +mod abi_check; mod move_check; use std::path::PathBuf; @@ -766,6 +767,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { self.used_mentioned_items.insert(MentionedItem::Fn(callee_ty)); let callee_ty = self.monomorphize(callee_ty); self.check_fn_args_move_size(callee_ty, args, *fn_span, location); + abi_check::check_call_site_abi(tcx, callee_ty, *fn_span, self.body.source.instance); visit_fn_use(self.tcx, callee_ty, true, source, &mut self.used_items) } mir::TerminatorKind::Drop { ref place, .. } => { @@ -1207,6 +1209,9 @@ fn collect_items_of_instance<'tcx>( mentioned_items: &mut MonoItems<'tcx>, mode: CollectionMode, ) { + // Check the instance for feature-dependent ABI. + abi_check::check_instance_abi(tcx, instance); + let body = tcx.instance_mir(instance.def); // Naively, in "used" collection mode, all functions get added to *both* `used_items` and // `mentioned_items`. Mentioned items processing will then notice that they have already been diff --git a/compiler/rustc_monomorphize/src/collector/abi_check.rs b/compiler/rustc_monomorphize/src/collector/abi_check.rs new file mode 100644 index 0000000000000..e2d0cc61d5e4d --- /dev/null +++ b/compiler/rustc_monomorphize/src/collector/abi_check.rs @@ -0,0 +1,111 @@ +use rustc_abi::Abi; +use rustc_hir::CRATE_HIR_ID; +use rustc_middle::ty::{self, Instance, InstanceKind, ParamEnv, Ty, TyCtxt}; +use rustc_session::lint::builtin::ABI_ERROR_DISABLED_VECTOR_TYPE; +use rustc_span::def_id::DefId; +use rustc_span::{Span, Symbol}; +use rustc_target::abi::call::{FnAbi, PassMode}; + +use crate::errors::{AbiErrorDisabledVectorTypeCall, AbiErrorDisabledVectorTypeDef}; + +// Represents the least-constraining feature that is required for vector types up to a certain size +// to have their "proper" ABI. +const X86_VECTOR_FEATURES: &'static [(u64, &'static str)] = + &[(128, "sse"), (256, "avx"), (512, "avx512f")]; + +const AARCH64_VECTOR_FEATURES: &'static [(u64, &'static str)] = &[(128, "neon")]; + +fn do_check_abi<'tcx>( + tcx: TyCtxt<'tcx>, + abi: &FnAbi<'tcx, Ty<'tcx>>, + target_feature_def: DefId, + emit_err: impl Fn(&'static str), +) { + let feature_def = if tcx.sess.target.arch == "x86" || tcx.sess.target.arch == "x86_64" { + X86_VECTOR_FEATURES + } else if tcx.sess.target.arch == "aarch64" { + AARCH64_VECTOR_FEATURES + } else { + // FIXME: add support for non-tier1 architectures + return; + }; + let codegen_attrs = tcx.codegen_fn_attrs(target_feature_def); + for arg_abi in abi.args.iter().chain(std::iter::once(&abi.ret)) { + let size = arg_abi.layout.size; + if matches!(arg_abi.layout.abi, Abi::Vector { .. }) + && !matches!(arg_abi.mode, PassMode::Indirect { .. }) + { + let feature = match feature_def.iter().find(|(bits, _)| size.bits() <= *bits) { + Some((_, feature)) => feature, + None => { + emit_err(""); + continue; + } + }; + let feature_sym = Symbol::intern(feature); + if !tcx.sess.unstable_target_features.contains(&feature_sym) + && !codegen_attrs.target_features.iter().any(|x| x.name == feature_sym) + { + emit_err(feature); + } + } + } +} + +/// Checks that the ABI of a given instance of a function does not contain vector-passed arguments +/// or return values for which the corresponding target feature is not enabled. +pub(super) fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { + let param_env = ParamEnv::reveal_all(); + let Ok(abi) = tcx.fn_abi_of_instance(param_env.and((instance, ty::List::empty()))) else { + // An error will be reported during codegen if we cannot determine the ABI of this + // function. + return; + }; + do_check_abi(tcx, abi, instance.def_id(), |required_feature| { + let span = tcx.def_span(instance.def_id()); + tcx.emit_node_span_lint( + ABI_ERROR_DISABLED_VECTOR_TYPE, + CRATE_HIR_ID, + span, + AbiErrorDisabledVectorTypeDef { span, required_feature }, + ); + }) +} + +/// Checks that a call expression does not try to pass a vector-passed argument which requires a +/// target feature that the caller does not have, as doing so causes UB because of ABI mismatch. +pub(super) fn check_call_site_abi<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + span: Span, + caller: InstanceKind<'tcx>, +) { + let param_env = ParamEnv::reveal_all(); + let callee_abi = match *ty.kind() { + ty::FnPtr(..) => tcx.fn_abi_of_fn_ptr(param_env.and((ty.fn_sig(tcx), ty::List::empty()))), + ty::FnDef(def_id, args) => { + // Intrinsics are handled separately by the compiler. + if tcx.intrinsic(def_id).is_some() { + return; + } + let instance = ty::Instance::expect_resolve(tcx, param_env, def_id, args, span); + tcx.fn_abi_of_instance(param_env.and((instance, ty::List::empty()))) + } + _ => { + panic!("Invalid function call"); + } + }; + + let Ok(callee_abi) = callee_abi else { + // ABI failed to compute; this will not get through codegen. + return; + }; + do_check_abi(tcx, callee_abi, caller.def_id(), |required_feature| { + tcx.emit_node_span_lint( + ABI_ERROR_DISABLED_VECTOR_TYPE, + CRATE_HIR_ID, + span, + AbiErrorDisabledVectorTypeCall { span, required_feature }, + ); + }) +} diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index d5fae6e23cb45..5048a8d5d993f 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -92,3 +92,21 @@ pub(crate) struct StartNotFound; pub(crate) struct UnknownCguCollectionMode<'a> { pub mode: &'a str, } + +#[derive(LintDiagnostic)] +#[diag(monomorphize_abi_error_disabled_vector_type_def)] +#[help] +pub(crate) struct AbiErrorDisabledVectorTypeDef<'a> { + #[label] + pub span: Span, + pub required_feature: &'a str, +} + +#[derive(LintDiagnostic)] +#[diag(monomorphize_abi_error_disabled_vector_type_call)] +#[help] +pub(crate) struct AbiErrorDisabledVectorTypeCall<'a> { + #[label] + pub span: Span, + pub required_feature: &'a str, +} diff --git a/tests/crashes/131342-2.rs b/tests/crashes/131342-2.rs deleted file mode 100644 index 79b6a837a49fb..0000000000000 --- a/tests/crashes/131342-2.rs +++ /dev/null @@ -1,40 +0,0 @@ -//@ known-bug: #131342 -// see also: 131342.rs - -fn main() { - problem_thingy(Once); -} - -struct Once; - -impl Iterator for Once { - type Item = (); -} - -fn problem_thingy(items: impl Iterator) { - let peeker = items.peekable(); - problem_thingy(&peeker); -} - -trait Iterator { - type Item; - - fn peekable(self) -> Peekable - where - Self: Sized, - { - loop {} - } -} - -struct Peekable { - _peeked: I::Item, -} - -impl Iterator for Peekable { - type Item = I::Item; -} - -impl Iterator for &I { - type Item = I::Item; -} diff --git a/tests/ui/layout/post-mono-layout-cycle-2.rs b/tests/ui/layout/post-mono-layout-cycle-2.rs index 356f1e777c7d0..e9a5292fbbdfb 100644 --- a/tests/ui/layout/post-mono-layout-cycle-2.rs +++ b/tests/ui/layout/post-mono-layout-cycle-2.rs @@ -45,7 +45,6 @@ where T: Blah, { async fn ice(&mut self) { - //~^ ERROR a cycle occurred during layout computation let arr: [(); 0] = []; self.t.iter(arr.into_iter()).await; } diff --git a/tests/ui/layout/post-mono-layout-cycle-2.stderr b/tests/ui/layout/post-mono-layout-cycle-2.stderr index ad01c2694faf5..ea69b39706f48 100644 --- a/tests/ui/layout/post-mono-layout-cycle-2.stderr +++ b/tests/ui/layout/post-mono-layout-cycle-2.stderr @@ -12,12 +12,12 @@ LL | Blah::iter(self, iterator).await | = note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future -error: a cycle occurred during layout computation - --> $DIR/post-mono-layout-cycle-2.rs:47:5 +note: the above error was encountered while instantiating `fn main::{closure#0}` + --> $DIR/post-mono-layout-cycle-2.rs:16:15 | -LL | async fn ice(&mut self) { - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | match fut.as_mut().poll(ctx) { + | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0733`. diff --git a/tests/ui/layout/post-mono-layout-cycle.rs b/tests/ui/layout/post-mono-layout-cycle.rs index 8d136190c0052..6753c01267ecd 100644 --- a/tests/ui/layout/post-mono-layout-cycle.rs +++ b/tests/ui/layout/post-mono-layout-cycle.rs @@ -14,7 +14,6 @@ struct Wrapper { } fn abi(_: Option>) {} -//~^ ERROR a cycle occurred during layout computation fn indirect() { abi::(None); diff --git a/tests/ui/layout/post-mono-layout-cycle.stderr b/tests/ui/layout/post-mono-layout-cycle.stderr index 47f7f30b1cb4c..e2f6ac595d006 100644 --- a/tests/ui/layout/post-mono-layout-cycle.stderr +++ b/tests/ui/layout/post-mono-layout-cycle.stderr @@ -5,12 +5,12 @@ error[E0391]: cycle detected when computing layout of `Wrapper<()>` = note: cycle used when computing layout of `core::option::Option>` = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error: a cycle occurred during layout computation - --> $DIR/post-mono-layout-cycle.rs:16:1 +note: the above error was encountered while instantiating `fn indirect::<()>` + --> $DIR/post-mono-layout-cycle.rs:23:5 | -LL | fn abi(_: Option>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | indirect::<()>(); + | ^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/simd-abi-checks.rs b/tests/ui/simd-abi-checks.rs new file mode 100644 index 0000000000000..48a690c75859b --- /dev/null +++ b/tests/ui/simd-abi-checks.rs @@ -0,0 +1,78 @@ +//@ only-x86_64 +//@ build-pass + +#![feature(avx512_target_feature)] +#![feature(portable_simd)] +#![allow(improper_ctypes_definitions)] + +use std::arch::x86_64::*; + +#[repr(transparent)] +struct Wrapper(__m256); + +unsafe extern "C" fn w(_: Wrapper) { + //~^ ABI error: this function definition uses a avx vector type, which is not enabled + //~| WARNING this was previously accepted by the compiler + todo!() +} + +unsafe extern "C" fn f(_: __m256) { + //~^ ABI error: this function definition uses a avx vector type, which is not enabled + //~| WARNING this was previously accepted by the compiler + todo!() +} + +unsafe extern "C" fn g() -> __m256 { + //~^ ABI error: this function definition uses a avx vector type, which is not enabled + //~| WARNING this was previously accepted by the compiler + todo!() +} + +#[target_feature(enable = "avx2")] +unsafe extern "C" fn favx(_: __m256) { + todo!() +} + +#[target_feature(enable = "avx")] +unsafe extern "C" fn gavx() -> __m256 { + todo!() +} + +fn as_f64x8(d: __m512d) -> std::simd::f64x8 { + unsafe { std::mem::transmute(d) } +} + +unsafe fn test() { + let arg = std::mem::transmute([0.0f64; 8]); + as_f64x8(arg); +} + +fn main() { + unsafe { + f(g()); + //~^ WARNING ABI error: this function call uses a avx vector type, which is not enabled in the caller + //~| WARNING ABI error: this function call uses a avx vector type, which is not enabled in the caller + //~| WARNING this was previously accepted by the compiler + //~| WARNING this was previously accepted by the compiler + } + + unsafe { + favx(gavx()); + //~^ WARNING ABI error: this function call uses a avx vector type, which is not enabled in the caller + //~| WARNING ABI error: this function call uses a avx vector type, which is not enabled in the caller + //~| WARNING this was previously accepted by the compiler + //~| WARNING this was previously accepted by the compiler + } + + unsafe { + test(); + } + + unsafe { + w(Wrapper(g())); + //~^ WARNING ABI error: this function call uses a avx vector type, which is not enabled in the caller + //~| WARNING ABI error: this function call uses a avx vector type, which is not enabled in the caller + //~| WARNING this was previously accepted by the compiler + //~| WARNING this was previously accepted by the compiler + } +} diff --git a/tests/ui/simd-abi-checks.stderr b/tests/ui/simd-abi-checks.stderr new file mode 100644 index 0000000000000..531088913cb81 --- /dev/null +++ b/tests/ui/simd-abi-checks.stderr @@ -0,0 +1,93 @@ +warning: ABI error: this function call uses a avx vector type, which is not enabled in the caller + --> $DIR/simd-abi-checks.rs:52:11 + | +LL | f(g()); + | ^^^ function called here + | + = 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 #116558 + = help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")]) + = note: `#[warn(abi_error_disabled_vector_type)]` on by default + +warning: ABI error: this function call uses a avx vector type, which is not enabled in the caller + --> $DIR/simd-abi-checks.rs:52:9 + | +LL | f(g()); + | ^^^^^^ function called here + | + = 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 #116558 + = help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")]) + +warning: ABI error: this function call uses a avx vector type, which is not enabled in the caller + --> $DIR/simd-abi-checks.rs:60:14 + | +LL | favx(gavx()); + | ^^^^^^ function called here + | + = 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 #116558 + = help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")]) + +warning: ABI error: this function call uses a avx vector type, which is not enabled in the caller + --> $DIR/simd-abi-checks.rs:60:9 + | +LL | favx(gavx()); + | ^^^^^^^^^^^^ function called here + | + = 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 #116558 + = help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")]) + +warning: ABI error: this function call uses a avx vector type, which is not enabled in the caller + --> $DIR/simd-abi-checks.rs:72:19 + | +LL | w(Wrapper(g())); + | ^^^ function called here + | + = 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 #116558 + = help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")]) + +warning: ABI error: this function call uses a avx vector type, which is not enabled in the caller + --> $DIR/simd-abi-checks.rs:72:9 + | +LL | w(Wrapper(g())); + | ^^^^^^^^^^^^^^^ function called here + | + = 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 #116558 + = help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")]) + +warning: ABI error: this function definition uses a avx vector type, which is not enabled + --> $DIR/simd-abi-checks.rs:25:1 + | +LL | unsafe extern "C" fn g() -> __m256 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = 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 #116558 + = help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")]) + +warning: ABI error: this function definition uses a avx vector type, which is not enabled + --> $DIR/simd-abi-checks.rs:19:1 + | +LL | unsafe extern "C" fn f(_: __m256) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = 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 #116558 + = help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")]) + +warning: ABI error: this function definition uses a avx vector type, which is not enabled + --> $DIR/simd-abi-checks.rs:13:1 + | +LL | unsafe extern "C" fn w(_: Wrapper) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = 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 #116558 + = help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")]) + +warning: 9 warnings emitted +