diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index e4b1069cde185..088f8b74983c8 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1364,6 +1364,8 @@ def err_acc_invalid_clause : Error<"invalid OpenACC clause %0">; def err_acc_missing_directive : Error<"expected OpenACC directive">; 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'">; // 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 54a29f8428e99..3eb0bf84208fd 100644 --- a/clang/include/clang/Basic/OpenACCKinds.h +++ b/clang/include/clang/Basic/OpenACCKinds.h @@ -90,9 +90,21 @@ enum class OpenACCClauseKind { Vector, /// 'nohost' clause, allowed on 'routine' directives. NoHost, + /// 'default' clause, allowed on parallel, serial, kernel (and compound) + /// constructs. + Default, /// Represents an invalid clause, for the purposes of parsing. Invalid, }; + +enum class OpenACCDefaultClauseKind { + /// 'none' option. + None, + /// 'present' option. + Present, + /// Not a valid option. + Invalid, +}; } // namespace clang #endif // LLVM_CLANG_BASIC_OPENACCKINDS_H diff --git a/clang/lib/Parse/ParseOpenACC.cpp b/clang/lib/Parse/ParseOpenACC.cpp index 67325f0a286a9..94c3d0c4e164c 100644 --- a/clang/lib/Parse/ParseOpenACC.cpp +++ b/clang/lib/Parse/ParseOpenACC.cpp @@ -76,12 +76,17 @@ OpenACCClauseKind getOpenACCClauseKind(Token Tok) { if (Tok.is(tok::kw_auto)) return OpenACCClauseKind::Auto; + // default is a keyword, so make sure we parse it correctly. + if (Tok.is(tok::kw_default)) + return OpenACCClauseKind::Default; + if (!Tok.is(tok::identifier)) return OpenACCClauseKind::Invalid; return llvm::StringSwitch( Tok.getIdentifierInfo()->getName()) .Case("auto", OpenACCClauseKind::Auto) + .Case("default", OpenACCClauseKind::Default) .Case("finalize", OpenACCClauseKind::Finalize) .Case("if_present", OpenACCClauseKind::IfPresent) .Case("independent", OpenACCClauseKind::Independent) @@ -106,6 +111,17 @@ OpenACCAtomicKind getOpenACCAtomicKind(Token Tok) { .Default(OpenACCAtomicKind::Invalid); } +OpenACCDefaultClauseKind getOpenACCDefaultClauseKind(Token Tok) { + if (!Tok.is(tok::identifier)) + return OpenACCDefaultClauseKind::Invalid; + + return llvm::StringSwitch( + Tok.getIdentifierInfo()->getName()) + .Case("none", OpenACCDefaultClauseKind::None) + .Case("present", OpenACCDefaultClauseKind::Present) + .Default(OpenACCDefaultClauseKind::Invalid); +} + enum class OpenACCSpecialTokenKind { ReadOnly, DevNum, @@ -176,6 +192,22 @@ bool isOpenACCDirectiveKind(OpenACCDirectiveKind Kind, Token Tok) { llvm_unreachable("Unknown 'Kind' Passed"); } +/// Used for cases where we expect an identifier-like token, but don't want to +/// give awkward error messages in cases where it is accidentially a keyword. +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())) + return false; + + P.Diag(P.getCurToken(), diag::err_expected) << tok::identifier; + return true; +} + OpenACCDirectiveKind ParseOpenACCEnterExitDataDirective(Parser &P, Token FirstTok, OpenACCDirectiveKindEx ExtDirKind) { @@ -291,13 +323,56 @@ OpenACCDirectiveKind ParseOpenACCDirectiveKind(Parser &P) { return DirKind; } +bool ClauseHasRequiredParens(OpenACCClauseKind Kind) { + return Kind == OpenACCClauseKind::Default; +} + +bool ParseOpenACCClauseParams(Parser &P, OpenACCClauseKind Kind) { + BalancedDelimiterTracker Parens(P, tok::l_paren, + tok::annot_pragma_openacc_end); + + if (ClauseHasRequiredParens(Kind)) { + if (Parens.expectAndConsume()) { + // We are missing a paren, so assume that the person just forgot the + // parameter. Return 'false' so we try to continue on and parse the next + // clause. + P.SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openacc_end, + Parser::StopBeforeMatch); + return false; + } + + switch (Kind) { + case OpenACCClauseKind::Default: { + Token DefKindTok = P.getCurToken(); + + if (expectIdentifierOrKeyword(P)) + break; + + P.ConsumeToken(); + + if (getOpenACCDefaultClauseKind(DefKindTok) == + OpenACCDefaultClauseKind::Invalid) + P.Diag(DefKindTok, diag::err_acc_invalid_default_clause_kind); + + break; + } + default: + llvm_unreachable("Not a required parens type?"); + } + + return Parens.consumeClose(); + } + // FIXME: Handle optional parens + return false; +} + // The OpenACC Clause List is a comma or space-delimited list of clauses (see // the comment on ParseOpenACCClauseList). The concept of a 'clause' doesn't // really have its owner grammar and each individual one has its own definition. -// However, they all are named with a single-identifier (or auto!) token, -// followed in some cases by either braces or parens. +// However, they all are named with a single-identifier (or auto/default!) +// token, followed in some cases by either braces or parens. bool ParseOpenACCClause(Parser &P) { - if (!P.getCurToken().isOneOf(tok::identifier, tok::kw_auto)) + if (!P.getCurToken().isOneOf(tok::identifier, tok::kw_auto, tok::kw_default)) return P.Diag(P.getCurToken(), diag::err_expected) << tok::identifier; OpenACCClauseKind Kind = getOpenACCClauseKind(P.getCurToken()); @@ -309,8 +384,7 @@ bool ParseOpenACCClause(Parser &P) { // Consume the clause name. P.ConsumeToken(); - // FIXME: For future clauses, we need to handle parens/etc below. - return false; + return ParseOpenACCClauseParams(P, Kind); } // Skip until we see the end of pragma token, but don't consume it. This is us diff --git a/clang/test/ParserOpenACC/parse-clauses.c b/clang/test/ParserOpenACC/parse-clauses.c index 1e05d82906aed..aedf0c711ad15 100644 --- a/clang/test/ParserOpenACC/parse-clauses.c +++ b/clang/test/ParserOpenACC/parse-clauses.c @@ -54,6 +54,102 @@ void func() { // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} #pragma acc loop seq, +} + +void DefaultClause() { + // expected-error@+2{{expected '('}} + // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} +#pragma acc serial loop default + for(;;){} + + // expected-error@+2{{expected '('}} + // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} +#pragma acc serial default seq + for(;;){} + + // expected-error@+2{{expected '('}} + // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} +#pragma acc serial default, seq + for(;;){} + + // expected-error@+4{{expected identifier}} + // expected-error@+3{{expected ')'}} + // expected-note@+2{{to match this '('}} + // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} +#pragma acc serial default( + for(;;){} + + // expected-error@+4{{invalid value for 'default' clause; expected 'present' or 'none'}} + // expected-error@+3{{expected ')'}} + // expected-note@+2{{to match this '('}} + // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} +#pragma acc serial default( seq + for(;;){} + + // expected-error@+4{{expected identifier}} + // expected-error@+3{{expected ')'}} + // expected-note@+2{{to match this '('}} + // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} +#pragma acc serial default(, seq + for(;;){} + + // expected-error@+3{{expected '('}} + // expected-error@+2{{expected identifier}} + // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} +#pragma acc serial default) + for(;;){} + + // expected-error@+3{{expected '('}} + // expected-error@+2{{expected identifier}} + // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} +#pragma acc serial default) seq + for(;;){} + + // expected-error@+3{{expected '('}} + // expected-error@+2{{expected identifier}} + // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} +#pragma acc serial default), seq + for(;;){} + + // expected-error@+2{{expected identifier}} + // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} +#pragma acc serial default() + for(;;){} + + // expected-error@+2{{expected identifier}} + // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} +#pragma acc serial default() seq + for(;;){} + + // expected-error@+2{{expected identifier}} + // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} +#pragma acc serial default(), seq + for(;;){} + + // expected-error@+2{{invalid value for 'default' clause; expected 'present' or 'none'}} + // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} +#pragma acc serial default(invalid) + for(;;){} + + // expected-error@+2{{invalid value for 'default' clause; expected 'present' or 'none'}} + // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} +#pragma acc serial default(auto) seq + for(;;){} + + // expected-error@+2{{invalid value for 'default' clause; expected 'present' or 'none'}} + // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} +#pragma acc serial default(invalid), seq + for(;;){} + + // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} +#pragma acc serial default(none) + for(;;){} + + // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}} +#pragma acc serial default(present), seq + for(;;){} + + } // expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}