[flang] Clean up messaging: make Say() member function templates more flexible, hide...
authorpeter klausler <pklausler@nvidia.com>
Wed, 8 Aug 2018 18:29:05 +0000 (11:29 -0700)
committerpeter klausler <pklausler@nvidia.com>
Thu, 9 Aug 2018 17:17:40 +0000 (10:17 -0700)
Original-commit: flang-compiler/f18@59d774382f859acd06341a1d407b4e2fe7c975f7
Reviewed-on: https://github.com/flang-compiler/f18/pull/165
Tree-same-pre-rewrite: false

20 files changed:
flang/lib/evaluate/common.h
flang/lib/evaluate/expression.cc
flang/lib/evaluate/expression.h
flang/lib/evaluate/variable.cc
flang/lib/evaluate/variable.h
flang/lib/parser/basic-parsers.h
flang/lib/parser/debug-parser.cc
flang/lib/parser/message.cc
flang/lib/parser/message.h
flang/lib/parser/parse-state.h
flang/lib/parser/parsing.cc
flang/lib/parser/preprocessor.cc
flang/lib/parser/prescan.cc
flang/lib/parser/prescan.h
flang/lib/semantics/expression.cc
flang/lib/semantics/expression.h
flang/lib/semantics/mod-file.cc
flang/lib/semantics/mod-file.h
flang/lib/semantics/resolve-names.cc
flang/tools/f18/f18.cc

index 4e80708..64bf46c 100644 (file)
@@ -18,6 +18,7 @@
 #include "../common/enum-set.h"
 #include "../common/idioms.h"
 #include "../common/indirection.h"
+#include "../parser/message.h"
 #include <cinttypes>
 
 namespace Fortran::evaluate {
@@ -127,5 +128,14 @@ using HostUnsignedInt =
 // Force availability of copy construction and assignment
 template<typename A> using CopyableIndirection = common::Indirection<A, true>;
 
+// 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_
index 835884c..e754621 100644 (file)
@@ -383,20 +383,16 @@ auto IntegerExpr<KIND>::Fold(FoldingContext &context) -> std::optional<Scalar> {
 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);
   }
 }
 
