From ce231b955955e28c731853d1bf641981268a9176 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Wed, 8 Aug 2018 11:29:05 -0700 Subject: [PATCH] [flang] Clean up messaging: make Say() member function templates more flexible, hide MessageFormattedText instances Original-commit: flang-compiler/f18@59d774382f859acd06341a1d407b4e2fe7c975f7 Reviewed-on: https://github.com/flang-compiler/f18/pull/165 Tree-same-pre-rewrite: false --- flang/lib/evaluate/common.h | 10 ++ flang/lib/evaluate/expression.cc | 12 +-- flang/lib/evaluate/expression.h | 15 +-- flang/lib/evaluate/variable.cc | 59 ++++++++++++ flang/lib/evaluate/variable.h | 2 + flang/lib/parser/basic-parsers.h | 6 +- flang/lib/parser/debug-parser.cc | 4 +- flang/lib/parser/message.cc | 8 +- flang/lib/parser/message.h | 22 ++--- flang/lib/parser/parse-state.h | 25 ++--- flang/lib/parser/parsing.cc | 7 +- flang/lib/parser/preprocessor.cc | 131 ++++++++++++--------------- flang/lib/parser/prescan.cc | 45 +++------ flang/lib/parser/prescan.h | 9 +- flang/lib/semantics/expression.cc | 49 +++++++--- flang/lib/semantics/expression.h | 3 +- flang/lib/semantics/mod-file.cc | 42 ++------- flang/lib/semantics/mod-file.h | 10 +- flang/lib/semantics/resolve-names.cc | 31 +++---- flang/tools/f18/f18.cc | 7 +- 20 files changed, 264 insertions(+), 233 deletions(-) diff --git a/flang/lib/evaluate/common.h b/flang/lib/evaluate/common.h index 4e807081fc27..64bf46cd12a4 100644 --- a/flang/lib/evaluate/common.h +++ b/flang/lib/evaluate/common.h @@ -18,6 +18,7 @@ #include "../common/enum-set.h" #include "../common/idioms.h" #include "../common/indirection.h" +#include "../parser/message.h" #include namespace Fortran::evaluate { @@ -127,5 +128,14 @@ using HostUnsignedInt = // Force availability of copy construction and assignment template using CopyableIndirection = common::Indirection; +// Classes that support a Fold(FoldingContext &) member function have the +// FoldableTrait set. +CLASS_TRAIT(FoldableTrait); +struct FoldingContext { + parser::ContextualMessages &messages; + Rounding rounding{defaultRounding}; + bool flushDenormalsToZero{false}; +}; + } // namespace Fortran::evaluate #endif // FORTRAN_EVALUATE_COMMON_H_ diff --git a/flang/lib/evaluate/expression.cc b/flang/lib/evaluate/expression.cc index 835884cae0a1..e754621977bb 100644 --- a/flang/lib/evaluate/expression.cc +++ b/flang/lib/evaluate/expression.cc @@ -383,20 +383,16 @@ auto IntegerExpr::Fold(FoldingContext &context) -> std::optional { static void RealFlagWarnings( FoldingContext &context, const RealFlags &flags, const char *operation) { if (flags.test(RealFlag::Overflow)) { - context.messages.Say( - parser::MessageFormattedText("overflow on %s"_en_US, operation)); + context.messages.Say("overflow on %s"_en_US, operation); } if (flags.test(RealFlag::DivideByZero)) { - context.messages.Say(parser::MessageFormattedText( - "division by zero on %s"_en_US, operation)); + context.messages.Say("division by zero on %s"_en_US, operation); } if (flags.test(RealFlag::InvalidArgument)) { - context.messages.Say(parser::MessageFormattedText( - "invalid argument on %s"_en_US, operation)); + context.messages.Say("invalid argument on %s"_en_US, operation); } if (flags.test(RealFlag::Underflow)) { - context.messages.Say( - parser::MessageFormattedText("underflow on %s"_en_US, operation)); + context.messages.Say("underflow on %s"_en_US, operation); } } diff --git a/flang/lib/evaluate/expression.h b/flang/lib/evaluate/expression.h index 34341ae0aec6..229d98356079 100644 --- a/flang/lib/evaluate/expression.h +++ b/flang/lib/evaluate/expression.h @@ -33,13 +33,6 @@ namespace Fortran::evaluate { -CLASS_TRAIT(FoldableTrait); -struct FoldingContext { - parser::ContextualMessages &messages; - Rounding rounding{defaultRounding}; - bool flushDenormalsToZero{false}; -}; - // Helper base classes for packaging subexpressions. template class Unary { protected: @@ -175,11 +168,15 @@ public: Expr(std::int64_t n) : u_{Scalar{n}} {} Expr(std::uint64_t n) : u_{Scalar{n}} {} Expr(int n) : u_{Scalar{n}} {} + Expr(const AnyKindIntegerExpr &x) : u_{ConvertInteger{x}} {} + Expr(AnyKindIntegerExpr &&x) : u_{ConvertInteger{std::move(x)}} {} template Expr(const IntegerExpr &x) : u_{ConvertInteger{AnyKindIntegerExpr{x}}} {} template Expr(IntegerExpr &&x) : u_{ConvertInteger{AnyKindIntegerExpr{std::move(x)}}} {} + Expr(const AnyKindRealExpr &x) : u_{ConvertReal{x}} {} + Expr(AnyKindRealExpr &&x) : u_{ConvertReal{std::move(x)}} {} template Expr(const RealExpr &x) : u_{ConvertReal{AnyKindRealExpr{x}}} {} template @@ -296,11 +293,15 @@ public: CLASS_BOILERPLATE(Expr) Expr(const Scalar &x) : u_{x} {} + Expr(const AnyKindIntegerExpr &x) : u_{ConvertInteger{x}} {} + Expr(AnyKindIntegerExpr &&x) : u_{ConvertInteger{std::move(x)}} {} template Expr(const IntegerExpr &x) : u_{ConvertInteger{AnyKindIntegerExpr{x}}} {} template Expr(IntegerExpr &&x) : u_{ConvertInteger{AnyKindIntegerExpr{std::move(x)}}} {} + Expr(const AnyKindRealExpr &x) : u_{ConvertReal{x}} {} + Expr(AnyKindRealExpr &&x) : u_{ConvertReal{std::move(x)}} {} template Expr(const RealExpr &x) : u_{ConvertReal{AnyKindRealExpr{x}}} {} template diff --git a/flang/lib/evaluate/variable.cc b/flang/lib/evaluate/variable.cc index bed55b7302a2..adaaf4a6b922 100644 --- a/flang/lib/evaluate/variable.cc +++ b/flang/lib/evaluate/variable.cc @@ -16,9 +16,12 @@ #include "../common/idioms.h" #include "../parser/char-block.h" #include "../parser/characters.h" +#include "../parser/message.h" #include "../semantics/symbol.h" #include +using namespace Fortran::parser::literals; + namespace Fortran::evaluate { // Constructors, accessors, mutators @@ -121,6 +124,62 @@ SubscriptIntegerExpr Substring::last() const { u_); } +std::optional Substring::Fold(FoldingContext &context) { + std::optional lbValue, ubValue; + if (first_.has_value()) { + lbValue = (*first_)->Fold(context); + } else { + lbValue = first().Fold(context); + } + if (lbValue.has_value()) { + first_ = IndirectSubscriptIntegerExpr{SubscriptIntegerExpr{*lbValue}}; + } + if (last_.has_value()) { + ubValue = (*last_)->Fold(context); + } else { + ubValue = last().Fold(context); + } + if (ubValue.has_value()) { + last_ = IndirectSubscriptIntegerExpr{SubscriptIntegerExpr{*ubValue}}; + } + if (lbValue.has_value() && ubValue.has_value()) { + std::int64_t lbi{lbValue->ToInt64()}; + std::int64_t ubi{ubValue->ToInt64()}; + if (ubi < lbi) { + // These cases are well defined, and they produce zero-length results. + u_ = ""s; + first_ = SubscriptIntegerExpr{1}; + last_ = SubscriptIntegerExpr{0}; + return {""s}; + } + if (lbi <= 0) { + context.messages.Say( + "lower bound on substring (%jd) is less than one"_en_US, + static_cast(lbi)); + lbi = 1; + first_ = SubscriptIntegerExpr{lbi}; + } + if (ubi <= 0) { + u_ = ""s; + last_ = SubscriptIntegerExpr{0}; + return {""s}; + } + if (std::string * str{std::get_if(&u_)}) { + std::int64_t len = str->size(); + if (ubi > len) { + context.messages.Say( + "upper bound on substring (%jd) is greater than character length (%jd)"_en_US); + ubi = len; + last_ = SubscriptIntegerExpr{ubi}; + } + std::string result{str->substr(lbi - 1, ubi - lbi + 1)}; + u_ = result; + return {result}; + } + } + return std::nullopt; +} + // Variable dumping template std::ostream &Emit(std::ostream &o, const A &x) { diff --git a/flang/lib/evaluate/variable.h b/flang/lib/evaluate/variable.h index 6c2f1eb577a1..24b68a25d9b6 100644 --- a/flang/lib/evaluate/variable.h +++ b/flang/lib/evaluate/variable.h @@ -164,6 +164,7 @@ private: // variants of sections instead. class Substring { public: + using FoldableTrait = std::true_type; CLASS_BOILERPLATE(Substring) Substring(DataRef &&, std::optional &&, std::optional &&); @@ -173,6 +174,7 @@ public: SubscriptIntegerExpr first() const; SubscriptIntegerExpr last() const; SubscriptIntegerExpr LEN() const; + std::optional Fold(FoldingContext &); private: std::variant u_; diff --git a/flang/lib/parser/basic-parsers.h b/flang/lib/parser/basic-parsers.h index e57c021a2f9c..34b39669d516 100644 --- a/flang/lib/parser/basic-parsers.h +++ b/flang/lib/parser/basic-parsers.h @@ -201,12 +201,12 @@ public: std::optional result{parser_.Parse(state)}; bool emitMessage{false}; if (result.has_value()) { - messages.Annex(state.messages()); + messages.Annex(std::move(state.messages())); if (backtrack.anyTokenMatched()) { state.set_anyTokenMatched(); } } else if (state.anyTokenMatched()) { - messages.Annex(state.messages()); + messages.Annex(std::move(state.messages())); backtrack.set_anyTokenMatched(); if (state.anyDeferredMessages()) { backtrack.set_anyDeferredMessages(true); @@ -407,7 +407,7 @@ public: state.messages().Restore(std::move(messages)); return ax; } - messages.Annex(state.messages()); + messages.Annex(std::move(state.messages())); bool hadDeferredMessages{state.anyDeferredMessages()}; bool anyTokenMatched{state.anyTokenMatched()}; state = std::move(backtrack); diff --git a/flang/lib/parser/debug-parser.cc b/flang/lib/parser/debug-parser.cc index 3a54192b97b0..60bd48b04c90 100644 --- a/flang/lib/parser/debug-parser.cc +++ b/flang/lib/parser/debug-parser.cc @@ -23,8 +23,8 @@ std::optional DebugParser::Parse(ParseState &state) const { if (auto ustate{state.userState()}) { if (auto out{ustate->debugOutput()}) { std::string note{str_, length_}; - Message message{state.GetLocation(), - MessageFormattedText{"parser debug: %s"_en_US, note.data()}}; + Message message{ + state.GetLocation(), "parser debug: %s"_en_US, note.data()}; message.SetContext(state.context().get()); message.Emit(*out, ustate->cooked(), true); } diff --git a/flang/lib/parser/message.cc b/flang/lib/parser/message.cc index efa06493a979..616c6d3f9bea 100644 --- a/flang/lib/parser/message.cc +++ b/flang/lib/parser/message.cc @@ -253,7 +253,7 @@ void Messages::Merge(Messages &&that) { void Messages::Copy(const Messages &that) { for (const Message &m : that.messages_) { Message copy{m}; - Put(std::move(copy)); + Say(std::move(copy)); } } @@ -276,6 +276,12 @@ void Messages::Emit( } } +void Messages::AttachTo(Message &msg) { + for (const Message &m : messages_) { + msg.Attach(m); + } +} + bool Messages::AnyFatalError() const { for (const auto &msg : messages_) { if (msg.IsFatal()) { diff --git a/flang/lib/parser/message.h b/flang/lib/parser/message.h index d05762e380b3..856a75ca8700 100644 --- a/flang/lib/parser/message.h +++ b/flang/lib/parser/message.h @@ -123,7 +123,7 @@ public: Message(ProvenanceRange pr, const MessageFixedText &t) : location_{pr}, text_{t} {} Message(ProvenanceRange pr, const MessageFormattedText &s) - : location_{pr}, text_{std::move(s)} {} + : location_{pr}, text_{s} {} Message(ProvenanceRange pr, MessageFormattedText &&s) : location_{pr}, text_{std::move(s)} {} Message(ProvenanceRange pr, const MessageExpectedText &t) @@ -132,12 +132,17 @@ public: Message(CharBlock csr, const MessageFixedText &t) : location_{csr}, text_{t} {} Message(CharBlock csr, const MessageFormattedText &s) - : location_{csr}, text_{std::move(s)} {} + : location_{csr}, text_{s} {} Message(CharBlock csr, MessageFormattedText &&s) : location_{csr}, text_{std::move(s)} {} Message(CharBlock csr, const MessageExpectedText &t) : location_{csr}, text_{t} {} + template + Message(RANGE r, const MessageFixedText &t, A1 a1, As... as) + : location_{r}, text_{ + MessageFormattedText{t, a1, std::forward(as)...}} {} + bool attachmentIsContext() const { return attachmentIsContext_; } Reference attachment() const { return attachment_; } @@ -199,17 +204,12 @@ public: bool empty() const { return messages_.empty(); } - Message &Put(Message &&m) { - last_ = messages_.emplace_after(last_, std::move(m)); - return *last_; - } - - template Message &Say(A &&... args) { + template Message &Say(A... args) { last_ = messages_.emplace_after(last_, std::forward(args)...); return *last_; } - void Annex(Messages &that) { + void Annex(Messages &&that) { if (!that.messages_.empty()) { messages_.splice_after(last_, that.messages_); last_ = that.last_; @@ -218,7 +218,7 @@ public: } void Restore(Messages &&that) { - that.Annex(*this); + that.Annex(std::move(*this)); *this = std::move(that); } @@ -228,7 +228,7 @@ public: void ResolveProvenances(const CookedSource &); void Emit(std::ostream &, const CookedSource &cooked, bool echoSourceLines = true) const; - + void AttachTo(Message &); bool AnyFatalError() const; private: diff --git a/flang/lib/parser/parse-state.h b/flang/lib/parser/parse-state.h index ab8b80a5fe41..9909f4660811 100644 --- a/flang/lib/parser/parse-state.h +++ b/flang/lib/parser/parse-state.h @@ -143,30 +143,19 @@ public: context_ = context_->attachment(); } - void Say(const MessageFixedText &t) { Say(p_, t); } - void Say(MessageFormattedText &&t) { Say(p_, std::move(t)); } - void Say(const MessageExpectedText &t) { Say(p_, t); } - - void Say(CharBlock range, const MessageFixedText &t) { + template void Say(CharBlock range, A &&... args) { if (deferMessages_) { anyDeferredMessages_ = true; } else { - messages_.Say(range, t).SetContext(context_.get()); + messages_.Say(range, std::forward(args)...).SetContext(context_.get()); } } - void Say(CharBlock range, MessageFormattedText &&t) { - if (deferMessages_) { - anyDeferredMessages_ = true; - } else { - messages_.Say(range, std::move(t)).SetContext(context_.get()); - } + template void Say(const MessageFixedText &text, A &&... args) { + Say(p_, text, std::forward(args)...); } - void Say(CharBlock range, const MessageExpectedText &t) { - if (deferMessages_) { - anyDeferredMessages_ = true; - } else { - messages_.Say(range, t).SetContext(context_.get()); - } + template + void Say(const MessageExpectedText &text, A &&... args) { + Say(p_, text, std::forward(args)...); } void Nonstandard(LanguageFeature lf, const MessageFixedText &msg) { diff --git a/flang/lib/parser/parsing.cc b/flang/lib/parser/parsing.cc index f8afcef5e7a3..16fc8211eb3d 100644 --- a/flang/lib/parser/parsing.cc +++ b/flang/lib/parser/parsing.cc @@ -43,13 +43,12 @@ void Parsing::Prescan(const std::string &path, Options options) { } if (sourceFile == nullptr) { ProvenanceRange range{allSources.AddCompilerInsertion(path)}; - MessageFormattedText msg("%s"_err_en_US, fileError.str().data()); - messages_.Put(Message{range, std::move(msg)}); + messages_.Say(range, "%s"_err_en_US, fileError.str().data()); return; } if (sourceFile->bytes() == 0) { ProvenanceRange range{allSources.AddCompilerInsertion(path)}; - messages_.Put(Message{range, "file is empty"_err_en_US}); + messages_.Say(range, "file is empty"_err_en_US); return; } @@ -112,7 +111,7 @@ void Parsing::Parse(std::ostream *out) { CHECK( !parseState.anyErrorRecovery() || parseState.messages().AnyFatalError()); consumedWholeFile_ = parseState.IsAtEnd(); - messages_.Annex(parseState.messages()); + messages_.Annex(std::move(parseState.messages())); finalRestingPlace_ = parseState.GetLocation(); } diff --git a/flang/lib/parser/preprocessor.cc b/flang/lib/parser/preprocessor.cc index 159619118885..882d97498ecf 100644 --- a/flang/lib/parser/preprocessor.cc +++ b/flang/lib/parser/preprocessor.cc @@ -362,7 +362,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { return; } if (dir.TokenAt(j).ToString() != "#") { - prescanner->Say("missing '#'"_err_en_US, dir.GetTokenProvenanceRange(j)); + prescanner->Say(dir.GetTokenProvenanceRange(j), "missing '#'"_err_en_US); return; } j = dir.SkipBlanks(j + 1); @@ -383,8 +383,8 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { // #line is ignored } else if (dirName == "define") { if (nameToken.empty()) { - prescanner->Say("#define: missing or invalid name"_err_en_US, - dir.GetTokenProvenanceRange(j < tokens ? j : tokens - 1)); + prescanner->Say(dir.GetTokenProvenanceRange(j < tokens ? j : tokens - 1), + "#define: missing or invalid name"_err_en_US); return; } nameToken = SaveTokenAsName(nameToken); @@ -401,17 +401,16 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { isVariadic = true; } else { if (an.empty() || !IsLegalIdentifierStart(an[0])) { - prescanner->Say( - "#define: missing or invalid argument name"_err_en_US, - dir.GetTokenProvenanceRange(j)); + prescanner->Say(dir.GetTokenProvenanceRange(j), + "#define: missing or invalid argument name"_err_en_US); return; } argName.push_back(an); } j = dir.SkipBlanks(j + 1); if (j == tokens) { - prescanner->Say("#define: malformed argument list"_err_en_US, - dir.GetTokenProvenanceRange(tokens - 1)); + prescanner->Say(dir.GetTokenProvenanceRange(tokens - 1), + "#define: malformed argument list"_err_en_US); return; } std::string punc{dir.TokenAt(j).ToString()}; @@ -419,21 +418,21 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { break; } if (isVariadic || punc != ",") { - prescanner->Say("#define: malformed argument list"_err_en_US, - dir.GetTokenProvenanceRange(j)); + prescanner->Say(dir.GetTokenProvenanceRange(j), + "#define: malformed argument list"_err_en_US); return; } j = dir.SkipBlanks(j + 1); if (j == tokens) { - prescanner->Say("#define: malformed argument list"_err_en_US, - dir.GetTokenProvenanceRange(tokens - 1)); + prescanner->Say(dir.GetTokenProvenanceRange(tokens - 1), + "#define: malformed argument list"_err_en_US); return; } } if (std::set(argName.begin(), argName.end()).size() != argName.size()) { - prescanner->Say("#define: argument names are not distinct"_err_en_US, - dir.GetTokenProvenance(dirOffset)); + prescanner->Say(dir.GetTokenProvenance(dirOffset), + "#define: argument names are not distinct"_err_en_US); return; } } @@ -447,13 +446,14 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { } } else if (dirName == "undef") { if (nameToken.empty()) { - prescanner->Say("# missing or invalid name"_err_en_US, - dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset)); + prescanner->Say( + dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset), + "# missing or invalid name"_err_en_US); } else { j = dir.SkipBlanks(j + 1); if (j != tokens) { - prescanner->Say("#undef: excess tokens at end of directive"_err_en_US, - dir.GetIntervalProvenanceRange(j, tokens - j)); + prescanner->Say(dir.GetIntervalProvenanceRange(j, tokens - j), + "#undef: excess tokens at end of directive"_err_en_US); } else { definitions_.erase(nameToken); } @@ -461,16 +461,14 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { } else if (dirName == "ifdef" || dirName == "ifndef") { if (nameToken.empty()) { prescanner->Say( - MessageFormattedText("#%s: missing name"_err_en_US, dirName.data()), - dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset)); + dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset), + "#%s: missing name"_err_en_US, dirName.data()); return; } j = dir.SkipBlanks(j + 1); if (j != tokens) { - prescanner->Say(MessageFormattedText( - "#%s: excess tokens at end of directive"_err_en_US, - dirName.data()), - dir.GetIntervalProvenanceRange(j, tokens - j)); + prescanner->Say(dir.GetIntervalProvenanceRange(j, tokens - j), + "#%s: excess tokens at end of directive"_err_en_US, dirName.data()); } else if (IsNameDefined(nameToken) == (dirName == "ifdef")) { ifStack_.push(CanDeadElseAppear::Yes); } else { @@ -486,16 +484,14 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { } } else if (dirName == "else") { if (j != tokens) { - prescanner->Say("#else: excess tokens at end of directive"_err_en_US, - dir.GetIntervalProvenanceRange(j, tokens - j)); + prescanner->Say(dir.GetIntervalProvenanceRange(j, tokens - j), + "#else: excess tokens at end of directive"_err_en_US); } else if (ifStack_.empty()) { - prescanner->Say( - "#else: not nested within #if, #ifdef, or #ifndef"_err_en_US, - dir.GetTokenProvenanceRange(dirOffset)); + prescanner->Say(dir.GetTokenProvenanceRange(dirOffset), + "#else: not nested within #if, #ifdef, or #ifndef"_err_en_US); } else if (ifStack_.top() != CanDeadElseAppear::Yes) { - prescanner->Say( - "#else: already appeared within this #if, #ifdef, or #ifndef"_err_en_US, - dir.GetTokenProvenanceRange(dirOffset)); + prescanner->Say(dir.GetTokenProvenanceRange(dirOffset), + "#else: already appeared within this #if, #ifdef, or #ifndef"_err_en_US); } else { ifStack_.pop(); SkipDisabledConditionalCode("else", IsElseActive::No, prescanner, @@ -503,13 +499,11 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { } } else if (dirName == "elif") { if (ifStack_.empty()) { - prescanner->Say( - "#elif: not nested within #if, #ifdef, or #ifndef"_err_en_US, - dir.GetTokenProvenanceRange(dirOffset)); + prescanner->Say(dir.GetTokenProvenanceRange(dirOffset), + "#elif: not nested within #if, #ifdef, or #ifndef"_err_en_US); } else if (ifStack_.top() != CanDeadElseAppear::Yes) { - prescanner->Say("#elif: #else previously appeared within this " - "#if, #ifdef, or #ifndef"_err_en_US, - dir.GetTokenProvenanceRange(dirOffset)); + prescanner->Say(dir.GetTokenProvenanceRange(dirOffset), + "#elif: #else previously appeared within this #if, #ifdef, or #ifndef"_err_en_US); } else { ifStack_.pop(); SkipDisabledConditionalCode("elif", IsElseActive::No, prescanner, @@ -517,33 +511,34 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { } } else if (dirName == "endif") { if (j != tokens) { - prescanner->Say("#endif: excess tokens at end of directive"_err_en_US, - dir.GetIntervalProvenanceRange(j, tokens - j)); + prescanner->Say(dir.GetIntervalProvenanceRange(j, tokens - j), + "#endif: excess tokens at end of directive"_err_en_US); } else if (ifStack_.empty()) { - prescanner->Say("#endif: no #if, #ifdef, or #ifndef"_err_en_US, - dir.GetTokenProvenanceRange(dirOffset)); + prescanner->Say(dir.GetTokenProvenanceRange(dirOffset), + "#endif: no #if, #ifdef, or #ifndef"_err_en_US); } else { ifStack_.pop(); } } else if (dirName == "error") { prescanner->Say( - MessageFormattedText("#error: %s"_err_en_US, dir.ToString().data()), - dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset)); + dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset), + "#error: %s"_err_en_US, dir.ToString().data()); } else if (dirName == "warning") { prescanner->Say( - MessageFormattedText("#warning: %s"_en_US, dir.ToString().data()), - dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset)); + dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset), + "#warning: %s"_en_US, dir.ToString().data()); } else if (dirName == "include") { if (j == tokens) { - prescanner->Say("#include: missing name of file to include"_err_en_US, - dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset)); + prescanner->Say( + dir.GetIntervalProvenanceRange(dirOffset, tokens - dirOffset), + "#include: missing name of file to include"_err_en_US); return; } std::string include; if (dir.TokenAt(j).ToString() == "<") { if (dir.TokenAt(tokens - 1).ToString() != ">") { - prescanner->Say("#include: expected '>' at end of directive"_err_en_US, - dir.GetIntervalProvenanceRange(j, tokens - j)); + prescanner->Say(dir.GetIntervalProvenanceRange(j, tokens - j), + "#include: expected '>' at end of directive"_err_en_US); return; } TokenSequence braced{dir, j + 1, tokens - j - 2}; @@ -553,31 +548,28 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { include.substr(include.size() - 1, 1) == "\"") { include = include.substr(1, include.size() - 2); } else { - prescanner->Say("#include: expected name of file to include"_err_en_US, - dir.GetTokenProvenanceRange(j < tokens ? j : tokens - 1)); + prescanner->Say(dir.GetTokenProvenanceRange(j < tokens ? j : tokens - 1), + "#include: expected name of file to include"_err_en_US); return; } if (include.empty()) { - prescanner->Say("#include: empty include file name"_err_en_US, - dir.GetTokenProvenanceRange(dirOffset)); + prescanner->Say(dir.GetTokenProvenanceRange(dirOffset), + "#include: empty include file name"_err_en_US); return; } std::stringstream error; const SourceFile *included{allSources_.Open(include, &error)}; if (included == nullptr) { - prescanner->Say( - MessageFormattedText("#include: %s"_err_en_US, error.str().data()), - dir.GetTokenProvenanceRange(dirOffset)); + prescanner->Say(dir.GetTokenProvenanceRange(dirOffset), + "#include: %s"_err_en_US, error.str().data()); } else if (included->bytes() > 0) { ProvenanceRange fileRange{ allSources_.AddIncludedFile(*included, dir.GetProvenanceRange())}; Prescanner{*prescanner}.Prescan(fileRange); } } else { - prescanner->Say(MessageFormattedText( - "#%s: unknown or unimplemented directive"_err_en_US, - dirName.data()), - dir.GetTokenProvenanceRange(dirOffset)); + prescanner->Say(dir.GetTokenProvenanceRange(dirOffset), + "#%s: unknown or unimplemented directive"_err_en_US, dirName.data()); } } @@ -639,8 +631,7 @@ void Preprocessor::SkipDisabledConditionalCode(const std::string &dirName, } } prescanner->Say( - MessageFormattedText("#%s: missing #endif"_err_en_US, dirName.data()), - provenanceRange); + provenanceRange, "#%s: missing #endif"_err_en_US, dirName.data()); } // Precedence level codes used here to accommodate mixed Fortran and C: @@ -759,8 +750,7 @@ static std::int64_t ExpressionValue(const TokenSequence &token, left = std::stoll(t, &consumed, 0 /*base to be detected*/); if (consumed < t.size()) { *error = Message{token.GetTokenProvenanceRange(opAt), - MessageFormattedText( - "uninterpretable numeric constant '%s'"_err_en_US, t.data())}; + "uninterpretable numeric constant '%s'"_err_en_US, t.data()}; return 0; } } else if (IsLegalIdentifierStart(t[0])) { @@ -980,7 +970,7 @@ bool Preprocessor::IsIfPredicateTrue(const TokenSequence &expr, expr3.RemoveBlanks(); } if (expr3.empty()) { - prescanner->Say("empty expression"_err_en_US, expr.GetProvenanceRange()); + prescanner->Say(expr.GetProvenanceRange(), "empty expression"_err_en_US); return false; } std::size_t atToken{0}; @@ -989,11 +979,10 @@ bool Preprocessor::IsIfPredicateTrue(const TokenSequence &expr, if (error.has_value()) { prescanner->Say(std::move(*error)); } else if (atToken < expr3.SizeInTokens()) { - prescanner->Say(atToken == 0 - ? "could not parse any expression"_err_en_US - : "excess characters after expression"_err_en_US, - expr3.GetIntervalProvenanceRange( - atToken, expr3.SizeInTokens() - atToken)); + prescanner->Say(expr3.GetIntervalProvenanceRange( + atToken, expr3.SizeInTokens() - atToken), + atToken == 0 ? "could not parse any expression"_err_en_US + : "excess characters after expression"_err_en_US); } return result; } diff --git a/flang/lib/parser/prescan.cc b/flang/lib/parser/prescan.cc index 99f8955297bb..1fc80126592f 100644 --- a/flang/lib/parser/prescan.cc +++ b/flang/lib/parser/prescan.cc @@ -71,8 +71,8 @@ void Prescanner::Prescan(ProvenanceRange range) { lineStart_ = start_; const bool beganInFixedForm{inFixedForm_}; if (prescannerNesting_ > maxPrescannerNesting) { - Say("too many nested INCLUDE/#include files, possibly circular"_err_en_US, - GetProvenance(start_)); + Say(GetProvenance(start_), + "too many nested INCLUDE/#include files, possibly circular"_err_en_US); return; } while (lineStart_ < limit_) { @@ -168,8 +168,8 @@ void Prescanner::Statement() { break; case LineClassification::Kind::ConditionalCompilationDirective: case LineClassification::Kind::PreprocessorDirective: - Say("preprocessed line resembles a preprocessor directive"_en_US, - preprocessed->GetProvenanceRange()); + Say(preprocessed->GetProvenanceRange(), + "preprocessed line resembles a preprocessor directive"_en_US); preprocessed->ToLowerCase().Emit(cooked_); break; case LineClassification::Kind::CompilerDirective: @@ -218,22 +218,6 @@ TokenSequence Prescanner::TokenizePreprocessorDirective() { return tokens; } -void Prescanner::Say(Message &&message) { - std::optional range{message.GetProvenanceRange(cooked_)}; - CHECK(!range.has_value() || cooked_.IsValid(*range)); - messages_.Put(std::move(message)); -} - -void Prescanner::Say(MessageFixedText text, ProvenanceRange r) { - CHECK(cooked_.IsValid(r)); - messages_.Put({r, text}); -} - -void Prescanner::Say(MessageFormattedText &&text, ProvenanceRange r) { - CHECK(cooked_.IsValid(r)); - messages_.Put({r, std::move(text)}); -} - void Prescanner::NextLine() { void *vstart{static_cast(const_cast(lineStart_))}; void *v{std::memchr(vstart, '\n', limit_ - lineStart_)}; @@ -480,8 +464,8 @@ void Prescanner::QuotedCharacterLiteral(TokenSequence &tokens) { } if (*at_ == '\n') { if (!inPreprocessorDirective_) { - Say("incomplete character literal"_err_en_US, - GetProvenanceRange(start, end)); + Say(GetProvenanceRange(start, end), + "incomplete character literal"_err_en_US); } break; } @@ -514,8 +498,8 @@ void Prescanner::Hollerith( while (count-- > 0) { if (PadOutCharacterLiteral(tokens)) { } else if (*at_ == '\n') { - Say("possible truncated Hollerith literal"_en_US, - GetProvenanceRange(start, at_)); + Say(GetProvenanceRange(start, at_), + "possible truncated Hollerith literal"_en_US); break; } else { NextChar(); @@ -629,8 +613,8 @@ void Prescanner::FortranInclude(const char *firstQuote) { path += *p; } if (*p != quote) { - Say("malformed path name string"_err_en_US, - GetProvenanceRange(firstQuote, p)); + Say(GetProvenanceRange(firstQuote, p), + "malformed path name string"_err_en_US); return; } p = SkipWhiteSpace(p + 1); @@ -638,8 +622,8 @@ void Prescanner::FortranInclude(const char *firstQuote) { const char *garbage{p}; for (; *p != '\n' && *p != '!'; ++p) { } - Say("excess characters after path name"_en_US, - GetProvenanceRange(garbage, p)); + Say(GetProvenanceRange(garbage, p), + "excess characters after path name"_en_US); } std::stringstream error; Provenance provenance{GetProvenance(lineStart_)}; @@ -653,8 +637,7 @@ void Prescanner::FortranInclude(const char *firstQuote) { allSources.PopSearchPathDirectory(); } if (included == nullptr) { - Say(MessageFormattedText("INCLUDE: %s"_err_en_US, error.str().data()), - provenance); + Say(provenance, "INCLUDE: %s"_err_en_US, error.str().data()); } else if (included->bytes() > 0) { ProvenanceRange includeLineRange{ provenance, static_cast(p - lineStart_)}; @@ -747,7 +730,7 @@ const char *Prescanner::FixedFormContinuationLine(bool mightNeedSpace) { // Extension: '&' as continuation marker if (features_.ShouldWarn( LanguageFeature::FixedFormContinuationWithColumn1Ampersand)) { - Say("nonstandard usage"_en_US, GetProvenance(lineStart_)); + Say(GetProvenance(lineStart_), "nonstandard usage"_en_US); } return lineStart_ + 1; } diff --git a/flang/lib/parser/prescan.h b/flang/lib/parser/prescan.h index c26d547deb1b..6fc6ea26b4e6 100644 --- a/flang/lib/parser/prescan.h +++ b/flang/lib/parser/prescan.h @@ -70,9 +70,12 @@ public: TokenSequence TokenizePreprocessorDirective(); Provenance GetCurrentProvenance() const { return GetProvenance(at_); } - void Say(Message &&); - void Say(MessageFixedText, ProvenanceRange); - void Say(MessageFormattedText &&, ProvenanceRange); + template Message &Say(A... a) { + Message &m{messages_.Say(std::forward(a)...)}; + std::optional range{m.GetProvenanceRange(cooked_)}; + CHECK(!range.has_value() || cooked_.IsValid(*range)); + return m; + } private: struct LineClassification { diff --git a/flang/lib/semantics/expression.cc b/flang/lib/semantics/expression.cc index 7ab2ad754949..50e0b68c4617 100644 --- a/flang/lib/semantics/expression.cc +++ b/flang/lib/semantics/expression.cc @@ -14,12 +14,13 @@ #include "expression.h" #include "../common/idioms.h" +#include "../evaluate/common.h" using namespace Fortran::parser::literals; namespace Fortran::semantics { -using Result = ExpressionAnalyzer::Result; +using Result = std::optional; // AnalyzeHelper is a local template function that keeps the API // member function ExpressionAnalyzer::Analyze from having to be a @@ -86,20 +87,45 @@ template<> Result AnalyzeHelper( ExpressionAnalyzer &ea, const parser::CharLiteralConstantSubstring &x) { const auto &range{std::get(x.t)}; - if (!std::get<0>(range.t).has_value() && !std::get<1>(range.t).has_value()) { + const std::optional &lbTree{std::get<0>(range.t)}; + const std::optional &ubTree{std::get<1>(range.t)}; + if (!lbTree.has_value() && !ubTree.has_value()) { // "..."(:) return AnalyzeHelper(ea, std::get(x.t)); } - Result lower{AnalyzeHelper(ea, std::get<0>(range.t))}; - Result upper{AnalyzeHelper(ea, std::get<1>(range.t))}; - if (lower.has_value() && upper.has_value()) { - if (std::optional lb{lower->ScalarValue()}) { - if (std::optional ub{upper->ScalarValue()}) { - // TODO pmk continue here with ToInt64() + // TODO: ensure that any kind parameter is 1 + std::string str{std::get(x.t).GetString()}; + std::optional lb, ub; + if (lbTree.has_value()) { + if (Result lbExpr{AnalyzeHelper(ea, *lbTree)}) { + if (auto *ie{std::get_if(&lbExpr->u)}) { + lb = evaluate::SubscriptIntegerExpr{std::move(*ie)}; + } else { + ea.context().messages.Say( + "scalar integer expression required for substring lower bound"_err_en_US); } } } - return std::nullopt; + if (ubTree.has_value()) { + if (Result ubExpr{AnalyzeHelper(ea, *ubTree)}) { + if (auto *ie{std::get_if(&ubExpr->u)}) { + ub = evaluate::SubscriptIntegerExpr{std::move(*ie)}; + } else { + ea.context().messages.Say( + "scalar integer expression required for substring upper bound"_err_en_US); + } + } + } + if (!lb.has_value() || !ub.has_value()) { + return std::nullopt; + } + evaluate::Substring substring{std::move(str), std::move(lb), std::move(ub)}; + evaluate::CopyableIndirection ind{std::move(substring)}; + evaluate::CharacterExpr<1> chExpr{std::move(ind)}; + chExpr.Fold(ea.context()); + evaluate::AnyKindCharacterExpr akcExpr{std::move(chExpr)}; + evaluate::GenericExpr gExpr{std::move(akcExpr)}; + return {gExpr}; } template<> @@ -116,9 +142,8 @@ Result AnalyzeHelper( FOR_EACH_INTEGER_KIND(CASE, ) #undef CASE default: - ea.context().messages.Say(parser::MessageFormattedText{ - "unimplemented INTEGER kind (%ju)"_err_en_US, - static_cast(kind)}); + ea.context().messages.Say("unimplemented INTEGER kind (%ju)"_err_en_US, + static_cast(kind)); return std::nullopt; } } diff --git a/flang/lib/semantics/expression.h b/flang/lib/semantics/expression.h index 4adb4fde05f7..8798f9f65642 100644 --- a/flang/lib/semantics/expression.h +++ b/flang/lib/semantics/expression.h @@ -26,7 +26,6 @@ namespace Fortran::semantics { class ExpressionAnalyzer { public: using KindParam = std::int64_t; - using Result = std::optional; ExpressionAnalyzer(evaluate::FoldingContext &c, KindParam dIK) : context_{c}, defaultIntegerKind_{dIK} {} @@ -36,7 +35,7 @@ public: // Performs semantic checking on an expression. If successful, // returns its typed expression representation. - Result Analyze(const parser::Expr &); + std::optional Analyze(const parser::Expr &); KindParam Analyze(const std::optional &, KindParam defaultKind, KindParam kanjiKind = -1 /* not allowed here */); diff --git a/flang/lib/semantics/mod-file.cc b/flang/lib/semantics/mod-file.cc index bb284024c7bc..749e341efe5d 100644 --- a/flang/lib/semantics/mod-file.cc +++ b/flang/lib/semantics/mod-file.cc @@ -37,12 +37,6 @@ static constexpr auto extension{".mod"}; // The initial characters of a file that identify it as a .mod file. static constexpr auto magic{"!mod$ v1 sum:"}; -// Helpers for creating error messages. -static parser::Message Error( - const SourceName &, parser::MessageFixedText, const std::string &); -static parser::Message Error(const SourceName &, parser::MessageFixedText, - const std::string &, const std::string &); - static const SourceName *GetSubmoduleParent(const parser::Program &); static std::string ModFilePath( const std::string &, const SourceName &, const std::string &); @@ -89,7 +83,7 @@ void ModFileWriter::Write(const Symbol &symbol) { auto path{ModFilePath(dir_, symbol.name(), ancestorName)}; PutSymbols(*symbol.scope()); if (!WriteFile(path, GetAsString(symbol))) { - errors_.emplace_back( + errors_.Say(symbol.name(), "Error writing %s: %s"_err_en_US, path.c_str(), std::strerror(errno)); } } @@ -467,9 +461,8 @@ Scope *ModFileReader::Read(const SourceName &name, Scope *ancestor) { auto &parseTree{parsing.parseTree()}; if (!parsing.messages().empty() || !parsing.consumedWholeFile() || !parseTree.has_value()) { - errors_.push_back( - Error(name, "Module file for '%s' is corrupt: %s"_err_en_US, - name.ToString(), *path)); + errors_.Say(name, "Module file for '%s' is corrupt: %s"_err_en_US, + name.ToString().data(), path->data()); return nullptr; } Scope *parentScope; // the scope this module/submodule goes into @@ -494,31 +487,28 @@ Scope *ModFileReader::Read(const SourceName &name, Scope *ancestor) { std::optional ModFileReader::FindModFile( const SourceName &name, const std::string &ancestor) { - std::vector errors; + parser::Messages attachments; for (auto &dir : directories_) { std::string path{ModFilePath(dir, name, ancestor)}; std::ifstream ifstream{path}; if (!ifstream.good()) { - errors.push_back( - Error(name, "%s: %s"_en_US, path, std::string{std::strerror(errno)})); + attachments.Say(name, "%s: %s"_en_US, path.data(), std::strerror(errno)); } else { std::string line; std::getline(ifstream, line); if (line.compare(0, strlen(magic), magic) == 0) { return path; } - errors.push_back(Error(name, "%s: Not a valid module file"_en_US, path)); + attachments.Say(name, "%s: Not a valid module file"_en_US, path.data()); } } - auto error{Error(name, + auto error{parser::Message{name, ancestor.empty() ? "Cannot find module file for '%s'"_err_en_US : "Cannot find module file for submodule '%s' of module '%s'"_err_en_US, - name.ToString(), ancestor)}; - for (auto &e : errors) { - error.Attach(e); - } - errors_.push_back(error); + name.ToString().data(), ancestor.data()}}; + attachments.AttachTo(error); + errors_.Say(error); return std::nullopt; } @@ -551,16 +541,4 @@ static std::string ModFilePath(const std::string &dir, const SourceName &name, return path.str(); } -static parser::Message Error(const SourceName &location, - parser::MessageFixedText fixedText, const std::string &arg) { - return parser::Message{ - location, parser::MessageFormattedText{fixedText, arg.data()}}; -} -static parser::Message Error(const SourceName &location, - parser::MessageFixedText fixedText, const std::string &arg1, - const std::string &arg2) { - return parser::Message{location, - parser::MessageFormattedText{fixedText, arg1.data(), arg2.data()}}; -} - } // namespace Fortran::semantics diff --git a/flang/lib/semantics/mod-file.h b/flang/lib/semantics/mod-file.h index 3a54c12bd1a9..7d3619f3a150 100644 --- a/flang/lib/semantics/mod-file.h +++ b/flang/lib/semantics/mod-file.h @@ -41,9 +41,7 @@ public: void set_directory(const std::string &dir) { dir_ = dir; } // Errors encountered during writing. Non-empty if WriteAll returns false. - const std::vector &errors() const { - return errors_; - } + parser::Messages &errors() { return errors_; } // Write out all .mod files; if error return false. bool WriteAll(); @@ -59,7 +57,7 @@ private: std::stringstream decls_; std::stringstream contains_; // Any errors encountered during writing: - std::vector errors_; + parser::Messages errors_; void WriteChildren(const Scope &); void WriteOne(const Scope &); @@ -86,11 +84,11 @@ public: // Return the Scope for that module/submodule or nullptr on error. Scope *Read(const SourceName &, Scope *ancestor = nullptr); // Errors that occurred when Read returns nullptr. - std::vector &errors() { return errors_; } + parser::Messages &errors() { return errors_; } private: std::vector directories_; - std::vector errors_; + parser::Messages errors_; std::optional FindModFile( const SourceName &, const std::string &); diff --git a/flang/lib/semantics/resolve-names.cc b/flang/lib/semantics/resolve-names.cc index 76b7a6696c42..8e891d74e8a0 100644 --- a/flang/lib/semantics/resolve-names.cc +++ b/flang/lib/semantics/resolve-names.cc @@ -174,7 +174,6 @@ class MessageHandler { public: using Message = parser::Message; using MessageFixedText = parser::MessageFixedText; - using MessageFormattedText = parser::MessageFormattedText; const parser::Messages &messages() const { return messages_; } @@ -203,6 +202,7 @@ public: // Emit a message and attached message with two names and locations. void Say2(const SourceName &, MessageFixedText &&, const SourceName &, MessageFixedText &&); + void Annex(parser::Messages &&); private: // Where messages are emitted: @@ -923,7 +923,7 @@ KindParamValue DeclTypeSpecVisitor::GetKindParamValue( // MessageHandler implementation MessageHandler::Message &MessageHandler::Say(Message &&msg) { - return messages_.Put(std::move(msg)); + return messages_.Say(std::move(msg)); } MessageHandler::Message &MessageHandler::Say(MessageFixedText &&msg) { CHECK(currStmtSource_); @@ -939,13 +939,12 @@ MessageHandler::Message &MessageHandler::Say( } MessageHandler::Message &MessageHandler::Say(const SourceName &location, MessageFixedText &&msg, const std::string &arg1) { - return Say(Message{location, MessageFormattedText{msg, arg1.c_str()}}); + return Say(Message{location, msg, arg1.c_str()}); } MessageHandler::Message &MessageHandler::Say(const SourceName &location, MessageFixedText &&msg, const SourceName &arg1, const SourceName &arg2) { - return Say(Message{location, - MessageFormattedText{ - msg, arg1.ToString().c_str(), arg2.ToString().c_str()}}); + return Say( + Message{location, msg, arg1.ToString().c_str(), arg2.ToString().c_str()}); } void MessageHandler::SayAlreadyDeclared( const SourceName &name, const Symbol &prev) { @@ -954,8 +953,10 @@ void MessageHandler::SayAlreadyDeclared( } void MessageHandler::Say2(const SourceName &name1, MessageFixedText &&msg1, const SourceName &name2, MessageFixedText &&msg2) { - Say(name1, std::move(msg1)) - .Attach(name2, MessageFormattedText{msg2, name2.ToString().data()}); + Say(name1, std::move(msg1)).Attach(name2, msg2, name2.ToString().data()); +} +void MessageHandler::Annex(parser::Messages &&msgs) { + messages_.Annex(std::move(msgs)); } // ImplicitRulesVisitor implementation @@ -1383,9 +1384,7 @@ Scope *ModuleVisitor::FindModule(const SourceName &name, Scope *ancestor) { ModFileReader reader{searchDirectories_}; auto *scope{reader.Read(name, ancestor)}; if (!scope) { - for (auto &error : reader.errors()) { - Say(std::move(error)); - } + Annex(std::move(reader.errors())); return nullptr; } if (scope->kind() != Scope::Kind::Module) { @@ -2163,9 +2162,8 @@ bool ResolveNamesVisitor::CheckUseError( for (const auto &pair : details->occurrences()) { const SourceName &location{*pair.first}; const SourceName &moduleName{pair.second->name()}; - msg.Attach(location, - MessageFormattedText{"'%s' was use-associated from module '%s'"_en_US, - name.ToString().data(), moduleName.ToString().data()}); + msg.Attach(location, "'%s' was use-associated from module '%s'"_en_US, + name.ToString().data(), moduleName.ToString().data()); } return true; } @@ -2244,9 +2242,8 @@ const Symbol *ResolveNamesVisitor::FindComponent( auto &typeName{scope->symbol()->name()}; Say(component, "Component '%s' not found in derived type '%s'"_err_en_US, component, typeName) - .Attach(typeName, - MessageFormattedText{ - "Declaration of '%s'"_en_US, typeName.ToString().data()}); + .Attach( + typeName, "Declaration of '%s'"_en_US, typeName.ToString().data()); return nullptr; } auto *symbol{it->second}; diff --git a/flang/tools/f18/f18.cc b/flang/tools/f18/f18.cc index 340b625a5e76..2b8b08683ee4 100644 --- a/flang/tools/f18/f18.cc +++ b/flang/tools/f18/f18.cc @@ -217,11 +217,8 @@ std::string CompileFortran( parseTree, parsing.cooked(), directories); Fortran::semantics::ModFileWriter writer; writer.set_directory(driver.moduleDirectory); - if (!writer.WriteAll()) { - for (const auto &message : writer.errors()) { - std::cerr << message.string() << '\n'; - } - } + writer.WriteAll(); + writer.errors().Emit(std::cerr, parsing.cooked()); if (driver.dumpSymbols) { Fortran::semantics::DumpSymbols(std::cout); } -- 2.34.1