[flang] Fix bogus message on internal subprogram with alternate return

Internal subprograms have explicit interfaces.  If an internal subprogram has
an alternate return, we check its explicit interface.  But we were not
putting the label values of alternate returns into the actual argument.

I fixed this by changing the definition of actual arguments to be able
to contain a common::Label and putting the label for an alternate return
into the actual argument.

I also verified that we were already doing all of the semantic checking
required for alternate returns and removed a "TODO" for this.

I also added the test altreturn06.f90.

Differential Revision: https://reviews.llvm.org/D94017
This commit is contained in:
Peter Steinfeld 2021-01-04 09:35:15 -08:00
parent 311b247c9f
commit ae0d1d2e5c
8 changed files with 60 additions and 23 deletions

View File

@ -67,6 +67,9 @@ enum class RoundingMode : std::uint8_t {
TiesAwayFromZero, // ROUND=COMPATIBLE, RC - ties round away from zero TiesAwayFromZero, // ROUND=COMPATIBLE, RC - ties round away from zero
}; };
// Fortran label. Must be in [1..99999].
using Label = std::uint64_t;
// Fortran arrays may have up to 15 dimensions (See Fortran 2018 section 5.4.6). // Fortran arrays may have up to 15 dimensions (See Fortran 2018 section 5.4.6).
static constexpr int maxRank{15}; static constexpr int maxRank{15};
} // namespace Fortran::common } // namespace Fortran::common

View File

@ -13,6 +13,7 @@
#include "constant.h" #include "constant.h"
#include "formatting.h" #include "formatting.h"
#include "type.h" #include "type.h"
#include "flang/Common/Fortran.h"
#include "flang/Common/indirection.h" #include "flang/Common/indirection.h"
#include "flang/Common/reference.h" #include "flang/Common/reference.h"
#include "flang/Parser/char-block.h" #include "flang/Parser/char-block.h"
@ -73,6 +74,7 @@ public:
explicit ActualArgument(Expr<SomeType> &&); explicit ActualArgument(Expr<SomeType> &&);
explicit ActualArgument(common::CopyableIndirection<Expr<SomeType>> &&); explicit ActualArgument(common::CopyableIndirection<Expr<SomeType>> &&);
explicit ActualArgument(AssumedType); explicit ActualArgument(AssumedType);
explicit ActualArgument(common::Label);
~ActualArgument(); ~ActualArgument();
ActualArgument &operator=(Expr<SomeType> &&); ActualArgument &operator=(Expr<SomeType> &&);
@ -101,6 +103,8 @@ public:
} }
} }
common::Label GetLabel() const { return std::get<common::Label>(u_); }
std::optional<DynamicType> GetType() const; std::optional<DynamicType> GetType() const;
int Rank() const; int Rank() const;
bool operator==(const ActualArgument &) const; bool operator==(const ActualArgument &) const;
@ -108,8 +112,9 @@ public:
std::optional<parser::CharBlock> keyword() const { return keyword_; } std::optional<parser::CharBlock> keyword() const { return keyword_; }
void set_keyword(parser::CharBlock x) { keyword_ = x; } void set_keyword(parser::CharBlock x) { keyword_ = x; }
bool isAlternateReturn() const { return isAlternateReturn_; } bool isAlternateReturn() const {
void set_isAlternateReturn() { isAlternateReturn_ = true; } return std::holds_alternative<common::Label>(u_);
}
bool isPassedObject() const { return isPassedObject_; } bool isPassedObject() const { return isPassedObject_; }
void set_isPassedObject(bool yes = true) { isPassedObject_ = yes; } void set_isPassedObject(bool yes = true) { isPassedObject_ = yes; }
@ -131,9 +136,10 @@ private:
// e.g. between X and (X). The parser attempts to parse each argument // e.g. between X and (X). The parser attempts to parse each argument
// first as a variable, then as an expression, and the distinction appears // first as a variable, then as an expression, and the distinction appears
// in the parse tree. // in the parse tree.
std::variant<common::CopyableIndirection<Expr<SomeType>>, AssumedType> u_; std::variant<common::CopyableIndirection<Expr<SomeType>>, AssumedType,
common::Label>
u_;
std::optional<parser::CharBlock> keyword_; std::optional<parser::CharBlock> keyword_;
bool isAlternateReturn_{false}; // whether expr is a "*label" number
bool isPassedObject_{false}; bool isPassedObject_{false};
common::Intent dummyIntent_{common::Intent::Default}; common::Intent dummyIntent_{common::Intent::Default};
}; };

