Make warning report which nested struct fields invalidate the argument, 
reporting first the outermost struct, followed by the nested fields from the 
outermost to the first found illegal one.

  For example, for a nested struct case like this:
  struct InnerInner
  {
    int* foo;
  };

  struct Valid
  {
    float c;
    float d;
  };

  struct Inner
  {
    struct Valid v;
    struct InnerInner a;
    struct Valid g;
    struct InnerInner b;
  };

  struct NestedPointer
  {
    int x;
    struct Inner inner;
  };

  kernel void pointer_in_nested_struct_arg(struct NestedPointer arg) { }

  The diagnostic produced is:

  error: struct or union kernel parameters may not contain OpenCL objects
  kernel void pointer_in_nested_struct_arg(struct NestedPointer arg, struct 
NestedPointer second) { }// expected-error{{struct or union kernel parameters 
may not contain OpenCL objects}}
                                                                ^
  test/SemaOpenCL/arst2.cl:29:8: note: 'NestedPointer' declared here
  struct NestedPointer
         ^
  test/SemaOpenCL/arst2.cl:32:16: note: member 'inner' declared here
    struct Inner inner;
                 ^
  test/SemaOpenCL/arst2.cl:26:21: note: member 'b' declared here
    struct InnerInner b;
                      ^
  test/SemaOpenCL/arst2.cl:12:8: note: member 'foo' declared here
    int* foo;
         ^

Hi rsmith,

http://llvm-reviews.chandlerc.com/D1052

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D1052?vs=2727&id=2785#toc

Files:
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaDecl.cpp
  test/SemaOpenCL/event_t.cl
  test/SemaOpenCL/invalid-kernel-parameters.cl
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -6441,16 +6441,18 @@
   "invalid reinterpretation: sizes of %0 and %1 must match">;
 def err_static_kernel : Error<
   "kernel functions cannot be declared static">;
-def err_opencl_ptrptr_kernel_arg : Error<
-  "kernel argument cannot be declared as a pointer to a pointer">;
+def err_opencl_ptrptr_kernel_param : Error<
+  "kernel parameter cannot be declared as a pointer to a pointer">;
 def err_static_function_scope : Error<
   "variables in function scope cannot be declared static">;
 def err_opencl_bitfields : Error<
   "bitfields are not supported in OpenCL">;
 def err_opencl_vla : Error<
   "variable length arrays are not supported in OpenCL">;
-def err_event_t_kernel_arg : Error<
-  "the event_t type cannot be used to declare a kernel function argument">;
+def err_bad_kernel_param_type : Error<
+  "%0 cannot be used to declare a kernel function parameter">;
+def err_struct_with_pointers_kernel_param : Error<
+  "struct or union kernel parameters may not contain OpenCL objects">;
 def err_event_t_global_var : Error<
   "the event_t type cannot be used to declare a program scope variable">;
 def err_event_t_struct_field : Error<
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -1446,6 +1446,8 @@
   void MaybeSuggestAddingStaticToDecl(const FunctionDecl *D);
   void ActOnStartFunctionDeclarator();
   void ActOnEndFunctionDeclarator();
+
+  void checkIsValidOpenCLKernelArgument(Declarator &D, ParmVarDecl *P);
   NamedDecl* ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
                                      TypeSourceInfo *TInfo,
                                      LookupResult &Previous,
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -6072,6 +6072,172 @@
   }
 }
 
