[clang] [clang][SYCL] Add sycl_external attribute and restrict emitting device code (PR #140282)

Tom Honermann via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 13 20:52:08 PDT 2025


================
@@ -0,0 +1,157 @@
+// RUN: %clang_cc1 -fsycl-is-host -std=c++17 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsycl-is-device -std=c++17 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsycl-is-host -std=c++20 -fsyntax-only -verify -DCPP20 %s
+// RUN: %clang_cc1 -fsycl-is-device -std=c++20 -fsyntax-only -verify -DCPP20 %s
+
+// Semantic tests for the sycl_external attribute.
+
+// expected-error at +1{{'clang::sycl_external' can only be applied to functions with external linkage}}
+[[clang::sycl_external]]
+static void func1() {}
+
+// expected-error at +2{{'clang::sycl_external' can only be applied to functions with external linkage}}
+namespace {
+  [[clang::sycl_external]]
+  void func2() {}
+}
+
+// expected-error at +2{{'clang::sycl_external' can only be applied to functions with external linkage}}
+namespace { struct S4 {}; }
+[[clang::sycl_external]] void func4(S4) {}
+
+// expected-error at +3{{'clang::sycl_external' can only be applied to functions with external linkage}}
+namespace { struct S6 {}; }
+template<typename>
+[[clang::sycl_external]] void func6() {}
+template void func6<S6>();
+// expected-note at -1{{in instantiation of function template specialization 'func6<(anonymous namespace)::S6>' requested here}}
+
+// FIXME: C++23 [temp.expl.spec]p12 states:
+//   ... Similarly, attributes appearing in the declaration of a template
+//   have no effect on an explicit specialization of that template.
+// Clang currently instantiates and propagates attributes from a function
+// template to its explicit specializations resulting in the following
+// spurious error.
+// expected-error at +3 2{{'clang::sycl_external' can only be applied to functions with external linkage}}
+namespace { struct S7 {}; }
+template<typename>
+[[clang::sycl_external]] void func7();
+template<> void func7<S7>() {}
+// expected-note at -1{{in instantiation of function template specialization 'func7<(anonymous namespace)::S7>' requested here}}
+
+// FIXME: The explicit function template specialization appears to trigger
+// instantiation of a declaration from the primary template without the
+// attribute leading to a spurious diagnostic that the sycl_external
+// attribute is not present on the first declaration.
+namespace { struct S8 {}; }
+template<typename>
+void func8();
+template<> [[clang::sycl_external]] void func8<S8>() {}
+// expected-warning at -1{{'clang::sycl_external' attribute does not appear on the first declaration}}
+// expected-error at -2{{'clang::sycl_external' can only be applied to functions with external linkage}}
+// expected-note at -3{{previous declaration is here}}
+
+// FIXME: The implicit instantiation of func9<S9>() is valid.
+namespace { struct S9 {}; }
+struct T9 {
+  using type = S9;
+};
+// expected-error at +2{{'clang::sycl_external' can only be applied to functions with external linkage}}
+template<typename>
+[[clang::sycl_external]] void func9() {}
+// expected-note at +3{{in instantiation of function template specialization 'func9<(anonymous namespace)::S9>' requested here}}
+template<typename T>
+[[clang::sycl_external]] void test_func9() {
+  func9<typename T::type>();
+}
+// expected-note at +1{{in instantiation of function template specialization 'test_func9<T9>' requested here}}
+template void test_func9<T9>(); // FIXME: don't diagnose implicit instantiation of func9<S9>().
+
+// The first declaration of a SYCL external function is required to have this attribute.
+// expected-note at +1{{previous declaration is here}}
+int foo();
+// expected-warning at +1{{'clang::sycl_external' attribute does not appear on the first declaration}}
+[[clang::sycl_external]] int foo();
+
+// expected-note at +1{{previous declaration is here}}
+void goo();
+// expected-warning at +1{{'clang::sycl_external' attribute does not appear on the first declaration}}
+[[clang::sycl_external]] void goo();
+void goo() {}
+
+// expected-note at +2{{previous definition is here}}
+// expected-note at +1{{previous declaration is here}}
+void hoo() {}
+// expected-warning at +2{{attribute declaration must precede definition}}
+// expected-warning at +1{{'clang::sycl_external' attribute does not appear on the first declaration}}
+[[clang::sycl_external]] void hoo();
----------------
tahonermann wrote:

The `attribute declaration must precede definition` warning is not desirable since we don't actually have such a requirement. The following addition to `checkNewAttributesAfterDef()` in `clang/lib/Sema/SemaDecl.cpp` will disable it. You'll see special handling there for the `sycl_kernel_entry_point` attribute as well.
```
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 34f4e83e74e6..995283a344e4 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -3068,6 +3068,10 @@ static void checkNewAttributesAfterDef(Sema &S, Decl *New, const Decl *Old) {
       cast<SYCLKernelEntryPointAttr>(NewAttribute)->setInvalidAttr();
       ++I;
       continue;
+    } else if (isa<SYCLExternalAttr>(NewAttribute)) {
+      // SYCLExternalAttr may be added after a definition.
+      ++I;
+      continue;
     }

     S.Diag(NewAttribute->getLocation(),
```
```suggestion
// expected-note at +1{{previous declaration is here}}
void hoo() {}
// expected-warning at +1{{'clang::sycl_external' attribute does not appear on the first declaration}}
[[clang::sycl_external]] void hoo();
```

https://github.com/llvm/llvm-project/pull/140282


More information about the cfe-commits mailing list