View File

@ -333,7 +333,7 @@ using ScalarDefaultCharExpr = Scalar<DefaultCharExpr>;
using ScalarDefaultCharConstantExpr = Scalar<DefaultChar<ConstantExpr>>; using ScalarDefaultCharConstantExpr = Scalar<DefaultChar<ConstantExpr>>;
// R611 label -> digit [digit]... // R611 label -> digit [digit]...
using Label = std::uint64_t; // validated later, must be in [1..99999] using Label = common::Label; // validated later, must be in [1..99999]
// A wrapper for xzy-stmt productions that are statements, so that // A wrapper for xzy-stmt productions that are statements, so that
// source provenances and labels have a uniform representation. // source provenances and labels have a uniform representation.

View File

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "flang/Evaluate/call.h" #include "flang/Evaluate/call.h"
#include "flang/Common/Fortran.h"
#include "flang/Common/idioms.h" #include "flang/Common/idioms.h"
#include "flang/Evaluate/characteristics.h" #include "flang/Evaluate/characteristics.h"
#include "flang/Evaluate/expression.h" #include "flang/Evaluate/expression.h"
@ -20,6 +21,7 @@ ActualArgument::ActualArgument(Expr<SomeType> &&x) : u_{std::move(x)} {}
ActualArgument::ActualArgument(common::CopyableIndirection<Expr<SomeType>> &&v) ActualArgument::ActualArgument(common::CopyableIndirection<Expr<SomeType>> &&v)
: u_{std::move(v)} {} : u_{std::move(v)} {}
ActualArgument::ActualArgument(AssumedType x) : u_{x} {} ActualArgument::ActualArgument(AssumedType x) : u_{x} {}
ActualArgument::ActualArgument(common::Label x) : u_{x} {}
ActualArgument::~ActualArgument() {} ActualArgument::~ActualArgument() {}
ActualArgument::AssumedType::AssumedType(const Symbol &symbol) ActualArgument::AssumedType::AssumedType(const Symbol &symbol)
@ -54,9 +56,8 @@ int ActualArgument::Rank() const {
} }
bool ActualArgument::operator==(const ActualArgument &that) const { bool ActualArgument::operator==(const ActualArgument &that) const {
return keyword_ == that.keyword_ && return keyword_ == that.keyword_ && isPassedObject_ == that.isPassedObject_ &&
isAlternateReturn_ == that.isAlternateReturn_ && u_ == that.u_;
isPassedObject_ == that.isPassedObject_ && u_ == that.u_;
} }
void ActualArgument::Parenthesize() { void ActualArgument::Parenthesize() {

View File

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "flang/Evaluate/formatting.h" #include "flang/Evaluate/formatting.h"
#include "flang/Common/Fortran.h"
#include "flang/Evaluate/call.h" #include "flang/Evaluate/call.h"
#include "flang/Evaluate/constant.h" #include "flang/Evaluate/constant.h"
#include "flang/Evaluate/expression.h" #include "flang/Evaluate/expression.h"
@ -108,14 +109,16 @@ llvm::raw_ostream &ActualArgument::AsFortran(llvm::raw_ostream &o) const {
if (keyword_) { if (keyword_) {
o << keyword_->ToString() << '='; o << keyword_->ToString() << '=';
} }
if (isAlternateReturn_) { std::visit(
o << '*'; common::visitors{
} [&](const common::CopyableIndirection<Expr<SomeType>> &expr) {
if (const auto *expr{UnwrapExpr()}) { expr.value().AsFortran(o);
return expr->AsFortran(o); },
} else { [&](const AssumedType &assumedType) { assumedType.AsFortran(o); },
return std::get<AssumedType>(u_).AsFortran(o); [&](const common::Label &label) { o << '*' << label; },
} },
u_);
return o;
} }
llvm::raw_ostream &SpecificIntrinsic::AsFortran(llvm::raw_ostream &o) const { llvm::raw_ostream &SpecificIntrinsic::AsFortran(llvm::raw_ostream &o) const {

View File

@ -647,7 +647,7 @@ static void CheckExplicitInterfaceArg(evaluate::ActualArgument &arg,
CheckProcedureArg(arg, proc, dummyName, context); CheckProcedureArg(arg, proc, dummyName, context);
}, },
[&](const characteristics::AlternateReturn &) { [&](const characteristics::AlternateReturn &) {
// TODO check alternate return // All semantic checking is done elsewhere
}, },
}, },
dummy.u); dummy.u);

View File

