Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
7 changes: 7 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ lint_builtin_allow_internal_unsafe =
lint_builtin_anonymous_params = anonymous parameters are deprecated and will be removed in the next edition
.suggestion = try naming the parameter or explicitly ignoring it

lint_builtin_black_box_zst_call = use of `black_box` on the zero-sized type `{$ty}` has no effect
.label = zero-sized value passed here

lint_builtin_black_box_zst_help = if this is a function, coerce it to a function pointer first

lint_builtin_black_box_zst_note = zero-sized values have no runtime representation, so the compiler can ignore the call

lint_builtin_clashing_extern_diff_name = `{$this}` redeclares `{$orig}` with a different signature
.previous_decl_label = `{$orig}` previously declared here
.mismatch_label = this signature doesn't match the previous declaration
Expand Down
94 changes: 84 additions & 10 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,17 @@ use rustc_trait_selection::traits::{self};

use crate::errors::BuiltinEllipsisInclusiveRangePatterns;
use crate::lints::{
BuiltinAnonymousParams, BuiltinConstNoMangle, BuiltinDerefNullptr, BuiltinDoubleNegations,
BuiltinDoubleNegationsAddParens, BuiltinEllipsisInclusiveRangePatternsLint,
BuiltinExplicitOutlives, BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote,
BuiltinIncompleteFeatures, BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures,
BuiltinKeywordIdents, BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,
BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds,
BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub,
BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment,
BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel,
BuiltinAnonymousParams, BuiltinBlackBoxZstCall, BuiltinConstNoMangle, BuiltinDerefNullptr,
BuiltinDoubleNegations, BuiltinDoubleNegationsAddParens,
BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures,
BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents,
BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, BuiltinMutablesTransmutes,
BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed,
BuiltinTrivialBounds, BuiltinTypeAliasBounds, BuiltinUngatedAsyncFnTrackCaller,
BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub,
BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
BuiltinWhileTrue, InvalidAsmLabel,
};
use crate::{
EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext,
Expand Down Expand Up @@ -3110,6 +3111,79 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels {
}
}

declare_lint! {
/// The `black_box_zst_calls` lint detects calls to `core::hint::black_box`
/// where the argument is a zero-sized callable (e.g. a function item or
/// a capture-less closure). These values have no runtime representation,
Comment thread
Darksonn marked this conversation as resolved.
Outdated
/// so the black boxing does not make subsequent calls opaque to the
/// optimizer.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![deny(black_box_zst_calls)]
/// use std::hint::black_box;
///
/// fn add(a: u32, b: u32) -> u32 {
/// a + b
/// }
///
/// fn main() {
/// let add_bb = black_box(add);
/// let _ = add_bb(1, 2);
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Function items and capture-less closures are zero-sized. Passing them
/// to `black_box` does not force the optimizer to treat the subsequent
/// call as opaque. Coerce the callable to a function pointer and black_box
/// that pointer instead.
pub BLACK_BOX_ZST_CALLS,
Warn,
"calling `black_box` on zero-sized callables has no effect on opacity"
}

declare_lint_pass!(BlackBoxZstCalls => [BLACK_BOX_ZST_CALLS]);

impl<'tcx> LateLintPass<'tcx> for BlackBoxZstCalls {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) {
let hir::ExprKind::Call(callee, args) = expr.kind else { return };
if args.len() != 1 {
return;
}

let hir::ExprKind::Path(ref qpath) = callee.kind else { return };
let Some(def_id) = cx.qpath_res(qpath, callee.hir_id).opt_def_id() else { return };

if !cx.tcx.is_diagnostic_item(sym::black_box, def_id) {
return;
}

let arg = &args[0];
let arg_ty = cx.typeck_results().expr_ty_adjusted(arg);

if !is_callable_zst(cx, arg_ty) {
return;
}

let ty_name = with_no_trimmed_paths!(arg_ty.to_string());
cx.emit_span_lint(
BLACK_BOX_ZST_CALLS,
expr.span,
BuiltinBlackBoxZstCall { arg_span: arg.span, ty: ty_name },
);
}
}

fn is_callable_zst<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
matches!(ty.kind(), ty::FnDef(..) | ty::Closure(..))
&& cx.tcx.layout_of(cx.typing_env().as_query_input(ty)).is_ok_and(|layout| layout.is_zst())
}

declare_lint! {
/// The `special_module_name` lint detects module
/// declarations for files that have a special meaning.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ late_lint_methods!(
InvalidFromUtf8: InvalidFromUtf8,
VariantSizeDifferences: VariantSizeDifferences,
PathStatements: PathStatements,
BlackBoxZstCalls: BlackBoxZstCalls,
LetUnderscore: LetUnderscore,
InvalidReferenceCasting: InvalidReferenceCasting,
ImplicitAutorefs: ImplicitAutorefs,
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ pub(crate) struct BuiltinNonShorthandFieldPatterns {
pub prefix: &'static str,
}

#[derive(LintDiagnostic)]
#[diag(lint_builtin_black_box_zst_call)]
#[note(lint_builtin_black_box_zst_note)]
#[help(lint_builtin_black_box_zst_help)]
pub(crate) struct BuiltinBlackBoxZstCall {
#[label]
pub arg_span: Span,
pub ty: String,
}

#[derive(LintDiagnostic)]
pub(crate) enum BuiltinUnsafe {
#[diag(lint_builtin_allow_internal_unsafe)]
Expand Down
1 change: 1 addition & 0 deletions library/core/src/hint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,7 @@ pub fn spin_loop() {
#[inline]
#[stable(feature = "bench_black_box", since = "1.66.0")]
#[rustc_const_stable(feature = "const_black_box", since = "1.86.0")]
#[rustc_diagnostic_item = "black_box"]
pub const fn black_box<T>(dummy: T) -> T {
crate::intrinsics::black_box(dummy)
}
Expand Down
13 changes: 13 additions & 0 deletions tests/ui/lint/lint-black-box-zst-call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#![deny(black_box_zst_calls)]

use std::hint::black_box;

fn add(a: u32, b: u32) -> u32 {
a + b
}

fn main() {
let add_bb = black_box(add);
//~^ ERROR use of `black_box` on the zero-sized type
let _ = add_bb(1, 2);
}
18 changes: 18 additions & 0 deletions tests/ui/lint/lint-black-box-zst-call.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error: use of `black_box` on the zero-sized type `fn(u32, u32) -> u32 {add}` has no effect
--> $DIR/lint-black-box-zst-call.rs:10:18
|
LL | let add_bb = black_box(add);
| ^^^^^^^^^^---^
| |
| zero-sized value passed here
|
= note: zero-sized values have no runtime representation, so the compiler can ignore the call
= help: if this is a function, coerce it to a function pointer first
note: the lint level is defined here
--> $DIR/lint-black-box-zst-call.rs:1:9
|
LL | #![deny(black_box_zst_calls)]
| ^^^^^^^^^^^^^^^^^^^

error: aborting due to 1 previous error

Loading