OpenCL disallows various types (bool, size_t, pointers, or structs containing them) to be used for kernel arguments, but currently they are accepted. The error message in the struct case is currently a bit awkward since it doesn't describe which struct member is at fault for not allowing the kernel argument.
Details
Diff Detail
Event Timeline
+ if (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 false;
+ }
+ }
No. It's completely insane to print an error message based on typedef
sugar. Someone could write "typedef size_t mysize_t;", or "typedef
__typeof(sizeof(int)) mysize_t;", or "#if SIZE_MAX == UINT_MAX [...]
typdef unsigned mysize_t [...]".
+static const RecordType *getAsStructOrUnionType(QualType QT) {
+ const RecordType *RT = QT->getAsStructureType();
+ if (!RT)
+ RT = QT->getAsUnionType();
+
+ return RT;
+}
This is equivalent to QT->getAs<RecordType>().
+ 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();
Diag(FD->getLocation(), diag::note_field_declared_here);
Please just fix this.
+kernel void bool_arg(bool x) // expected-error{{'bool' cannot be used
to declare a kernel function argument}}
+{
+
+}
"kernel void bool_arg(bool x) {}" would save 3 lines of useless whitespace.
-Eli
lib/Sema/SemaDecl.cpp | ||
---|---|---|
5917 | "...ParameterType" not "...ArgumentType". | |
5919 | I assume this part of the OpenCL spec is defective and it means "Parameters" not "Arguments" here? | |
5927–5937 | OpenCL 6.1.1 seems to imply that size_t, ptrdiff_t, intptr_t and uintptr_t are distinct built-in types in OpenCL, rather than being typedefs for some standard integer type, and it's the underlying types that we should be checking for here, not the typedefs. "typedef size_t my_size_t;" should also be prohibited here (as should, presumably, a typedef for __typeof(sizeof(0))). However, the OpenCL spec is unclear, so I'm somewhat guessing... :( | |
5952–5958 | This is just QT->getAs<RecordType>(). | |
5960 | This doesn't need to be a Sema member; a static non-member function would be preferred. | |
5966–5969 | It would seem cleaner to fold both this pointer check and the one below into isValidOpenCLKernelArgumentType. You can return an enum value from there to indicate the flavor of problem, and fold together the diagnostics with a %select. You can also pass a value into this function to indicate whether you're checking a direct argument (where only a pointer-to-pointer is blocked) or a field of an argument (where any pointer is blocked). | |
5989–5990 | Have you considered caching the result of this check? Or at least, storing a set of the types for which you've performed the check -- we don't really need to diagnose the same type more than once. | |
6000–6002 | Maybe reformulate the queue as a recursive walk, and produce notes on the way back up after diagnosing a problem? |
Partially address review comments. Remove Sema member, rename argument to parameter, combine parameter check function, and use diag::note_member_declared_here on structs with invalid types in them. Does not yet show the chain of fields that are problematic, just the lowest one. Also update event_t test for changed warning.
lib/Sema/SemaDecl.cpp | ||
---|---|---|
5919 | I just copied the text out of the spec | |
5927–5937 | I wasn't really sure what to do about these. I didn't see an equivalent in the builtin type enums I found. It sort of depends on the library implementation. Apple and libclc at least implicitly include a header before the program which has these typedefs in them. For example, the Apple header defines them as I don't actually see them defined in libclc, but size_t is used so I have no idea where that is coming from. | |
5989–5990 | The type itself isn't the problem, it's only when used as a kernel argument. I don't think only diagnosing it on the first parameter found and then not on a second use as another argument would make much sense. |
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; ^
For the struct diagnostic, it's not very clear what the notes mean (sure, there are members declared there, but why are you pointing them out?). Maybe you could produce something like:
test/SemaOpenCL/arst2.cl:35:62: error: 'NestedPointer' cannot be used as the type of a kernel function parameter because it contains a field of pointer type 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:32:16: note: within field of type 'Inner' declared here struct Inner inner; ^ test/SemaOpenCL/arst2.cl:26:21: note: within field of type 'InnerInner' declared here struct InnerInner b; ^ test/SemaOpenCL/arst2.cl:12:8: note: field of pointer type 'int*' declared here int* foo; ^
include/clang/Basic/DiagnosticSemaKinds.td | ||
---|---|---|
6453 | "%0 cannot be used as the type of a kernel function parameter" would be better. | |
6454–6455 | Instead of "struct or union", please say which, and instead of "OpenCL objects" (which is not correct in several cases) please include the actual type as you do in the previous diagnostic. | |
include/clang/Sema/Sema.h | ||
1449–1450 | Remove this, you don't define such a Sema member any more. | |
lib/Sema/SemaDecl.cpp | ||
6178–6196 | This would be cleaner if you made this function recursive. | |
6236 | I don't think this is necessary. |
Change wording of nested struct notes, remove old Sema member declaration, fix pop_back on empty vector.
Add cache of valid types encountered (but only is within the same kernel declaration).
error: struct kernel parameters may not contain OpenCL objects
kernel void pointer_in_nested_struct_arg(struct NestedPointer arg) { }
^
/tmp/arst.cl:23:8: note: within field of type 'NestedPointer' declared here
struct NestedPointer
^
/tmp/arst.cl:27:14: note: within field of type 'struct Inner' declared here
struct Inner inner;
^
/tmp/arst.cl:20:19: note: within field of type 'struct InnerInner' declared here
struct InnerInner b;
^
/tmp/arst.cl:4:6: note: field of illegal pointer type 'int *' declared here
int* foo;
^
Or for a non-pointer
error: 'struct NestedBool' cannot be used as the type of a kernel parameter
kernel void bool_in_nested_struct_arg(struct NestedBool arg) { }
^
/tmp/arst.cl:23:8: note: within field of type 'NestedBool' declared here
struct NestedBool
^
/tmp/arst.cl:27:14: note: within field of type 'struct Inner' declared here
struct Inner inner;
^
/tmp/arst.cl:20:19: note: within field of type 'struct InnerInner' declared here
struct InnerInner b;
^
/tmp/arst.cl:4:6: note: field of illegal type 'bool' declared here
bool foo;
Couple of minor tweaks, then LGTM
include/clang/Basic/DiagnosticSemaKinds.td | ||
---|---|---|
6459–6460 | "may not contain OpenCL objects" doesn't seem appropriate here. "may not contain pointers" seems to match the cases where this diagnostic is emitted. | |
6463–6464 | Please use a %select in the place of %0 here (we try to keep our diagnostics translatable, even though we don't support localization yet). |
"%0 cannot be used as the type of a kernel function parameter" would be better.