Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6423,8 +6423,10 @@ "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_arg_type : Error< + "%0 cannot be used to declare a kernel function argument">; +def err_struct_with_pointers_kernel_arg : Error< + "struct or union kernel arguments 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 @@ -1433,6 +1433,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 @@ -5914,6 +5914,101 @@ } } +static bool isValidOpenCLKernelArgumentType(QualType PT) { + // 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. + if (PT->isIntegerType()) { + if (PT->isBooleanType()) + return false; + + if (const TypedefType *Typedef = dyn_cast(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 false; + } + } + } + + // OpenCL v1.2 s6.8 n: + // A kernel function argument cannot be declared + // of event_t type. + if (PT->isEventT()) + return false; + + if (PT->isHalfType()) + return false; + + return true; +} + +static const RecordType *getAsStructOrUnionType(QualType QT) { + const RecordType *RT = QT->getAsStructureType(); + if (!RT) + RT = QT->getAsUnionType(); + + return RT; +} + +void Sema::checkIsValidOpenCLKernelArgument(Declarator &D, ParmVarDecl *Param) { + 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(); + } + + if (!isValidOpenCLKernelArgumentType(PT)) { + Diag(Param->getLocation(), diag::err_bad_kernel_arg_type) << PT; + D.setInvalidType(); + } + + // 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. + SmallVector NestedStructQueue; + const RecordType *RT = getAsStructOrUnionType(PT); + if (RT) + NestedStructQueue.push_back(RT); + + while (!NestedStructQueue.empty()) { + RT = NestedStructQueue.pop_back_val(); + + const RecordDecl *RD = RT->getDecl(); + for (RecordDecl::field_iterator I = RD->field_begin(), + E = RD->field_end(); I != E; ++I) { + const FieldDecl *FD = *I; + + QualType QT = FD->getType(); + if (QT->isPointerType() || QT->isImageType()) { + Diag(Param->getLocation(), diag::err_struct_with_pointers_kernel_arg); + D.setInvalidType(); + } + + if (!isValidOpenCLKernelArgumentType(QT)) { + // TODO: A more specific warning about which struct members forbid this + // would be useful + Diag(Param->getLocation(), diag::err_bad_kernel_arg_type) << PT; + D.setInvalidType(); + } + + if (const RecordType *InnerRecord = getAsStructOrUnionType(QT)) + NestedStructQueue.push_back(InnerRecord); + } + } +} + NamedDecl* Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo, LookupResult &Previous, @@ -6651,34 +6746,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(); - } + checkIsValidOpenCLKernelArgument(D, Param); } } Index: test/SemaOpenCL/invalid-kernel-arguments.cl =================================================================== --- /dev/null +++ test/SemaOpenCL/invalid-kernel-arguments.cl @@ -0,0 +1,119 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#pragma OPENCL EXTENSION cl_khr_fp16 : enable + + +// Disallowed: arguments 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; + + +kernel void bool_arg(bool x) // expected-error{{'bool' cannot be used to declare a kernel function argument}} +{ + +} + +kernel void half_arg(half x) // expected-error{{'half' cannot be used to declare a kernel function argument}} +{ + +} + +kernel void size_t_arg(size_t x) // expected-error{{'size_t' (aka 'unsigned int') cannot be used to declare a kernel function argument}} +{ + +} + +kernel void ptrdiff_t_arg(ptrdiff_t x) // expected-error{{ptrdiff_t' (aka 'unsigned int') cannot be used to declare a kernel function argument}} +{ + +} + + +kernel void intptr_t_arg(intptr_t x) // expected-error{{'intptr_t' (aka 'unsigned int') cannot be used to declare a kernel function argument}} +{ + +} + +kernel void uintptr_t_arg(uintptr_t x) // expected-error{{uintptr_t' (aka 'unsigned int') cannot be used to declare a kernel function argument}} +{ + +} + +typedef struct ContainsBool +{ + bool x; +} ContainsBool; + +kernel void bool_in_struct_arg(ContainsBool x) // expected-error{{'ContainsBool' (aka 'struct ContainsBool') cannot be used to declare a kernel function argument}} +{ + +} + + + +typedef struct FooImage2D +{ + image2d_t* ptrField; +} FooImage2D; + +kernel void image_in_struct_arg(FooImage2D arg) // expected-error{{struct or union kernel arguments may not contain OpenCL object}} +{ + +} + + +typedef struct Foo +{ + int* ptrField; +} Foo; + +kernel void pointer_in_struct_arg(Foo arg) // expected-error{{struct or union kernel arguments may not contain OpenCL object}} +{ + +} + +typedef union FooUnion +{ + int* ptrField; +} FooUnion; + +kernel void pointer_in_union_arg(FooUnion arg) // expected-error{{struct or union kernel arguments may not contain OpenCL object}} +{ + +} + +typedef struct NestedPointer +{ + int x; + struct InnerNestedPointer + { + int* ptrField; + } inner; +} NestedPointer; + +kernel void pointer_in_nested_struct_arg(NestedPointer arg) // expected-error{{struct or union kernel arguments may not contain OpenCL objects}} +{ + +} + +typedef struct NestedBool +{ + int x; + struct InnerNestedBool + { + bool boolField; + } inner; +} NestedBool; + +kernel void bool_in_nested_struct_arg(NestedBool arg) // expected-error{{'NestedBool' (aka 'struct NestedBool') cannot be used to declare a kernel function argument}} +{ + +} +