index 34341ae..229d983 100644 (file)
 
 namespace Fortran::evaluate {
 
-CLASS_TRAIT(FoldableTrait);
-struct FoldingContext {
-  parser::ContextualMessages &messages;
-  Rounding rounding{defaultRounding};
-  bool flushDenormalsToZero{false};
-};
-
 // Helper base classes for packaging subexpressions.
 template<typename CRTP, typename RESULT, typename A = RESULT> 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<int K>
   Expr(const IntegerExpr<K> &x) : u_{ConvertInteger{AnyKindIntegerExpr{x}}} {}
   template<int K>
   Expr(IntegerExpr<K> &&x)
     : u_{ConvertInteger{AnyKindIntegerExpr{std::move(x)}}} {}
+  Expr(const AnyKindRealExpr &x) : u_{ConvertReal{x}} {}
+  Expr(AnyKindRealExpr &&x) : u_{ConvertReal{std::move(x)}} {}
   template<int K>
   Expr(const RealExpr<K> &x) : u_{ConvertReal{AnyKindRealExpr{x}}} {}
   template<int K>
@@ -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<int K>
   Expr(const IntegerExpr<K> &x) : u_{ConvertInteger{AnyKindIntegerExpr{x}}} {}
   template<int K>
   Expr(IntegerExpr<K> &&x)
     : u_{ConvertInteger{AnyKindIntegerExpr{std::move(x)}}} {}
+  Expr(const AnyKindRealExpr &x) : u_{ConvertReal{x}} {}
+  Expr(AnyKindRealExpr &&x) : u_{ConvertReal{std::move(x)}} {}
   template<int K>
   Expr(const RealExpr<K> &x) : u_{ConvertReal{AnyKindRealExpr{x}}} {}
   template<int K>
index bed55b7..adaaf4a 100644 (file)
 #include "../common/idioms.h"
 #include "../parser/char-block.h"
 #include "../parser/characters.h"
+#include "../parser/message.h"
 #include "../semantics/symbol.h"
 #include <ostream>
 
+using namespace Fortran::parser::literals;
+
 namespace Fortran::evaluate {
 
 // Constructors, accessors, mutators
@@ -121,6 +124,62 @@ SubscriptIntegerExpr Substring::last() const {
       u_);
 }
 
+std::optional<std::string> Substring::Fold(FoldingContext &context) {
+  std::optional<SubscriptIntegerExpr::Scalar> 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<std::intmax_t>(lbi));
+      lbi = 1;
+      first_ = SubscriptIntegerExpr{lbi};
+    }
+    if (ubi <= 0) {
+      u_ = ""s;
+      last_ = SubscriptIntegerExpr{0};
+      return {""s};
+    }
+    if (std::string * str{std::get_if<std::string>(&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<typename A> std::ostream &Emit(std::ostream &o, const A &x) {
index 6c2f1eb..24b68a2 100644 (file)
@@ -164,6 +164,7 @@ private:
 // variants of sections instead.
 class Substring {
 public:
+  using FoldableTrait = std::true_type;
   CLASS_BOILERPLATE(Substring)
   Substring(DataRef &&, std::optional<SubscriptIntegerExpr> &&,
       std::optional<SubscriptIntegerExpr> &&);
@@ -173,6 +174,7 @@ public:
   SubscriptIntegerExpr first() const;
   SubscriptIntegerExpr last() const;
   SubscriptIntegerExpr LEN() const;
+  std::optional<std::string> Fold(FoldingContext &);
 
 private:
   std::variant<DataRef, std::string> u_;
index e57c021..34b3966 100644 (file)
@@ -201,12 +201,12 @@ public:
     std::optional<resultType> 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);
index 3a54192..60bd48b 100644 (file)
@@ -23,8 +23,8 @@ std::optional<Success> 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);
     }
index efa0649..616c6d3 100644 (file)
@@ -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()) {
index d05762e..856a75c 100644 (file)
@@ -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<typename RANGE, typename A1, typename... As>
+  Message(RANGE r, const MessageFixedText &t, A1 a1, As... as)
+    : location_{r}, text_{
+                        MessageFormattedText{t, a1, std::forward<As>(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<typename... A> Message &Say(A &&... args) {
+  template<typename... A> Message &Say(A... args) {
     last_ = messages_.emplace_after(last_, std::forward<A>(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:
index ab8b80a..9909f46 100644 (file)
@@ -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<typename... A> void Say(CharBlock range, A &&... args) {
     if (deferMessages_) {
       anyDeferredMessages_ = true;
     } else {
-      messages_.Say(range, t).SetContext(context_.get());
+      messages_.Say(range, std::forward<A>(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<typename... A> void Say(const MessageFixedText &text, A &&... args) {
+    Say(p_, text, std::forward<A>(args)...);
   }
-  void Say(CharBlock range, const MessageExpectedText &t) {
-    if (deferMessages_) {
-      anyDeferredMessages_ = true;
-    } else {
-      messages_.Say(range, t).SetContext(context_.get());
-    }
+  template<typename... A>
+  void Say(const MessageExpectedText &text, A &&... args) {
+    Say(p_, text, std::forward<A>(args)...);
   }
 
   void Nonstandard(LanguageFeature lf, const MessageFixedText &msg) {
index f8afcef..16fc821 100644 (file)
@@ -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();
 }
 
index 1596191..882d974 100644 (file)
@@ -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<std::string>(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;
 }
index 99f8955..1fc8012 100644 (file)
@@ -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<ProvenanceRange> 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<void *>(const_cast<char *>(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<std::size_t>(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;
     }
index c26d547..6fc6ea2 100644 (file)
@@ -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<typename... A> Message &Say(A... a) {
+    Message &m{messages_.Say(std::forward<A>(a)...)};
+    std::optional<ProvenanceRange> range{m.GetProvenanceRange(cooked_)};
+    CHECK(!range.has_value() || cooked_.IsValid(*range));
+    return m;
+  }
 
 private:
   struct LineClassification {
index 7ab2ad7..50e0b68 100644 (file)
 
 #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<evaluate::GenericExpr>;
 
 // 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<parser::SubstringRange>(x.t)};
-  if (!std::get<0>(range.t).has_value() && !std::get<1>(range.t).has_value()) {
+  const std::optional<parser::ScalarIntExpr> &lbTree{std::get<0>(range.t)};
+  const std::optional<parser::ScalarIntExpr> &ubTree{std::get<1>(range.t)};
+  if (!lbTree.has_value() && !ubTree.has_value()) {
     // "..."(:)
     return AnalyzeHelper(ea, std::get<parser::CharLiteralConstant>(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<evaluate::GenericScalar> lb{lower->ScalarValue()}) {
-      if (std::optional<evaluate::GenericScalar> ub{upper->ScalarValue()}) {
-        // TODO pmk continue here with ToInt64()
+  // TODO: ensure that any kind parameter is 1
+  std::string str{std::get<parser::CharLiteralConstant>(x.t).GetString()};
+  std::optional<evaluate::SubscriptIntegerExpr> lb, ub;
+  if (lbTree.has_value()) {
+    if (Result lbExpr{AnalyzeHelper(ea, *lbTree)}) {
+      if (auto *ie{std::get_if<evaluate::AnyKindIntegerExpr>(&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<evaluate::AnyKindIntegerExpr>(&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<evaluate::Substring> 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<std::uintmax_t>(kind)});
+    ea.context().messages.Say("unimplemented INTEGER kind (%ju)"_err_en_US,
+        static_cast<std::uintmax_t>(kind));
     return std::nullopt;
   }
 }
index 4adb4fd..8798f9f 100644 (file)
@@ -26,7 +26,6 @@ namespace Fortran::semantics {
 class ExpressionAnalyzer {
 public:
   using KindParam = std::int64_t;
-  using Result = std::optional<evaluate::GenericExpr>;
 
   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<evaluate::GenericExpr> Analyze(const parser::Expr &);
   KindParam Analyze(const std::optional<parser::KindParam> &,
       KindParam defaultKind, KindParam kanjiKind = -1 /* not allowed here */);
 
index bb28402..749e341 100644 (file)
@@ -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<std::string> ModFileReader::FindModFile(
     const SourceName &name, const std::string &ancestor) {
-  std::vector<parser::Message> 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
index 3a54c12..7d3619f 100644 (file)
@@ -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<parser::MessageFormattedText> &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<parser::MessageFormattedText> 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<parser::Message> &errors() { return errors_; }
+  parser::Messages &errors() { return errors_; }
 
 private:
   std::vector<std::string> directories_;
-  std::vector<parser::Message> errors_;
+  parser::Messages errors_;
 
   std::optional<std::string> FindModFile(
       const SourceName &, const std::string &);
index 76b7a66..8e891d7 100644 (file)
@@ -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};
index 340b625..2b8b086 100644 (file)
@@ -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);
     }