| use std::iter; | |
| use rustc_abi::ExternAbi; | |
| use rustc_hir::{self as hir, find_attr}; | |
| use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable}; | |
| use rustc_session::{declare_lint, declare_lint_pass}; | |
| use rustc_span::Span; | |
| use rustc_span::def_id::LocalDefId; | |
| use crate::lints::{ImproperGpuKernelArg, MissingGpuKernelExportName}; | |
| use crate::{LateContext, LateLintPass, LintContext}; | |
| declare_lint! { | |
| /// The `improper_gpu_kernel_arg` lint detects incorrect use of types in `gpu-kernel` | |
| /// arguments. | |
| /// | |
| /// ### Example | |
| /// | |
| /// ```rust,ignore (fails on non-GPU targets) | |
| /// #[unsafe(no_mangle)] | |
| /// extern "gpu-kernel" fn kernel(_: [i32; 10]) {} | |
| /// ``` | |
| /// | |
| /// This will produce: | |
| /// | |
| /// ```text | |
| /// warning: passing type `[i32; 10]` to a function with "gpu-kernel" ABI may have unexpected behavior | |
| /// --> t.rs:2:34 | |
| /// | | |
| /// 2 | extern "gpu-kernel" fn kernel(_: [i32; 10]) {} | |
| /// | ^^^^^^^^^ | |
| /// | | |
| /// = help: use primitive types and raw pointers to get reliable behavior | |
| /// = note: `#[warn(improper_gpu_kernel_arg)]` on by default | |
| /// ``` | |
| /// | |
| /// ### Explanation | |
| /// | |
| /// The compiler has several checks to verify that types used as arguments in `gpu-kernel` | |
| /// functions follow certain rules to ensure proper compatibility with the foreign interfaces. | |
| /// This lint is issued when it detects a probable mistake in a signature. | |
| IMPROPER_GPU_KERNEL_ARG, | |
| Warn, | |
| "GPU kernel entry points have a limited ABI" | |
| } | |
| declare_lint! { | |
| /// The `missing_gpu_kernel_export_name` lint detects `gpu-kernel` functions that have a mangled name. | |
| /// | |
| /// ### Example | |
| /// | |
| /// ```rust,ignore (fails on non-GPU targets) | |
| /// extern "gpu-kernel" fn kernel() { } | |
| /// ``` | |
| /// | |
| /// This will produce: | |
| /// | |
| /// ```text | |
| /// warning: function with the "gpu-kernel" ABI has a mangled name | |
| /// --> t.rs:1:1 | |
| /// | | |
| /// 1 | extern "gpu-kernel" fn kernel() {} | |
| /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| /// | | |
| /// = help: use `unsafe(no_mangle)` or `unsafe(export_name = "<name>")` | |
| /// = note: mangled names make it hard to find the kernel, this is usually not intended | |
| /// = note: `#[warn(missing_gpu_kernel_export_name)]` on by default | |
| /// ``` | |
| /// | |
| /// ### Explanation | |
| /// | |
| /// `gpu-kernel` functions are usually searched by name in the compiled file. | |
| /// A mangled name is usually unintentional as it would need to be searched by the mangled name. | |
| /// | |
| /// To use an unmangled name for the kernel, either `no_mangle` or `export_name` can be used. | |
| /// ```rust,ignore (fails on non-GPU targets) | |
| /// // Can be found by the name "kernel" | |
| /// #[unsafe(no_mangle)] | |
| /// extern "gpu-kernel" fn kernel() { } | |
| /// | |
| /// // Can be found by the name "new_name" | |
| /// #[unsafe(export_name = "new_name")] | |
| /// extern "gpu-kernel" fn other_kernel() { } | |
| /// ``` | |
| MISSING_GPU_KERNEL_EXPORT_NAME, | |
| Warn, | |
| "mangled gpu-kernel function" | |
| } | |
| declare_lint_pass!(ImproperGpuKernelLint => [ | |
| IMPROPER_GPU_KERNEL_ARG, | |
| MISSING_GPU_KERNEL_EXPORT_NAME, | |
| ]); | |
| /// Check for valid and invalid types. | |
| struct CheckGpuKernelTypes<'tcx> { | |
| tcx: TyCtxt<'tcx>, | |
| // If one or more invalid types were encountered while folding. | |
| has_invalid: bool, | |
| } | |
| impl<'tcx> TypeFolder<TyCtxt<'tcx>> for CheckGpuKernelTypes<'tcx> { | |
| fn cx(&self) -> TyCtxt<'tcx> { | |
| self.tcx | |
| } | |
| fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { | |
| match ty.kind() { | |
| ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => {} | |
| // Thin pointers are allowed but fat pointers with metadata are not | |
| ty::RawPtr(_, _) => { | |
| if !ty.pointee_metadata_ty_or_projection(self.tcx).is_unit() { | |
| self.has_invalid = true; | |
| } | |
| } | |
| ty::Adt(_, _) | |
| | ty::Alias(_, _) | |
| | ty::Array(_, _) | |
| | ty::Bound(_, _) | |
| | ty::Closure(_, _) | |
| | ty::Coroutine(_, _) | |
| | ty::CoroutineClosure(_, _) | |
| | ty::CoroutineWitness(..) | |
| | ty::Dynamic(_, _) | |
| | ty::FnDef(_, _) | |
| | ty::FnPtr(..) | |
| | ty::Foreign(_) | |
| | ty::Never | |
| | ty::Pat(_, _) | |
| | ty::Placeholder(_) | |
| | ty::Ref(_, _, _) | |
| | ty::Slice(_) | |
| | ty::Str | |
| | ty::Tuple(_) => self.has_invalid = true, | |
| _ => return ty.super_fold_with(self), | |
| } | |
| ty | |
| } | |
| } | |
| /// `ImproperGpuKernelLint` checks `gpu-kernel` function definitions: | |
| /// | |
| /// - `extern "gpu-kernel" fn` arguments should be primitive types. | |
| /// - `extern "gpu-kernel" fn` should have an unmangled name. | |
| impl<'tcx> LateLintPass<'tcx> for ImproperGpuKernelLint { | |
| fn check_fn( | |
| &mut self, | |
| cx: &LateContext<'tcx>, | |
| kind: hir::intravisit::FnKind<'tcx>, | |
| decl: &'tcx hir::FnDecl<'_>, | |
| _: &'tcx hir::Body<'_>, | |
| span: Span, | |
| id: LocalDefId, | |
| ) { | |
| use hir::intravisit::FnKind; | |
| let abi = match kind { | |
| FnKind::ItemFn(_, _, header, ..) => header.abi, | |
| FnKind::Method(_, sig, ..) => sig.header.abi, | |
| _ => return, | |
| }; | |
| if abi != ExternAbi::GpuKernel { | |
| return; | |
| } | |
| let sig = cx.tcx.fn_sig(id).instantiate_identity(); | |
| let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); | |
| for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { | |
| let mut checker = CheckGpuKernelTypes { tcx: cx.tcx, has_invalid: false }; | |
| input_ty.fold_with(&mut checker); | |
| if checker.has_invalid { | |
| cx.tcx.emit_node_span_lint( | |
| IMPROPER_GPU_KERNEL_ARG, | |
| input_hir.hir_id, | |
| input_hir.span, | |
| ImproperGpuKernelArg { ty: *input_ty }, | |
| ); | |
| } | |
| } | |
| // Check for no_mangle/export_name, so the kernel can be found when querying the compiled object for the kernel function by name | |
| if !find_attr!(cx.tcx, id, NoMangle(..) | ExportName { .. }) { | |
| cx.emit_span_lint(MISSING_GPU_KERNEL_EXPORT_NAME, span, MissingGpuKernelExportName); | |
| } | |
| } | |
| } |