-
Notifications
You must be signed in to change notification settings - Fork 802
[SYCL] Update kernel name diagnostics logic #3069
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b96891f
55efe70
f7fe8cc
a71a016
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3025,19 +3025,34 @@ class SYCLKernelNameTypeVisitor | |
| S.getASTContext().getLangOpts().SYCLUnnamedLambda; | ||
| const DeclContext *DeclCtx = Tag->getDeclContext(); | ||
| if (DeclCtx && !UnnamedLambdaEnabled) { | ||
| auto *NameSpace = dyn_cast_or_null<NamespaceDecl>(DeclCtx); | ||
| if (NameSpace && NameSpace->isStdNamespace()) { | ||
| S.Diag(KernelInvocationFuncLoc, diag::err_sycl_kernel_incorrectly_named) | ||
| << KernelNameType; | ||
| S.Diag(KernelInvocationFuncLoc, diag::note_invalid_type_in_sycl_kernel) | ||
| << /* kernel name cannot be a type in the std namespace */ 2 | ||
| << QualType(Tag->getTypeForDecl(), 0); | ||
| IsInvalid = true; | ||
| return; | ||
| } | ||
| if (!DeclCtx->isTranslationUnit() && !isa<NamespaceDecl>(DeclCtx)) { | ||
| const bool KernelNameIsMissing = Tag->getName().empty(); | ||
| if (KernelNameIsMissing) { | ||
|
|
||
| while (!DeclCtx->isTranslationUnit()) { | ||
| auto *NSDecl = dyn_cast_or_null<NamespaceDecl>(DeclCtx); | ||
| if (NSDecl && NSDecl->isStdNamespace()) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Recursive check will now throw an error for
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right that
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, huh... I figured we would use something like the text-dumpers use, which visit each node along the way individually. They seem to better fit the integration-header/kernel name checking.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean things like
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the context. We definitely need to prevent std::something::SomethingElse, the fact that we aren't is likely a regression (as I seem to remember it worked before the refactor? Unfortunate there were no tests). This patch does NOT seem to do anything with the integration header though, which would need to be taught how to forward-declare these globally visible names. That said, I don't think this necessary part of this is actually possible with integration-headers (see my other comment). At the moment, I'd suggest a separate patch to fix the std::something::something_else case (as well as the something::<anon_ns>::another::type case), and we wait for the nested-struct version until we can get @mkinsner and the SYCL spec clarifications of what we have to do. if we have to support this nested struct case, I think we have to figure out an alternative to integration headers for runtime support.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Refactor did not break anything. std::something::SomethingElse was not diagnosed before.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting. That diffs from my memory(though it is historically not particularly accurate, so I can buy I mis-remembered), but still needs fixing. The rule to disallow std::string has the same motivation/conclusion as disallowing std::ranges::views::view_base
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can look at the history. I can fix it, just clarifying that it was something not previously supported and something I encountered while working on the original issue.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe you :) I could swear it was caught at SOME point (as I was sure I encountered it at one point...), but it doesn't mean it survived until the refactor in question I guess. |
||
| S.Diag(KernelInvocationFuncLoc, | ||
| diag::err_sycl_kernel_incorrectly_named) | ||
| << KernelNameType; | ||
| S.Diag(KernelInvocationFuncLoc, | ||
| diag::note_invalid_type_in_sycl_kernel) | ||
| << /* kernel name cannot be a type in the std namespace */ 2 | ||
| << QualType(Tag->getTypeForDecl(), 0); | ||
| IsInvalid = true; | ||
| return; | ||
| } | ||
| if (NSDecl && NSDecl->isAnonymousNamespace()) { | ||
| S.Diag(KernelInvocationFuncLoc, | ||
| diag::err_sycl_kernel_incorrectly_named) | ||
| << KernelNameType; | ||
| S.Diag(KernelInvocationFuncLoc, | ||
| diag::note_invalid_type_in_sycl_kernel) | ||
| << /* kernel name is not globally-visible */ 0 | ||
| << QualType(Tag->getTypeForDecl(), 0); | ||
| IsInvalid = true; | ||
| return; | ||
| } | ||
|
|
||
| const bool UnnamedTypeUsed = Tag->getName().empty(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be outside the while loop right? |
||
| if (UnnamedTypeUsed) { | ||
| S.Diag(KernelInvocationFuncLoc, | ||
| diag::err_sycl_kernel_incorrectly_named) | ||
| << KernelNameType; | ||
|
|
@@ -3047,7 +3062,7 @@ class SYCLKernelNameTypeVisitor | |
| IsInvalid = true; | ||
| return; | ||
| } | ||
| if (Tag->isCompleteDefinition()) { | ||
| if (isa<FunctionDecl>(DeclCtx) && Tag->isCompleteDefinition()) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The if at L3065 is to handle cases like the following: The order of decl context for InvalidKernelName1 is And
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess I'm more asking for a situation where that isa is required. It seems that both your examples are positive things here, but what cases are you trying to exclude from these 'if's?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Trying to exclude cases like nested named structs and classes inside structs.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm I see... You're limiting the check to only function scope. I think it might be better to instead check if the scope is file context or something, and throw the error if it isn't. @erichkeane thoughts?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I unfortunately don't know the visitor here well enough to know exactly what is going on here, but this part of the change "feels" wrong, but I obviously cannot specify what makes me think that. It just generally seems like the visitor isn't doing what it is supposed to if all this is necessary, and these two 'if' statements are particularly constrained for situations that are not sufficiently thought out. I'm hoping that @Fznamznon can analyze the patch/problem and make sure this is the right approach.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, the original purpose of this PR was to fix cases when error was reported for nested name specifiers, i.e. Turns out it is a valid case and we diagnosed that.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're correct in a class inside a function scope is not globally visible. So when the 'visitor' finds a function-type/decl, I would assume it would reject it. Globally visible names appear only in namespaces, record types, or enums I believe (though I don't believe you can create a record-type inside an enum in any way, unless there is an incredible trick I'm missing).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I looked at "5.2 Naming of kernels" in the SYCL2020-provisional-spec and based on my understanding of rule#2,3,4 made this change. |
||
| S.Diag(KernelInvocationFuncLoc, | ||
| diag::err_sycl_kernel_incorrectly_named) | ||
| << KernelNameType; | ||
|
|
@@ -3056,11 +3071,16 @@ class SYCLKernelNameTypeVisitor | |
| << /* kernel name is not globally-visible */ 0 | ||
| << QualType(Tag->getTypeForDecl(), 0); | ||
| IsInvalid = true; | ||
| } else { | ||
| return; | ||
| } | ||
| if (isa<CXXMethodDecl>(DeclCtx) && !Tag->isCompleteDefinition()) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This and line 3065 seems to change the logic quite a bit here. Can you explain what is happening here?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The if block at L3076 is to handle the cases like: The order of DeclContexts for fake_kernel2 is So I just check if the DeclCtx is clang::Decl::CXXMethod and "class fake_kernel2" does not have complete definition.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you need the context check here? Won't the warning for implicit decl be generated even without this check? |
||
| S.Diag(KernelInvocationFuncLoc, diag::warn_sycl_implicit_decl); | ||
| S.Diag(Tag->getSourceRange().getBegin(), diag::note_previous_decl) | ||
| << Tag->getName(); | ||
| } | ||
|
|
||
| // Repeat the above checks for DeclCtx's in the parent-chain | ||
| DeclCtx = DeclCtx->getParent(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| // RUN: %clang_cc1 -fsycl -fsycl-is-device -internal-isystem %S/Inputs -fsyntax-only -sycl-std=2020 -verify %s | ||
|
|
||
| #include "sycl.hpp" | ||
|
|
||
| struct NestedStruct1 { | ||
| struct NestedStruct2 { | ||
| struct NestedStruct3 {}; | ||
| }; | ||
| }; | ||
|
|
||
| namespace { | ||
| struct StructInAnonymousNS {}; | ||
| } // namespace | ||
|
|
||
| namespace ValidNS { | ||
| struct StructinValidNS {}; | ||
| } // namespace ValidNS | ||
|
|
||
| struct Parent { | ||
| using A = struct { | ||
| struct Child1 { | ||
| struct Child2 {}; | ||
| }; | ||
| }; | ||
| }; | ||
|
|
||
| struct MyWrapper { | ||
|
|
||
| public: | ||
| void test() { | ||
| cl::sycl::queue q; | ||
| struct StructInsideFunc {}; | ||
|
|
||
| // no error | ||
| q.submit([&](cl::sycl::handler &h) { | ||
| h.single_task<NestedStruct1::NestedStruct2::NestedStruct3>([] {}); | ||
| }); | ||
|
|
||
| // no error | ||
| q.submit([&](cl::sycl::handler &h) { | ||
| h.single_task<ValidNS::StructinValidNS>([] {}); | ||
| }); | ||
|
|
||
| // no error | ||
| q.submit([&](cl::sycl::handler &h) { | ||
| h.single_task<Parent::A::Child1::Child2>([] {}); | ||
| }); | ||
|
|
||
| // expected-error@Inputs/sycl.hpp:220 {{'(anonymous namespace)::StructInAnonymousNS' is an invalid kernel name type}} | ||
| // expected-note@Inputs/sycl.hpp:220 {{'(anonymous namespace)::StructInAnonymousNS' should be globally-visible}} | ||
| // expected-note@+2{{in instantiation of function template specialization}} | ||
| q.submit([&](cl::sycl::handler &h) { | ||
| h.single_task<StructInAnonymousNS>([] {}); | ||
| }); | ||
|
|
||
| // expected-error@Inputs/sycl.hpp:220 {{'StructInsideFunc' is an invalid kernel name type}} | ||
| // expected-note@Inputs/sycl.hpp:220 {{'StructInsideFunc' should be globally-visible}} | ||
| // expected-note@+2{{in instantiation of function template specialization}} | ||
| q.submit([&](cl::sycl::handler &h) { | ||
| h.single_task<StructInsideFunc>([] {}); | ||
| }); | ||
| } | ||
| }; | ||
|
|
||
| int main() { | ||
| cl::sycl::queue q; | ||
|
|
||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,20 +52,12 @@ struct MyWrapper { | |
| h.single_task<namespace1::KernelName<InvalidKernelName2>>([] {}); | ||
| }); | ||
|
|
||
| #ifndef __SYCL_UNNAMED_LAMBDA__ | ||
| // expected-error@Inputs/sycl.hpp:220 {{'MyWrapper::InvalidKernelName0' is an invalid kernel name type}} | ||
| // expected-note@Inputs/sycl.hpp:220 {{'MyWrapper::InvalidKernelName0' should be globally-visible}} | ||
| // expected-note@+3{{in instantiation of function template specialization}} | ||
| #endif | ||
| // no error | ||
| q.submit([&](cl::sycl::handler &h) { | ||
| h.single_task<InvalidKernelName0>([] {}); | ||
| }); | ||
|
|
||
| #ifndef __SYCL_UNNAMED_LAMBDA__ | ||
| // expected-error@Inputs/sycl.hpp:220 {{'namespace1::KernelName<MyWrapper::InvalidKernelName3>' is an invalid kernel name type}} | ||
| // expected-note@Inputs/sycl.hpp:220 {{'MyWrapper::InvalidKernelName3' should be globally-visible}} | ||
| // expected-note@+3{{in instantiation of function template specialization}} | ||
| #endif | ||
| //no error | ||
| q.submit([&](cl::sycl::handler &h) { | ||
| h.single_task<namespace1::KernelName<InvalidKernelName3>>([] {}); | ||
| }); | ||
|
|
@@ -85,21 +77,13 @@ struct MyWrapper { | |
| }); | ||
|
|
||
| using InvalidAlias = InvalidKernelName4; | ||
| #ifndef __SYCL_UNNAMED_LAMBDA__ | ||
| // expected-error@Inputs/sycl.hpp:220 {{'MyWrapper::InvalidKernelName4' is an invalid kernel name type}} | ||
| // expected-note@Inputs/sycl.hpp:220 {{'MyWrapper::InvalidKernelName4' should be globally-visible}} | ||
| // expected-note@+3{{in instantiation of function template specialization}} | ||
| #endif | ||
| // no error | ||
| q.submit([&](cl::sycl::handler &h) { | ||
| h.single_task<InvalidAlias>([] {}); | ||
| }); | ||
|
|
||
| using InvalidAlias1 = InvalidKernelName5; | ||
| #ifndef __SYCL_UNNAMED_LAMBDA__ | ||
| // expected-error@Inputs/sycl.hpp:220 {{'namespace1::KernelName<MyWrapper::InvalidKernelName5>' is an invalid kernel name type}} | ||
| // expected-note@Inputs/sycl.hpp:220 {{'MyWrapper::InvalidKernelName5' should be globally-visible}} | ||
| // expected-note@+3{{in instantiation of function template specialization}} | ||
| #endif | ||
| // no error | ||
| q.submit([&](cl::sycl::handler &h) { | ||
| h.single_task<namespace1::KernelName<InvalidAlias1>>([] {}); | ||
| }); | ||
|
|
@@ -124,4 +108,4 @@ int main() { | |
| q.submit([&](cl::sycl::handler &h) { h.single_task([] {}); }); | ||
|
|
||
| return 0; | ||
| } | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Restore it. |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can just be a dyn_cast, otherwise the condition in the 'while' is UB :)