rustc_lint/
gpukernel_abi.rs1use std::iter;
2
3use rustc_abi::ExternAbi;
4use rustc_hir::{self as hir, find_attr};
5use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable};
6use rustc_session::{declare_lint, declare_lint_pass};
7use rustc_span::Span;
8use rustc_span::def_id::LocalDefId;
9
10use crate::lints::{ImproperGpuKernelArg, MissingGpuKernelExportName};
11use crate::{LateContext, LateLintPass, LintContext};
12
13#[doc =
r" The `improper_gpu_kernel_arg` lint detects incorrect use of types in `gpu-kernel`"]
#[doc = r" arguments."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,ignore (fails on non-GPU targets)"]
#[doc = r" #[unsafe(no_mangle)]"]
#[doc = r#" extern "gpu-kernel" fn kernel(_: [i32; 10]) {}"#]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" This will produce:"]
#[doc = r""]
#[doc = r" ```text"]
#[doc =
r#" warning: passing type `[i32; 10]` to a function with "gpu-kernel" ABI may have unexpected behavior"#]
#[doc = r" --> t.rs:2:34"]
#[doc = r" |"]
#[doc = r#" 2 | extern "gpu-kernel" fn kernel(_: [i32; 10]) {}"#]
#[doc = r" | ^^^^^^^^^"]
#[doc = r" |"]
#[doc =
r" = help: use primitive types and raw pointers to get reliable behavior"]
#[doc = r" = note: `#[warn(improper_gpu_kernel_arg)]` on by default"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" The compiler has several checks to verify that types used as arguments in `gpu-kernel`"]
#[doc =
r" functions follow certain rules to ensure proper compatibility with the foreign interfaces."]
#[doc =
r" This lint is issued when it detects a probable mistake in a signature."]
static IMPROPER_GPU_KERNEL_ARG: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "IMPROPER_GPU_KERNEL_ARG",
default_level: ::rustc_lint_defs::Warn,
desc: "GPU kernel entry points have a limited ABI",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
14 IMPROPER_GPU_KERNEL_ARG,
43 Warn,
44 "GPU kernel entry points have a limited ABI"
45}
46
47#[doc =
r" The `missing_gpu_kernel_export_name` lint detects `gpu-kernel` functions that have a mangled name."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,ignore (fails on non-GPU targets)"]
#[doc = r#" extern "gpu-kernel" fn kernel() { }"#]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" This will produce:"]
#[doc = r""]
#[doc = r" ```text"]
#[doc = r#" warning: function with the "gpu-kernel" ABI has a mangled name"#]
#[doc = r" --> t.rs:1:1"]
#[doc = r" |"]
#[doc = r#" 1 | extern "gpu-kernel" fn kernel() {}"#]
#[doc = r" | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"]
#[doc = r" |"]
#[doc =
r#" = help: use `unsafe(no_mangle)` or `unsafe(export_name = "<name>")`"#]
#[doc =
r" = note: mangled names make it hard to find the kernel, this is usually not intended"]
#[doc = r" = note: `#[warn(missing_gpu_kernel_export_name)]` on by default"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" `gpu-kernel` functions are usually searched by name in the compiled file."]
#[doc =
r" A mangled name is usually unintentional as it would need to be searched by the mangled name."]
#[doc = r""]
#[doc =
r" To use an unmangled name for the kernel, either `no_mangle` or `export_name` can be used."]
#[doc = r" ```rust,ignore (fails on non-GPU targets)"]
#[doc = r#" // Can be found by the name "kernel""#]
#[doc = r" #[unsafe(no_mangle)]"]
#[doc = r#" extern "gpu-kernel" fn kernel() { }"#]
#[doc = r""]
#[doc = r#" // Can be found by the name "new_name""#]
#[doc = r#" #[unsafe(export_name = "new_name")]"#]
#[doc = r#" extern "gpu-kernel" fn other_kernel() { }"#]
#[doc = r" ```"]
static MISSING_GPU_KERNEL_EXPORT_NAME: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "MISSING_GPU_KERNEL_EXPORT_NAME",
default_level: ::rustc_lint_defs::Warn,
desc: "mangled gpu-kernel function",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
48 MISSING_GPU_KERNEL_EXPORT_NAME,
86 Warn,
87 "mangled gpu-kernel function"
88}
89
90pub struct ImproperGpuKernelLint;
#[automatically_derived]
impl ::core::marker::Copy for ImproperGpuKernelLint { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for ImproperGpuKernelLint { }
#[automatically_derived]
impl ::core::clone::Clone for ImproperGpuKernelLint {
#[inline]
fn clone(&self) -> ImproperGpuKernelLint { *self }
}
impl ::rustc_lint_defs::LintPass for ImproperGpuKernelLint {
fn name(&self) -> &'static str { "ImproperGpuKernelLint" }
fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[IMPROPER_GPU_KERNEL_ARG, MISSING_GPU_KERNEL_EXPORT_NAME]))
}
}
impl ImproperGpuKernelLint {
#[allow(unused)]
pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[IMPROPER_GPU_KERNEL_ARG, MISSING_GPU_KERNEL_EXPORT_NAME]))
}
}declare_lint_pass!(ImproperGpuKernelLint => [
91 IMPROPER_GPU_KERNEL_ARG,
92 MISSING_GPU_KERNEL_EXPORT_NAME,
93]);
94
95struct CheckGpuKernelTypes<'tcx> {
97 tcx: TyCtxt<'tcx>,
98 has_invalid: bool,
100}
101
102impl<'tcx> TypeFolder<TyCtxt<'tcx>> for CheckGpuKernelTypes<'tcx> {
103 fn cx(&self) -> TyCtxt<'tcx> {
104 self.tcx
105 }
106
107 fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
108 match ty.kind() {
109 ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => {}
110 ty::RawPtr(_, _) => {
112 if !ty.pointee_metadata_ty_or_projection(self.tcx).is_unit() {
113 self.has_invalid = true;
114 }
115 }
116
117 ty::Adt(_, _)
118 | ty::Alias(_, _)
119 | ty::Array(_, _)
120 | ty::Bound(_, _)
121 | ty::Closure(_, _)
122 | ty::Coroutine(_, _)
123 | ty::CoroutineClosure(_, _)
124 | ty::CoroutineWitness(..)
125 | ty::Dynamic(_, _)
126 | ty::FnDef(_, _)
127 | ty::FnPtr(..)
128 | ty::Foreign(_)
129 | ty::Never
130 | ty::Pat(_, _)
131 | ty::Placeholder(_)
132 | ty::Ref(_, _, _)
133 | ty::Slice(_)
134 | ty::Str
135 | ty::Tuple(_) => self.has_invalid = true,
136
137 _ => return ty.super_fold_with(self),
138 }
139 ty
140 }
141}
142
143impl<'tcx> LateLintPass<'tcx> for ImproperGpuKernelLint {
148 fn check_fn(
149 &mut self,
150 cx: &LateContext<'tcx>,
151 kind: hir::intravisit::FnKind<'tcx>,
152 decl: &'tcx hir::FnDecl<'_>,
153 _: &'tcx hir::Body<'_>,
154 span: Span,
155 id: LocalDefId,
156 ) {
157 use hir::intravisit::FnKind;
158
159 let abi = match kind {
160 FnKind::ItemFn(_, _, header, ..) => header.abi,
161 FnKind::Method(_, sig, ..) => sig.header.abi,
162 _ => return,
163 };
164
165 if abi != ExternAbi::GpuKernel {
166 return;
167 }
168
169 let sig = cx.tcx.fn_sig(id).instantiate_identity();
170 let sig = cx.tcx.instantiate_bound_regions_with_erased(sig);
171
172 for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
173 let mut checker = CheckGpuKernelTypes { tcx: cx.tcx, has_invalid: false };
174 input_ty.fold_with(&mut checker);
175 if checker.has_invalid {
176 cx.tcx.emit_node_span_lint(
177 IMPROPER_GPU_KERNEL_ARG,
178 input_hir.hir_id,
179 input_hir.span,
180 ImproperGpuKernelArg { ty: *input_ty },
181 );
182 }
183 }
184
185 if !{
#[allow(deprecated)]
{
{
'done:
{
for i in cx.tcx.get_all_attrs(id) {
#[allow(unused_imports)]
use rustc_hir::attrs::AttributeKind::*;
let i: &rustc_hir::Attribute = i;
match i {
rustc_hir::Attribute::Parsed(NoMangle(..) | ExportName { ..
}) => {
break 'done Some(());
}
rustc_hir::Attribute::Unparsed(..) =>
{}
#[deny(unreachable_patterns)]
_ => {}
}
}
None
}
}
}
}.is_some()find_attr!(cx.tcx, id, NoMangle(..) | ExportName { .. }) {
187 cx.emit_span_lint(MISSING_GPU_KERNEL_EXPORT_NAME, span, MissingGpuKernelExportName);
188 }
189 }
190}