diff --git a/flang/documentation/ParserCombinators.md b/flang/documentation/ParserCombinators.md --- a/flang/documentation/ParserCombinators.md +++ b/flang/documentation/ParserCombinators.md @@ -47,10 +47,10 @@ * `ok` is a trivial parser that always succeeds without advancing. * `pure(x)` returns a trivial parser that always succeeds without advancing, returning some value `x`. +* `pure()` is `pure(T{})` but does not require that T be copy-constructible. * `fail(msg)` denotes a trivial parser that always fails, emitting the given message as a side effect. The template parameter is the type of the value that the parser never returns. -* `cut` is a trivial parser that always fails silently. * `nextCh` consumes the next character and returns its location, and fails at EOF. * `"xyz"_ch` succeeds if the next character consumed matches any of those @@ -100,8 +100,10 @@ * `recovery(p, q)` is equivalent to `p || q`, except that error messages generated from the first parser are retained, and a flag is set in the ParseState to remember that error recovery was necessary. -* `localRecovery(msg, p, q)` is equivalent to `recovery(withMessage(msg, p), defaulted(cut >> p) >> q)`. It is useful for targeted error recovery situations - within statements. +* `localRecovery(msg, p, q)` is equivalent to + `recovery(withMessage(msg, p), q >> pure())` where `A` is the + result type of 'p'. + It is useful for targeted error recovery situations within statements. Note that ``` diff --git a/flang/include/flang/Evaluate/traverse.h b/flang/include/flang/Evaluate/traverse.h --- a/flang/include/flang/Evaluate/traverse.h +++ b/flang/include/flang/Evaluate/traverse.h @@ -257,7 +257,7 @@ template > struct AllTraverse : public Base { - AllTraverse(Visitor &v) : Base{v} {} + explicit AllTraverse(Visitor &v) : Base{v} {} using Base::operator(); static bool Default() { return DefaultValue; } static bool Combine(bool x, bool y) { return x && y; } @@ -268,10 +268,11 @@ // and std::optional<>. template > -struct AnyTraverse : public Base { - AnyTraverse(Visitor &v) : Base{v} {} +class AnyTraverse : public Base { +public: + explicit AnyTraverse(Visitor &v) : Base{v} {} using Base::operator(); - static Result Default() { return {}; } + Result Default() const { return default_; } static Result Combine(Result &&x, Result &&y) { if (x) { return std::move(x); @@ -279,12 +280,15 @@ return std::move(y); } } + +private: + Result default_{}; }; template > struct SetTraverse : public Base { - SetTraverse(Visitor &v) : Base{v} {} + explicit SetTraverse(Visitor &v) : Base{v} {} using Base::operator(); static Set Default() { return {}; } static Set Combine(Set &&x, Set &&y) { diff --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp --- a/flang/lib/Parser/Fortran-parsers.cpp +++ b/flang/lib/Parser/Fortran-parsers.cpp @@ -1184,7 +1184,7 @@ TYPE_PARSER(construct("STRUCTURE /" >> name / "/", pure(true), optionalList(entityDecl)) || construct( - "STRUCTURE" >> name, pure(false), defaulted(cut >> many(entityDecl)))) + "STRUCTURE" >> name, pure(false), pure>())) TYPE_PARSER(construct(statement(StructureComponents{})) || construct(indirect(Parser{})) || diff --git a/flang/lib/Parser/basic-parsers.h b/flang/lib/Parser/basic-parsers.h --- a/flang/lib/Parser/basic-parsers.h +++ b/flang/lib/Parser/basic-parsers.h @@ -65,7 +65,10 @@ } // pure(x) returns a parser that always succeeds, does not advance the -// parse, and returns a captured value whose type must be copy-constructible. +// parse, and returns a captured value x whose type must be copy-constructible. +// +// pure() is essentially pure(A{}); it returns a default-constructed A{}, +// and works even when A is not copy-constructible. template class PureParser { public: using resultType = A; @@ -81,6 +84,18 @@ return PureParser(std::move(x)); } +template class PureDefaultParser { +public: + using resultType = A; + constexpr PureDefaultParser(const PureDefaultParser &) = default; + constexpr PureDefaultParser() {} + std::optional Parse(ParseState &) const { return std::make_optional(); } +}; + +template inline constexpr auto pure() { + return PureDefaultParser(); +} + // If a is a parser, attempt(a) is the same parser, but on failure // the ParseState is guaranteed to have been restored to its initial value. template class BacktrackingParser { @@ -239,10 +254,10 @@ public: using resultType = typename PB::resultType; constexpr SequenceParser(const SequenceParser &) = default; - constexpr SequenceParser(PA pa, PB pb) : pa_{pa}, pb_{pb} {} + constexpr SequenceParser(PA pa, PB pb) : pa_{pa}, pb2_{pb} {} std::optional Parse(ParseState &state) const { if (pa_.Parse(state)) { - return pb_.Parse(state); + return pb2_.Parse(state); } else { return std::nullopt; } @@ -250,7 +265,7 @@ private: const PA pa_; - const PB pb_; + const PB pb2_; }; template @@ -336,7 +351,7 @@ using resultType = typename PA::resultType; static_assert(std::is_same_v); constexpr RecoveryParser(const RecoveryParser &) = default; - constexpr RecoveryParser(PA pa, PB pb) : pa_{pa}, pb_{pb} {} + constexpr RecoveryParser(PA pa, PB pb) : pa_{pa}, pb3_{pb} {} std::optional Parse(ParseState &state) const { bool originallyDeferred{state.deferMessages()}; ParseState backtrack{state}; @@ -364,7 +379,7 @@ bool anyTokenMatched{state.anyTokenMatched()}; state = std::move(backtrack); state.set_deferMessages(true); - std::optional bx{pb_.Parse(state)}; + std::optional bx{pb3_.Parse(state)}; state.messages() = std::move(messages); state.set_deferMessages(originallyDeferred); if (anyTokenMatched) { @@ -383,7 +398,7 @@ private: const PA pa_; - const PB pb_; + const PB pb3_; }; template @@ -785,31 +800,18 @@ // must discard its result in order to be compatible in type with other // parsers in an alternative, e.g. "x >> ok || y >> ok" is type-safe even // when x and y have distinct result types. -// -// cut is a parser that always fails. It is useful when a parser must -// have its type implicitly set; one use is the idiom "defaulted(cut >> x)", -// which is essentially what "pure(T{})" would be able to do for x's -// result type T, but without requiring that T have a default constructor -// or a non-trivial destructor. The state is preserved. -template struct FixedParser { +constexpr struct OkParser { using resultType = Success; - constexpr FixedParser() {} + constexpr OkParser() {} static constexpr std::optional Parse(ParseState &) { - if constexpr (pass) { - return Success{}; - } else { - return std::nullopt; - } + return Success{}; } -}; - -constexpr FixedParser ok; -constexpr FixedParser cut; +} ok; // A variant of recovery() above for convenience. template inline constexpr auto localRecovery(MessageFixedText msg, PA pa, PB pb) { - return recovery(withMessage(msg, pa), pb >> defaulted(cut >> pa)); + return recovery(withMessage(msg, pa), pb >> pure()); } // nextCh is a parser that succeeds if the parsing state is not diff --git a/flang/lib/Parser/program-parsers.cpp b/flang/lib/Parser/program-parsers.cpp --- a/flang/lib/Parser/program-parsers.cpp +++ b/flang/lib/Parser/program-parsers.cpp @@ -29,7 +29,7 @@ // and then skip over the end of the line here. TYPE_PARSER(construct( extension(skipStuffBeforeStatement >> - !nextCh >> defaulted(cut >> some(Parser{}))) || + !nextCh >> pure>()) || some(StartNewSubprogram{} >> Parser{} / skipMany(";"_tok) / space / recovery(endOfLine, SkipPast<'\n'>{})) / skipStuffBeforeStatement)) @@ -509,8 +509,8 @@ construct(many(prefixSpec), "SUBROUTINE" >> name, parenthesized(optionalList(dummyArg)), maybe(languageBindingSpec)) || construct(many(prefixSpec), "SUBROUTINE" >> name, - defaulted(cut >> many(dummyArg)), - defaulted(cut >> maybe(languageBindingSpec)))) + pure>(), + pure>())) // R1536 dummy-arg -> dummy-arg-name | * TYPE_PARSER(construct(name) || construct(star)) diff --git a/flang/lib/Parser/stmt-parser.h b/flang/lib/Parser/stmt-parser.h --- a/flang/lib/Parser/stmt-parser.h +++ b/flang/lib/Parser/stmt-parser.h @@ -83,7 +83,7 @@ !("!$OMP "_sptok >> ("END"_tok || "SECTION"_id)) >> skipBadLine}; // END statement error recovery -constexpr auto missingOptionalName{defaulted(cut >> maybe(name))}; +constexpr auto missingOptionalName{pure>()}; constexpr auto noNameEnd{"END" >> missingOptionalName}; constexpr auto atEndOfStmt{space >> withMessage("expected end of statement"_err_en_US, lookAhead(";\n"_ch))}; diff --git a/flang/lib/Parser/token-parsers.h b/flang/lib/Parser/token-parsers.h --- a/flang/lib/Parser/token-parsers.h +++ b/flang/lib/Parser/token-parsers.h @@ -584,13 +584,15 @@ // [[, xyz] ::] is optionalBeforeColons(xyz) // [[, xyz]... ::] is optionalBeforeColons(nonemptyList(xyz)) template inline constexpr auto optionalBeforeColons(const PA &p) { - return "," >> construct>(p) / "::" || - ("::"_tok || !","_tok) >> defaulted(cut >> maybe(p)); + using resultType = std::optional; + return "," >> construct(p) / "::" || + ("::"_tok || !","_tok) >> pure(); } template inline constexpr auto optionalListBeforeColons(const PA &p) { + using resultType = std::list; return "," >> nonemptyList(p) / "::" || - ("::"_tok || !","_tok) >> defaulted(cut >> nonemptyList(p)); + ("::"_tok || !","_tok) >> pure(); } // Skip over empty lines, leading spaces, and some compiler directives (viz.,