+enum OpenCLParamType {
+  ValidKernelParam,
+  PtrPtrKernelParam,
+  BoolKernelParam,
+  IntTypeKernelParam,
+  EventKernelParam,
+  HalfKernelParam,
+  PtrKernelParam,
+  RecordKernelParam
+};
+
+static OpenCLParamType getOpenCLKernelParameterType(QualType PT) {
+  if (PT->isPointerType()) {
+    QualType PointeeType = PT->getPointeeType();
+    return PointeeType->isPointerType() ? PtrPtrKernelParam : PtrKernelParam;
+  }
+
+  if (PT->isImageType())
+    return PtrKernelParam;
+
+  if (PT->isIntegerType()) {
+    if (PT->isBooleanType())
+      return BoolKernelParam;
+
+    while (const TypedefType *Typedef = dyn_cast<TypedefType>(PT)) {
+      const IdentifierInfo *Identifier = Typedef->getDecl()->getIdentifier();
+      StringRef Name = Identifier->getName();
+
+      if (Name == "size_t" ||
+          Name == "ptrdiff_t" ||
+          Name == "intptr_t" ||
+          Name == "uintptr_t") {
+        return IntTypeKernelParam;
+      }
+
+      PT = Typedef->getDecl()->getUnderlyingType();
+    }
+  }
+
+  if (PT->isEventT())
+    return EventKernelParam;
+
+  if (PT->isHalfType())
+    return HalfKernelParam;
+
+  if (PT->isRecordType())
+    return RecordKernelParam;
+
+  return ValidKernelParam;
+}
+
+static void checkIsValidOpenCLKernelParameter(Sema &S,
+                                              Declarator &D,
+                                              ParmVarDecl *Param) {
+  QualType PT = Param->getType();
+
+  switch (getOpenCLKernelParameterType(PT)) {
+  case PtrPtrKernelParam:
+    // OpenCL v1.2 s6.9.a:
+    // A kernel function argument cannot be declared as a
+    // pointer to a pointer type.
+    S.Diag(Param->getLocation(), diag::err_opencl_ptrptr_kernel_param);
+    D.setInvalidType();
+    return;
+
+  case BoolKernelParam:
+  case HalfKernelParam:
+  case IntTypeKernelParam:
+    // OpenCL v1.2 s6.9.k:
+    // Arguments to kernel functions in a program cannot be declared with the
+    // built-in scalar types bool, half, size_t, ptrdiff_t, intptr_t, and
+    // uintptr_t or a struct and/or union that contain fields declared to be
+    // one of these built-in scalar types.
+
+  case EventKernelParam:
+    // OpenCL v1.2 s6.8 n:
+    // A kernel function argument cannot be declared
+    // of event_t type.
+    S.Diag(Param->getLocation(), diag::err_bad_kernel_param_type) << PT;
+    D.setInvalidType();
+    return;
+
+  case PtrKernelParam:
+  case ValidKernelParam:
+    return;
+
+  case RecordKernelParam:
+    break;
+  }
+
+  // Track nested structs we will inspect
+  SmallVector<const Decl *, 4> VisitStack;
+
+  // Track where we are in the nested structs. Items will migrate from
+  // VisitStack to HistoryStack as we do the DFS for bad field.
+  SmallVector<const FieldDecl *, 4> HistoryStack;
+
+  const RecordDecl *PD = PT->castAs<RecordType>()->getDecl();
+  VisitStack.push_back(PD);
+
+  assert(VisitStack.back() && "First decl null?");
+
+  do {
+    const Decl *Next = VisitStack.pop_back_val();
+    if (!Next) {
+      // Found a marker, we have gone up a level
+      HistoryStack.pop_back();
+      continue;
+    }
+
+    // Adds everything except the original parameter declaration (which is not a
+    // field itself) to the history stack.
+    const RecordDecl *RD;
+    if (const FieldDecl *Field = dyn_cast<FieldDecl>(Next)) {
+      HistoryStack.push_back(Field);
+      RD = Field->getType()->castAs<RecordType>()->getDecl();
+    } else {
+      RD = cast<RecordDecl>(Next);
+    }
+
+    // Add a null marker so we know when we've gone back up a level
+    VisitStack.push_back((const Decl *) 0);
+
+    for (RecordDecl::field_iterator I = RD->field_begin(),
+           E = RD->field_end(); I != E; ++I) {
+      const FieldDecl *FD = *I;
+      QualType QT = FD->getType();
+
+      OpenCLParamType ParamType = getOpenCLKernelParameterType(QT);
+      if (ParamType == ValidKernelParam)
+        continue;
+
+      if (ParamType == RecordKernelParam) {
+        VisitStack.push_back(FD);
+        continue;
+      }
+
+      // OpenCL v1.2 s6.9.p:
+      // Arguments to kernel functions that are declared to be a struct or union
+      // do not allow OpenCL objects to be passed as elements of the struct or
+      // union.
+      unsigned DiagID
+        = (ParamType == PtrKernelParam || ParamType == PtrPtrKernelParam)
+        ? diag::err_struct_with_pointers_kernel_param
+        : diag::err_bad_kernel_param_type;
+
+      S.Diag(Param->getLocation(), DiagID) << PT;
+      S.Diag(PD->getLocation(), diag::note_previous_decl)
+        << PD->getDeclName();
+
+      // We have an error, now let's go back up through history and show where
+      // the offending field came from
+      for (ArrayRef<const FieldDecl *>::const_iterator I = HistoryStack.begin(),
+             E = HistoryStack.end(); I != E; ++I) {
+        const FieldDecl *OuterField = *I;
+        S.Diag(OuterField->getLocation(), diag::note_member_declared_here)
+          << OuterField->getDeclName();
+      }
+
+      S.Diag(FD->getLocation(), diag::note_member_declared_here)
+        << FD->getDeclName();
+      D.setInvalidType();
+    }
+  } while (!VisitStack.empty());
+}
+
 NamedDecl*
 Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
                               TypeSourceInfo *TInfo, LookupResult &Previous,
