[clang] [clang][SYCL] Add additional Sema rules for SYCL kernel parameters (PR #208571)

Ian Li via cfe-commits cfe-commits at lists.llvm.org
Fri Aug 21 12:10:39 PDT 2026


https://github.com/ianayl updated https://github.com/llvm/llvm-project/pull/208571

>From bb73b24cf5e0a77209576f8d8253f2793d848050 Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Tue, 7 Jul 2026 07:26:01 -0700
Subject: [PATCH 01/19] Initial work

---
 .../clang/Basic/DiagnosticSemaKinds.td        |   4 +
 clang/lib/Sema/SemaSYCL.cpp                   |  30 +++++
 .../sycl-kernel-param-restrictions.cpp        | 107 ++++++++++++++++++
 3 files changed, 141 insertions(+)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 86b765fdf1fab..3f393fa574434 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13514,6 +13514,10 @@ def err_sycl_special_type_num_init_method : Error<
   "method defined">;
 def err_sycl_device_invalid_target : Error<
   "%0 is not a supported SYCL device target">;
+def warn_sycl_kernel_has_ptr_param : Warning<
+  "pointers parameters in SYCL kernels must point to device-accessible memory, "
+  "i.e. the USM">,
+  InGroup<NonPortableSYCL>, DefaultIgnore;
 
 // SYCL external attribute diagnostics
 def err_sycl_external_invalid_linkage : Error<
diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index b942f19761f40..0b35172c3deb4 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -750,6 +750,36 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
       // invalid use of a reference.
       IsValid = false;
       return false;
+    } 
+    
+    Ty.dump();
+    if (Ty->isAtomicType() ||
+        Ty->isStructureTypeWithFlexibleArrayMember() ||
+        Ty->isVariablyModifiedType()) {
+
+      auto DirectParent = ObjectAccessPath.back();
+      auto *DirectFieldParent = cast<const FieldDecl *>(DirectParent);
+      SemaSYCLRef.Diag(DirectFieldParent->getLocation(),
+                       diag::err_bad_kernel_param_type)
+          << DirectFieldParent->getType();
+      emitObjectAccessPathNotes();
+
+      IsValid = false;
+      return false;
+    } /*
+    } else if (0 ) { // TODO detect virtual base class 
+
+    } // TODO introduce warning to detect pointers 
+    // "sycl portability warning: it is user's responsibility to ensure pointers being used are USM"
+    */
+    // Warn about pointer parameters in SYCL kernels if non-portable SYCL
+    // warnings are enabled.
+    if (Ty->isPointerType()) {
+      auto DirectParent = ObjectAccessPath.back();
+      auto *DirectFieldParent = cast<const FieldDecl *>(DirectParent);
+      SemaSYCLRef.Diag(DirectFieldParent->getLocation(),
+                   diag::warn_sycl_kernel_has_ptr_param);
+      emitObjectAccessPathNotes();
     }
     return true;
   }
diff --git a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
index 66aa00da18a04..f93cc17d72d89 100644
--- a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
@@ -1,6 +1,8 @@
 // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -Wno-vla-cxx-extension -fsycl-is-host -verify %s
 // RUN: %clang_cc1 -triple spirv64 -std=c++17 -fsyntax-only -Wno-vla-cxx-extension -fsycl-is-device -verify %s
 
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -Wnonportable-sycl -Wno-vla-cxx-extension -fsycl-is-host -verify=nonportable %s
+
 // A unique kernel name type is required for each declared kernel entry point.
 template<int, int = 0> struct KN;
 
@@ -213,3 +215,108 @@ void test() {
 }
 
 } // namespace badref7
+
+
+#include <stdatomic.h>
+// Check for atomic parameters and subobjects.
+namespace badref8 {
+// Kernel entry point template definition.
+template<typename KNT, typename T>
+[[clang::sycl_kernel_entry_point(KNT)]]
+void kernel_single_task(T t) {}
+
+struct S { 
+  int a;
+  _Atomic int b;
+};
+
+class Kernel {
+  S data{1, 2};
+public:
+  void operator()() { }
+};
+
+void test() {
+  _Atomic int a = 0;
+  // TODO _Atomic(int) a
+  S s{1, 2};
+  S arr[] = {s, s};
+  kernel_single_task<class KN<15>>([=]{ (void)a; });
+  kernel_single_task<class KN<16>>([=]{ (void)s; });
+  kernel_single_task<class KN<17>>([=]{ (void)arr; });
+  kernel_single_task<class KN<18>>(Kernel());
+  // kernel_single_task<class KN<17>>([](S s) { (void)s; }); // DOESNT DETECT
+}
+
+} // namespace badref8
+
+
+// Check for flexible array members -- would not be copyable to device
+namespace badref9 {
+// Kernel entry point template definition.
+template<typename KNT, typename T>
+[[clang::sycl_kernel_entry_point(KNT)]]
+void kernel_single_task(T t) {}
+
+struct FAM { 
+  int a;
+  int[] b;
+};
+
+class Kernel {
+  FAM fam;
+public:
+  void operator()() { }
+};
+
+void test() {
+  FAM fam;
+  kernel_single_task<class KN<19>>([=]{ (void)fam; }); // DOESNT DETECT
+  kernel_single_task<class KN<20>>(Kernel());
+}
+
+} // namespace badref9
+
+// Check for variable member array -- would not be copyable to device
+namespace badref10 {
+// Kernel entry point template definition.
+template<typename KNT, typename T>
+[[clang::sycl_kernel_entry_point(KNT)]]
+void kernel_single_task(T t) {}
+
+class Kernel {
+  int len;
+public:
+  Kernel(int l, int vmt[n]) : len(l) {}
+  void operator()(int n, int vmt[n]) { (void)vmt; }
+};
+
+void test() {
+  int n;
+  int Vmt[n];
+  kernel_single_task<class KN<21>>([=](int n, int vmt[n]) { (void)vmt; }); // DOESNT DETECT
+  kernel_single_task<class KN<22>>(Kernel()); // DOESNT DETECT
+  kernel_single_task<class KN<23>>([=]{ (void)Vmt; });
+}
+
+} // namespace badref10
+
+// Check for pointer parameters
+namespace nonportable1 {
+// Kernel entry point template definition.
+template<typename KNT, typename T>
+[[clang::sycl_kernel_entry_point(KNT)]]
+void kernel_single_task(T t) {}
+
+class Kernel {
+public:
+  void operator()(int *ptr) { (void)ptr; }
+};
+
+void test() {
+  int *ptr;
+  kernel_single_task<class KN<24>>(Kernel()); // DOESNT DETECT
+  kernel_single_task<class KN<25>>([=]{ (void)ptr; }); // DOESNT DETECT
+}
+
+} // namespace nonportable1
\ No newline at end of file

>From 57abfe8abd081828564881b6398ac5b079c2f00f Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Wed, 8 Jul 2026 08:16:40 -0700
Subject: [PATCH 02/19] Tentative implementation

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 +
 clang/lib/Sema/SemaSYCL.cpp                   | 20 ++++--
 .../sycl-kernel-param-restrictions.cpp        | 61 ++++++++++---------
 3 files changed, 48 insertions(+), 35 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 3f393fa574434..acef5af0b34f8 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13518,6 +13518,8 @@ def warn_sycl_kernel_has_ptr_param : Warning<
   "pointers parameters in SYCL kernels must point to device-accessible memory, "
   "i.e. the USM">,
   InGroup<NonPortableSYCL>, DefaultIgnore;
+def err_sycl_kernel_param_has_vbase : Error<
+  "%0 inherits virtual base classes and cannot be used as a SYCL kernel parameter">;
 
 // SYCL external attribute diagnostics
 def err_sycl_external_invalid_linkage : Error<
diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index 0b35172c3deb4..88b6aefa187f9 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -766,12 +766,20 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
 
       IsValid = false;
       return false;