@ -10,6 +10,7 @@
#include "check-call.h" #include "check-call.h"
#include "pointer-assignment.h" #include "pointer-assignment.h"
#include "resolve-names.h" #include "resolve-names.h"
#include "flang/Common/Fortran.h"
#include "flang/Common/idioms.h" #include "flang/Common/idioms.h"
#include "flang/Evaluate/common.h" #include "flang/Evaluate/common.h"
#include "flang/Evaluate/fold.h" #include "flang/Evaluate/fold.h"
@ -2129,6 +2130,15 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::FunctionReference &funcRef,
return std::nullopt; return std::nullopt;
} }
static bool HasAlternateReturns(const evaluate::ActualArguments &args) {
for (const auto &arg : args) {
if (arg && arg->isAlternateReturn()) {
return true;
}
}
return false;
}
void ExpressionAnalyzer::Analyze(const parser::CallStmt &callStmt) { void ExpressionAnalyzer::Analyze(const parser::CallStmt &callStmt) {
const parser::Call &call{callStmt.v}; const parser::Call &call{callStmt.v};
auto restorer{GetContextualMessages().SetLocation(call.source)}; auto restorer{GetContextualMessages().SetLocation(call.source)};
@ -2144,8 +2154,7 @@ void ExpressionAnalyzer::Analyze(const parser::CallStmt &callStmt) {
ProcedureDesignator *proc{std::get_if<ProcedureDesignator>(&callee->u)}; ProcedureDesignator *proc{std::get_if<ProcedureDesignator>(&callee->u)};
CHECK(proc); CHECK(proc);
if (CheckCall(call.source, *proc, callee->arguments)) { if (CheckCall(call.source, *proc, callee->arguments)) {
bool hasAlternateReturns{ bool hasAlternateReturns{HasAlternateReturns(callee->arguments)};
callee->arguments.size() < actualArgList.size()};
callStmt.typedCall.Reset( callStmt.typedCall.Reset(
new ProcedureRef{std::move(*proc), std::move(callee->arguments), new ProcedureRef{std::move(*proc), std::move(callee->arguments),
hasAlternateReturns}, hasAlternateReturns},
@ -2851,20 +2860,19 @@ void ArgumentAnalyzer::Analyze(
// be detected and represented (they're not expressions). // be detected and represented (they're not expressions).
// TODO: C1534: Don't allow a "restricted" specific intrinsic to be passed. // TODO: C1534: Don't allow a "restricted" specific intrinsic to be passed.
std::optional<ActualArgument> actual; std::optional<ActualArgument> actual;
bool isAltReturn{false};
std::visit(common::visitors{ std::visit(common::visitors{
[&](const common::Indirection<parser::Expr> &x) { [&](const common::Indirection<parser::Expr> &x) {
// TODO: Distinguish & handle procedure name and // TODO: Distinguish & handle procedure name and
// proc-component-ref // proc-component-ref
actual = AnalyzeExpr(x.value()); actual = AnalyzeExpr(x.value());
}, },
[&](const parser::AltReturnSpec &) { [&](const parser::AltReturnSpec &label) {
if (!isSubroutine) { if (!isSubroutine) {
context_.Say( context_.Say(
"alternate return specification may not appear on" "alternate return specification may not appear on"
" function reference"_err_en_US); " function reference"_err_en_US);
} }
isAltReturn = true; actual = ActualArgument(label.v);
}, },
[&](const parser::ActualArg::PercentRef &) { [&](const parser::ActualArg::PercentRef &) {
context_.Say("TODO: %REF() argument"_err_en_US); context_.Say("TODO: %REF() argument"_err_en_US);
@ -2879,7 +2887,7 @@ void ArgumentAnalyzer::Analyze(
actual->set_keyword(argKW->v.source); actual->set_keyword(argKW->v.source);
} }
actuals_.emplace_back(std::move(*actual)); actuals_.emplace_back(std::move(*actual));
} else if (!isAltReturn) { } else {
fatalErrors_ = true; fatalErrors_ = true;
} }
} }

View File

@ -0,0 +1,16 @@
! RUN: %S/test_errors.sh %s %t %f18
! Test alternat return argument passing for internal and external subprograms
! Both of the following are OK
call extSubprogram (*100)
call intSubprogram (*100)
call extSubprogram (*101)
call intSubprogram (*101)
100 PRINT *,'First alternate return'
!ERROR: Label '101' is not a branch target
!ERROR: Label '101' is not a branch target
101 FORMAT("abc")
contains
subroutine intSubprogram(*)
return(1)
end subroutine
end