rustc_lint/
gpukernel_abi.rs1use std::iter;
2
3use rustc_abi::ExternAbi;
4use rustc_hir::attrs::AttributeKind;
5use rustc_hir::{self as hir, find_attr};
6use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable};
7use rustc_session::{declare_lint, declare_lint_pass};
8use rustc_span::Span;
9use rustc_span::def_id::LocalDefId;
10
11use crate::lints::{ImproperGpuKernelArg, MissingGpuKernelExportName};
12use crate::{LateContext, LateLintPass, LintContext};
13
14#[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! {
15 IMPROPER_GPU_KERNEL_ARG,
44 Warn,
45 "GPU kernel entry points have a limited ABI"
46}
47
48#[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! {
49 MISSING_GPU_KERNEL_EXPORT_NAME,
87 Warn,
88 "mangled gpu-kernel function"
89}
90
91pub 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 {
<[_]>::into_vec(::alloc::boxed::box_new([IMPROPER_GPU_KERNEL_ARG,
MISSING_GPU_KERNEL_EXPORT_NAME]))
}
}
impl ImproperGpuKernelLint {
#[allow(unused)]
pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
<[_]>::into_vec(::alloc::boxed::box_new([IMPROPER_GPU_KERNEL_ARG,
MISSING_GPU_KERNEL_EXPORT_NAME]))
}
}declare_lint_pass!(ImproperGpuKernelLint => [
92 IMPROPER_GPU_KERNEL_ARG,
93 MISSING_GPU_KERNEL_EXPORT_NAME,
94]);
95
96struct CheckGpuKernelTypes<'tcx> {
98 tcx: TyCtxt<'tcx>,
99 has_invalid: bool,
101}
102
103impl<'tcx> TypeFolder<TyCtxt<'tcx>> for CheckGpuKernelTypes<'tcx> {
104 fn cx(&self) -> TyCtxt<'tcx> {
105 self.tcx
106 }
107
108 fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
109 match ty.kind() {
110 ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => {}
111 ty::RawPtr(_, _) => {
113 if !ty.pointee_metadata_ty_or_projection(self.tcx).is_unit() {
114 self.has_invalid = true;
115 }
116 }
117
118 ty::Adt(_, _)
119 | ty::Alias(_, _)
120 | ty::Array(_, _)
121 | ty::Bound(_, _)
122 | ty::Closure(_, _)
123 | ty::Coroutine(_, _)
124 | ty::CoroutineClosure(_, _)
125 | ty::CoroutineWitness(..)
126 | ty::Dynamic(_, _)
127 | ty::FnDef(_, _)
128 | ty::FnPtr(..)
129 | ty::Foreign(_)
130 | ty::Never
131 | ty::Pat(_, _)
132 | ty::Placeholder(_)
133 | ty::Ref(_, _, _)
134 | ty::Slice(_)
135 | ty::Str
136 | ty::Tuple(_) => self.has_invalid = true,
137
138 _ => return ty.super_fold_with(self),
139 }
140 ty
141 }
142}
143
144impl<'tcx> LateLintPass<'tcx> for ImproperGpuKernelLint {
149 fn check_fn(
150 &mut self,
151 cx: &LateContext<'tcx>,
152 kind: hir::intravisit::FnKind<'tcx>,
153 decl: &'tcx hir::FnDecl<'_>,
154 _: &'tcx hir::Body<'_>,
155 span: Span,
156 id: LocalDefId,
157 ) {
158 use hir::intravisit::FnKind;
159
160 let abi = match kind {
161 FnKind::ItemFn(_, _, header, ..) => header.abi,
162 FnKind::Method(_, sig, ..) => sig.header.abi,
163 _ => return,
164 };
165
166 if abi != ExternAbi::GpuKernel {
167 return;
168 }
169
170 let sig = cx.tcx.fn_sig(id).instantiate_identity();
171 let sig = cx.tcx.instantiate_bound_regions_with_erased(sig);
172
173 for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
174 let mut checker = CheckGpuKernelTypes { tcx: cx.tcx, has_invalid: false };
175 input_ty.fold_with(&mut checker);
176 if checker.has_invalid {
177 cx.tcx.emit_node_span_lint(
178 IMPROPER_GPU_KERNEL_ARG,
179 input_hir.hir_id,
180 input_hir.span,
181 ImproperGpuKernelArg { ty: *input_ty },
182 );
183 }
184 }
185
186 if !{
{
'done:
{
for i in cx.tcx.get_all_attrs(id) {
let i: &rustc_hir::Attribute = i;
match i {
rustc_hir::Attribute::Parsed(AttributeKind::NoMangle(..) |
AttributeKind::ExportName { .. }) => {
break 'done Some(());
}
_ => {}
}
}
None
}
}.is_some()
}find_attr!(
188 cx.tcx.get_all_attrs(id),
189 AttributeKind::NoMangle(..) | AttributeKind::ExportName { .. }
190 ) {
191 cx.emit_span_lint(MISSING_GPU_KERNEL_EXPORT_NAME, span, MissingGpuKernelExportName);
192 }
193 }
194}