diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h index b2341226c7688..7540d38baa584 100644 --- a/flang/include/flang/Parser/dump-parse-tree.h +++ b/flang/include/flang/Parser/dump-parse-tree.h @@ -522,6 +522,7 @@ class ParseTreeDumper { NODE(parser, OmpAtomicDefaultMemOrderClause) NODE(parser, OmpAutomapModifier) NODE_ENUM(OmpAutomapModifier, Value) + NODE(parser, OmpBaseVariantNames) NODE(parser, OmpBeginDirective) NODE(parser, OmpBeginLoopDirective) NODE(parser, OmpBeginSectionsDirective) diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index 39dbeb5e7cfbe..4808a5b844a6f 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -3555,6 +3555,18 @@ struct OmpLocator { WRAPPER_CLASS(OmpLocatorList, std::list); +// Ref: [4.5:58-60], [5.0:58-60], [5.1:63-68], [5.2:197-198], [6.0:334-336] +// +// Argument to DECLARE VARIANT with the base-name present. (When only +// variant-name is present, it is a simple OmpObject). +// +// base-name-variant-name -> // since 4.5 +// base-name : variant-name +struct OmpBaseVariantNames { + TUPLE_CLASS_BOILERPLATE(OmpBaseVariantNames); + std::tuple t; +}; + // Ref: [5.0:326:10-16], [5.1:359:5-11], [5.2:163:2-7], [6.0:293:16-21] // // mapper-specifier -> @@ -3584,6 +3596,7 @@ struct OmpArgument { CharBlock source; UNION_CLASS_BOILERPLATE(OmpArgument); std::variant u; }; diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp index 30bc02ce851db..3b32e1a4a67b1 100644 --- a/flang/lib/Parser/openmp-parsers.cpp +++ b/flang/lib/Parser/openmp-parsers.cpp @@ -315,15 +315,56 @@ TYPE_PARSER( // construct(Parser{}) || construct(Parser{})) -TYPE_PARSER(sourced( // - construct(Parser{}) || - construct(Parser{}) || - construct(Parser{}))) +TYPE_PARSER(construct( + Parser{} / ":", Parser{})) + +// Make the parsing of OmpArgument directive-sensitive. The issue is that +// name1:name2 can match either OmpBaseVariantNames or OmpReductionSpecifier. +// In the former case, "name2" is a name of a function, in the latter, of a +// type. To resolve the conflict we need information provided by name +// resolution, but by that time we can't modify the AST anymore, and the +// name resolution may have implicitly declared a symbol, or issued a message. +template +struct OmpArgumentParser { + using resultType = OmpArgument; + + std::optional Parse(ParseState &state) const { + constexpr auto parser{sourced(first( // + construct(Parser{}), + // By default, prefer OmpReductionSpecifier over OmpBaseVariantNames. + construct(Parser{}), + construct(Parser{})))}; + return parser.Parse(state); + } +}; + +template <> +struct OmpArgumentParser { + using resultType = OmpArgument; + + std::optional Parse(ParseState &state) const { + constexpr auto parser{sourced(first( // + construct(Parser{}), + // In DECLARE_VARIANT parse OmpBaseVariantNames instead of + // OmpReductionSpecifier. + construct(Parser{}), + construct(Parser{})))}; + return parser.Parse(state); + } +}; TYPE_PARSER(construct(nonemptyList(Parser{}))) -TYPE_PARSER(sourced( // - construct(nonemptyList(Parser{})))) +template +struct OmpArgumentListParser { + using resultType = OmpArgumentList; + + std::optional Parse(ParseState &state) const { + return sourced( + construct(nonemptyList(OmpArgumentParser{}))) + .Parse(state); + } +}; TYPE_PARSER( // construct(Parser{}) || @@ -1312,12 +1353,23 @@ TYPE_PARSER( applyFunction(makeFlushFromOldSyntax, verbatim("FLUSH"_tok) / !lookAhead("("_tok), maybe(Parser{}), - maybe(parenthesized(Parser{})), + maybe(parenthesized( + OmpArgumentListParser{})), pure(OmpDirectiveSpecification::Flags::DeprecatedSyntax)))) || + // Parse DECLARE_VARIANT individually, because the "[base:]variant" + // argument will conflict with DECLARE_REDUCTION's "ident:types...". + predicated(Parser{}, + IsDirective(llvm::omp::Directive::OMPD_declare_variant)) >= + sourced(construct( + sourced(OmpDirectiveNameParser{}), + maybe(parenthesized(OmpArgumentListParser< + llvm::omp::Directive::OMPD_declare_variant>{})), + maybe(Parser{}), + pure(OmpDirectiveSpecification::Flags::None))) || // Parse the standard syntax: directive [(arguments)] [clauses] sourced(construct( // sourced(OmpDirectiveNameParser{}), - maybe(parenthesized(Parser{})), + maybe(parenthesized(OmpArgumentListParser<>{})), maybe(Parser{}), pure(OmpDirectiveSpecification::Flags::None)))) diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp index 3455b535ccb51..1b3eef0eefba3 100644 --- a/flang/lib/Parser/unparse.cpp +++ b/flang/lib/Parser/unparse.cpp @@ -2089,6 +2089,11 @@ class UnparseVisitor { // OpenMP Clauses & Directives void Unparse(const OmpArgumentList &x) { Walk(x.v, ", "); } + void Unparse(const OmpBaseVariantNames &x) { + Walk(std::get<0>(x.t)); // OmpObject + Put(":"); + Walk(std::get<1>(x.t)); // OmpObject + } void Unparse(const OmpTypeNameList &x) { // Walk(x.v, ","); } diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp index 699cb562da8cc..b73d794c11d31 100644 --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -1998,6 +1998,10 @@ bool OmpVisitor::Pre(const parser::OmpDirectiveSpecification &x) { ProcessReductionSpecifier(spec, clauses); visitClauses = false; }, + [&](const parser::OmpBaseVariantNames &names) { + Walk(std::get<0>(names.t)); + Walk(std::get<1>(names.t)); + }, [&](const parser::OmpLocator &locator) { // Manually resolve names in CRITICAL directives. This is because // these names do not denote Fortran objects, and the CRITICAL