-    } /*
-    } else if (0 ) { // TODO detect virtual base class 
-
-    } // TODO introduce warning to detect pointers 
-    // "sycl portability warning: it is user's responsibility to ensure pointers being used are USM"
-    */
+    } 
+    if (CXXRecordDecl *RD = Ty->getAsCXXRecordDecl()) {
+      if (RD->getNumVBases() > 0) {
+        auto DirectParent = ObjectAccessPath.back();
+        auto *DirectFieldParent = cast<const FieldDecl *>(DirectParent);
+        SemaSYCLRef.Diag(DirectFieldParent->getLocation(),
+                         diag::err_sycl_kernel_param_has_vbase)
+            << DirectFieldParent->getType();
+        emitObjectAccessPathNotes();
+
+        IsValid = false;
+        return false;
+      }
+    }
     // Warn about pointer parameters in SYCL kernels if non-portable SYCL
     // warnings are enabled.
     if (Ty->isPointerType()) {
diff --git a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
index f93cc17d72d89..5cbf6ce472ebc 100644
--- a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
@@ -151,33 +151,6 @@ void test(int AS) {
 
 } // namespace badref4
 
-// Test virtual bases.
-// FIXME: explicitly diagnose virtual bases within kernel parameters.
-namespace badref5 {
-// Kernel entry point template definition.
-template<typename KNT, typename T>
-[[clang::sycl_kernel_entry_point(KNT)]]
-void kernel_single_task(T t) {} //expected-note {{within parameter 't' of type 'badref5::Derived' declared here}}
-
-class Base { // expected-note {{within field of type 'Base' declared here}}}
-  int &data; // expected-error {{'int &' cannot be used as the type of a kernel parameter}}
-public:
-  Base(int &a) : data(a) {}
-};
-
-class Derived : virtual Base { // expected-note {{within base class of type 'Base' declared here}}
-public:
-  Derived(int &a) : Base(a) {}
-
-};
-
-void test() {
-  int p = 0;
-  kernel_single_task<class KN<12>>(Derived{p});
-  // expected-note-re at -1 {{in instantiation of function template specialization 'badref5::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
-}
-} // namespace badref5
-
 // Check that a struct that hold a reference and captured by reference by lambda
 // kernel object is diagnosed correctly.
 namespace badref6 {
@@ -316,7 +289,37 @@ class Kernel {
 void test() {
   int *ptr;
   kernel_single_task<class KN<24>>(Kernel()); // DOESNT DETECT
-  kernel_single_task<class KN<25>>([=]{ (void)ptr; }); // DOESNT DETECT
+  kernel_single_task<class KN<25>>([=]{ (void)ptr; });
 }
 
-} // namespace nonportable1
\ No newline at end of file
+} // namespace nonportable1
+
+// Test virtual bases.
+// FIXME: explicitly diagnose virtual bases within kernel parameters.
+namespace badref5 {
+// Kernel entry point template definition.
+template<typename KNT, typename T>
+[[clang::sycl_kernel_entry_point(KNT)]]
+void kernel_single_task(T t) {} //expected-note {{within parameter 't' of type 'badref5::Derived' declared here}}
+
+class Base { // expected-note {{within field of type 'Base' declared here}}}
+  int &data; // expected-error {{'int &' cannot be used as the type of a kernel parameter}}
+public:
+  Base(int &a) : data(a) {}
+};
+
+class Derived : virtual Base { // expected-note {{within base class of type 'Base' declared here}}
+public:
+  Derived(int &a) : Base(a) {}
+
+};
+
+void test() {
+  int p = 0;
+  kernel_single_task<class KN<12>>(Derived{p});
+  // expected-note-re at -1 {{in instantiation of function template specialization 'badref5::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
+
+  Derived d{p};
+  kernel_single_task<class KN<30>>([=]{ (void)d; });
+}
+} // namespace badref5

>From 34521226c5b70a9ab21eeabe93b2371ae7fc4b66 Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Thu, 9 Jul 2026 15:10:19 -0700
Subject: [PATCH 03/19] update handling of object path

---
 .../clang/Basic/DiagnosticSemaKinds.td        |   8 +-
 clang/lib/Sema/SemaSYCL.cpp                   |  53 +++---
 .../sycl-kernel-param-restrictions.cpp        | 164 ++++++++++++------
 3 files changed, 148 insertions(+), 77 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index acef5af0b34f8..bd59d17b08b12 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13514,12 +13514,16 @@ def err_sycl_special_type_num_init_method : Error<
   "method defined">;
 def err_sycl_device_invalid_target : Error<
   "%0 is not a supported SYCL device target">;
-def warn_sycl_kernel_has_ptr_param : Warning<
-  "pointers parameters in SYCL kernels must point to device-accessible memory, "
+def warn_sycl_kernel_param_has_ptr: Warning<
+  "pointer parameters in SYCL kernels must point to device-accessible memory, "
   "i.e. the USM">,
   InGroup<NonPortableSYCL>, DefaultIgnore;
 def err_sycl_kernel_param_has_vbase : Error<
   "%0 inherits virtual base classes and cannot be used as a SYCL kernel parameter">;
+def err_sycl_kernel_param_has_fam : Error<
+  "%0 contains a flexible array member and cannot be used as a SYCL kernel parameter">;
+def err_sycl_kernel_param_has_vmt : Error<
+  "%0 is a variably modified type and cannot be used as a SYCL kernel parameter">;
 
 // SYCL external attribute diagnostics
 def err_sycl_external_invalid_linkage : Error<
diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index 88b6aefa187f9..3eaaacefe396b 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -674,6 +674,16 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
                          const FieldDecl *>;
   SmallVector<ObjectAccess, 4> ObjectAccessPath;
 
+  std::pair<QualType, SourceLocation> getObjectAccessDebugInfo(ObjectAccess o) {
+      if (auto *PVD = dyn_cast<const ParmVarDecl *>(o))
+          return std::pair{PVD->getType(), PVD->getLocation()};
+      else if (auto *FD = dyn_cast<const FieldDecl *>(o))
+          return std::pair{FD->getType(), FD->getLocation()};
+      else if (auto *BS = dyn_cast<const CXXBaseSpecifier *>(o))
+          return std::pair{BS->getType(), BS->getBaseTypeLoc()};
+      llvm_unreachable("Unexpected type in ObjectAccess");
+  }
+
   void emitObjectAccessPathNotes() {
     for (auto Parent : llvm::reverse(ObjectAccessPath)) {
       if (auto *FD = Parent.dyn_cast<const FieldDecl *>()) {
@@ -751,29 +761,34 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
       IsValid = false;
       return false;
     } 
-    
-    Ty.dump();
-    if (Ty->isAtomicType() ||
-        Ty->isStructureTypeWithFlexibleArrayMember() ||
-        Ty->isVariablyModifiedType()) {
-
-      auto DirectParent = ObjectAccessPath.back();
-      auto *DirectFieldParent = cast<const FieldDecl *>(DirectParent);
-      SemaSYCLRef.Diag(DirectFieldParent->getLocation(),
-                       diag::err_bad_kernel_param_type)
-          << DirectFieldParent->getType();
+    if (Ty->isAtomicType()) {
+      const auto& [Type, Loc] = getObjectAccessDebugInfo(ObjectAccessPath.back());
+      SemaSYCLRef.Diag(Loc, diag::err_bad_kernel_param_type) << Type;
       emitObjectAccessPathNotes();
 
       IsValid = false;
       return false;
     } 
+    if (Ty->isVariablyModifiedType()) {
+      const auto& [Type, Loc] = getObjectAccessDebugInfo(ObjectAccessPath.back());
+      SemaSYCLRef.Diag(Loc, diag::err_sycl_kernel_param_has_vmt) << Type;
+      emitObjectAccessPathNotes();
+
+      IsValid = false;
+      return false;
+    }
+    if (Ty->isStructureTypeWithFlexibleArrayMember()) {
+      const auto& [Type, Loc] = getObjectAccessDebugInfo(ObjectAccessPath.back());
+      SemaSYCLRef.Diag(Loc, diag::err_sycl_kernel_param_has_fam) << Type;
+      emitObjectAccessPathNotes();
+
+      IsValid = false;
+      return false;
+    }
     if (CXXRecordDecl *RD = Ty->getAsCXXRecordDecl()) {
       if (RD->getNumVBases() > 0) {
-        auto DirectParent = ObjectAccessPath.back();
-        auto *DirectFieldParent = cast<const FieldDecl *>(DirectParent);
-        SemaSYCLRef.Diag(DirectFieldParent->getLocation(),
-                         diag::err_sycl_kernel_param_has_vbase)
-            << DirectFieldParent->getType();
+        const auto& [Type, Loc] = getObjectAccessDebugInfo(ObjectAccessPath.back());
+        SemaSYCLRef.Diag(Loc, diag::err_sycl_kernel_param_has_vbase) << Type;
         emitObjectAccessPathNotes();
 
         IsValid = false;
@@ -783,10 +798,8 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
     // Warn about pointer parameters in SYCL kernels if non-portable SYCL
     // warnings are enabled.
     if (Ty->isPointerType()) {
-      auto DirectParent = ObjectAccessPath.back();
-      auto *DirectFieldParent = cast<const FieldDecl *>(DirectParent);
-      SemaSYCLRef.Diag(DirectFieldParent->getLocation(),
-                   diag::warn_sycl_kernel_has_ptr_param);
+      const auto& [Type, Loc] = getObjectAccessDebugInfo(ObjectAccessPath.back());
+      SemaSYCLRef.Diag(Loc, diag::warn_sycl_kernel_param_has_ptr) << Type;
       emitObjectAccessPathNotes();
     }
     return true;
diff --git a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
index 5cbf6ce472ebc..a32c3f7c14355 100644
--- a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
@@ -1,7 +1,8 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -Wno-vla-cxx-extension -fsycl-is-host -verify %s
-// RUN: %clang_cc1 -triple spirv64 -std=c++17 -fsyntax-only -Wno-vla-cxx-extension -fsycl-is-device -verify %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -Wnonportable-sycl -Wno-vla-cxx-extension -fsycl-is-host -verify %s
+// RUN: %clang_cc1 -triple spirv64 -std=c++17 -fsyntax-only -Wnonportable-sycl -Wno-vla-cxx-extension -fsycl-is-device -verify %s
+
+// TODO conditionally toggle Wnonportable-sycl
 
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -Wnonportable-sycl -Wno-vla-cxx-extension -fsycl-is-host -verify=nonportable %s
 
 // A unique kernel name type is required for each declared kernel entry point.
 template<int, int = 0> struct KN;
@@ -192,123 +193,171 @@ void test() {
 
 #include <stdatomic.h>
 // Check for atomic parameters and subobjects.
-namespace badref8 {
+namespace atomic1 {
 // Kernel entry point template definition.
 template<typename KNT, typename T>
 [[clang::sycl_kernel_entry_point(KNT)]]
-void kernel_single_task(T t) {}
+void kernel_single_task(T t) {} // expected-note-re {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+                                // expected-note-re at -1 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+                                // expected-note-re at -2 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+                                // expected-note-re at -3 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+                                // expected-note at -4 {{within parameter 't' of type 'atomic1::Kernel' declared here}}
 
-struct S { 
+struct Sa { 
   int a;
-  _Atomic int b;
+  _Atomic int b; // expected-error {{'_Atomic(int)' cannot be used as the type of a kernel parameter}}
+                 // expected-note at -3 {{within field of type 'Sa' declared here}}
+                 // expected-error at -2 {{'_Atomic(int)' cannot be used as the type of a kernel parameter}}
+                 // expected-note at -5 {{within field of type 'Sa' declared here}}
+                 // expected-error at -4 {{'_Atomic(int)' cannot be used as the type of a kernel parameter}}
+                 // expected-note at -7 {{within field of type 'Sa' declared here}}
 };
 
 class Kernel {
-  S data{1, 2};
+  Sa data{1, 2}; // expected-note at -1 {{within field of type 'Kernel' declared here}}
 public:
   void operator()() { }
 };
 
 void test() {
   _Atomic int a = 0;
-  // TODO _Atomic(int) a
-  S s{1, 2};
-  S arr[] = {s, s};
+  _Atomic(int) b = 2;
+  Sa s{1, 2};
+  Sa arr[] = {s, s};
   kernel_single_task<class KN<15>>([=]{ (void)a; });
-  kernel_single_task<class KN<16>>([=]{ (void)s; });
-  kernel_single_task<class KN<17>>([=]{ (void)arr; });
-  kernel_single_task<class KN<18>>(Kernel());
-  // kernel_single_task<class KN<17>>([](S s) { (void)s; }); // DOESNT DETECT
+  // expected-error at -1 {{'_Atomic(int)' cannot be used as the type of a kernel parameter}}
+  // expected-note-re at -2 {{in instantiation of function template specialization 'atomic1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
+  // expected-note at -3 {{within capture 'a' of lambda expression here}}
+  kernel_single_task<class KN<16>>([=]{ (void)b; });
+  // expected-error at -1 {{'_Atomic(int)' cannot be used as the type of a kernel parameter}}
+  // expected-note-re at -2 {{in instantiation of function template specialization 'atomic1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
+  // expected-note at -3 {{within capture 'b' of lambda expression here}}
+  kernel_single_task<class KN<17>>([=]{ (void)s; });
+  // expected-note-re at -1 {{in instantiation of function template specialization 'atomic1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
+  // expected-note at -2 {{within capture 's' of lambda expression here}}
+  kernel_single_task<class KN<18>>([=]{ (void)arr; });
+  // expected-note-re at -1 {{in instantiation of function template specialization 'atomic1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
+  // expected-note at -2 {{within capture 'arr' of lambda expression here}}
+  kernel_single_task<class KN<19>>(Kernel());
+  // expected-note-re at -1 {{in instantiation of function template specialization 'atomic1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
 }
 
-} // namespace badref8
-
+} // namespace atomic1
 
 // Check for flexible array members -- would not be copyable to device
-namespace badref9 {
+namespace fam1 {
 // Kernel entry point template definition.
 template<typename KNT, typename T>
 [[clang::sycl_kernel_entry_point(KNT)]]
-void kernel_single_task(T t) {}
+void kernel_single_task(T t) {} // expected-note {{within parameter 't' of type 'fam1::Kernel' declared here}}
 
 struct FAM { 
   int a;
-  int[] b;
+  int b[];
 };
 
-class Kernel {
-  FAM fam;
+class Kernel { // expected-note {{within field of type 'Kernel' declared here}}
+  FAM fam; // expected-error {{'FAM' contains a flexible array member and cannot be used as a SYCL kernel parameter}}
 public:
-  void operator()() { }
+  void operator()() { (void)fam; }
 };
 
 void test() {
   FAM fam;
-  kernel_single_task<class KN<19>>([=]{ (void)fam; }); // DOESNT DETECT
-  kernel_single_task<class KN<20>>(Kernel());
+  //kernel_single_task<class KN<40>>([=]{ (void)fam; }); // Doesn't detect, FAM might not be capturable
+  //kernel_single_task<class KN<40>>([](FAM f){ (void)f; return 1; }(fam)); // DOESNT DETECT
+  //kernel_single_task<class KN<20>>([](FAM f){ return f; }(fam)); // not the result I'm looking for
+  kernel_single_task<class KN<21>>(Kernel());
+  // expected-note-re at -1 {{in instantiation of function template specialization 'fam1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
 }
 
-} // namespace badref9
+} // namespace fam1
 
 // Check for variable member array -- would not be copyable to device
-namespace badref10 {
+namespace vmt1 {
 // Kernel entry point template definition.
 template<typename KNT, typename T>
 [[clang::sycl_kernel_entry_point(KNT)]]
-void kernel_single_task(T t) {}
-
-class Kernel {
-  int len;
-public:
-  Kernel(int l, int vmt[n]) : len(l) {}
-  void operator()(int n, int vmt[n]) { (void)vmt; }
-};
+void kernel_single_task(T t) {} // expected-note-re {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
 
 void test() {
   int n;
   int Vmt[n];
-  kernel_single_task<class KN<21>>([=](int n, int vmt[n]) { (void)vmt; }); // DOESNT DETECT
-  kernel_single_task<class KN<22>>(Kernel()); // DOESNT DETECT
+  //kernel_single_task<class KN<22>>([](int n, int vmt[n]){ (void)vmt; }(n, Vmt)); // DOESNT DETECT
   kernel_single_task<class KN<23>>([=]{ (void)Vmt; });
+  // expected-error at -1 {{'int[n]' is a variably modified type and cannot be used as a SYCL kernel parameter}}
+  // expected-note-re at -2 {{in instantiation of function template specialization 'vmt1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
+  // expected-note at -3 {{within capture 'Vmt' of lambda expression here}}
+  // expected-error at -4 {{variable-sized object may not be initialized}}
 }
 
-} // namespace badref10
-
+} // namespace vmt1
+ 
 // Check for pointer parameters
 namespace nonportable1 {
 // Kernel entry point template definition.
 template<typename KNT, typename T>
 [[clang::sycl_kernel_entry_point(KNT)]]
-void kernel_single_task(T t) {}
+void kernel_single_task(T t) {} // expected-note-re {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+                                // expected-note-re at -1 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+                                // expected-note-re at -2 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+                                // expected-note-re at -3 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
 
-class Kernel {
+struct S { // expected-note {{within field of type 'S' declared here}}
+           // expected-note at -1 {{within field of type 'S' declared here}}
+  int *ptr;
+  // expected-warning at -1 {{pointer parameters in SYCL kernels must point to device-accessible memory, i.e. the USM}}
+  // expected-warning at -2 {{pointer parameters in SYCL kernels must point to device-accessible memory, i.e. the USM}}
+};
+
+class C { // expected-note {{within field of type 'C' declared here}}
+private:
+  int *ptr;
+  // expected-warning at -1 {{pointer parameters in SYCL kernels must point to device-accessible memory, i.e. the USM}}
+  // TODO double-check this warning actually tells me what field is the problem
 public:
-  void operator()(int *ptr) { (void)ptr; }
+  C(int *p) : ptr(p) {}
 };
 
 void test() {
   int *ptr;
-  kernel_single_task<class KN<24>>(Kernel()); // DOESNT DETECT
   kernel_single_task<class KN<25>>([=]{ (void)ptr; });
+  // expected-warning at -1 {{pointer parameters in SYCL kernels must point to device-accessible memory, i.e. the USM}}
+  // expected-note-re at -2 {{in instantiation of function template specialization 'nonportable1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
+  // expected-note at -3 {{within capture 'ptr' of lambda expression here}}
+
+  S s{ptr};
+  kernel_single_task<class KN<26>>([=]{ (void)s; });
+  // expected-note-re at -1 {{in instantiation of function template specialization 'nonportable1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
+  // expected-note at -2 {{within capture 's' of lambda expression here}}
+  
+  C c{ptr};
+  kernel_single_task<class KN<27>>([=]{ (void)c; });
+  // expected-note-re at -1 {{in instantiation of function template specialization 'nonportable1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
+  // expected-note at -2 {{within capture 'c' of lambda expression here}}
+  
+  S arr[3] = {{ptr}, {ptr}, {ptr}};
+  kernel_single_task<class KN<28>>([=]{ (void)arr; });
+  // expected-note-re at -1 {{in instantiation of function template specialization 'nonportable1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
+  // expected-note at -2 {{within capture 'arr' of lambda expression here}}
 }
 
 } // namespace nonportable1
 
-// Test virtual bases.
-// FIXME: explicitly diagnose virtual bases within kernel parameters.
-namespace badref5 {
+// Check for virtual bases
+namespace vbase1 {
 // Kernel entry point template definition.
 template<typename KNT, typename T>
 [[clang::sycl_kernel_entry_point(KNT)]]
-void kernel_single_task(T t) {} //expected-note {{within parameter 't' of type 'badref5::Derived' declared here}}
+void kernel_single_task(T t) {} // expected-note-re {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
 
-class Base { // expected-note {{within field of type 'Base' declared here}}}
-  int &data; // expected-error {{'int &' cannot be used as the type of a kernel parameter}}
+class Base { 
+  int &data; 
 public:
   Base(int &a) : data(a) {}
 };
 
-class Derived : virtual Base { // expected-note {{within base class of type 'Base' declared here}}
+class Derived : virtual Base { 
 public:
   Derived(int &a) : Base(a) {}
 
@@ -316,10 +365,15 @@ class Derived : virtual Base { // expected-note {{within base class of type 'Bas
 
 void test() {
   int p = 0;
-  kernel_single_task<class KN<12>>(Derived{p});
-  // expected-note-re at -1 {{in instantiation of function template specialization 'badref5::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
-
   Derived d{p};
-  kernel_single_task<class KN<30>>([=]{ (void)d; });
+  kernel_single_task<class KN<29>>([=]{ (void)d; });
+  // expected-error at -1 {{'Derived' inherits virtual base classes and cannot be used as a SYCL kernel parameter}}
+  // expected-note-re at -2 {{in instantiation of function template specialization 'vbase1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
+  // expected-note at -3 {{within capture 'd' of lambda expression here}}
+
+  //kernel_single_task<class KN<30>>([](Derived d){ return d; }(d)); // not sure if this gives me what I actually want
+  // xpected-error at -1 {{'vbase1::Derived' inherits virtual base classes and cannot be used as a SYCL kernel parameter}}
+  // xpected-note-re at -2 {{in instantiation of function template specialization 'vbase1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
+  // xpected-note at -3 {{within capture 'd' of lambda expression here}}
 }
-} // namespace badref5
+} // namespace vbase1

>From 8be6ac0148757871460c5ec8759261d41055e0ec Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Fri, 10 Jul 2026 10:08:14 -0700
Subject: [PATCH 04/19] Address issues with test coverage

---
 clang/lib/Sema/SemaSYCL.cpp                         | 13 +++++--------
 .../SemaSYCL/sycl-kernel-param-restrictions.cpp     | 13 +++++++------
 2 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index 3eaaacefe396b..a52a412fc174b 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -760,32 +760,28 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
       // invalid use of a reference.
       IsValid = false;
       return false;
-    } 
-    if (Ty->isAtomicType()) {
+    } else if (Ty->isAtomicType()) {
       const auto& [Type, Loc] = getObjectAccessDebugInfo(ObjectAccessPath.back());
       SemaSYCLRef.Diag(Loc, diag::err_bad_kernel_param_type) << Type;
       emitObjectAccessPathNotes();
 
       IsValid = false;
       return false;
-    } 
-    if (Ty->isVariablyModifiedType()) {
+    } else if (Ty->isVariablyModifiedType()) {
       const auto& [Type, Loc] = getObjectAccessDebugInfo(ObjectAccessPath.back());
       SemaSYCLRef.Diag(Loc, diag::err_sycl_kernel_param_has_vmt) << Type;
       emitObjectAccessPathNotes();
 
       IsValid = false;
       return false;
-    }
-    if (Ty->isStructureTypeWithFlexibleArrayMember()) {
+    } else if (Ty->isStructureTypeWithFlexibleArrayMember()) {
       const auto& [Type, Loc] = getObjectAccessDebugInfo(ObjectAccessPath.back());
       SemaSYCLRef.Diag(Loc, diag::err_sycl_kernel_param_has_fam) << Type;
       emitObjectAccessPathNotes();
 
       IsValid = false;
       return false;
-    }
-    if (CXXRecordDecl *RD = Ty->getAsCXXRecordDecl()) {
+    } else if (CXXRecordDecl *RD = Ty->getAsCXXRecordDecl()) {
       if (RD->getNumVBases() > 0) {
         const auto& [Type, Loc] = getObjectAccessDebugInfo(ObjectAccessPath.back());
         SemaSYCLRef.Diag(Loc, diag::err_sycl_kernel_param_has_vbase) << Type;
@@ -795,6 +791,7 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
         return false;
       }
     }
+    
     // Warn about pointer parameters in SYCL kernels if non-portable SYCL
     // warnings are enabled.
     if (Ty->isPointerType()) {
diff --git a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
index a32c3f7c14355..493c1843dca72 100644
--- a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
@@ -264,16 +264,14 @@ class Kernel { // expected-note {{within field of type 'Kernel' declared here}}
 
 void test() {
   FAM fam;
-  //kernel_single_task<class KN<40>>([=]{ (void)fam; }); // Doesn't detect, FAM might not be capturable
-  //kernel_single_task<class KN<40>>([](FAM f){ (void)f; return 1; }(fam)); // DOESNT DETECT
-  //kernel_single_task<class KN<20>>([](FAM f){ return f; }(fam)); // not the result I'm looking for
-  kernel_single_task<class KN<21>>(Kernel());
+  // Flexible array members cannot be captured in a lambda, thus no lambda tests are provided.
+  kernel_single_task<class KN<21>>(Kernel{});
   // expected-note-re at -1 {{in instantiation of function template specialization 'fam1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
 }
 
 } // namespace fam1
 
-// Check for variable member array -- would not be copyable to device
+// Check for variably modified types -- would not be copyable to device
 namespace vmt1 {
 // Kernel entry point template definition.
 template<typename KNT, typename T>
@@ -283,7 +281,10 @@ void kernel_single_task(T t) {} // expected-note-re {{within parameter 't' of ty
 void test() {
   int n;
   int Vmt[n];
-  //kernel_single_task<class KN<22>>([](int n, int vmt[n]){ (void)vmt; }(n, Vmt)); // DOESNT DETECT
+  // VLAs as parameters are lowered to a pointer, preventing test cases with VLAs
+  // as normal lambda parameters. The below test case uses technically invalid
+  // code, but preserves the VLA in the lambda.
+  // As a result, suppress "variable-sized object may not be initialized" is needed.
   kernel_single_task<class KN<23>>([=]{ (void)Vmt; });
   // expected-error at -1 {{'int[n]' is a variably modified type and cannot be used as a SYCL kernel parameter}}
   // expected-note-re at -2 {{in instantiation of function template specialization 'vmt1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}

>From dbb30993402cdb6ca9d8a78dd560e9d9d09c77f3 Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Fri, 10 Jul 2026 10:37:21 -0700
Subject: [PATCH 05/19] Clean up code

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 +-
 clang/lib/Sema/SemaSYCL.cpp                   | 46 ++++++++-----------
 .../sycl-kernel-param-restrictions.cpp        |  8 ++--
 3 files changed, 24 insertions(+), 32 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index bd59d17b08b12..eb28a6a4494d2 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13515,7 +13515,7 @@ def err_sycl_special_type_num_init_method : Error<
 def err_sycl_device_invalid_target : Error<
   "%0 is not a supported SYCL device target">;
 def warn_sycl_kernel_param_has_ptr: Warning<
-  "pointer parameters in SYCL kernels must point to device-accessible memory, "
+  "pointers used in SYCL kernels must point to device-accessible memory, "
   "i.e. the USM">,
   InGroup<NonPortableSYCL>, DefaultIgnore;
 def err_sycl_kernel_param_has_vbase : Error<
diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index a52a412fc174b..40ff8d35a4305 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -675,13 +675,13 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
   SmallVector<ObjectAccess, 4> ObjectAccessPath;
 
   std::pair<QualType, SourceLocation> getObjectAccessDebugInfo(ObjectAccess o) {
-      if (auto *PVD = dyn_cast<const ParmVarDecl *>(o))
-          return std::pair{PVD->getType(), PVD->getLocation()};
-      else if (auto *FD = dyn_cast<const FieldDecl *>(o))
-          return std::pair{FD->getType(), FD->getLocation()};
-      else if (auto *BS = dyn_cast<const CXXBaseSpecifier *>(o))
-          return std::pair{BS->getType(), BS->getBaseTypeLoc()};
-      llvm_unreachable("Unexpected type in ObjectAccess");
+    if (auto *PVD = dyn_cast<const ParmVarDecl *>(o))
+      return std::pair{PVD->getType(), PVD->getLocation()};
+    else if (auto *FD = dyn_cast<const FieldDecl *>(o))
+      return std::pair{FD->getType(), FD->getLocation()};
+    else if (auto *BS = dyn_cast<const CXXBaseSpecifier *>(o))
+      return std::pair{BS->getType(), BS->getBaseTypeLoc()};
+    llvm_unreachable("Unexpected type in ObjectAccess");
   }
 
   void emitObjectAccessPathNotes() {
@@ -738,6 +738,12 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
 
   // Returns true if subobjects should be visited and false otherwise.
   bool checkType(QualType Ty) {
+    auto emitDiagnostic = [this](clang::diag::kind mesg) {
+      const auto &[Type, Loc] =
+          getObjectAccessDebugInfo(ObjectAccessPath.back());
+      SemaSYCLRef.Diag(Loc, mesg) << Type;
+      emitObjectAccessPathNotes();
+    };
     if (Ty->isReferenceType()) {
       auto DirectParent = ObjectAccessPath.back();
       // Reference cannot be a base, so just assume we came via a FieldDecl.
@@ -761,43 +767,29 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
       IsValid = false;
       return false;
     } else if (Ty->isAtomicType()) {
-      const auto& [Type, Loc] = getObjectAccessDebugInfo(ObjectAccessPath.back());
-      SemaSYCLRef.Diag(Loc, diag::err_bad_kernel_param_type) << Type;
-      emitObjectAccessPathNotes();
-
+      emitDiagnostic(diag::err_bad_kernel_param_type);
       IsValid = false;
       return false;
     } else if (Ty->isVariablyModifiedType()) {
-      const auto& [Type, Loc] = getObjectAccessDebugInfo(ObjectAccessPath.back());
-      SemaSYCLRef.Diag(Loc, diag::err_sycl_kernel_param_has_vmt) << Type;
-      emitObjectAccessPathNotes();
-
+      emitDiagnostic(diag::err_sycl_kernel_param_has_vmt);
       IsValid = false;
       return false;
     } else if (Ty->isStructureTypeWithFlexibleArrayMember()) {
-      const auto& [Type, Loc] = getObjectAccessDebugInfo(ObjectAccessPath.back());
-      SemaSYCLRef.Diag(Loc, diag::err_sycl_kernel_param_has_fam) << Type;
-      emitObjectAccessPathNotes();
-
+      emitDiagnostic(diag::err_sycl_kernel_param_has_fam);
       IsValid = false;
       return false;
     } else if (CXXRecordDecl *RD = Ty->getAsCXXRecordDecl()) {
       if (RD->getNumVBases() > 0) {
-        const auto& [Type, Loc] = getObjectAccessDebugInfo(ObjectAccessPath.back());
-        SemaSYCLRef.Diag(Loc, diag::err_sycl_kernel_param_has_vbase) << Type;
-        emitObjectAccessPathNotes();
-
+        emitDiagnostic(diag::err_sycl_kernel_param_has_vbase);
         IsValid = false;
         return false;
       }
     }
-    
+
     // Warn about pointer parameters in SYCL kernels if non-portable SYCL
     // warnings are enabled.
     if (Ty->isPointerType()) {
-      const auto& [Type, Loc] = getObjectAccessDebugInfo(ObjectAccessPath.back());
-      SemaSYCLRef.Diag(Loc, diag::warn_sycl_kernel_param_has_ptr) << Type;
-      emitObjectAccessPathNotes();
+      emitDiagnostic(diag::warn_sycl_kernel_param_has_ptr);
     }
     return true;
   }
diff --git a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
index 493c1843dca72..c0b3d910bc7d9 100644
--- a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
@@ -307,14 +307,14 @@ void kernel_single_task(T t) {} // expected-note-re {{within parameter 't' of ty
 struct S { // expected-note {{within field of type 'S' declared here}}
            // expected-note at -1 {{within field of type 'S' declared here}}
   int *ptr;
-  // expected-warning at -1 {{pointer parameters in SYCL kernels must point to device-accessible memory, i.e. the USM}}
-  // expected-warning at -2 {{pointer parameters in SYCL kernels must point to device-accessible memory, i.e. the USM}}
+  // expected-warning at -1 {{pointers used in SYCL kernels must point to device-accessible memory, i.e. the USM}}
+  // expected-warning at -2 {{pointers used in SYCL kernels must point to device-accessible memory, i.e. the USM}}
 };
 
 class C { // expected-note {{within field of type 'C' declared here}}
 private:
   int *ptr;
-  // expected-warning at -1 {{pointer parameters in SYCL kernels must point to device-accessible memory, i.e. the USM}}
+  // expected-warning at -1 {{pointers used in SYCL kernels must point to device-accessible memory, i.e. the USM}}
   // TODO double-check this warning actually tells me what field is the problem
 public:
   C(int *p) : ptr(p) {}
@@ -323,7 +323,7 @@ class C { // expected-note {{within field of type 'C' declared here}}
 void test() {
   int *ptr;
   kernel_single_task<class KN<25>>([=]{ (void)ptr; });
-  // expected-warning at -1 {{pointer parameters in SYCL kernels must point to device-accessible memory, i.e. the USM}}
+  // expected-warning at -1 {{pointers used in SYCL kernels must point to device-accessible memory, i.e. the USM}}
   // expected-note-re at -2 {{in instantiation of function template specialization 'nonportable1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
   // expected-note at -3 {{within capture 'ptr' of lambda expression here}}
 

>From b2815bd778bebeb35e45e56d2be949abc204893a Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Fri, 10 Jul 2026 11:33:19 -0700
Subject: [PATCH 06/19] remove VMT checks, split nonportable lit test into
 separate run

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 -
 clang/lib/Sema/SemaSYCL.cpp                   |  4 --
 .../sycl-kernel-param-restrictions.cpp        | 70 ++++++-------------
 3 files changed, 23 insertions(+), 53 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index eb28a6a4494d2..784bde5d68f08 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13522,8 +13522,6 @@ def err_sycl_kernel_param_has_vbase : Error<
   "%0 inherits virtual base classes and cannot be used as a SYCL kernel parameter">;
 def err_sycl_kernel_param_has_fam : Error<
   "%0 contains a flexible array member and cannot be used as a SYCL kernel parameter">;
-def err_sycl_kernel_param_has_vmt : Error<
-  "%0 is a variably modified type and cannot be used as a SYCL kernel parameter">;
 
 // SYCL external attribute diagnostics
 def err_sycl_external_invalid_linkage : Error<
diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index 40ff8d35a4305..47e41d81b9a68 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -770,10 +770,6 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
       emitDiagnostic(diag::err_bad_kernel_param_type);
       IsValid = false;
       return false;
-    } else if (Ty->isVariablyModifiedType()) {
-      emitDiagnostic(diag::err_sycl_kernel_param_has_vmt);
-      IsValid = false;
-      return false;
     } else if (Ty->isStructureTypeWithFlexibleArrayMember()) {
       emitDiagnostic(diag::err_sycl_kernel_param_has_fam);
       IsValid = false;
diff --git a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
index c0b3d910bc7d9..120b00e1fc41f 100644
--- a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
@@ -1,8 +1,7 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -Wnonportable-sycl -Wno-vla-cxx-extension -fsycl-is-host -verify %s
-// RUN: %clang_cc1 -triple spirv64 -std=c++17 -fsyntax-only -Wnonportable-sycl -Wno-vla-cxx-extension -fsycl-is-device -verify %s
-
-// TODO conditionally toggle Wnonportable-sycl
-
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -Wno-vla-cxx-extension -fsycl-is-host -verify=expected %s
+// RUN: %clang_cc1 -triple spirv64 -std=c++17 -fsyntax-only -Wno-vla-cxx-extension -fsycl-is-device -verify=expected %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -Wnonportable-sycl -Wno-vla-cxx-extension -fsycl-is-host -verify=expected,nonportable %s
+// RUN: %clang_cc1 -triple spirv64 -std=c++17 -fsyntax-only -Wnonportable-sycl -Wno-vla-cxx-extension -fsycl-is-device -verify=expected,nonportable %s
 
 // A unique kernel name type is required for each declared kernel entry point.
 template<int, int = 0> struct KN;
@@ -271,50 +270,27 @@ void test() {
 
 } // namespace fam1
 
-// Check for variably modified types -- would not be copyable to device
-namespace vmt1 {
-// Kernel entry point template definition.
-template<typename KNT, typename T>
-[[clang::sycl_kernel_entry_point(KNT)]]
-void kernel_single_task(T t) {} // expected-note-re {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
-
-void test() {
-  int n;
-  int Vmt[n];
-  // VLAs as parameters are lowered to a pointer, preventing test cases with VLAs
-  // as normal lambda parameters. The below test case uses technically invalid
-  // code, but preserves the VLA in the lambda.
-  // As a result, suppress "variable-sized object may not be initialized" is needed.
-  kernel_single_task<class KN<23>>([=]{ (void)Vmt; });
-  // expected-error at -1 {{'int[n]' is a variably modified type and cannot be used as a SYCL kernel parameter}}
-  // expected-note-re at -2 {{in instantiation of function template specialization 'vmt1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
-  // expected-note at -3 {{within capture 'Vmt' of lambda expression here}}
-  // expected-error at -4 {{variable-sized object may not be initialized}}
-}
-
-} // namespace vmt1
- 
 // Check for pointer parameters
 namespace nonportable1 {
 // Kernel entry point template definition.
 template<typename KNT, typename T>
 [[clang::sycl_kernel_entry_point(KNT)]]
-void kernel_single_task(T t) {} // expected-note-re {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
-                                // expected-note-re at -1 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
-                                // expected-note-re at -2 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
-                                // expected-note-re at -3 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+void kernel_single_task(T t) {} // nonportable-note-re {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+                                // nonportable-note-re at -1 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+                                // nonportable-note-re at -2 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+                                // nonportable-note-re at -3 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
 
-struct S { // expected-note {{within field of type 'S' declared here}}
-           // expected-note at -1 {{within field of type 'S' declared here}}
+struct S { // nonportable-note {{within field of type 'S' declared here}}
+           // nonportable-note at -1 {{within field of type 'S' declared here}}
   int *ptr;
-  // expected-warning at -1 {{pointers used in SYCL kernels must point to device-accessible memory, i.e. the USM}}
-  // expected-warning at -2 {{pointers used in SYCL kernels must point to device-accessible memory, i.e. the USM}}
+  // nonportable-warning at -1 {{pointers used in SYCL kernels must point to device-accessible memory, i.e. the USM}}
+  // nonportable-warning at -2 {{pointers used in SYCL kernels must point to device-accessible memory, i.e. the USM}}
 };
 
-class C { // expected-note {{within field of type 'C' declared here}}
+class C { // nonportable-note {{within field of type 'C' declared here}}
 private:
   int *ptr;
-  // expected-warning at -1 {{pointers used in SYCL kernels must point to device-accessible memory, i.e. the USM}}
+  // nonportable-warning at -1 {{pointers used in SYCL kernels must point to device-accessible memory, i.e. the USM}}
   // TODO double-check this warning actually tells me what field is the problem
 public:
   C(int *p) : ptr(p) {}
@@ -323,24 +299,24 @@ class C { // expected-note {{within field of type 'C' declared here}}
 void test() {
   int *ptr;
   kernel_single_task<class KN<25>>([=]{ (void)ptr; });
-  // expected-warning at -1 {{pointers used in SYCL kernels must point to device-accessible memory, i.e. the USM}}
-  // expected-note-re at -2 {{in instantiation of function template specialization 'nonportable1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
-  // expected-note at -3 {{within capture 'ptr' of lambda expression here}}
+  // nonportable-warning at -1 {{pointers used in SYCL kernels must point to device-accessible memory, i.e. the USM}}
+  // nonportable-note-re at -2 {{in instantiation of function template specialization 'nonportable1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
+  // nonportable-note at -3 {{within capture 'ptr' of lambda expression here}}
 
   S s{ptr};
   kernel_single_task<class KN<26>>([=]{ (void)s; });
-  // expected-note-re at -1 {{in instantiation of function template specialization 'nonportable1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
-  // expected-note at -2 {{within capture 's' of lambda expression here}}
+  // nonportable-note-re at -1 {{in instantiation of function template specialization 'nonportable1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
+  // nonportable-note at -2 {{within capture 's' of lambda expression here}}
   
   C c{ptr};
   kernel_single_task<class KN<27>>([=]{ (void)c; });
-  // expected-note-re at -1 {{in instantiation of function template specialization 'nonportable1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
-  // expected-note at -2 {{within capture 'c' of lambda expression here}}
+  // nonportable-note-re at -1 {{in instantiation of function template specialization 'nonportable1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
+  // nonportable-note at -2 {{within capture 'c' of lambda expression here}}
   
   S arr[3] = {{ptr}, {ptr}, {ptr}};
   kernel_single_task<class KN<28>>([=]{ (void)arr; });
-  // expected-note-re at -1 {{in instantiation of function template specialization 'nonportable1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
-  // expected-note at -2 {{within capture 'arr' of lambda expression here}}
+  // nonportable-note-re at -1 {{in instantiation of function template specialization 'nonportable1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
+  // nonportable-note at -2 {{within capture 'arr' of lambda expression here}}
 }
 
 } // namespace nonportable1

>From e868ab9f789f6ff79395eca98bfd783b9e34d39a Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Fri, 10 Jul 2026 11:38:10 -0700
Subject: [PATCH 07/19] Make formatting conform

---
 clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
index 120b00e1fc41f..80235804b2b26 100644
--- a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
@@ -237,7 +237,7 @@ void test() {
   kernel_single_task<class KN<18>>([=]{ (void)arr; });
   // expected-note-re at -1 {{in instantiation of function template specialization 'atomic1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
   // expected-note at -2 {{within capture 'arr' of lambda expression here}}
-  kernel_single_task<class KN<19>>(Kernel());
+  kernel_single_task<class KN<19>>(Kernel{});
   // expected-note-re at -1 {{in instantiation of function template specialization 'atomic1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
 }
 

>From 0abd5060f208a3bd3188afe5c883937b52ef164f Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Mon, 13 Jul 2026 09:50:58 -0700
Subject: [PATCH 08/19] Remove leftover todos

---
 clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
index 80235804b2b26..52b683c017a7b 100644
--- a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
@@ -291,7 +291,6 @@ class C { // nonportable-note {{within field of type 'C' declared here}}
 private:
   int *ptr;
   // nonportable-warning at -1 {{pointers used in SYCL kernels must point to device-accessible memory, i.e. the USM}}
-  // TODO double-check this warning actually tells me what field is the problem
 public:
   C(int *p) : ptr(p) {}
 };
@@ -347,10 +346,5 @@ void test() {
   // expected-error at -1 {{'Derived' inherits virtual base classes and cannot be used as a SYCL kernel parameter}}
   // expected-note-re at -2 {{in instantiation of function template specialization 'vbase1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
   // expected-note at -3 {{within capture 'd' of lambda expression here}}
-
-  //kernel_single_task<class KN<30>>([](Derived d){ return d; }(d)); // not sure if this gives me what I actually want
-  // xpected-error at -1 {{'vbase1::Derived' inherits virtual base classes and cannot be used as a SYCL kernel parameter}}
-  // xpected-note-re at -2 {{in instantiation of function template specialization 'vbase1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
-  // xpected-note at -3 {{within capture 'd' of lambda expression here}}
 }
 } // namespace vbase1

>From 5686ff791c2d8729c0bb3b82ac9c3a3df95e98fb Mon Sep 17 00:00:00 2001
From: Ian Li <ianayl.work at gmail.com>
Date: Wed, 15 Jul 2026 13:50:27 -0400
Subject: [PATCH 09/19] Apply suggestions from code review

Co-authored-by: Tom Honermann <tom at honermann.net>
---
 clang/include/clang/Basic/DiagnosticSemaKinds.td       | 3 +--
 clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp | 2 ++
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 784bde5d68f08..a78af3bb06362 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13515,8 +13515,7 @@ def err_sycl_special_type_num_init_method : Error<
 def err_sycl_device_invalid_target : Error<
   "%0 is not a supported SYCL device target">;
 def warn_sycl_kernel_param_has_ptr: Warning<
-  "pointers used in SYCL kernels must point to device-accessible memory, "
-  "i.e. the USM">,
+  "pointers used in parameters to a SYCL kernel require a device that supports Unified Shared Memory (USM)">,
   InGroup<NonPortableSYCL>, DefaultIgnore;
 def err_sycl_kernel_param_has_vbase : Error<
   "%0 inherits virtual base classes and cannot be used as a SYCL kernel parameter">;
diff --git a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
index 52b683c017a7b..452ce7660e017 100644
--- a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
@@ -328,6 +328,8 @@ template<typename KNT, typename T>
 void kernel_single_task(T t) {} // expected-note-re {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
 
 class Base { 
+  // No diagnostic is issued for data because recursive subobject visitation
+  // stops once a virtual base class is found.
   int &data; 
 public:
   Base(int &a) : data(a) {}

>From 495c34617381a98e3bc7919c50faf873d534600d Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Thu, 16 Jul 2026 13:52:44 -0700
Subject: [PATCH 10/19] Address reviewer comments

---
 .../clang/Basic/DiagnosticSemaKinds.td        | 15 +--
 clang/lib/Sema/SemaSYCL.cpp                   | 52 +++++-----
 .../sycl-kernel-param-restrictions.cpp        | 95 ++++++++++++-------
 3 files changed, 100 insertions(+), 62 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a78af3bb06362..9a355e85ce46f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13514,13 +13514,6 @@ def err_sycl_special_type_num_init_method : Error<
   "method defined">;
 def err_sycl_device_invalid_target : Error<
   "%0 is not a supported SYCL device target">;
-def warn_sycl_kernel_param_has_ptr: Warning<
-  "pointers used in parameters to a SYCL kernel require a device that supports Unified Shared Memory (USM)">,
-  InGroup<NonPortableSYCL>, DefaultIgnore;
-def err_sycl_kernel_param_has_vbase : Error<
-  "%0 inherits virtual base classes and cannot be used as a SYCL kernel parameter">;
-def err_sycl_kernel_param_has_fam : Error<
-  "%0 contains a flexible array member and cannot be used as a SYCL kernel parameter">;
 
 // SYCL external attribute diagnostics
 def err_sycl_external_invalid_linkage : Error<
@@ -13581,6 +13574,14 @@ def note_sycl_kernel_launch_overload_resolution_here : Note<
 def err_sycl_entry_point_device_use : Error<
   "function %0 cannot be used in device code because it is declared with the"
   " %1 attribute">;
+def warn_sycl_kernel_param_has_ptr: Warning<
+  "pointers used in parameters to a SYCL kernel require a device that supports Unified Shared Memory (USM)">,
+  InGroup<NonPortableSYCL>, DefaultIgnore;
+def err_sycl_kernel_bad_param_type : Error<
+  "%0 %enum_select<InvalidSYCLKernelParamReason>{"
+      "%FAM{contains a flexible array member}|"
+      "%VirtualBases{inherits virtual base classes}|"
+      "}1 and cannot be used as the type of a SYCL kernel parameter">;
 
 def warn_cuda_maxclusterrank_sm_90 : Warning<
   "maxclusterrank requires sm_90 or higher, CUDA arch provided: %0, ignoring "
diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index 47e41d81b9a68..b8b26588e004f 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -674,13 +674,19 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
                          const FieldDecl *>;
   SmallVector<ObjectAccess, 4> ObjectAccessPath;
 
-  std::pair<QualType, SourceLocation> getObjectAccessDebugInfo(ObjectAccess o) {
+  struct DiagDetails {
+    QualType Type;
+    SourceLocation Loc;
+  };
+
+  // Return diagnostics info for an 'ObjectAccess' stored on ObjectAccessPath
+  DiagDetails getObjectAccessDiagDetails(ObjectAccess o) {
     if (auto *PVD = dyn_cast<const ParmVarDecl *>(o))
-      return std::pair{PVD->getType(), PVD->getLocation()};
-    else if (auto *FD = dyn_cast<const FieldDecl *>(o))
-      return std::pair{FD->getType(), FD->getLocation()};
-    else if (auto *BS = dyn_cast<const CXXBaseSpecifier *>(o))
-      return std::pair{BS->getType(), BS->getBaseTypeLoc()};
+      return {PVD->getType(), PVD->getLocation()};
+    if (auto *FD = dyn_cast<const FieldDecl *>(o))
+      return {FD->getType(), FD->getLocation()};
+    if (auto *BS = dyn_cast<const CXXBaseSpecifier *>(o))
+      return {BS->getType(), BS->getBaseTypeLoc()};
     llvm_unreachable("Unexpected type in ObjectAccess");
   }
 
@@ -738,12 +744,14 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
 
   // Returns true if subobjects should be visited and false otherwise.
   bool checkType(QualType Ty) {
-    auto emitDiagnostic = [this](clang::diag::kind mesg) {
-      const auto &[Type, Loc] =
-          getObjectAccessDebugInfo(ObjectAccessPath.back());
-      SemaSYCLRef.Diag(Loc, mesg) << Type;
+    auto emitDiagnostic = [this](clang::diag::kind DiagId, auto &&...Args) {
+      const auto &DiagDetail =
+          getObjectAccessDiagDetails(ObjectAccessPath.back());
+      (SemaSYCLRef.Diag(DiagDetail.Loc, DiagId)
+       << DiagDetail.Type << ... << Args);
       emitObjectAccessPathNotes();
     };
+
     if (Ty->isReferenceType()) {
       auto DirectParent = ObjectAccessPath.back();
       // Reference cannot be a base, so just assume we came via a FieldDecl.
@@ -754,34 +762,34 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
         return true;
       }
 
-      auto *DirectFieldParent = cast<const FieldDecl *>(DirectParent);
-      SemaSYCLRef.Diag(DirectFieldParent->getLocation(),
-                       diag::err_bad_kernel_param_type)
-          << DirectFieldParent->getType();
-      emitObjectAccessPathNotes();
-
       // Don't visit the type of the reference since any further invalid
       // kernel parameter types contained within the referenced type
       // might not be relevant once the programmer addresses the
       // invalid use of a reference.
+      emitDiagnostic(diag::err_bad_kernel_param_type);
       IsValid = false;
       return false;
-    } else if (Ty->isAtomicType()) {
+    }
+    if (Ty->isAtomicType()) {
       emitDiagnostic(diag::err_bad_kernel_param_type);
       IsValid = false;
       return false;
-    } else if (Ty->isStructureTypeWithFlexibleArrayMember()) {
-      emitDiagnostic(diag::err_sycl_kernel_param_has_fam);
+    }
+    if (Ty->isStructureTypeWithFlexibleArrayMember()) {
+      emitDiagnostic(diag::err_sycl_kernel_bad_param_type,
+                     diag::InvalidSYCLKernelParamReason::FAM);
       IsValid = false;
       return false;
-    } else if (CXXRecordDecl *RD = Ty->getAsCXXRecordDecl()) {
+    }
+    // Disallow classes that inherit virtual base classes.
+    if (CXXRecordDecl *RD = Ty->getAsCXXRecordDecl()) {
       if (RD->getNumVBases() > 0) {
-        emitDiagnostic(diag::err_sycl_kernel_param_has_vbase);
+        emitDiagnostic(diag::err_sycl_kernel_bad_param_type,
+                       diag::InvalidSYCLKernelParamReason::VirtualBases);
         IsValid = false;
         return false;
       }
     }
-
     // Warn about pointer parameters in SYCL kernels if non-portable SYCL
     // warnings are enabled.
     if (Ty->isPointerType()) {
diff --git a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
index 452ce7660e017..01768bc79b52b 100644
--- a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
@@ -122,7 +122,7 @@ template <typename T> class Callable { // expected-note 2{{within field of type
   T data; // expected-error 2{{'int &' cannot be used as the type of a kernel parameter}}
 public:
   Callable(T d) : data(d) {}
-  void operator()() {
+  void operator()() const {
   }
 };
 
@@ -190,32 +190,34 @@ void test() {
 } // namespace badref7
 
 
-#include <stdatomic.h>
 // Check for atomic parameters and subobjects.
 namespace atomic1 {
 // Kernel entry point template definition.
 template<typename KNT, typename T>
 [[clang::sycl_kernel_entry_point(KNT)]]
-void kernel_single_task(T t) {} // expected-note-re {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
-                                // expected-note-re at -1 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
-                                // expected-note-re at -2 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
-                                // expected-note-re at -3 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
-                                // expected-note at -4 {{within parameter 't' of type 'atomic1::Kernel' declared here}}
+void kernel_single_task(T t) {}
+// expected-note-re at -1 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+// expected-note-re at -2 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+// expected-note-re at -3 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+// expected-note-re at -4 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+// expected-note at -5 {{within parameter 't' of type 'atomic1::Kernel' declared here}}
 
 struct Sa { 
   int a;
-  _Atomic int b; // expected-error {{'_Atomic(int)' cannot be used as the type of a kernel parameter}}
-                 // expected-note at -3 {{within field of type 'Sa' declared here}}
-                 // expected-error at -2 {{'_Atomic(int)' cannot be used as the type of a kernel parameter}}
-                 // expected-note at -5 {{within field of type 'Sa' declared here}}
-                 // expected-error at -4 {{'_Atomic(int)' cannot be used as the type of a kernel parameter}}
-                 // expected-note at -7 {{within field of type 'Sa' declared here}}
+  _Atomic int b; 
+  // expected-error at -1 {{'_Atomic(int)' cannot be used as the type of a kernel parameter}}
+  // expected-note at -4 {{within field of type 'Sa' declared here}}
+  // expected-error at -3 {{'_Atomic(int)' cannot be used as the type of a kernel parameter}}
+  // expected-note at -6 {{within field of type 'Sa' declared here}}
+  // expected-error at -5 {{'_Atomic(int)' cannot be used as the type of a kernel parameter}}
+  // expected-note at -8 {{within field of type 'Sa' declared here}}
 };
 
 class Kernel {
-  Sa data{1, 2}; // expected-note at -1 {{within field of type 'Kernel' declared here}}
+  // expected-note at -1 {{within field of type 'Kernel' declared here}}
+  Sa data{1, 2};
 public:
-  void operator()() { }
+  void operator()() const { }
 };
 
 void test() {
@@ -248,17 +250,20 @@ namespace fam1 {
 // Kernel entry point template definition.
 template<typename KNT, typename T>
 [[clang::sycl_kernel_entry_point(KNT)]]
-void kernel_single_task(T t) {} // expected-note {{within parameter 't' of type 'fam1::Kernel' declared here}}
+void kernel_single_task(T t) {}
+// expected-note at -1 {{within parameter 't' of type 'fam1::Kernel' declared here}}
 
 struct FAM { 
   int a;
   int b[];
 };
 
-class Kernel { // expected-note {{within field of type 'Kernel' declared here}}
-  FAM fam; // expected-error {{'FAM' contains a flexible array member and cannot be used as a SYCL kernel parameter}}
+class Kernel {
+// expected-note at -1 {{within field of type 'Kernel' declared here}}
+  FAM fam;
+  // expected-error at -1 {{'FAM' contains a flexible array member and cannot be used as the type of a SYCL kernel parameter}}
 public:
-  void operator()() { (void)fam; }
+  void operator()() const { (void)fam; }
 };
 
 void test() {
@@ -275,22 +280,25 @@ namespace nonportable1 {
 // Kernel entry point template definition.
 template<typename KNT, typename T>
 [[clang::sycl_kernel_entry_point(KNT)]]
-void kernel_single_task(T t) {} // nonportable-note-re {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
-                                // nonportable-note-re at -1 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
-                                // nonportable-note-re at -2 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
-                                // nonportable-note-re at -3 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
-
-struct S { // nonportable-note {{within field of type 'S' declared here}}
-           // nonportable-note at -1 {{within field of type 'S' declared here}}
+void kernel_single_task(T t) {} 
+// nonportable-note-re at -1 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}                                  
+// nonportable-note-re at -2 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}                               
+// nonportable-note-re at -3 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}                               
+// nonportable-note-re at -4 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+
+struct S {
+// nonportable-note at -1 {{within field of type 'S' declared here}}
+// nonportable-note at -2 {{within field of type 'S' declared here}}          
   int *ptr;
-  // nonportable-warning at -1 {{pointers used in SYCL kernels must point to device-accessible memory, i.e. the USM}}
-  // nonportable-warning at -2 {{pointers used in SYCL kernels must point to device-accessible memory, i.e. the USM}}
+  // nonportable-warning at -1 {{pointers used in parameters to a SYCL kernel require a device that supports Unified Shared Memory (USM)}}
+  // nonportable-warning at -2 {{pointers used in parameters to a SYCL kernel require a device that supports Unified Shared Memory (USM)}}
 };
 
-class C { // nonportable-note {{within field of type 'C' declared here}}
+class C {
+// nonportable-note at -1 {{within field of type 'C' declared here}}
 private:
   int *ptr;
-  // nonportable-warning at -1 {{pointers used in SYCL kernels must point to device-accessible memory, i.e. the USM}}
+  // nonportable-warning at -1 {{pointers used in parameters to a SYCL kernel require a device that supports Unified Shared Memory (USM)}}
 public:
   C(int *p) : ptr(p) {}
 };
@@ -298,7 +306,7 @@ class C { // nonportable-note {{within field of type 'C' declared here}}
 void test() {
   int *ptr;
   kernel_single_task<class KN<25>>([=]{ (void)ptr; });
-  // nonportable-warning at -1 {{pointers used in SYCL kernels must point to device-accessible memory, i.e. the USM}}
+  // nonportable-warning at -1 {{pointers used in parameters to a SYCL kernel require a device that supports Unified Shared Memory (USM)}}
   // nonportable-note-re at -2 {{in instantiation of function template specialization 'nonportable1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
   // nonportable-note at -3 {{within capture 'ptr' of lambda expression here}}
 
@@ -325,7 +333,9 @@ namespace vbase1 {
 // Kernel entry point template definition.
 template<typename KNT, typename T>
 [[clang::sycl_kernel_entry_point(KNT)]]
-void kernel_single_task(T t) {} // expected-note-re {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+void kernel_single_task(T t) {}
+// expected-note-re at -1 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+// expected-note-re at -2 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
 
 class Base { 
   // No diagnostic is issued for data because recursive subobject visitation
@@ -338,15 +348,34 @@ class Base {
 class Derived : virtual Base { 
 public:
   Derived(int &a) : Base(a) {}
+};
 
+class A : public virtual Base { 
+public:
+  A(int &a) : Base(a) {}
+};
+
+class B : public virtual Base { 
+public:
+  B(int &a) : Base(a) {}
+};
+
+class Diamond : public A, public B {
+public:
+  Diamond(int &a) : Base(a), A(a), B(a) {}
 };
 
 void test() {
   int p = 0;
   Derived d{p};
   kernel_single_task<class KN<29>>([=]{ (void)d; });
-  // expected-error at -1 {{'Derived' inherits virtual base classes and cannot be used as a SYCL kernel parameter}}
+  // expected-error at -1 {{'Derived' inherits virtual base classes and cannot be used as the type of a SYCL kernel parameter}}
   // expected-note-re at -2 {{in instantiation of function template specialization 'vbase1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
   // expected-note at -3 {{within capture 'd' of lambda expression here}}
+  Diamond ab{p};
+  kernel_single_task<class KN<30>>([=]{ (void)ab; });
+  // expected-error at -1 {{'Diamond' inherits virtual base classes and cannot be used as the type of a SYCL kernel parameter}}
+  // expected-note-re at -2 {{in instantiation of function template specialization 'vbase1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
+  // expected-note at -3 {{within capture 'ab' of lambda expression here}}
 }
 } // namespace vbase1

>From f547989362f2e5ce3b4b1ca4d0a4c82a993c5c4d Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Fri, 17 Jul 2026 09:13:34 -0700
Subject: [PATCH 11/19] Oh...

---
 clang/lib/Sema/SemaSYCL.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index b8b26588e004f..b4145893b0b2d 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -747,8 +747,10 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
     auto emitDiagnostic = [this](clang::diag::kind DiagId, auto &&...Args) {
       const auto &DiagDetail =
           getObjectAccessDiagDetails(ObjectAccessPath.back());
-      (SemaSYCLRef.Diag(DiagDetail.Loc, DiagId)
-       << DiagDetail.Type << ... << Args);
+      {
+        auto DB = SemaSYCLRef.Diag(DiagDetail.Loc, DiagId) << DiagDetail.Type;
+        (void)(DB << ... << Args);
+      }
       emitObjectAccessPathNotes();
     };
 

>From d6371ce2ee8619282c85a426e7f6175eda695c3f Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Wed, 5 Aug 2026 16:17:04 -0700
Subject: [PATCH 12/19] Improve diagnostics printing

---
 .../clang/Basic/DiagnosticSemaKinds.td        | 13 +++-
 clang/lib/Sema/SemaSYCL.cpp                   | 72 +++++++++++++++----
 .../sycl-kernel-param-restrictions.cpp        | 45 ++++++------
 3 files changed, 91 insertions(+), 39 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a68a226ee97c4..f89de5e355d19 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13584,10 +13584,17 @@ def warn_sycl_kernel_param_has_ptr: Warning<
   "pointers used in parameters to a SYCL kernel require a device that supports Unified Shared Memory (USM)">,
   InGroup<NonPortableSYCL>, DefaultIgnore;
 def err_sycl_kernel_bad_param_type : Error<
-  "%0 %enum_select<InvalidSYCLKernelParamReason>{"
+  "%0 type cannot be used in a SYCL kernel parameter because it "
+      "%enum_select<InvalidSYCLKernelParamReason>{"
+      "%ReferenceType{is a reference type}|"
+      "%AtomicType{is an atomic type}|"
       "%FAM{contains a flexible array member}|"
-      "%VirtualBases{inherits virtual base classes}|"
-      "}1 and cannot be used as the type of a SYCL kernel parameter">;
+      "%VirtualBase{inherits a virtual base class}|"
+      "}1">;
+def note_flexible_array_member_defined_here : Note<
+  "flexible array member %0 of %1 defined here">;
+def note_vbase_specifier_here : Note<
+  "%0 inherits virtual base class %1 specified here">;
 
 def warn_cuda_maxclusterrank_sm_90 : Warning<
   "maxclusterrank requires sm_90 or higher, CUDA arch provided: %0, ignoring "
diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index b4145893b0b2d..8faeb339652e0 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -715,6 +715,32 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
     }
   }
 
+  template<typename Func, typename ...Ts>
+  void emitDiagnosticImpl(clang::diag::kind DiagId, Func additionalDiagnostics,
+    Ts &&...Args) {
+    const auto &Detail =
+        getObjectAccessDiagDetails(ObjectAccessPath.back());
+    {
+      ((SemaSYCLRef.Diag(Detail.Loc, DiagId) << Detail.Type)
+        << ... << Args);
+    }
+    additionalDiagnostics();
+    emitObjectAccessPathNotes();
+  }
+
+  template<typename Func, typename ...Ts>
+  std::enable_if_t<std::is_invocable_v<Func>, void>
+  emitDiagnostic(clang::diag::kind DiagId, Func additionalDiagnostics,
+    Ts &&...Args) {
+      emitDiagnosticImpl(DiagId, additionalDiagnostics,
+        std::forward<Ts>(Args)...);
+  }
+
+  template<typename ...Ts>
+  void emitDiagnostic(clang::diag::kind DiagId, Ts &&...Args) {
+    emitDiagnosticImpl(DiagId, []{}, std::forward<Ts>(Args)...);
+  }
+
 public:
   KernelParamsChecker(SemaSYCL &SR, SourceLocation Loc)
       : ConstSubobjectVisitor<KernelParamsChecker>(SR.getASTContext()),
@@ -744,16 +770,6 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
 
   // Returns true if subobjects should be visited and false otherwise.
   bool checkType(QualType Ty) {
-    auto emitDiagnostic = [this](clang::diag::kind DiagId, auto &&...Args) {
-      const auto &DiagDetail =
-          getObjectAccessDiagDetails(ObjectAccessPath.back());
-      {
-        auto DB = SemaSYCLRef.Diag(DiagDetail.Loc, DiagId) << DiagDetail.Type;
-        (void)(DB << ... << Args);
-      }
-      emitObjectAccessPathNotes();
-    };
-
     if (Ty->isReferenceType()) {
       auto DirectParent = ObjectAccessPath.back();
       // Reference cannot be a base, so just assume we came via a FieldDecl.
@@ -768,26 +784,54 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
       // kernel parameter types contained within the referenced type
       // might not be relevant once the programmer addresses the
       // invalid use of a reference.
-      emitDiagnostic(diag::err_bad_kernel_param_type);
+      emitDiagnostic(diag::err_sycl_kernel_bad_param_type,
+        diag::InvalidSYCLKernelParamReason::ReferenceType);
       IsValid = false;
       return false;
     }
     if (Ty->isAtomicType()) {
-      emitDiagnostic(diag::err_bad_kernel_param_type);
+      emitDiagnostic(diag::err_sycl_kernel_bad_param_type,
+        diag::InvalidSYCLKernelParamReason::AtomicType);
       IsValid = false;
       return false;
     }
     if (Ty->isStructureTypeWithFlexibleArrayMember()) {
+      // Traversal to find FAM location:
+      std::function<void(QualType)> findFAM = [&](QualType Ty) {
+        const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
+        // FAM can only be defined as the last element of a structure.
+        const FieldDecl *FAM = nullptr;
+        for (const FieldDecl* FD : RD->fields())
+          FAM = FD;
+        assert(FAM && "structure with flexible array member has no fields??");
+
+        if (FAM->getType()->isIncompleteArrayType())
+          SemaSYCLRef.Diag(FAM->getLocation(),
+            diag::note_flexible_array_member_defined_here) << FAM << RD;
+        else
+          findFAM(FAM->getType());
+        SemaSYCLRef.Diag(RD->getLocation(), diag::note_within_field_of_type)
+            << RD;
+      };
+
       emitDiagnostic(diag::err_sycl_kernel_bad_param_type,
+                     [&] { findFAM(Ty); },
                      diag::InvalidSYCLKernelParamReason::FAM);
       IsValid = false;
       return false;
     }
     // Disallow classes that inherit virtual base classes.
-    if (CXXRecordDecl *RD = Ty->getAsCXXRecordDecl()) {
+    if (const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl()) {
       if (RD->getNumVBases() > 0) {
+        // Issue a diagnostic for virtual bases inherited by RD:
+        auto findVBases = [&] {
+          for (const CXXBaseSpecifier &VB : RD->vbases())
+            SemaSYCLRef.Diag(VB.getBaseTypeLoc(),
+              diag::note_vbase_specifier_here) << Ty << VB.getType();
+        };
         emitDiagnostic(diag::err_sycl_kernel_bad_param_type,
-                       diag::InvalidSYCLKernelParamReason::VirtualBases);
+                       [&] { findVBases(); },
+                       diag::InvalidSYCLKernelParamReason::VirtualBase);
         IsValid = false;
         return false;
       }
diff --git a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
index 01768bc79b52b..03748ffc14e89 100644
--- a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
@@ -25,9 +25,9 @@ void test() {
   kernel_single_task<class KN<1>>(
       [ // expected-note{{within capture 'p' of lambda expression here}}
         // expected-note at -1{{within capture 's' of lambda expression here}}
-          // expected-error at +1 {{'int &' cannot be used as the type of a kernel parameter}}
+          // expected-error at +1 {{'int &' type cannot be used in a SYCL kernel parameter because it is a reference type}}
           &p, q,
-          // expected-error at +1 {{'float &' cannot be used as the type of a kernel parameter}}
+          // expected-error at +1 {{'float &' type cannot be used in a SYCL kernel parameter because it is a reference type}}
           &s] {
         (void)q;
         (void)p;
@@ -45,12 +45,12 @@ void kernel_single_task(T t) {} // expected-note-re 3{{within parameter 't' of t
 
 struct S { // expected-note 2{{within field of type 'S' declared here}}
   int a;
-  int &b; //expected-error 2{{'int &' cannot be used as the type of a kernel parameter}}
+  int &b; //expected-error 2{{'int &' type cannot be used in a SYCL kernel parameter because it is a reference type}}
 };
 
 void test() {
   int p = 0;
-  auto L = [&]() { (void)p;}; // expected-error {{'int &' cannot be used as the type of a kernel parameter}}
+  auto L = [&]() { (void)p;}; // expected-error {{'int &' type cannot be used in a SYCL kernel parameter because it is a reference type}}
                                // expected-note at -1 {{within capture 'p' of lambda expression here}}
   S Str {p, p};
   // expected-note-re at +1 {{in instantiation of function template specialization 'badref2::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
@@ -79,7 +79,7 @@ void kernel_single_task(T t) {} // expected-note-re 3{{within parameter 't' of t
 
 struct S { // expected-note {{within field of type 'S' declared here}}
   int a;
-  int &b; //expected-error {{'int &' cannot be used as the type of a kernel parameter}}
+  int &b; //expected-error {{'int &' type cannot be used in a SYCL kernel parameter because it is a reference type}}
 };
 
 void fooarr(int (&arr)[5]) {
@@ -98,13 +98,13 @@ void test(int AS) {
   // expected-note-re at +1 {{in instantiation of function template specialization 'badref3::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
   kernel_single_task<class KN<5>>(
       [&] { // expected-note {{within capture 'arr1' of lambda expression here}}
-        (void)arr1; // expected-error {{'int (&)[AS]' cannot be used as the type of a kernel parameter}}
+        (void)arr1; // expected-error {{'int (&)[AS]' type cannot be used in a SYCL kernel parameter because it is a reference type}}
       });
   int arrayints[5] = {0};
   // expected-note-re at +1 {{in instantiation of function template specialization 'badref3::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
   kernel_single_task<class KN<7>>(
       [&] { // expected-note {{within capture 'arrayints' of lambda expression here}}
-        fooarr(arrayints); // expected-error {{'int (&)[5]' cannot be used as the type of a kernel parameter}}
+        fooarr(arrayints); // expected-error {{'int (&)[5]' type cannot be used in a SYCL kernel parameter because it is a reference type}}
       });
 }
 } // namespace badref3
@@ -119,7 +119,7 @@ void kernel_single_task(T t) {} // expected-note {{within parameter 't' of type
                                 // expected-note at -2 {{within parameter 't' of type 'badref4::Derived2' declared here}}
 
 template <typename T> class Callable { // expected-note 2{{within field of type 'Callable<int &>' declared here}}
-  T data; // expected-error 2{{'int &' cannot be used as the type of a kernel parameter}}
+  T data; // expected-error 2{{'int &' type cannot be used in a SYCL kernel parameter because it is a reference type}}
 public:
   Callable(T d) : data(d) {}
   void operator()() const {
@@ -127,7 +127,7 @@ template <typename T> class Callable { // expected-note 2{{within field of type
 };
 
 class Derived1 : Callable<int> { // expected-note {{within field of type 'Derived1' declared here}}
-  int &a; // expected-error {{'int &' cannot be used as the type of a kernel parameter}}
+  int &a; // expected-error {{'int &' type cannot be used in a SYCL kernel parameter because it is a reference type}}
 public:
   Derived1(int d, int &b) : Callable<int>(d), a(b) {}
 };
@@ -166,7 +166,7 @@ void test() {
   };
   S s {a};
   kernel_single_task<class KN<13>>([&] { (void)s; });
-  // expected-error at -1 {{'S &' cannot be used as the type of a kernel parameter}}
+  // expected-error at -1 {{'S &' type cannot be used in a SYCL kernel parameter because it is a reference type}}
   // expected-note-re at -2 {{in instantiation of function template specialization 'badref6::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
   // expected-note at -3 {{within capture 's' of lambda expression here}}
 }
@@ -182,7 +182,7 @@ void kernel_single_task(T t) {} //expected-note-re {{within parameter 't' of typ
 void test() {
   int p = 0;
   kernel_single_task<class KN<14>>([&x=p] { (void)x; });
-  // expected-error at -1 {{'int &' cannot be used as the type of a kernel parameter}}
+  // expected-error at -1 {{'int &' type cannot be used in a SYCL kernel parameter because it is a reference type}}
   // expected-note-re at -2 {{in instantiation of function template specialization 'badref7::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
   // expected-note at -3 {{within capture 'x' of lambda expression here}}
 }
@@ -205,11 +205,11 @@ void kernel_single_task(T t) {}
 struct Sa { 
   int a;
   _Atomic int b; 
-  // expected-error at -1 {{'_Atomic(int)' cannot be used as the type of a kernel parameter}}
+  // expected-error at -1 {{'_Atomic(int)' type cannot be used in a SYCL kernel parameter because it is an atomic type}}
   // expected-note at -4 {{within field of type 'Sa' declared here}}
-  // expected-error at -3 {{'_Atomic(int)' cannot be used as the type of a kernel parameter}}
+  // expected-error at -3 {{'_Atomic(int)' type cannot be used in a SYCL kernel parameter because it is an atomic type}}
   // expected-note at -6 {{within field of type 'Sa' declared here}}
-  // expected-error at -5 {{'_Atomic(int)' cannot be used as the type of a kernel parameter}}
+  // expected-error at -5 {{'_Atomic(int)' type cannot be used in a SYCL kernel parameter because it is an atomic type}}
   // expected-note at -8 {{within field of type 'Sa' declared here}}
 };
 
@@ -226,11 +226,11 @@ void test() {
   Sa s{1, 2};
   Sa arr[] = {s, s};
   kernel_single_task<class KN<15>>([=]{ (void)a; });
-  // expected-error at -1 {{'_Atomic(int)' cannot be used as the type of a kernel parameter}}
+  // expected-error at -1 {{'_Atomic(int)' type cannot be used in a SYCL kernel parameter because it is an atomic type}}
   // expected-note-re at -2 {{in instantiation of function template specialization 'atomic1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
   // expected-note at -3 {{within capture 'a' of lambda expression here}}
   kernel_single_task<class KN<16>>([=]{ (void)b; });
-  // expected-error at -1 {{'_Atomic(int)' cannot be used as the type of a kernel parameter}}
+  // expected-error at -1 {{'_Atomic(int)' type cannot be used in a SYCL kernel parameter because it is an atomic type}}
   // expected-note-re at -2 {{in instantiation of function template specialization 'atomic1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
   // expected-note at -3 {{within capture 'b' of lambda expression here}}
   kernel_single_task<class KN<17>>([=]{ (void)s; });
@@ -254,14 +254,15 @@ void kernel_single_task(T t) {}
 // expected-note at -1 {{within parameter 't' of type 'fam1::Kernel' declared here}}
 
 struct FAM { 
+// expected-note at -1 {{within field of type 'FAM' declared here}}
   int a;
-  int b[];
+  int b[]; // expected-note {{flexible array member 'b' of 'FAM' defined here}}
 };
 
 class Kernel {
 // expected-note at -1 {{within field of type 'Kernel' declared here}}
   FAM fam;
-  // expected-error at -1 {{'FAM' contains a flexible array member and cannot be used as the type of a SYCL kernel parameter}}
+  // expected-error at -1 {{'FAM' type cannot be used in a SYCL kernel parameter because it contains a flexible array member}}
 public:
   void operator()() const { (void)fam; }
 };
@@ -345,12 +346,12 @@ class Base {
   Base(int &a) : data(a) {}
 };
 
-class Derived : virtual Base { 
+class Derived : virtual Base { // expected-note {{'Derived' inherits virtual base class 'Base' specified here}}
 public:
   Derived(int &a) : Base(a) {}
 };
 
-class A : public virtual Base { 
+class A : public virtual Base { // expected-note {{'Diamond' inherits virtual base class 'Base' specified here}}
 public:
   A(int &a) : Base(a) {}
 };
@@ -369,12 +370,12 @@ void test() {
   int p = 0;
   Derived d{p};
   kernel_single_task<class KN<29>>([=]{ (void)d; });
-  // expected-error at -1 {{'Derived' inherits virtual base classes and cannot be used as the type of a SYCL kernel parameter}}
+  // expected-error at -1 {{'Derived' type cannot be used in a SYCL kernel parameter because it inherits a virtual base class}}
   // expected-note-re at -2 {{in instantiation of function template specialization 'vbase1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
   // expected-note at -3 {{within capture 'd' of lambda expression here}}
   Diamond ab{p};
   kernel_single_task<class KN<30>>([=]{ (void)ab; });
-  // expected-error at -1 {{'Diamond' inherits virtual base classes and cannot be used as the type of a SYCL kernel parameter}}
+  // expected-error at -1 {{'Diamond' type cannot be used in a SYCL kernel parameter because it inherits a virtual base class}}
   // expected-note-re at -2 {{in instantiation of function template specialization 'vbase1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
   // expected-note at -3 {{within capture 'ab' of lambda expression here}}
 }

>From 4fa68f52b222c6e06ceea0bdc78fb4f505eaa39e Mon Sep 17 00:00:00 2001
From: Ian Li <ianayl.work at gmail.com>
Date: Wed, 5 Aug 2026 19:22:04 -0400
Subject: [PATCH 13/19] Update
 clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp

Co-authored-by: Tom Honermann <tom at honermann.net>
---
 clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
index 03748ffc14e89..3848ccd3ccadb 100644
--- a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
@@ -196,10 +196,7 @@ namespace atomic1 {
 template<typename KNT, typename T>
 [[clang::sycl_kernel_entry_point(KNT)]]
 void kernel_single_task(T t) {}
-// expected-note-re at -1 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
-// expected-note-re at -2 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
-// expected-note-re at -3 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
-// expected-note-re at -4 {{within parameter 't' of type '(lambda at {{.*}})' declared here}}
+// expected-note-re at -1 4{{within parameter 't' of type '(lambda at {{.*}})' declared here}}
 // expected-note at -5 {{within parameter 't' of type 'atomic1::Kernel' declared here}}
 
 struct Sa { 

>From ee21459ab34229bb1b5fdf426f7b19764ecf6229 Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Wed, 5 Aug 2026 16:25:57 -0700
Subject: [PATCH 14/19] clang-format

---
 clang/lib/Sema/SemaSYCL.cpp | 46 ++++++++++++++++++-------------------
 1 file changed, 23 insertions(+), 23 deletions(-)

diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index 8faeb339652e0..fd79366a024d2 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -715,30 +715,28 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
     }
   }
 
-  template<typename Func, typename ...Ts>
+  template <typename Func, typename... Ts>
   void emitDiagnosticImpl(clang::diag::kind DiagId, Func additionalDiagnostics,
-    Ts &&...Args) {
-    const auto &Detail =
-        getObjectAccessDiagDetails(ObjectAccessPath.back());
+                          Ts &&...Args) {
+    const auto &Detail = getObjectAccessDiagDetails(ObjectAccessPath.back());
     {
-      ((SemaSYCLRef.Diag(Detail.Loc, DiagId) << Detail.Type)
-        << ... << Args);
+      ((SemaSYCLRef.Diag(Detail.Loc, DiagId) << Detail.Type) << ... << Args);
     }
     additionalDiagnostics();
     emitObjectAccessPathNotes();
   }
 
-  template<typename Func, typename ...Ts>
+  template <typename Func, typename... Ts>
   std::enable_if_t<std::is_invocable_v<Func>, void>
   emitDiagnostic(clang::diag::kind DiagId, Func additionalDiagnostics,
-    Ts &&...Args) {
-      emitDiagnosticImpl(DiagId, additionalDiagnostics,
-        std::forward<Ts>(Args)...);
+                 Ts &&...Args) {
+    emitDiagnosticImpl(DiagId, additionalDiagnostics,
+                       std::forward<Ts>(Args)...);
   }
 
-  template<typename ...Ts>
+  template <typename... Ts>
   void emitDiagnostic(clang::diag::kind DiagId, Ts &&...Args) {
-    emitDiagnosticImpl(DiagId, []{}, std::forward<Ts>(Args)...);
+    emitDiagnosticImpl(DiagId, [] {}, std::forward<Ts>(Args)...);
   }
 
 public:
@@ -785,13 +783,13 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
       // might not be relevant once the programmer addresses the
       // invalid use of a reference.
       emitDiagnostic(diag::err_sycl_kernel_bad_param_type,
-        diag::InvalidSYCLKernelParamReason::ReferenceType);
+                     diag::InvalidSYCLKernelParamReason::ReferenceType);
       IsValid = false;
       return false;
     }
     if (Ty->isAtomicType()) {
       emitDiagnostic(diag::err_sycl_kernel_bad_param_type,
-        diag::InvalidSYCLKernelParamReason::AtomicType);
+                     diag::InvalidSYCLKernelParamReason::AtomicType);
       IsValid = false;
       return false;
     }
@@ -801,22 +799,23 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
         const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
         // FAM can only be defined as the last element of a structure.
         const FieldDecl *FAM = nullptr;
-        for (const FieldDecl* FD : RD->fields())
+        for (const FieldDecl *FD : RD->fields())
           FAM = FD;
         assert(FAM && "structure with flexible array member has no fields??");
 
         if (FAM->getType()->isIncompleteArrayType())
           SemaSYCLRef.Diag(FAM->getLocation(),
-            diag::note_flexible_array_member_defined_here) << FAM << RD;
+                           diag::note_flexible_array_member_defined_here)
+              << FAM << RD;
         else
           findFAM(FAM->getType());
         SemaSYCLRef.Diag(RD->getLocation(), diag::note_within_field_of_type)
             << RD;
       };
 
-      emitDiagnostic(diag::err_sycl_kernel_bad_param_type,
-                     [&] { findFAM(Ty); },
-                     diag::InvalidSYCLKernelParamReason::FAM);
+      emitDiagnostic(
+          diag::err_sycl_kernel_bad_param_type, [&] { findFAM(Ty); },
+          diag::InvalidSYCLKernelParamReason::FAM);
       IsValid = false;
       return false;
     }
@@ -827,11 +826,12 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
         auto findVBases = [&] {
           for (const CXXBaseSpecifier &VB : RD->vbases())
             SemaSYCLRef.Diag(VB.getBaseTypeLoc(),
-              diag::note_vbase_specifier_here) << Ty << VB.getType();
+                             diag::note_vbase_specifier_here)
+                << Ty << VB.getType();
         };
-        emitDiagnostic(diag::err_sycl_kernel_bad_param_type,
-                       [&] { findVBases(); },
-                       diag::InvalidSYCLKernelParamReason::VirtualBase);
+        emitDiagnostic(
+            diag::err_sycl_kernel_bad_param_type, [&] { findVBases(); },
+            diag::InvalidSYCLKernelParamReason::VirtualBase);
         IsValid = false;
         return false;
       }

>From 8a4841a5fdbd110536525d94bfec53f2df937879 Mon Sep 17 00:00:00 2001
From: Ian Li <ianayl.work at gmail.com>
Date: Wed, 5 Aug 2026 19:27:23 -0400
Subject: [PATCH 15/19] Update clang/include/clang/Basic/DiagnosticSemaKinds.td

Co-authored-by: Tom Honermann <tom at honermann.net>
---
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f89de5e355d19..b6c99e18a873d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13581,7 +13581,7 @@ def err_sycl_entry_point_device_use : Error<
   "function %0 cannot be used in device code because it is declared with the"
   " %1 attribute">;
 def warn_sycl_kernel_param_has_ptr: Warning<
-  "pointers used in parameters to a SYCL kernel require a device that supports Unified Shared Memory (USM)">,
+  "pointers used in SYCL kernel parameters require a device that supports Unified Shared Memory (USM)">,
   InGroup<NonPortableSYCL>, DefaultIgnore;
 def err_sycl_kernel_bad_param_type : Error<
   "%0 type cannot be used in a SYCL kernel parameter because it "

>From a9c7338d5d0c22e2038ee9eb8ebf255519ced069 Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Wed, 5 Aug 2026 16:30:00 -0700
Subject: [PATCH 16/19] clang-format

---
 clang/lib/Sema/SemaSYCL.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp
index fd79366a024d2..6353d95eed9d8 100644
--- a/clang/lib/Sema/SemaSYCL.cpp
+++ b/clang/lib/Sema/SemaSYCL.cpp
@@ -718,7 +718,8 @@ class KernelParamsChecker : public ConstSubobjectVisitor<KernelParamsChecker> {
   template <typename Func, typename... Ts>
   void emitDiagnosticImpl(clang::diag::kind DiagId, Func additionalDiagnostics,
                           Ts &&...Args) {
-    const auto &Detail = getObjectAccessDiagDetails(ObjectAccessPath.back());
+    const DiagDetails &Detail =
+        getObjectAccessDiagDetails(ObjectAccessPath.back());
     {
       ((SemaSYCLRef.Diag(Detail.Loc, DiagId) << Detail.Type) << ... << Args);
     }

>From c236d4a184a3c61cb8c6c52d27d6761bbd92f524 Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Wed, 5 Aug 2026 18:14:26 -0700
Subject: [PATCH 17/19] Fix lit test after applying review comments

---
 clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
index 3848ccd3ccadb..bad672e10597e 100644
--- a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
@@ -197,7 +197,7 @@ template<typename KNT, typename T>
 [[clang::sycl_kernel_entry_point(KNT)]]
 void kernel_single_task(T t) {}
 // expected-note-re at -1 4{{within parameter 't' of type '(lambda at {{.*}})' declared here}}
-// expected-note at -5 {{within parameter 't' of type 'atomic1::Kernel' declared here}}
+// expected-note at -2 {{within parameter 't' of type 'atomic1::Kernel' declared here}}
 
 struct Sa { 
   int a;
@@ -288,8 +288,7 @@ struct S {
 // nonportable-note at -1 {{within field of type 'S' declared here}}
 // nonportable-note at -2 {{within field of type 'S' declared here}}          
   int *ptr;
-  // nonportable-warning at -1 {{pointers used in parameters to a SYCL kernel require a device that supports Unified Shared Memory (USM)}}
-  // nonportable-warning at -2 {{pointers used in parameters to a SYCL kernel require a device that supports Unified Shared Memory (USM)}}
+  // nonportable-warning at -1 2{{pointers used in SYCL kernel parameters require a device that supports Unified Shared Memory (USM)}}
 };
 
 class C {

>From 1b625443a9446223fa58d32382c62240f117596c Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Wed, 5 Aug 2026 18:30:19 -0700
Subject: [PATCH 18/19] update more wording in lit test after github comments

---
 clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
index bad672e10597e..31d5c26727556 100644
--- a/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
+++ b/clang/test/SemaSYCL/sycl-kernel-param-restrictions.cpp
@@ -295,7 +295,7 @@ class C {
 // nonportable-note at -1 {{within field of type 'C' declared here}}
 private:
   int *ptr;
-  // nonportable-warning at -1 {{pointers used in parameters to a SYCL kernel require a device that supports Unified Shared Memory (USM)}}
+  // nonportable-warning at -1 {{pointers used in SYCL kernel parameters require a device that supports Unified Shared Memory (USM)}}
 public:
   C(int *p) : ptr(p) {}
 };
@@ -303,7 +303,7 @@ class C {
 void test() {
   int *ptr;
   kernel_single_task<class KN<25>>([=]{ (void)ptr; });
-  // nonportable-warning at -1 {{pointers used in parameters to a SYCL kernel require a device that supports Unified Shared Memory (USM)}}
+  // nonportable-warning at -1 {{pointers used in SYCL kernel parameters require a device that supports Unified Shared Memory (USM)}}
   // nonportable-note-re at -2 {{in instantiation of function template specialization 'nonportable1::kernel_single_task<KN<{{[0-9]+}}>, {{.*}}>' requested here}}
   // nonportable-note at -3 {{within capture 'ptr' of lambda expression here}}
 

>From 00120e91b60701a55e8a697487c75083730ab2c0 Mon Sep 17 00:00:00 2001
From: "Li, Ian" <ian.li at intel.com>
Date: Fri, 21 Aug 2026 12:10:07 -0700
Subject: [PATCH 19/19] Add release notes

---
 clang/docs/ReleaseNotes.md | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 8c9467ca7b742..c41bcdb53f1cb 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -690,6 +690,14 @@ features cannot lower the translation-unit ABI level;
 
 #### Improvements
 
+- Added additional SYCL kernel parameter checks. Diagnostics are now issued for
+  the following kernel parameter types:
+  - Atomic types.
+  - Structs/classes with a flexible array members (FAM).
+  - Classes with virtual base classes.
+  - If `-Wnonportable-sycl` is enabled, warnings are issued for pointers as a
+    kernel parameter. 
+
 ## Additional Information
 
 A wide variety of additional information is available on the [Clang web



More information about the cfe-commits mailing list