@@ -6804,34 +6970,18 @@
       Diag(D.getIdentifierLoc(), diag::err_static_kernel);
       D.setInvalidType();
     }
-    
+
     // OpenCL v1.2, s6.9 -- Kernels can only have return type void.
     if (!NewFD->getResultType()->isVoidType()) {
       Diag(D.getIdentifierLoc(),
            diag::err_expected_kernel_void_return_type);
       D.setInvalidType();
     }
-    
+
     for (FunctionDecl::param_iterator PI = NewFD->param_begin(),
          PE = NewFD->param_end(); PI != PE; ++PI) {
       ParmVarDecl *Param = *PI;
-      QualType PT = Param->getType();
-
-      // OpenCL v1.2 s6.9.a:
-      // A kernel function argument cannot be declared as a
-      // pointer to a pointer type.
-      if (PT->isPointerType() && PT->getPointeeType()->isPointerType()) {
-        Diag(Param->getLocation(), diag::err_opencl_ptrptr_kernel_arg);
-        D.setInvalidType();
-      }
-
-      // OpenCL v1.2 s6.8 n:
-      // A kernel function argument cannot be declared
-      // of event_t type.
-      if (PT->isEventT()) {
-        Diag(Param->getLocation(), diag::err_event_t_kernel_arg);
-        D.setInvalidType();
-      }
+      checkIsValidOpenCLKernelParameter(*this, D, Param);
     }
   }
 
Index: test/SemaOpenCL/event_t.cl
===================================================================
--- test/SemaOpenCL/event_t.cl
+++ test/SemaOpenCL/event_t.cl
@@ -8,7 +8,7 @@
 
 void foo(event_t evt); // expected-note {{passing argument to parameter 'evt' here}}
 
