From 38d48ef53778674baa99dd0a3a193cec78f74e63 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 27 Mar 2018 11:42:04 -0700 Subject: [PATCH] rustc: Forbid #[inline(always)] with #[target_feature] Once a target feature is enabled for a function that means that it in general can't be inlined into other functions which don't have that target feature enabled. This can cause both safety and LLVM issues if we were to actually inline it, so `#[inline(always)]` both can't be respected and would be an error if we did so! Today LLVM doesn't inline functions with different `#[target_feature]` annotations, but it turns out that if one is tagged with `#[inline(always)]` it'll override this and cause scary LLVM error to arise! This commit fixes this issue by forbidding these two attributes to be used in conjunction with one another. cc rust-lang-nursery/stdsimd#404 --- src/librustc_typeck/collect.rs | 15 +++++++++++++++ src/test/ui/target-feature-wrong.rs | 5 +++++ src/test/ui/target-feature-wrong.stderr | 8 +++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 6f24d06844bb4..59156bf0dfeaa 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1769,6 +1769,7 @@ fn trans_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> TransFnAt let whitelist = tcx.target_features_whitelist(LOCAL_CRATE); + let mut inline_span = None; for attr in attrs.iter() { if attr.check_name("cold") { trans_fn_attrs.flags |= TransFnAttrFlags::COLD; @@ -1800,6 +1801,7 @@ fn trans_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> TransFnAt } MetaItemKind::List(ref items) => { mark_used(attr); + inline_span = Some(attr.span); if items.len() != 1 { span_err!(tcx.sess.diagnostic(), attr.span, E0534, "expected one argument"); @@ -1855,5 +1857,18 @@ fn trans_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> TransFnAt } } + // If a function uses #[target_feature] it can't be inlined into general + // purpose functions as they wouldn't have the right target features + // enabled. For that reason we also forbid #[inline(always)] as it can't be + // respected. + if trans_fn_attrs.target_features.len() > 0 { + if trans_fn_attrs.inline == InlineAttr::Always { + if let Some(span) = inline_span { + tcx.sess.span_err(span, "cannot use #[inline(always)] with \ + #[target_feature]"); + } + } + } + trans_fn_attrs } diff --git a/src/test/ui/target-feature-wrong.rs b/src/test/ui/target-feature-wrong.rs index c1e6245d24be8..56acbed47210a 100644 --- a/src/test/ui/target-feature-wrong.rs +++ b/src/test/ui/target-feature-wrong.rs @@ -33,6 +33,11 @@ fn bar() {} //~^ ERROR: should be applied to a function mod another {} +#[inline(always)] +//~^ ERROR: cannot use #[inline(always)] +#[target_feature(enable = "sse2")] +unsafe fn test() {} + fn main() { unsafe { foo(); diff --git a/src/test/ui/target-feature-wrong.stderr b/src/test/ui/target-feature-wrong.stderr index 0fa6910f2bb3f..8773f8504cb01 100644 --- a/src/test/ui/target-feature-wrong.stderr +++ b/src/test/ui/target-feature-wrong.stderr @@ -37,5 +37,11 @@ LL | //~^ ERROR: should be applied to a function LL | mod another {} | -------------- not a function -error: aborting due to 5 previous errors +error: cannot use #[inline(always)] with #[target_feature] + --> $DIR/target-feature-wrong.rs:36:1 + | +LL | #[inline(always)] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors