Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6423,16 +6423,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 @@ -6042,6 +6042,143 @@ } } +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; + + 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 IntTypeKernelParam; + } + } + } + + 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; + } + + const RecordType *RT = PT->getAs(); + assert(RT && "Should only have RecordType arguments at this point"); + + SmallVector NestedStructQueue; + NestedStructQueue.push_back(RT); + + while (!NestedStructQueue.empty()) { + const RecordType *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(); + switch (getOpenCLKernelParameterType(QT)) { + case ValidKernelParam: + break; + + case PtrKernelParam: + case PtrPtrKernelParam: + // 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. + S.Diag(Param->getLocation(), + diag::err_struct_with_pointers_kernel_param); + S.Diag(FD->getLocation(), diag::note_member_declared_here) + << FD->getDeclName(); + D.setInvalidType(); + break; + + case BoolKernelParam: + case HalfKernelParam: + case IntTypeKernelParam: + case EventKernelParam: + S.Diag(Param->getLocation(), diag::err_bad_kernel_param_type) << PT; + S.Diag(FD->getLocation(), diag::note_member_declared_here) + << FD->getDeclName(); + D.setInvalidType(); + break; + + case RecordKernelParam: + NestedStructQueue.push_back(QT->getAs()); + } + } + } +} + NamedDecl* Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo, LookupResult &Previous, @@ -6777,34 +6914,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,96 @@ +// 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; + + +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 +{ + 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 +{ + 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 +{ + 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 +{ + 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 +{ + int x; + struct InnerNestedPointer + { + int* ptrField; // expected-note{{member 'ptrField' declared here}} + } inner; +} NestedPointer; + +kernel void pointer_in_nested_struct_arg(NestedPointer arg) { }// expected-error{{struct or union kernel parameters may not contain OpenCL objects}} + +typedef struct NestedBool +{ + int x; + struct InnerNestedBool + { + bool boolField; // expected-note{{member 'boolField' declared here}} + } 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 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 +{ + int x; + NestedBool2Inner inner; +} 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}} +