[flang] Revamp C1502 checking of END INTERFACE [generic-spec]

Validation of the optional generic-spec on an END INTERFACE statement
was missing many possible error cases; reimplement it.

Differential Revision: https://reviews.llvm.org/D109910
This commit is contained in:
peter klausler 2021-09-09 15:23:48 -07:00
parent 8a7a28075b
commit 1894250291
4 changed files with 101 additions and 96 deletions

View File

@ -346,17 +346,11 @@ public:
const auto &firstStmt{std::get<parser::Statement<FIRST>>(a.t)}; const auto &firstStmt{std::get<parser::Statement<FIRST>>(a.t)};
if (const parser::CharBlock * firstName{GetStmtName(firstStmt)}) { if (const parser::CharBlock * firstName{GetStmtName(firstStmt)}) {
if (*firstName != *name) { if (*firstName != *name) {
context_ context_.Say(*name, "%s name mismatch"_err_en_US, constructTag)
.Say(*name,
parser::MessageFormattedText{
"%s name mismatch"_err_en_US, constructTag})
.Attach(*firstName, "should be"_en_US); .Attach(*firstName, "should be"_en_US);
} }
} else { } else {
context_ context_.Say(*name, "%s name not allowed"_err_en_US, constructTag)
.Say(*name,
parser::MessageFormattedText{
"%s name not allowed"_err_en_US, constructTag})
.Attach(firstStmt.source, "in unnamed %s"_en_US, constructTag); .Attach(firstStmt.source, "in unnamed %s"_en_US, constructTag);
} }
} }
@ -383,32 +377,51 @@ public:
// C1502 // C1502
void Post(const parser::InterfaceBlock &interfaceBlock) { void Post(const parser::InterfaceBlock &interfaceBlock) {
auto &interfaceStmt{ if (const auto &endGenericSpec{
std::get<parser::Statement<parser::InterfaceStmt>>(interfaceBlock.t)}; std::get<parser::Statement<parser::EndInterfaceStmt>>(
if (const auto *optionalGenericSpecPointer{ interfaceBlock.t)
std::get_if<std::optional<parser::GenericSpec>>( .statement.v}) {
&interfaceStmt.statement.u)}) { const auto &interfaceStmt{
if (*optionalGenericSpecPointer) { std::get<parser::Statement<parser::InterfaceStmt>>(interfaceBlock.t)};
if (const auto *namePointer{ if (std::holds_alternative<parser::Abstract>(interfaceStmt.statement.u)) {
std::get_if<parser::Name>(&(*optionalGenericSpecPointer)->u)}) { context_
auto &optionalGenericSpec{ .Say(endGenericSpec->source,
std::get<parser::Statement<parser::EndInterfaceStmt>>( "END INTERFACE generic name (%s) may not appear for ABSTRACT INTERFACE"_err_en_US,
interfaceBlock.t) endGenericSpec->source)
.statement.v}; .Attach(
if (optionalGenericSpec) { interfaceStmt.source, "corresponding ABSTRACT INTERFACE"_en_US);
if (const auto *otherPointer{ } else if (const auto &genericSpec{
std::get_if<parser::Name>(&optionalGenericSpec->u)}) { std::get<std::optional<parser::GenericSpec>>(
if (namePointer->source != otherPointer->source) { interfaceStmt.statement.u)}) {
context_ bool ok{genericSpec->source == endGenericSpec->source};
.Say(currentPosition_, if (!ok) {
parser::MessageFormattedText{ // Accept variant spellings of .LT. &c.
"INTERFACE generic-name (%s) mismatch"_err_en_US, const auto *endOp{
namePointer->source}) std::get_if<parser::DefinedOperator>(&endGenericSpec->u)};
.Attach(interfaceStmt.source, "mismatched INTERFACE"_en_US); const auto *op{std::get_if<parser::DefinedOperator>(&genericSpec->u)};
} if (endOp && op) {
} const auto *endIntrin{
std::get_if<parser::DefinedOperator::IntrinsicOperator>(
&endOp->u)};
const auto *intrin{
std::get_if<parser::DefinedOperator::IntrinsicOperator>(
&op->u)};
ok = endIntrin && intrin && *endIntrin == *intrin;
} }
} }
if (!ok) {
context_
.Say(endGenericSpec->source,
"END INTERFACE generic name (%s) does not match generic INTERFACE (%s)"_err_en_US,
endGenericSpec->source, genericSpec->source)
.Attach(genericSpec->source, "corresponding INTERFACE"_en_US);
}
} else {
context_
.Say(endGenericSpec->source,
"END INTERFACE generic name (%s) may not appear for non-generic INTERFACE"_err_en_US,
endGenericSpec->source)
.Attach(interfaceStmt.source, "corresponding INTERFACE"_en_US);
} }
} }
} }
@ -441,8 +454,7 @@ public:
} }
} else { } else {
context_.Say(*endName, context_.Say(*endName,
parser::MessageFormattedText{ "END PROGRAM has name without PROGRAM statement"_err_en_US);
"END PROGRAM has name without PROGRAM statement"_err_en_US});
} }
} }
} }
@ -640,24 +652,20 @@ private:
if (endName) { if (endName) {
if (*constructName != *endName) { if (*constructName != *endName) {
context_ context_
.Say(*endName, .Say(*endName, "%s construct name mismatch"_err_en_US,
parser::MessageFormattedText{ constructTag)
"%s construct name mismatch"_err_en_US, constructTag})
.Attach(*constructName, "should be"_en_US); .Attach(*constructName, "should be"_en_US);
} }
} else { } else {
context_ context_
.Say(endStmt.source, .Say(endStmt.source,
parser::MessageFormattedText{ "%s construct name required but missing"_err_en_US,
"%s construct name required but missing"_err_en_US, constructTag)
constructTag})
.Attach(*constructName, "should be"_en_US); .Attach(*constructName, "should be"_en_US);
} }
} else if (endName) { } else if (endName) {
context_ context_
.Say(*endName, .Say(*endName, "%s construct name unexpected"_err_en_US, constructTag)
parser::MessageFormattedText{
"%s construct name unexpected"_err_en_US, constructTag})
.Attach( .Attach(
constructStmt.source, "unnamed %s statement"_en_US, constructTag); constructStmt.source, "unnamed %s statement"_en_US, constructTag);
} }
@ -737,18 +745,16 @@ private:
const auto iter{std::find(constructNames_.crbegin(), const auto iter{std::find(constructNames_.crbegin(),
constructNames_.crend(), constructName.ToString())}; constructNames_.crend(), constructName.ToString())};
if (iter == constructNames_.crend()) { if (iter == constructNames_.crend()) {
context_.Say(constructName, context_.Say(constructName, "%s construct-name is not in scope"_err_en_US,
parser::MessageFormattedText{ stmtString);
"%s construct-name is not in scope"_err_en_US, stmtString});
} }
} }
// 6.2.5, paragraph 2 // 6.2.5, paragraph 2
void CheckLabelInRange(parser::Label label) { void CheckLabelInRange(parser::Label label) {
if (label < 1 || label > 99999) { if (label < 1 || label > 99999) {
context_.Say(currentPosition_, context_.Say(currentPosition_, "Label '%u' is out of range"_err_en_US,
parser::MessageFormattedText{ SayLabel(label));
"Label '%u' is out of range"_err_en_US, SayLabel(label)});
} }
} }
@ -761,9 +767,8 @@ private:
LabeledStatementInfoTuplePOD{scope, currentPosition_, LabeledStatementInfoTuplePOD{scope, currentPosition_,
labeledStmtClassificationSet, isExecutableConstructEndStmt})}; labeledStmtClassificationSet, isExecutableConstructEndStmt})};
if (!pair.second) { if (!pair.second) {
context_.Say(currentPosition_, context_.Say(currentPosition_, "Label '%u' is not distinct"_err_en_US,
parser::MessageFormattedText{ SayLabel(label));
"Label '%u' is not distinct"_err_en_US, SayLabel(label)});
} }
} }
@ -799,7 +804,7 @@ private:
std::vector<UnitAnalysis> programUnits_; std::vector<UnitAnalysis> programUnits_;
SemanticsContext &context_; SemanticsContext &context_;
parser::CharBlock currentPosition_{nullptr}; parser::CharBlock currentPosition_;
ProxyForScope currentScope_; ProxyForScope currentScope_;
std::vector<std::string> constructNames_; std::vector<std::string> constructNames_;
}; };
@ -904,15 +909,13 @@ void CheckLabelDoConstraints(const SourceStmtList &dos,
auto doTarget{GetLabel(labels, label)}; auto doTarget{GetLabel(labels, label)};
if (!HasScope(doTarget.proxyForScope)) { if (!HasScope(doTarget.proxyForScope)) {
// C1133 // C1133
context.Say(position, context.Say(
parser::MessageFormattedText{ position, "Label '%u' cannot be found"_err_en_US, SayLabel(label));
"Label '%u' cannot be found"_err_en_US, SayLabel(label)});
} else if (doTarget.parserCharBlock.begin() < position.begin()) { } else if (doTarget.parserCharBlock.begin() < position.begin()) {
// R1119 // R1119
context.Say(position, context.Say(position,
parser::MessageFormattedText{ "Label '%u' doesn't lexically follow DO stmt"_err_en_US,
"Label '%u' doesn't lexically follow DO stmt"_err_en_US, SayLabel(label));
SayLabel(label)});
} else if ((InInclusiveScope(scopes, scope, doTarget.proxyForScope) && } else if ((InInclusiveScope(scopes, scope, doTarget.proxyForScope) &&
doTarget.labeledStmtClassificationSet.test( doTarget.labeledStmtClassificationSet.test(
@ -924,20 +927,17 @@ void CheckLabelDoConstraints(const SourceStmtList &dos,
common::LanguageFeature::OldLabelDoEndStatements)) { common::LanguageFeature::OldLabelDoEndStatements)) {
context context
.Say(position, .Say(position,
parser::MessageFormattedText{ "A DO loop should terminate with an END DO or CONTINUE"_en_US)
"A DO loop should terminate with an END DO or CONTINUE"_en_US})
.Attach(doTarget.parserCharBlock, .Attach(doTarget.parserCharBlock,
"DO loop currently ends at statement:"_en_US); "DO loop currently ends at statement:"_en_US);
} }
} else if (!InInclusiveScope(scopes, scope, doTarget.proxyForScope)) { } else if (!InInclusiveScope(scopes, scope, doTarget.proxyForScope)) {
context.Say(position, context.Say(position, "Label '%u' is not in DO loop scope"_err_en_US,
parser::MessageFormattedText{ SayLabel(label));
"Label '%u' is not in DO loop scope"_err_en_US, SayLabel(label)});
} else if (!doTarget.labeledStmtClassificationSet.test( } else if (!doTarget.labeledStmtClassificationSet.test(
TargetStatementEnum::Do)) { TargetStatementEnum::Do)) {
context.Say(doTarget.parserCharBlock, context.Say(doTarget.parserCharBlock,
parser::MessageFormattedText{ "A DO loop should terminate with an END DO or CONTINUE"_err_en_US);
"A DO loop should terminate with an END DO or CONTINUE"_err_en_US});
} else { } else {
loopBodies.emplace_back(SkipLabel(position), doTarget.parserCharBlock); loopBodies.emplace_back(SkipLabel(position), doTarget.parserCharBlock);
} }
@ -957,9 +957,8 @@ void CheckScopeConstraints(const SourceStmtList &stmts,
const auto &position{stmt.parserCharBlock}; const auto &position{stmt.parserCharBlock};
auto target{GetLabel(labels, label)}; auto target{GetLabel(labels, label)};
if (!HasScope(target.proxyForScope)) { if (!HasScope(target.proxyForScope)) {
context.Say(position, context.Say(
parser::MessageFormattedText{ position, "Label '%u' was not found"_err_en_US, SayLabel(label));
"Label '%u' was not found"_err_en_US, SayLabel(label)});
} else if (!InInclusiveScope(scopes, scope, target.proxyForScope)) { } else if (!InInclusiveScope(scopes, scope, target.proxyForScope)) {
// Clause 11.1.2.1 prohibits transfer of control to the interior of a // Clause 11.1.2.1 prohibits transfer of control to the interior of a
// block from outside the block, but this does not apply to formats. // block from outside the block, but this does not apply to formats.
@ -967,9 +966,8 @@ void CheckScopeConstraints(const SourceStmtList &stmts,
TargetStatementEnum::Format)) { TargetStatementEnum::Format)) {
continue; continue;
} }
context.Say(position, context.Say(
parser::MessageFormattedText{ position, "Label '%u' is not in scope"_en_US, SayLabel(label));
"Label '%u' is not in scope"_en_US, SayLabel(label)});
} }
} }
} }
@ -986,21 +984,16 @@ void CheckBranchTargetConstraints(const SourceStmtList &stmts,
TargetStatementEnum::CompatibleBranch)) { // error TargetStatementEnum::CompatibleBranch)) { // error
context context
.Say(branchTarget.parserCharBlock, .Say(branchTarget.parserCharBlock,
parser::MessageFormattedText{ "Label '%u' is not a branch target"_err_en_US, SayLabel(label))
"Label '%u' is not a branch target"_err_en_US, .Attach(stmt.parserCharBlock, "Control flow use of '%u'"_en_US,
SayLabel(label)}) SayLabel(label));
.Attach(stmt.parserCharBlock,
parser::MessageFormattedText{
"Control flow use of '%u'"_en_US, SayLabel(label)});
} else if (!branchTarget.labeledStmtClassificationSet.test( } else if (!branchTarget.labeledStmtClassificationSet.test(
TargetStatementEnum::Branch)) { // warning TargetStatementEnum::Branch)) { // warning
context context
.Say(branchTarget.parserCharBlock, .Say(branchTarget.parserCharBlock,
parser::MessageFormattedText{ "Label '%u' is not a branch target"_en_US, SayLabel(label))
"Label '%u' is not a branch target"_en_US, SayLabel(label)}) .Attach(stmt.parserCharBlock, "Control flow use of '%u'"_en_US,
.Attach(stmt.parserCharBlock, SayLabel(label));
parser::MessageFormattedText{
"Control flow use of '%u'"_en_US, SayLabel(label)});
} }
} }
} }
@ -1022,12 +1015,10 @@ void CheckDataXferTargetConstraints(const SourceStmtList &stmts,
if (!ioTarget.labeledStmtClassificationSet.test( if (!ioTarget.labeledStmtClassificationSet.test(
TargetStatementEnum::Format)) { TargetStatementEnum::Format)) {
context context
.Say(ioTarget.parserCharBlock, .Say(ioTarget.parserCharBlock, "'%u' not a FORMAT"_err_en_US,
parser::MessageFormattedText{ SayLabel(label))
"'%u' not a FORMAT"_err_en_US, SayLabel(label)}) .Attach(stmt.parserCharBlock, "data transfer use of '%u'"_en_US,
.Attach(stmt.parserCharBlock, SayLabel(label));
parser::MessageFormattedText{
"data transfer use of '%u'"_en_US, SayLabel(label)});
} }
} }
} }

