[clang] [OpenACC} Improve diagnostics for 'tag's on clauses/directives (PR #77957)

via cfe-commits cfe-commits at lists.llvm.org
Fri Jan 12 10:15:58 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Erich Keane (erichkeane)

<details>
<summary>Changes</summary>

The 'cache' directive and various clauses have a 'tag' name that is optional.  This patch cleans up the use of the 'cache' version so that we get a nicer diagnostic, and enables us to do the same with clauses in the same situation.

---
Full diff: https://github.com/llvm/llvm-project/pull/77957.diff


4 Files Affected:

- (modified) clang/include/clang/Basic/DiagnosticParseKinds.td (+2) 
- (modified) clang/include/clang/Basic/OpenACCKinds.h (+156) 
- (modified) clang/lib/Parse/ParseOpenACC.cpp (+49-13) 
- (modified) clang/test/ParserOpenACC/parse-cache-construct.c (+12) 


``````````diff
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 088f8b74983c86..31530444d6920c 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1366,6 +1366,8 @@ def err_acc_invalid_open_paren
     : Error<"expected clause-list or newline in OpenACC directive">;
 def err_acc_invalid_default_clause_kind
     : Error<"invalid value for 'default' clause; expected 'present' or 'none'">;
+def err_acc_invalid_tag_kind
+    : Error<"invalid tag %0 on '%1' %select{directive|clause}2">;
 
 // OpenMP support.
 def warn_pragma_omp_ignored : Warning<
diff --git a/clang/include/clang/Basic/OpenACCKinds.h b/clang/include/clang/Basic/OpenACCKinds.h
index e860893b933ca3..a06db98fcba112 100644
--- a/clang/include/clang/Basic/OpenACCKinds.h
+++ b/clang/include/clang/Basic/OpenACCKinds.h
@@ -14,6 +14,9 @@
 #ifndef LLVM_CLANG_BASIC_OPENACCKINDS_H
 #define LLVM_CLANG_BASIC_OPENACCKINDS_H
 
+#include "clang/Basic/Diagnostic.h"
+#include "llvm/Support/ErrorHandling.h"
+
 namespace clang {
 // Represents the Construct/Directive kind of a pragma directive. Note the
 // OpenACC standard is inconsistent between calling these Construct vs
@@ -62,6 +65,76 @@ enum class OpenACCDirectiveKind {
   Invalid,
 };
 
+inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &Out,
+                                             OpenACCDirectiveKind K) {
+  switch (K) {
+  case OpenACCDirectiveKind::Parallel:
+    return Out << "parallel";
+
+  case OpenACCDirectiveKind::Serial:
+    return Out << "serial";
+
+  case OpenACCDirectiveKind::Kernels:
+    return Out << "kernels";
+
+  case OpenACCDirectiveKind::Data:
+    return Out << "data";
+
+  case OpenACCDirectiveKind::EnterData:
+    return Out << "enter data";
+
+  case OpenACCDirectiveKind::ExitData:
+    return Out << "exit data";
+
+  case OpenACCDirectiveKind::HostData:
+    return Out << "host_data";
+
+  case OpenACCDirectiveKind::Loop:
+    return Out << "loop";
+
+  case OpenACCDirectiveKind::Cache:
+    return Out << "cache";
+
+  case OpenACCDirectiveKind::ParallelLoop:
+    return Out << "parallel loop";
+
+  case OpenACCDirectiveKind::SerialLoop:
+    return Out << "serial loop";
+
+  case OpenACCDirectiveKind::KernelsLoop:
+    return Out << "kernels loop";
+
+  case OpenACCDirectiveKind::Atomic:
+    return Out << "atomic";
+
+  case OpenACCDirectiveKind::Declare:
+    return Out << "declare";
+
+  case OpenACCDirectiveKind::Init:
+    return Out << "init";
+
+  case OpenACCDirectiveKind::Shutdown:
+    return Out << "shutdown";
+
+  case OpenACCDirectiveKind::Set:
+    return Out << "set";
+
+  case OpenACCDirectiveKind::Update:
+    return Out << "update";
+
+  case OpenACCDirectiveKind::Wait:
+    return Out << "wait";
+
+  case OpenACCDirectiveKind::Routine:
+    return Out << "routine";
+
+  case OpenACCDirectiveKind::Invalid:
+    return Out << "<invalid>";
+
+  }
+  llvm_unreachable("Uncovered directive kind");
+}
+
 enum class OpenACCAtomicKind {
   Read,
   Write,
@@ -138,6 +211,89 @@ enum class OpenACCClauseKind {
   Invalid,
 };
 
+inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &Out,
+                                             OpenACCClauseKind K) {
+  switch (K) {
+  case OpenACCClauseKind::Finalize:
+    return Out << "finalize";
+
+  case OpenACCClauseKind::IfPresent:
+    return Out << "if_present";
+
+  case OpenACCClauseKind::Seq:
+    return Out << "seq";
+
+  case OpenACCClauseKind::Independent:
+    return Out << "independent";
+
+  case OpenACCClauseKind::Auto:
+    return Out << "auto";
+
+  case OpenACCClauseKind::Worker:
+    return Out << "worker";
+
+  case OpenACCClauseKind::Vector:
+    return Out << "vector";
+
+  case OpenACCClauseKind::NoHost:
+    return Out << "nohost";
+
+  case OpenACCClauseKind::Default:
+    return Out << "default";
+
+  case OpenACCClauseKind::If:
+    return Out << "if";
+
+  case OpenACCClauseKind::Self:
+    return Out << "self";
+
+  case OpenACCClauseKind::Copy:
+    return Out << "copy";
+
+  case OpenACCClauseKind::UseDevice:
+    return Out << "use_device";
+
+  case OpenACCClauseKind::Attach:
+    return Out << "attach";
+
+  case OpenACCClauseKind::Delete:
+    return Out << "delete";
+
+  case OpenACCClauseKind::Detach:
+    return Out << "detach";
+
+  case OpenACCClauseKind::Device:
+    return Out << "device";
+
+  case OpenACCClauseKind::DevicePtr:
+    return Out << "deviceptr";
+
+  case OpenACCClauseKind::DeviceResident:
+    return Out << "device_resident";
+
+  case OpenACCClauseKind::FirstPrivate:
+    return Out << "firstprivate";
+
+  case OpenACCClauseKind::Host:
+    return Out << "host";
+
+  case OpenACCClauseKind::Link:
+    return Out << "link";
+
+  case OpenACCClauseKind::NoCreate:
+    return Out << "no_create";
+
+  case OpenACCClauseKind::Present:
+    return Out << "present";
+
+  case OpenACCClauseKind::Private:
+    return Out << "private";
+
+  case OpenACCClauseKind::Invalid:
+    return Out << "<invalid>";
+  }
+  llvm_unreachable("Uncovered clause kind");
+}
 enum class OpenACCDefaultClauseKind {
   /// 'none' option.
   None,
diff --git a/clang/lib/Parse/ParseOpenACC.cpp b/clang/lib/Parse/ParseOpenACC.cpp
index 83378a094492b2..fb4364fa1dd8a7 100644
--- a/clang/lib/Parse/ParseOpenACC.cpp
+++ b/clang/lib/Parse/ParseOpenACC.cpp
@@ -164,6 +164,49 @@ bool isOpenACCSpecialToken(OpenACCSpecialTokenKind Kind, Token Tok) {
   llvm_unreachable("Unknown 'Kind' Passed");
 }
 
+// Used for cases where we have a token we want to check against an
+// 'identifier-like' token, but don't want to give awkward error messages in
+// cases where it is accidentially a keyword.
+bool isTokenIdentifierOrKeyword(Parser &P, Token Tok) {
+  if (Tok.is(tok::identifier))
+    return true;
+
+  if (!Tok.isAnnotation() && Tok.getIdentifierInfo() &&
+      Tok.getIdentifierInfo()->isKeyword(P.getLangOpts()))
+    return true;
+
+  return false;
+}
+
+
+/// Parses and consumes an identifer followed immediately by a single colon, and
+/// diagnoses if it is not the 'special token' kind that we require. Used when
+/// the tag is the only valid value.
+/// Return 'true' if the special token was matched, false if no special token,
+/// or an invalid special token was found.
+template <typename DirOrClauseTy>
+bool TryParseAndConsumeSpecialTokenKind(Parser &P, OpenACCSpecialTokenKind Kind,
+                                        DirOrClauseTy DirOrClause) {
+  Token IdentTok = P.getCurToken();
+  // If this is an identifier-like thing followed by ':', it is one of the
+  // OpenACC 'special' name tags, so consume it.
+  if (isTokenIdentifierOrKeyword(P, IdentTok) && P.NextToken().is(tok::colon)) {
+    P.ConsumeToken();
+    P.ConsumeToken();
+
+    if (!isOpenACCSpecialToken(Kind, IdentTok)) {
+      P.Diag(IdentTok, diag::err_acc_invalid_tag_kind)
+          << IdentTok.getIdentifierInfo() << DirOrClause
+          << std::is_same_v<DirOrClauseTy, OpenACCClauseKind>;
+      return false;
+    }
+
+    return true;
+  }
+
+  return false;
+}
+
 bool isOpenACCDirectiveKind(OpenACCDirectiveKind Kind, Token Tok) {
   if (!Tok.is(tok::identifier))
     return false;
@@ -218,11 +261,7 @@ bool isOpenACCDirectiveKind(OpenACCDirectiveKind Kind, Token Tok) {
 bool expectIdentifierOrKeyword(Parser &P) {
   Token Tok = P.getCurToken();
 
-  if (Tok.is(tok::identifier))
-    return false;
-
-  if (!Tok.isAnnotation() && Tok.getIdentifierInfo() &&
-      Tok.getIdentifierInfo()->isKeyword(P.getLangOpts()))
+  if (isTokenIdentifierOrKeyword(P, Tok))
     return false;
 
   P.Diag(P.getCurToken(), diag::err_expected) << tok::identifier;
@@ -674,14 +713,11 @@ void Parser::ParseOpenACCCacheVarList() {
     return;
 
   // The VarList is an optional `readonly:` followed by a list of a variable
-  // specifications.  First, see if we have `readonly:`, else we back-out and
-  // treat it like the beginning of a reference to a potentially-existing
-  // `readonly` variable.
-  if (isOpenACCSpecialToken(OpenACCSpecialTokenKind::ReadOnly, Tok) &&
-      NextToken().is(tok::colon)) {
-    // Consume both tokens.
-    ConsumeToken();
-    ConsumeToken();
+  // specifications. Consume something that looks like a 'tag', and diagnose if
+  // it isn't 'readonly'.
+  if (TryParseAndConsumeSpecialTokenKind(*this,
+                                         OpenACCSpecialTokenKind::ReadOnly,
+                                         OpenACCDirectiveKind::Cache)) {
     // FIXME: Record that this is a 'readonly' so that we can use that during
     // Sema/AST generation.
   }
diff --git a/clang/test/ParserOpenACC/parse-cache-construct.c b/clang/test/ParserOpenACC/parse-cache-construct.c
index d54632fc8f466a..093587f37df4fc 100644
--- a/clang/test/ParserOpenACC/parse-cache-construct.c
+++ b/clang/test/ParserOpenACC/parse-cache-construct.c
@@ -114,6 +114,18 @@ void func() {
     #pragma acc cache(readonly)
   }
 
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +2{{invalid tag 'devnum' on 'cache' directive}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(devnum:ArrayPtr)
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +2{{invalid tag 'invalid' on 'cache' directive}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(invalid:ArrayPtr)
+  }
+
   for (int i = 0; i < 10; ++i) {
     // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
     #pragma acc cache(readonly:ArrayPtr)

``````````

</details>


https://github.com/llvm/llvm-project/pull/77957


More information about the cfe-commits mailing list