[flang-commits] [flang] 93b0638 - [flang] Handle "type(foo) function f" when foo is defined in f
Peter Klausler via flang-commits
flang-commits at lists.llvm.org
Thu Feb 10 10:29:44 PST 2022
Author: Peter Klausler
Date: 2022-02-10T10:29:36-08:00
New Revision: 93b0638eff586fabb38c18e8bb34c89661e525e5
URL: https://github.com/llvm/llvm-project/commit/93b0638eff586fabb38c18e8bb34c89661e525e5
DIFF: https://github.com/llvm/llvm-project/commit/93b0638eff586fabb38c18e8bb34c89661e525e5.diff
LOG: [flang] Handle "type(foo) function f" when foo is defined in f
Fortran allows forward references to derived types, including
function results that are typed in a prefix of a FUNCTION statement.
If a type is defined in the body of the function, a reference to
that type from a prefix on the FUNCTION statement must resolve to
the local symbol, even and especially when that type shadows one
from the host scope.
The solution is to defer the processing of that type until the
end of the function's specification part. But the language doesn't
allow for forward references to other names in the prefix, so defer
the processing of the type only when it is not an intrinsic type.
The data structures in name resolution that track this information
for functions needed to become a stack in order to make this work,
since functions can contain interfaces that are functions.
Differential Revision: https://reviews.llvm.org/D119448
Added:
flang/test/Semantics/resolve108.f90
Modified:
flang/lib/Evaluate/tools.cpp
flang/lib/Semantics/resolve-names.cpp
Removed:
################################################################################
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index 25ea7992b057a..726d5308eedf1 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -1199,20 +1199,21 @@ bool IsPureProcedure(const Scope &scope) {
}
bool IsFunction(const Symbol &symbol) {
- return std::visit(
- common::visitors{
- [](const SubprogramDetails &x) { return x.isFunction(); },
- [&](const SubprogramNameDetails &) {
- return symbol.test(Symbol::Flag::Function);
- },
- [](const ProcEntityDetails &x) {
- const auto &ifc{x.interface()};
- return ifc.type() || (ifc.symbol() && IsFunction(*ifc.symbol()));
- },
- [](const ProcBindingDetails &x) { return IsFunction(x.symbol()); },
- [](const auto &) { return false; },
- },
- symbol.GetUltimate().details());
+ const Symbol &ultimate{symbol.GetUltimate()};
+ return ultimate.test(Symbol::Flag::Function) ||
+ std::visit(common::visitors{
+ [](const SubprogramDetails &x) { return x.isFunction(); },
+ [](const ProcEntityDetails &x) {
+ const auto &ifc{x.interface()};
+ return ifc.type() ||
+ (ifc.symbol() && IsFunction(*ifc.symbol()));
+ },
+ [](const ProcBindingDetails &x) {
+ return IsFunction(x.symbol());
+ },
+ [](const auto &) { return false; },
+ },
+ ultimate.details());
}
bool IsFunction(const Scope &scope) {
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 4308260e9fb7c..11d9acad21eaa 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -746,6 +746,7 @@ class InterfaceVisitor : public virtual ScopeHandler {
class SubprogramVisitor : public virtual ScopeHandler, public InterfaceVisitor {
public:
+ ~SubprogramVisitor();
bool HandleStmtFunction(const parser::StmtFunctionStmt &);
bool Pre(const parser::SubroutineStmt &);
void Post(const parser::SubroutineStmt &);
@@ -759,7 +760,6 @@ class SubprogramVisitor : public virtual ScopeHandler, public InterfaceVisitor {
void Post(const parser::InterfaceBody::Function &);
bool Pre(const parser::Suffix &);
bool Pre(const parser::PrefixSpec &);
- void Post(const parser::ImplicitPart &);
bool BeginSubprogram(
const parser::Name &, Symbol::Flag, bool hasModulePrefix = false);
@@ -768,18 +768,21 @@ class SubprogramVisitor : public virtual ScopeHandler, public InterfaceVisitor {
void EndSubprogram();
protected:
+ void FinishFunctionResult();
// Set when we see a stmt function that is really an array element assignment
bool badStmtFuncFound_{false};
private:
// Info about the current function: parse tree of the type in the PrefixSpec;
// name and symbol of the function result from the Suffix; source location.
- struct {
+ struct FuncInfo {
const parser::DeclarationTypeSpec *parsedType{nullptr};
const parser::Name *resultName{nullptr};
Symbol *resultSymbol{nullptr};
std::optional<SourceName> source;
- } funcInfo_;
+ bool inFunctionStmt{false}; // true between Pre/Post of FunctionStmt
+ };
+ std::vector<FuncInfo> funcInfoStack_;
// Edits an existing symbol created for earlier calls to a subprogram or ENTRY
// so that it can be replaced by a later definition.
@@ -1456,6 +1459,7 @@ class ResolveNamesVisitor : public virtual ScopeHandler,
void ResolveSpecificationParts(ProgramTree &);
void AddSubpNames(ProgramTree &);
bool BeginScopeForNode(const ProgramTree &);
+ void EndScopeForNode(const ProgramTree &);
void FinishSpecificationParts(const ProgramTree &);
void FinishDerivedTypeInstantiation(Scope &);
void ResolveExecutionParts(const ProgramTree &);
@@ -2943,6 +2947,8 @@ void InterfaceVisitor::CheckGenericProcedures(Symbol &generic) {
// SubprogramVisitor implementation
+SubprogramVisitor::~SubprogramVisitor() { CHECK(funcInfoStack_.empty()); }
+
// Return false if it is actually an assignment statement.
bool SubprogramVisitor::HandleStmtFunction(const parser::StmtFunctionStmt &x) {
const auto &name{std::get<parser::Name>(x.t)};
@@ -2998,7 +3004,22 @@ bool SubprogramVisitor::HandleStmtFunction(const parser::StmtFunctionStmt &x) {
bool SubprogramVisitor::Pre(const parser::Suffix &suffix) {
if (suffix.resultName) {
- funcInfo_.resultName = &suffix.resultName.value();
+ if (IsFunction(currScope())) {
+ if (!funcInfoStack_.empty()) {
+ FuncInfo &info{funcInfoStack_.back()};
+ if (info.inFunctionStmt) {
+ info.resultName = &suffix.resultName.value();
+ } else {
+ // will check the result name in Post(EntryStmt)
+ }
+ }
+ } else {
+ Message &msg{Say(*suffix.resultName,
+ "RESULT(%s) may appear only in a function"_err_en_US)};
+ if (const Symbol * subprogram{InclusiveScope().symbol()}) {
+ msg.Attach(subprogram->name(), "Containing subprogram"_en_US);
+ }
+ }
}
return true;
}
@@ -3006,13 +3027,15 @@ bool SubprogramVisitor::Pre(const parser::Suffix &suffix) {
bool SubprogramVisitor::Pre(const parser::PrefixSpec &x) {
// Save this to process after UseStmt and ImplicitPart
if (const auto *parsedType{std::get_if<parser::DeclarationTypeSpec>(&x.u)}) {
- if (funcInfo_.parsedType) { // C1543
+ CHECK(!funcInfoStack_.empty());
+ FuncInfo &info{funcInfoStack_.back()};
+ if (info.parsedType) { // C1543
Say(currStmtSource().value(),
"FUNCTION prefix cannot specify the type more than once"_err_en_US);
return false;
} else {
- funcInfo_.parsedType = parsedType;
- funcInfo_.source = currStmtSource();
+ info.parsedType = parsedType;
+ info.source = currStmtSource();
return false;
}
} else {
@@ -3020,17 +3043,21 @@ bool SubprogramVisitor::Pre(const parser::PrefixSpec &x) {
}
}
-void SubprogramVisitor::Post(const parser::ImplicitPart &) {
- // If the function has a type in the prefix, process it now
- if (funcInfo_.parsedType) {
- messageHandler().set_currStmtSource(funcInfo_.source);
- if (const auto *type{ProcessTypeSpec(*funcInfo_.parsedType, true)}) {
- if (!context().HasError(funcInfo_.resultSymbol)) {
- funcInfo_.resultSymbol->SetType(*type);
+void SubprogramVisitor::FinishFunctionResult() {
+ // If the function has a type in the prefix, process it now.
+ if (IsFunction(currScope())) {
+ CHECK(!funcInfoStack_.empty());
+ FuncInfo &info{funcInfoStack_.back()};
+ if (info.parsedType) {
+ messageHandler().set_currStmtSource(info.source);
+ if (const auto *type{ProcessTypeSpec(*info.parsedType, true)}) {
+ if (!context().HasError(info.resultSymbol)) {
+ info.resultSymbol->SetType(*type);
+ }
}
+ info.parsedType = nullptr;
}
}
- funcInfo_ = {};
}
bool SubprogramVisitor::Pre(const parser::InterfaceBody::Subroutine &x) {
@@ -3054,6 +3081,10 @@ bool SubprogramVisitor::Pre(const parser::SubroutineStmt &) {
return BeginAttrs();
}
bool SubprogramVisitor::Pre(const parser::FunctionStmt &) {
+ CHECK(!funcInfoStack_.empty());
+ FuncInfo &info{funcInfoStack_.back()};
+ CHECK(!info.inFunctionStmt);
+ info.inFunctionStmt = true;
return BeginAttrs();
}
bool SubprogramVisitor::Pre(const parser::EntryStmt &) { return BeginAttrs(); }
@@ -3079,9 +3110,13 @@ void SubprogramVisitor::Post(const parser::FunctionStmt &stmt) {
details.add_dummyArg(dummy);
}
const parser::Name *funcResultName;
- if (funcInfo_.resultName && funcInfo_.resultName->source != name.source) {
+ CHECK(!funcInfoStack_.empty());
+ FuncInfo &info{funcInfoStack_.back()};
+ CHECK(info.inFunctionStmt);
+ info.inFunctionStmt = false;
+ if (info.resultName && info.resultName->source != name.source) {
// Note that RESULT is ignored if it has the same name as the function.
- funcResultName = funcInfo_.resultName;
+ funcResultName = info.resultName;
} else {
EraseSymbol(name); // was added by PushSubprogramScope
funcResultName = &name;
@@ -3093,28 +3128,35 @@ void SubprogramVisitor::Post(const parser::FunctionStmt &stmt) {
// add function result to function scope
EntityDetails funcResultDetails;
funcResultDetails.set_funcResult(true);
- funcInfo_.resultSymbol =
+ funcInfoStack_.back().resultSymbol =
&MakeSymbol(*funcResultName, std::move(funcResultDetails));
- details.set_result(*funcInfo_.resultSymbol);
+ details.set_result(*funcInfoStack_.back().resultSymbol);
}
-
// C1560.
- if (funcInfo_.resultName && funcInfo_.resultName->source == name.source) {
- Say(funcInfo_.resultName->source,
+ if (info.resultName && info.resultName->source == name.source) {
+ Say(info.resultName->source,
"The function name should not appear in RESULT, references to '%s' "
- "inside"
- " the function will be considered as references to the result only"_en_US,
+ "inside the function will be considered as references to the "
+ "result only"_en_US,
name.source);
// RESULT name was ignored above, the only side effect from doing so will be
// the inability to make recursive calls. The related parser::Name is still
// resolved to the created function result symbol because every parser::Name
// should be resolved to avoid internal errors.
- Resolve(*funcInfo_.resultName, funcInfo_.resultSymbol);
+ Resolve(*info.resultName, info.resultSymbol);
}
name.symbol = currScope().symbol(); // must not be function result symbol
// Clear the RESULT() name now in case an ENTRY statement in the implicit-part
// has a RESULT() suffix.
- funcInfo_.resultName = nullptr;
+ info.resultName = nullptr;
+ // If there was a type on the function statement, and it is an intrinsic
+ // type, process that type now so that inquiries in specification expressions
+ // will work. Derived types are deferred to the end of the specification part
+ // so that they can resolve to a locally declared type.
+ if (info.parsedType &&
+ std::holds_alternative<parser::IntrinsicTypeSpec>(info.parsedType->u)) {
+ FinishFunctionResult();
+ }
}
SubprogramDetails &SubprogramVisitor::PostSubprogramStmt(
@@ -3138,15 +3180,15 @@ void SubprogramVisitor::Post(const parser::EntryStmt &stmt) {
return;
}
const auto &name{std::get<parser::Name>(stmt.t)};
- const auto *parentDetails{subprogram->detailsIf<SubprogramDetails>()};
- bool inFunction{parentDetails && parentDetails->isFunction()};
- const parser::Name *resultName{funcInfo_.resultName};
+ const parser::Name *resultName{nullptr};
+ if (const auto &maybeSuffix{
+ std::get<std::optional<parser::Suffix>>(stmt.t)}) {
+ resultName = common::GetPtrFromOptional(maybeSuffix->resultName);
+ }
+ bool inFunction{IsFunction(currScope())};
if (resultName) { // RESULT(result) is present
- funcInfo_.resultName = nullptr;
if (!inFunction) {
- Say2(resultName->source,
- "RESULT(%s) may appear only in a function"_err_en_US,
- subprogram->name(), "Containing subprogram"_en_US);
+ // error was already emitted for the suffix
} else if (resultName->source == subprogram->name()) { // C1574
Say2(resultName->source,
"RESULT(%s) may not have the same name as the function"_err_en_US,
@@ -3292,12 +3334,13 @@ bool SubprogramVisitor::BeginMpSubprogram(const parser::Name &name) {
if (details.isFunction()) {
currScope().erase(symbol->name());
newDetails.set_result(*currScope().CopySymbol(details.result()));
+ funcInfoStack_.emplace_back(); // just to be popped later
}
}
return true;
}
-// A subprogram declared with SUBROUTINE or FUNCTION
+// A subprogram or interface declared with SUBROUTINE or FUNCTION
bool SubprogramVisitor::BeginSubprogram(
const parser::Name &name, Symbol::Flag subpFlag, bool hasModulePrefix) {
if (hasModulePrefix && currScope().IsGlobal()) { // C1547
@@ -3314,10 +3357,18 @@ bool SubprogramVisitor::BeginSubprogram(
return false;
}
PushSubprogramScope(name, subpFlag);
+ if (IsFunction(currScope())) {
+ funcInfoStack_.emplace_back();
+ }
return true;
}
-void SubprogramVisitor::EndSubprogram() { PopScope(); }
+void SubprogramVisitor::EndSubprogram() {
+ if (IsFunction(currScope())) {
+ funcInfoStack_.pop_back();
+ }
+ PopScope();
+}
bool SubprogramVisitor::HandlePreviousCalls(
const parser::Name &name, Symbol &symbol, Symbol::Flag subpFlag) {
@@ -6687,6 +6738,7 @@ void ResolveNamesVisitor::CreateGeneric(const parser::GenericSpec &x) {
void ResolveNamesVisitor::FinishSpecificationPart(
const std::list<parser::DeclarationConstruct> &decls) {
badStmtFuncFound_ = false;
+ FinishFunctionResult();
CheckImports();
bool inModule{currScope().kind() == Scope::Kind::Module};
for (auto &pair : currScope()) {
@@ -6979,7 +7031,7 @@ void ResolveNamesVisitor::ResolveSpecificationParts(ProgramTree &node) {
ResolveSpecificationParts(child);
}
ExecutionPartSkimmer{*this}.Walk(node.exec());
- PopScope();
+ EndScopeForNode(node);
// Ensure that every object entity has a type.
for (auto &pair : *node.scope()) {
ApplyImplicitRules(*pair.second);
@@ -7029,6 +7081,10 @@ bool ResolveNamesVisitor::BeginScopeForNode(const ProgramTree &node) {
}
}
+void ResolveNamesVisitor::EndScopeForNode(const ProgramTree &node) {
+ EndSubprogram();
+}
+
// Some analyses and checks, such as the processing of initializers of
// pointers, are deferred until all of the pertinent specification parts
// have been visited. This deferred processing enables the use of forward
diff --git a/flang/test/Semantics/resolve108.f90 b/flang/test/Semantics/resolve108.f90
new file mode 100644
index 0000000000000..15dd94a061605
--- /dev/null
+++ b/flang/test/Semantics/resolve108.f90
@@ -0,0 +1,69 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+! Tests attempts at forward references to local names in a FUNCTION prefix
+
+! This case is not an error, but will elicit bogus errors if the
+! result type of the function is badly resolved.
+module m1
+ type t1
+ sequence
+ integer not_m
+ end type
+ contains
+ type(t1) function foo(n)
+ integer, intent(in) :: n
+ type t1
+ sequence
+ integer m
+ end type
+ foo%m = n
+ end function
+end module
+
+subroutine s1
+ use :: m1, only: foo
+ type t1
+ sequence
+ integer m
+ end type
+ type(t1) x
+ x = foo(234)
+ print *, x
+end subroutine
+
+module m2
+ integer, parameter :: k = kind(1.e0)
+ contains
+ real(kind=k) function foo(n)
+ integer, parameter :: k = kind(1.d0)
+ integer, intent(in) :: n
+ foo = n
+ end function
+end module
+
+subroutine s2
+ use :: m2, only: foo
+ !If we got the type of foo right, this declaration will fail
+ !due to an attempted division by zero.
+ !ERROR: Must be a constant value
+ integer, parameter :: test = 1 / (kind(foo(1)) - kind(1.e0))
+end subroutine
+
+module m3
+ integer, parameter :: k = kind(1.e0)
+ contains
+ real(kind=kind(x)) function foo(x)
+ !ERROR: The type of 'x' has already been implicitly declared
+ real(kind=kind(1.0d0)) x
+ foo = n
+ end function
+end module
+
+module m4
+ contains
+ !ERROR: Must be a constant value
+ real(n) function foo(x)
+ integer, parameter :: n = kind(foo)
+ real(n), intent(in) :: x
+ foo = x
+ end function
+end module
More information about the flang-commits
mailing list