Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emit error when calling/declaring functions with vectors that require missing target feature #127731

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4139,6 +4139,7 @@ dependencies = [
name = "rustc_monomorphize"
version = "0.0.0"
dependencies = [
"rustc_abi",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
Expand Down
74 changes: 74 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/116558>",
};
}
1 change: 1 addition & 0 deletions compiler/rustc_monomorphize/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ edition = "2021"

[dependencies]
# tidy-alphabetical-start
rustc_abi = { path = "../rustc_abi" }
veluca93 marked this conversation as resolved.
Show resolved Hide resolved
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_monomorphize/messages.ftl
Original file line number Diff line number Diff line change
@@ -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}
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_monomorphize/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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, .. } => {
Expand Down Expand Up @@ -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
Expand Down
111 changes: 111 additions & 0 deletions compiler/rustc_monomorphize/src/collector/abi_check.rs
Original file line number Diff line number Diff line change
@@ -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("<no available feature for this size>");
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)
Comment on lines +46 to +47
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unstable_target_features is an odd name... just leaving a note here so that the t-compiler reviewer can carefully check that this is checking the right thing: we have to ensure that the target feature is enabled when generating code for this function.

{
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;
veluca93 marked this conversation as resolved.
Show resolved Hide resolved
};
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 },
);
})
}
18 changes: 18 additions & 0 deletions compiler/rustc_monomorphize/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
40 changes: 0 additions & 40 deletions tests/crashes/131342-2.rs

This file was deleted.

1 change: 0 additions & 1 deletion tests/ui/layout/post-mono-layout-cycle-2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
10 changes: 5 additions & 5 deletions tests/ui/layout/post-mono-layout-cycle-2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
1 change: 0 additions & 1 deletion tests/ui/layout/post-mono-layout-cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ struct Wrapper<T: Trait> {
}

fn abi<T: Trait>(_: Option<Wrapper<T>>) {}
//~^ ERROR a cycle occurred during layout computation

fn indirect<T: Trait>() {
abi::<T>(None);
Expand Down
10 changes: 5 additions & 5 deletions tests/ui/layout/post-mono-layout-cycle.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ error[E0391]: cycle detected when computing layout of `Wrapper<()>`
= note: cycle used when computing layout of `core::option::Option<Wrapper<()>>`
= 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<T: Trait>(_: Option<Wrapper<T>>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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`.
Loading
Loading