diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 088f8b74983c8..31530444d6920 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 e860893b933ca..399222dbf71b2 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,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 << ""; + } + llvm_unreachable("Uncovered directive kind"); +} + enum class OpenACCAtomicKind { Read, Write, @@ -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 << ""; + } + 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 05087a6565a11..a5a028e1c6a79 100644 --- a/clang/lib/Parse/ParseOpenACC.cpp +++ b/clang/lib/Parse/ParseOpenACC.cpp @@ -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 +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; + return false; + } + + return true; + } + + return false; +} + bool isOpenACCDirectiveKind(OpenACCDirectiveKind Kind, Token Tok) { if (!Tok.is(tok::identifier)) return false; @@ -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; @@ -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. } diff --git a/clang/test/ParserOpenACC/parse-cache-construct.c b/clang/test/ParserOpenACC/parse-cache-construct.c index d54632fc8f466..093587f37df4f 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@+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)