View File

@ -76,8 +76,8 @@ std::optional<std::int64_t> EvaluateInt64(
// Analyze a generic-spec and generate a symbol name and GenericKind for it. // Analyze a generic-spec and generate a symbol name and GenericKind for it.
class GenericSpecInfo { class GenericSpecInfo {
public: public:
GenericSpecInfo(const parser::DefinedOpName &x) { Analyze(x); } explicit GenericSpecInfo(const parser::DefinedOpName &x) { Analyze(x); }
GenericSpecInfo(const parser::GenericSpec &x) { Analyze(x); } explicit GenericSpecInfo(const parser::GenericSpec &x) { Analyze(x); }
GenericKind kind() const { return kind_; } GenericKind kind() const { return kind_; }
const SourceName &symbolName() const { return symbolName_.value(); } const SourceName &symbolName() const { return symbolName_.value(); }
@ -88,12 +88,12 @@ public:
llvm::raw_ostream &, const GenericSpecInfo &); llvm::raw_ostream &, const GenericSpecInfo &);
private: private:
void Analyze(const parser::DefinedOpName &);
void Analyze(const parser::GenericSpec &);
GenericKind kind_; GenericKind kind_;
const parser::Name *parseName_{nullptr}; const parser::Name *parseName_{nullptr};
std::optional<SourceName> symbolName_; std::optional<SourceName> symbolName_;
void Analyze(const parser::DefinedOpName &);
void Analyze(const parser::GenericSpec &);
}; };
// Analyze a parser::ArraySpec or parser::CoarraySpec // Analyze a parser::ArraySpec or parser::CoarraySpec

