Skip to content

[OpenACC} Improve diagnostics for 'tag's on clauses/directives #77957

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

Merged
merged 4 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -164,6 +164,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 @@ -218,11 +260,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 @@ -674,14 +712,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