[flang] Clean up messaging: make Say() member function templates more flexible, hide MessageFormattedText instances

Original-commit: flang-compiler/f18@59d774382f
Reviewed-on: https://github.com/flang-compiler/f18/pull/165
Tree-same-pre-rewrite: false
This commit is contained in:
peter klausler 2018-08-08 11:29:05 -07:00
parent 27be6855bf
commit ce231b9559
20 changed files with 264 additions and 233 deletions

View 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_

View 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);
}
}

View File

@ -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<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>

View File

@ -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 <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) {

View 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_;

View 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);

View 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);
}

View 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()) {

View 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:

View 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) {

View 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();
}

View 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;
}

View 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;
}

View 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 {

View File

@ -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<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;
}
}

View 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 */);

View 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

View 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 &);

View 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};

View 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);
}