View File

@ -2423,7 +2423,7 @@ void ScopeHandler::MakeExternal(Symbol &symbol) {
bool ModuleVisitor::Pre(const parser::Only &x) { bool ModuleVisitor::Pre(const parser::Only &x) {
std::visit(common::visitors{ std::visit(common::visitors{
[&](const Indirection<parser::GenericSpec> &generic) { [&](const Indirection<parser::GenericSpec> &generic) {
const GenericSpecInfo &genericSpecInfo{generic.value()}; GenericSpecInfo genericSpecInfo{generic.value()};
AddUseOnly(genericSpecInfo.symbolName()); AddUseOnly(genericSpecInfo.symbolName());
AddUse(genericSpecInfo); AddUse(genericSpecInfo);
}, },

View File

@ -41,8 +41,22 @@ end submodule t16
module t5 module t5
interface t7 interface t7
!ERROR: INTERFACE generic-name (t7) mismatch !ERROR: END INTERFACE generic name (t8) does not match generic INTERFACE (t7)
end interface t8 end interface t8
abstract interface
!ERROR: END INTERFACE generic name (t19) may not appear for ABSTRACT INTERFACE
end interface t19
interface
!ERROR: END INTERFACE generic name (t20) may not appear for non-generic INTERFACE
end interface t20
interface
!ERROR: END INTERFACE generic name (assignment(=)) may not appear for non-generic INTERFACE
end interface assignment(=)
interface operator(<)
end interface operator(.LT.) ! not an error
interface operator(.EQ.)
end interface operator(==) ! not an error
type t17 type t17
!ERROR: derived type definition name mismatch !ERROR: derived type definition name mismatch
end type t18 end type t18