Skip to content

Commit

Permalink
[OpenACC} Improve diagnostics for 'tag's on clauses/directives (#77957)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
erichkeane committed Jan 16, 2024
1 parent 93d3965 commit 9e068cd
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 13 deletions.
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<
Expand Down
155 changes: 155 additions & 0 deletions clang/include/clang/Basic/OpenACCKinds.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -62,6 +65,75 @@ 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,
Expand Down Expand Up @@ -138,6 +210,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,
Expand Down
61 changes: 48 additions & 13 deletions clang/lib/Parse/ParseOpenACC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,48 @@ 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;
Expand Down Expand Up @@ -217,11 +259,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;
Expand Down Expand Up @@ -673,14 +711,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.
}
Expand Down
12 changes: 12 additions & 0 deletions clang/test/ParserOpenACC/parse-cache-construct.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,18 @@ void func() {
#pragma acc cache(readonly)
}

for (int i = 0; i < 10; ++i) {
// expected-error@+2{{invalid tag 'devnum' on 'cache' directive}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc cache(devnum:ArrayPtr)
}

for (int i = 0; i < 10; ++i) {
// expected-error@+2{{invalid tag 'invalid' on 'cache' directive}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc cache(invalid:ArrayPtr)
}

for (int i = 0; i < 10; ++i) {
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc cache(readonly:ArrayPtr)
Expand Down

0 comments on commit 9e068cd

Please sign in to comment.