[flang] Collect implicit type rules and report related errors.

ImplicitRules maintains a mapping of initial character of an identifier
to its implicit type.

ImplicitRulesVisitor visits the ImplicitStmt and related parse tree
nodes and updates the rules.

Emit errors messages in these cases:
- implicit type specified more than once for a letter
- IMPLICIT statement and IMPLICIT NONE in same scope
- IMPLICIT statement and IMPLICIT NONE(TYPE) in same scope
- more than one IMPLICIT NONE in a scope
- IMPLICIT statement with empty range of letters
- IMPLICIT NONE statement after PARAMETER statement
- TYPE or EXTERNAL repeated in an IMPLICIT NONE statement

Also start emitting error messages through the parser::Messages
interface. An instance of Messages is created from the cooked source and
saved in the MessageHandler class. It tracks the source location of the
current statement and provides a way to emit messages associated with
that statement.

Original-commit: flang-compiler/f18@e12fea9850
Reviewed-on: https://github.com/flang-compiler/f18/pull/41
Tree-same-pre-rewrite: false
This commit is contained in:
Tim Keith 2018-04-04 09:03:51 -07:00 committed by GitHub
parent 6d23ceb7bd
commit da414d7c3f
4 changed files with 367 additions and 42 deletions

View file

@ -291,7 +291,7 @@ public:
void Post(const parser::DerivedTypeStmt &x) {
builder_->name(std::get<parser::Name>(x.t).ToString());
builder_->attrs(*attrs_);
attrs_.release();
attrs_.reset();
}
void Post(const parser::Program &) {
@ -315,9 +315,10 @@ private:
};
void MakeTypes(std::ostream &out, const parser::Program &program) {
void ResolveNames(const parser::Program &program);
ResolveNames(program);
void MakeTypes(
const parser::Program &program, const parser::CookedSource &cookedSource) {
void ResolveNames(const parser::Program &, const parser::CookedSource &);
ResolveNames(program, cookedSource);
}
static KindParamValue GetKindParamValue(

View file

@ -1,13 +1,11 @@
#include <iosfwd>
namespace Fortran {
namespace parser {
namespace Fortran::parser {
class Program;
}
class CookedSource;
} // namespace Fortran::parser
namespace semantics {
void MakeTypes(std::ostream &out, const parser::Program &program);
namespace Fortran::semantics {
void MakeTypes(
const parser::Program &program, const parser::CookedSource &cookedSource);
}
} // namespace Fortran

View file

@ -12,6 +12,39 @@
namespace Fortran::semantics {
using namespace parser::literals;
class MessageHandler;
// ImplicitRules maps initial character of identifier to the DeclTypeSpec*
// representing the implicit type; nullptr if none.
class ImplicitRules {
public:
ImplicitRules(MessageHandler &messages);
// Get the implicit type for identifiers starting with ch. May be null.
const DeclTypeSpec *GetType(char ch) const;
// Record the implicit type for this range of characters.
void SetType(const DeclTypeSpec &type, parser::Location lo, parser::Location,
bool isDefault = false);
// Apply the default implicit rules (if no IMPLICIT NONE).
void ApplyDefaultRules();
private:
// Map 'a'-'z' to index into map_ and vice versa.
static const int CHARS_BEFORE_J = 9;
static const int CHARS_BEFORE_S = 18;
static const int CHARS_TOTAL = 26;
static int CharToIndex(char ch);
static char IndexToChar(int i);
MessageHandler &messages_;
// each type referenced in implicit specs is in types_
std::list<DeclTypeSpec> types_;
// map_ goes from char index (0-25) to nullptr or pointer to element of types_
std::array<const DeclTypeSpec *, CHARS_TOTAL> map_;
friend std::ostream &operator<<(std::ostream &, const ImplicitRules &);
};
// Provide Post methods to collect attributes into a member variable.
class AttrsVisitor {
public:
@ -90,17 +123,88 @@ private:
const std::optional<parser::KindSelector> &kind);
};
// Walk the parse tree and resolve names to symbols.
class ResolveNamesVisitor : public DeclTypeSpecVisitor {
// Track statement source locations and save messages.
class MessageHandler {
public:
using Message = parser::Message;
MessageHandler(parser::Messages &messages) : messages_{messages} {}
template<typename T> bool Pre(const parser::Statement<T> &x) {
currStmtSource_ = &x.source;
return true;
}
template<typename T> void Post(const parser::Statement<T> &) {
currStmtSource_ = nullptr;
}
const parser::CharBlock *currStmtSource() { return currStmtSource_; }
// Emit a message associated with the current statement source.
void Say(Message &&);
void Say(parser::MessageFixedText &&);
void Say(parser::MessageFormattedText &&);
private:
// Where messages are emitted:
parser::Messages &messages_;
// Source location of current statement; null if not in a statement
const parser::CharBlock *currStmtSource_{nullptr};
};
class ImplicitRulesVisitor : public DeclTypeSpecVisitor, public MessageHandler {
public:
using DeclTypeSpecVisitor::Post;
using DeclTypeSpecVisitor::Pre;
using MessageHandler::Post;
using MessageHandler::Pre;
using ImplicitNoneNameSpec = parser::ImplicitStmt::ImplicitNoneNameSpec;
ResolveNamesVisitor() { PushScope(Scope::globalScope); }
ImplicitRulesVisitor(parser::Messages &messages) : MessageHandler(messages) {}
void Post(const parser::ParameterStmt &);
bool Pre(const parser::ImplicitStmt &);
bool Pre(const parser::LetterSpec &);
bool Pre(const parser::ImplicitSpec &);
void Post(const parser::ImplicitSpec &);
void Post(const parser::ImplicitPart &);
protected:
void StartScope();
void EndScope();
private:
// implicit rules in effect for current scope
std::unique_ptr<ImplicitRules> implicitRules_;
// previous occurence of these kinds of statements:
const parser::CharBlock *prevImplicit_{nullptr};
const parser::CharBlock *prevImplicitNone_{nullptr};
const parser::CharBlock *prevImplicitNoneType_{nullptr};
const parser::CharBlock *prevParameterStmt_{nullptr};
bool HandleImplicitNone(const std::list<ImplicitNoneNameSpec> &nameSpecs);
};
// Walk the parse tree and resolve names to symbols.
class ResolveNamesVisitor : public ImplicitRulesVisitor {
public:
using ImplicitRulesVisitor::Post;
using ImplicitRulesVisitor::Pre;
ResolveNamesVisitor(parser::Messages &messages)
: ImplicitRulesVisitor(messages) {
PushScope(Scope::globalScope);
}
Scope &CurrScope() { return *scopes_.top(); }
void PushScope(Scope &scope) { scopes_.push(&scope); }
void PopScope() { scopes_.pop(); }
void PushScope(Scope &scope) {
scopes_.push(&scope);
StartScope();
}
void PopScope() {
scopes_.pop();
EndScope();
}
// Default action for a parse tree node is to visit children.
template<typename T> bool Pre(const T &) { return true; }
@ -138,7 +242,88 @@ private:
}
};
// ImplicitRules implementation
ImplicitRules::ImplicitRules(MessageHandler &messages) : messages_{messages} {
for (int i = 0; i < CHARS_TOTAL; ++i) {
map_[i] = nullptr;
}
}
const DeclTypeSpec *ImplicitRules::GetType(char ch) const {
int index = CharToIndex(ch);
return index >= 0 ? map_[index] : nullptr;
}
// isDefault is set when we are applying the default rules, so it is not
// an error if the type is already set.
void ImplicitRules::SetType(const DeclTypeSpec &type, parser::Location lo,
parser::Location hi, bool isDefault) {
types_.push_back(type);
int loIndex = CharToIndex(*lo);
CHECK(loIndex >= 0 && "not a letter");
int hiIndex = CharToIndex(*hi);
CHECK(hiIndex >= 0 && "not a letter");
for (int i = loIndex; i <= hiIndex; ++i) {
if (!map_[i]) {
map_[i] = &types_.back();
} else if (!isDefault) {
messages_.Say(parser::Message{lo,
parser::MessageFormattedText{
"More than one implicit type specified for '%c'"_err_en_US,
IndexToChar(i)}});
}
}
}
void ImplicitRules::ApplyDefaultRules() {
SetType(DeclTypeSpec::MakeIntrinsic(IntegerTypeSpec::Make()), "i", "n", true);
SetType(DeclTypeSpec::MakeIntrinsic(RealTypeSpec::Make()), "a", "z", true);
}
// Map 'a'-'z' to 0-25, others to -1
int ImplicitRules::CharToIndex(char ch) {
if (ch >= 'a' && ch <= 'i') {
return ch - 'a';
} else if (ch >= 'j' && ch <= 'r') {
return ch - 'j' + CHARS_BEFORE_J;
} else if (ch >= 's' && ch <= 'z') {
return ch - 's' + CHARS_BEFORE_S;
} else {
return -1; // not a letter
}
}
// Map 0-25 to 'a'-'z', others to 0
char ImplicitRules::IndexToChar(int i) {
if (i < 0 || i > 25) {
return 0;
} else if (i < CHARS_BEFORE_J) {
return i + 'a';
} else if (i < CHARS_BEFORE_S) {
return i - CHARS_BEFORE_J + 'j';
} else {
return i - CHARS_BEFORE_S + 's';
}
}
std::ostream &operator<<(std::ostream &o, const ImplicitRules &implicitRules) {
o << "ImplicitRules:\n";
for (const auto &type : implicitRules.types_) {
o << " " << type << ':';
for (int i{0}; i < ImplicitRules::CHARS_TOTAL; ++i) {
if (implicitRules.map_[i] == &type) {
o << ' ' << ImplicitRules::IndexToChar(i);
}
}
o << '\n';
}
return o;
}
// AttrsVisitor implementation
void AttrsVisitor::beginAttrs() {
CHECK(!attrs_);
attrs_ = std::make_optional<Attrs>();
@ -165,12 +350,8 @@ bool AttrsVisitor::Pre(const parser::AccessSpec &x) {
}
bool AttrsVisitor::Pre(const parser::IntentSpec &x) {
switch (x.v) {
case parser::IntentSpec::Intent::In:
attrs_->set(Attr::INTENT_IN);
break;
case parser::IntentSpec::Intent::Out:
attrs_->set(Attr::INTENT_OUT);
break;
case parser::IntentSpec::Intent::In: attrs_->set(Attr::INTENT_IN); break;
case parser::IntentSpec::Intent::Out: attrs_->set(Attr::INTENT_OUT); break;
case parser::IntentSpec::Intent::InOut:
attrs_->set(Attr::INTENT_IN);
attrs_->set(Attr::INTENT_OUT);
@ -181,6 +362,7 @@ bool AttrsVisitor::Pre(const parser::IntentSpec &x) {
}
// DeclTypeSpecVisitor implementation
void DeclTypeSpecVisitor::beginDeclTypeSpec() {
CHECK(!expectDeclTypeSpec_);
expectDeclTypeSpec_ = true;
@ -214,12 +396,15 @@ void DeclTypeSpecVisitor::Post(const parser::TypeParamSpec &x) {
typeParamValue_.reset();
}
bool DeclTypeSpecVisitor::Pre(const parser::TypeParamValue &x) {
typeParamValue_ = std::make_unique<ParamValue>(
std::visit(parser::visitors{
[&](const parser::ScalarIntExpr &x) { return Bound{IntExpr{x}}; },
[&](const parser::Star &x) { return Bound::ASSUMED; },
[&](const parser::TypeParamValue::Deferred &x) { return Bound::DEFERRED; },
}, x.u));
typeParamValue_ = std::make_unique<ParamValue>(std::visit(
parser::visitors{
[&](const parser::ScalarIntExpr &x) { return Bound{IntExpr{x}}; },
[&](const parser::Star &x) { return Bound::ASSUMED; },
[&](const parser::TypeParamValue::Deferred &x) {
return Bound::DEFERRED;
},
},
x.u));
return false;
}
@ -258,7 +443,8 @@ void DeclTypeSpecVisitor::MakeIntrinsic(
// Check that we're expecting to see a DeclTypeSpec (and haven't seen one yet)
// and save it in declTypeSpec_.
void DeclTypeSpecVisitor::SetDeclTypeSpec(const DeclTypeSpec &declTypeSpec) {
CHECK(expectDeclTypeSpec_ && !declTypeSpec_);
CHECK(expectDeclTypeSpec_);
CHECK(!declTypeSpec_);
declTypeSpec_ = std::make_unique<DeclTypeSpec>(declTypeSpec);
}
@ -277,6 +463,142 @@ KindParamValue DeclTypeSpecVisitor::GetKindParamValue(
}
}
// MessageHandler implementation
void MessageHandler::Say(Message &&x) { messages_.Put(std::move(x)); }
void MessageHandler::Say(parser::MessageFixedText &&x) {
CHECK(currStmtSource_);
messages_.Put(Message{currStmtSource_->begin(), std::move(x)});
}
void MessageHandler::Say(parser::MessageFormattedText &&x) {
CHECK(currStmtSource_);
messages_.Put(Message{currStmtSource_->begin(), std::move(x)});
}
// ImplicitRulesVisitor implementation
void ImplicitRulesVisitor::Post(const parser::ParameterStmt &x) {
prevParameterStmt_ = currStmtSource();
}
bool ImplicitRulesVisitor::Pre(const parser::ImplicitStmt &x) {
bool res = std::visit(
parser::visitors{
[&](const std::list<ImplicitNoneNameSpec> &x) {
return HandleImplicitNone(x);
},
[&](const std::list<parser::ImplicitSpec> &x) {
if (prevImplicitNoneType_) {
Say("IMPLICIT statement after IMPLICIT NONE or "
"IMPLICIT NONE(TYPE) statement"_err_en_US);
return false;
}
return true;
},
},
x.u);
prevImplicit_ = currStmtSource();
return res;
}
bool ImplicitRulesVisitor::Pre(const parser::LetterSpec &x) {
auto loLoc{std::get<parser::Location>(x.t)};
auto hiLoc{loLoc};
if (auto hiLocOpt = std::get<std::optional<parser::Location>>(x.t)) {
hiLoc = *hiLocOpt;
if (*hiLoc < *loLoc) {
Say(Message{hiLoc,
parser::MessageFormattedText{
"'%c' does not follow '%c' alphabetically"_err_en_US, *hiLoc,
*loLoc}});
return false;
}
}
implicitRules_->SetType(*declTypeSpec_.get(), loLoc, hiLoc);
return false;
}
void ImplicitRulesVisitor::Post(const parser::ImplicitPart &) {
if (!prevImplicitNoneType_) {
implicitRules_->ApplyDefaultRules();
}
}
bool ImplicitRulesVisitor::Pre(const parser::ImplicitSpec &) {
beginDeclTypeSpec();
return true;
}
void ImplicitRulesVisitor::Post(const parser::ImplicitSpec &) {
endDeclTypeSpec();
}
void ImplicitRulesVisitor::StartScope() {
implicitRules_ = std::make_unique<ImplicitRules>(*this);
prevImplicit_ = nullptr;
prevImplicitNone_ = nullptr;
prevImplicitNoneType_ = nullptr;
prevParameterStmt_ = nullptr;
}
void ImplicitRulesVisitor::EndScope() { implicitRules_.reset(); }
// TODO: for all of these errors, reference previous statement too
bool ImplicitRulesVisitor::HandleImplicitNone(
const std::list<ImplicitNoneNameSpec> &nameSpecs) {
if (prevImplicitNone_ != nullptr) {
Say("More than one IMPLICIT NONE statement"_err_en_US);
return false;
}
if (prevParameterStmt_ != nullptr) {
Say("IMPLICIT NONE statement after PARAMETER statement"_err_en_US);
return false;
}
prevImplicitNone_ = currStmtSource();
if (nameSpecs.empty()) {
prevImplicitNoneType_ = currStmtSource();
if (prevImplicit_) {
Say("IMPLICIT NONE statement after IMPLICIT statement"_err_en_US);
return false;
}
} else {
int sawType{0};
int sawExternal{0};
for (const auto noneSpec : nameSpecs) {
switch (noneSpec) {
case ImplicitNoneNameSpec::External:
sawExternal += 1;
// TODO:
// C894 If IMPLICIT NONE with an implicit-none-spec of EXTERNAL
// appears within a scoping unit, the name of an external or dummy
// procedure in that scoping unit or in a contained subprogram or
// BLOCK construct shall have an explicit interface or be explicitly
// declared to have the EXTERNAL attribute.
break;
case ImplicitNoneNameSpec::Type:
prevImplicitNoneType_ = currStmtSource();
if (prevImplicit_) {
Say("IMPLICIT NONE(TYPE) after IMPLICIT statement"_err_en_US);
return false;
}
sawType += 1;
break;
default: CRASH_NO_CASE;
}
}
if (sawType > 1) {
Say("TYPE specified more than once in IMPLICIT NONE statement"_err_en_US);
return false;
}
if (sawExternal > 1) {
Say("EXTERNAL specified more than once in IMPLICIT NONE statement"_err_en_US);
return false;
}
}
return true;
}
// ResolveNamesVisitor implementation
@ -291,14 +613,17 @@ void ResolveNamesVisitor::Post(const parser::EntityDecl &x) {
}
if (EntityDetails *details = symbol.detailsIf<EntityDetails>()) {
if (details->type().has_value()) {
std::cerr << "ERROR: symbol already has a type declared: "
<< name.ToString() << "\n";
Say(parser::Message{name.source.begin(),
parser::MessageFormattedText{
"'%s' already has a type declared"_err_en_US,
name.ToString().c_str()}});
} else {
details->set_type(*declTypeSpec_);
}
} else {
std::cerr << "ERROR: symbol already declared, can't appear in entity-decl: "
<< name.ToString() << "\n";
Say(parser::Message{name.source.begin(),
parser::MessageFormattedText{
"'%s' is already declared"_err_en_US, name.ToString().c_str()}});
}
}
@ -383,7 +708,8 @@ void ResolveNamesVisitor::Post(const parser::FunctionStmt &stmt) {
funcResultName_ = std::nullopt;
}
void ResolveNamesVisitor::PostSubprogram(const Name &name, const std::list<Name> &dummyNames) {
void ResolveNamesVisitor::PostSubprogram(
const Name &name, const std::list<Name> &dummyNames) {
const auto attrs = endAttrs();
MakeSymbol(name, attrs, SubprogramDetails(dummyNames));
Scope &subpScope = CurrScope().MakeScope(Scope::Kind::Subprogram);
@ -399,10 +725,12 @@ void ResolveNamesVisitor::Post(const parser::Program &) {
CHECK(!declTypeSpec_);
}
void ResolveNames(const parser::Program &program) {
ResolveNamesVisitor visitor;
void ResolveNames(
const parser::Program &program, const parser::CookedSource &cookedSource) {
parser::Messages messages{cookedSource};
ResolveNamesVisitor visitor{messages};
parser::Walk(program, visitor);
messages.Emit(std::cerr);
}
} // namespace Fortran::semantics

View file

@ -2,8 +2,6 @@
#include "../../lib/semantics/make-types.h"
#include <iostream>
#include <optional>
#include <sstream>
#include <string>
using namespace Fortran;
@ -15,9 +13,9 @@ int main(int argc, char *const argv[]) {
return EXIT_FAILURE;
}
std::string path{argv[1]};
Parsing parsing;
parser::Parsing parsing;
if (parsing.ForTesting(path, std::cerr)) {
semantics::MakeTypes(std::cout, *parsing.parseTree());
semantics::MakeTypes(*parsing.parseTree(), parsing.messages().cooked());
return EXIT_SUCCESS;
}
return EXIT_FAILURE;