-void kernel ker(event_t argevt) { // expected-error {{the event_t type cannot be used to declare a kernel function argument}}
+void kernel ker(event_t argevt) { // expected-error {{'event_t' cannot be used to declare a kernel function parameter}}
   event_t e;
   constant event_t const_evt; // expected-error {{the event_t type can only be used with __private address space qualifier}}
   foo(e);
Index: test/SemaOpenCL/invalid-kernel-parameters.cl
===================================================================
--- /dev/null
+++ test/SemaOpenCL/invalid-kernel-parameters.cl
@@ -0,0 +1,122 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+#pragma OPENCL EXTENSION cl_khr_fp16 : enable
+
+
+// Disallowed: parameters with type
+// bool, half, size_t, ptrdiff_t, intptr_t, and uintptr_t
+// or a struct / union with any of these types in them
+
+
+// These would normally come from a header with the OpenCL builtins
+typedef unsigned int size_t;
+typedef size_t ptrdiff_t;
+typedef size_t intptr_t;
+typedef size_t uintptr_t;
+
+
+
+typedef size_t size_t_typedefed;
+
+kernel void size_t_typedef_arg(size_t_typedefed x) { } // expected-error{{'size_t_typedefed' (aka 'unsigned int') cannot be used to declare a kernel function parameter}}
+
+
+kernel void bool_arg(bool x) { } // expected-error{{'bool' cannot be used to declare a kernel function parameter}}
+
+kernel void half_arg(half x) { } // expected-error{{'half' cannot be used to declare a kernel function parameter}}
+
+kernel void size_t_arg(size_t x) { } // expected-error{{'size_t' (aka 'unsigned int') cannot be used to declare a kernel function parameter}}
+
+kernel void ptrdiff_t_arg(ptrdiff_t x) { } // expected-error{{ptrdiff_t' (aka 'unsigned int') cannot be used to declare a kernel function parameter}}
+
+kernel void intptr_t_arg(intptr_t x) { } // expected-error{{'intptr_t' (aka 'unsigned int') cannot be used to declare a kernel function parameter}}
+
+kernel void uintptr_t_arg(uintptr_t x) { } // expected-error{{uintptr_t' (aka 'unsigned int') cannot be used to declare a kernel function parameter}}
+
+typedef struct ContainsBool // expected-note{{'ContainsBool' declared here}}
+{
+  bool x; // expected-note{{member 'x' declared here}}
+} ContainsBool;
+
+kernel void bool_in_struct_arg(ContainsBool x) { } // expected-error{{'ContainsBool' (aka 'struct ContainsBool') cannot be used to declare a kernel function parameter}}
+
+
+
+typedef struct FooImage2D // expected-note{{'FooImage2D' declared here}}
+{
+  image2d_t imageField; // expected-note{{member 'imageField' declared here}}
+} FooImage2D;
+
+kernel void image_in_struct_arg(FooImage2D arg) { } // expected-error{{struct or union kernel parameters may not contain OpenCL object}}
+
+typedef struct Foo // expected-note{{'Foo' declared here}}
+{
+  int* ptrField; // expected-note{{member 'ptrField' declared here}}
+} Foo;
+
+kernel void pointer_in_struct_arg(Foo arg) { } // expected-error{{struct or union kernel parameters may not contain OpenCL object}}
+
+typedef union FooUnion // expected-note{{'FooUnion' declared here}}
+{
+  int* ptrField; // expected-note{{member 'ptrField' declared here}}
+} FooUnion;
+
+kernel void pointer_in_union_arg(FooUnion arg) { }// expected-error{{struct or union kernel parameters may not contain OpenCL object}}
+
+typedef struct NestedPointer // expected-note{{'NestedPointer' declared here}}
+{
+  int x;
+  struct InnerNestedPointer
+  {
+    int* ptrField; // expected-note{{member 'ptrField' declared here}}
+  } inner; // expected-note{{member 'inner' declared here}}
+} NestedPointer;
+
+kernel void pointer_in_nested_struct_arg(NestedPointer arg) { }// expected-error{{struct or union kernel parameters may not contain OpenCL objects}}
+
+struct NestedPointerComplex // expected-note{{'NestedPointerComplex' declared here}}
+{
+  int foo;
+  float bar;
+
+  struct InnerNestedPointerComplex
+  {
+    int innerFoo;
+    int* innerPtrField; // expected-note{{member 'innerPtrField' declared here}}
+  } inner; // expected-note{{member 'inner' declared here}}
+
+  float y;
+  float z[4];
+};
+
+kernel void pointer_in_nested_struct_arg_complex(struct NestedPointerComplex arg) { }// expected-error{{struct or union kernel parameters may not contain OpenCL objects}}
+
+typedef struct NestedBool // expected-note 2 {{'NestedBool' declared here}}
+{
+  int x;
+  struct InnerNestedBool
+  {
+    bool boolField; // expected-note 2 {{member 'boolField' declared here}}
+  } inner; // expected-note 2 {{member 'inner' declared here}}
+} NestedBool;
+
+kernel void bool_in_nested_struct_arg(NestedBool arg) { } // expected-error{{'NestedBool' (aka 'struct NestedBool') cannot be used to declare a kernel function parameter}}
+
+// Warning emitted again for argument used in other kernel
+kernel void bool_in_nested_struct_arg_again(NestedBool arg) { } // expected-error{{'NestedBool' (aka 'struct NestedBool') cannot be used to declare a kernel function parameter}}
+
+
+// Check for note with a struct not defined inside the struct
+typedef struct NestedBool2Inner
+{
+  bool boolField; // expected-note{{member 'boolField' declared here}}
+} NestedBool2Inner;
+
+typedef struct NestedBool2 // expected-note{{'NestedBool2' declared here}}
+{
+  int x;
+  NestedBool2Inner inner; // expected-note{{member 'inner' declared here}}
+} NestedBool2;
+
+kernel void bool_in_nested_struct_2_arg(NestedBool2 arg) { } // expected-error{{'NestedBool2' (aka 'struct NestedBool2') cannot be used to declare a kernel function parameter}}
+
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to