[flang-commits] [flang] [flang] Warn about undefined function results (PR #99533)
Peter Klausler via flang-commits
flang-commits at lists.llvm.org
Thu Jul 18 14:08:17 PDT 2024
https://github.com/klausler updated https://github.com/llvm/llvm-project/pull/99533
>From f430acbfb70a61f58e89ce348b627527ca50af3e Mon Sep 17 00:00:00 2001
From: Peter Klausler <pklausler at nvidia.com>
Date: Thu, 18 Jul 2024 09:50:38 -0700
Subject: [PATCH] [flang] Warn about undefined function results
When the result of a function never appears in a variable
definition context, emit a warning.
If the function has multiple result variables due to alternate
ENTRY statements, any definition will suffice.
The implementation of this check is tied to the general variable
definability checking utility in semantics. Every variable
definition context uses it to ensure that no undefinable variable
is being defined. A set of defined variables is maintained in
the SemanticsContext and, when the warning is enabled and no
fatal error has been reported, the scope tree is traversed and
all the function subprograms' results are tested for membership
in that set.
---
flang/include/flang/Common/Fortran-features.h | 3 +-
flang/include/flang/Semantics/semantics.h | 4 ++
flang/include/flang/Semantics/tools.h | 1 -
flang/lib/Semantics/check-allocate.cpp | 11 ++++--
flang/lib/Semantics/check-purity.cpp | 2 +-
flang/lib/Semantics/definable.cpp | 6 +++
flang/lib/Semantics/definable.h | 1 +
flang/lib/Semantics/semantics.cpp | 39 +++++++++++++++++++
flang/test/Evaluate/folding08.f90 | 8 ++--
flang/test/Semantics/call02.f90 | 2 +
flang/test/Semantics/call05.f90 | 2 +
flang/test/Semantics/contiguous01.f90 | 2 +
flang/test/Semantics/resolve53.f90 | 6 +++
13 files changed, 77 insertions(+), 10 deletions(-)
diff --git a/flang/include/flang/Common/Fortran-features.h b/flang/include/flang/Common/Fortran-features.h
index 7346d702b073d..938da08e19d6b 100644
--- a/flang/include/flang/Common/Fortran-features.h
+++ b/flang/include/flang/Common/Fortran-features.h
@@ -70,7 +70,7 @@ ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
IgnoredIntrinsicFunctionType, PreviousScalarUse,
RedeclaredInaccessibleComponent, ImplicitShared, IndexVarRedefinition,
IncompatibleImplicitInterfaces, BadTypeForTarget,
- VectorSubscriptFinalization)
+ VectorSubscriptFinalization, UndefinedFunctionResult)
using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
using UsageWarnings = EnumSet<UsageWarning, UsageWarning_enumSize>;
@@ -144,6 +144,7 @@ class LanguageFeatureControl {
warnUsage_.set(UsageWarning::IncompatibleImplicitInterfaces);
warnUsage_.set(UsageWarning::BadTypeForTarget);
warnUsage_.set(UsageWarning::VectorSubscriptFinalization);
+ warnUsage_.set(UsageWarning::UndefinedFunctionResult);
}
LanguageFeatureControl(const LanguageFeatureControl &) = default;
diff --git a/flang/include/flang/Semantics/semantics.h b/flang/include/flang/Semantics/semantics.h
index 3ee71fe485524..ec8d12b0f9865 100644
--- a/flang/include/flang/Semantics/semantics.h
+++ b/flang/include/flang/Semantics/semantics.h
@@ -254,6 +254,9 @@ class SemanticsContext {
// behavior.
CommonBlockList GetCommonBlocks() const;
+ void NoteDefinedSymbol(const Symbol &);
+ bool IsSymbolDefined(const Symbol &) const;
+
private:
struct ScopeIndexComparator {
bool operator()(parser::CharBlock, parser::CharBlock) const;
@@ -303,6 +306,7 @@ class SemanticsContext {
std::unique_ptr<CommonBlockMap> commonBlockMap_;
ModuleDependences moduleDependences_;
std::map<const Symbol *, SourceName> moduleFileOutputRenamings_;
+ UnorderedSymbolSet isDefined_;
};
class Semantics {
diff --git a/flang/include/flang/Semantics/tools.h b/flang/include/flang/Semantics/tools.h
index 0fcba3131fad1..ae0b79a6e4849 100644
--- a/flang/include/flang/Semantics/tools.h
+++ b/flang/include/flang/Semantics/tools.h
@@ -52,7 +52,6 @@ const Symbol *FindPointerComponent(const DeclTypeSpec &);
const Symbol *FindPointerComponent(const Symbol &);
const Symbol *FindInterface(const Symbol &);
const Symbol *FindSubprogram(const Symbol &);
-const Symbol *FindFunctionResult(const Symbol &);
const Symbol *FindOverriddenBinding(
const Symbol &, bool &isInaccessibleDeferred);
const Symbol *FindGlobal(const Symbol &);
diff --git a/flang/lib/Semantics/check-allocate.cpp b/flang/lib/Semantics/check-allocate.cpp
index e344390372c12..8f7a200d23239 100644
--- a/flang/lib/Semantics/check-allocate.cpp
+++ b/flang/lib/Semantics/check-allocate.cpp
@@ -600,10 +600,13 @@ bool AllocationCheckerHelper::RunChecks(SemanticsContext &context) {
const Scope &subpScope{
GetProgramUnitContaining(context.FindScope(name_.source))};
if (allocateObject_.typedExpr && allocateObject_.typedExpr->v) {
- if (auto whyNot{WhyNotDefinable(name_.source, subpScope,
- {DefinabilityFlag::PointerDefinition,
- DefinabilityFlag::AcceptAllocatable},
- *allocateObject_.typedExpr->v)}) {
+ DefinabilityFlags flags{DefinabilityFlag::PointerDefinition,
+ DefinabilityFlag::AcceptAllocatable};
+ if (allocateInfo_.gotSource) {
+ flags.set(DefinabilityFlag::SourcedAllocation);
+ }
+ if (auto whyNot{WhyNotDefinable(
+ name_.source, subpScope, flags, *allocateObject_.typedExpr->v)}) {
context
.Say(name_.source,
"Name in ALLOCATE statement is not definable"_err_en_US)
diff --git a/flang/lib/Semantics/check-purity.cpp b/flang/lib/Semantics/check-purity.cpp
index 55a9a2f107388..1046f363e9485 100644
--- a/flang/lib/Semantics/check-purity.cpp
+++ b/flang/lib/Semantics/check-purity.cpp
@@ -31,7 +31,7 @@ void PurityChecker::Enter(const parser::FunctionSubprogram &func) {
stmt.source, std::get<std::list<parser::PrefixSpec>>(stmt.statement.t));
}
-void PurityChecker::Leave(const parser::FunctionSubprogram &) { Left(); }
+void PurityChecker::Leave(const parser::FunctionSubprogram &func) { Left(); }
bool PurityChecker::InPureSubprogram() const {
return pureDepth_ >= 0 && depth_ >= pureDepth_;
diff --git a/flang/lib/Semantics/definable.cpp b/flang/lib/Semantics/definable.cpp
index 96af46abd6180..67fe8c2f06a72 100644
--- a/flang/lib/Semantics/definable.cpp
+++ b/flang/lib/Semantics/definable.cpp
@@ -127,6 +127,12 @@ static std::optional<parser::Message> WhyNotDefinableBase(parser::CharBlock at,
(!IsPointer(ultimate) || (isWholeSymbol && isPointerDefinition))) {
return BlameSymbol(
at, "'%s' is an INTENT(IN) dummy argument"_en_US, original);
+ } else if (acceptAllocatable &&
+ !flags.test(DefinabilityFlag::SourcedAllocation)) {
+ // allocating a function result doesn't count as a def'n
+ // unless there's SOURCE=
+ } else {
+ scope.context().NoteDefinedSymbol(ultimate);
}
if (const Scope * pure{FindPureProcedureContaining(scope)}) {
// Additional checking for pure subprograms.
diff --git a/flang/lib/Semantics/definable.h b/flang/lib/Semantics/definable.h
index b14c644349674..07cd42a5cd85c 100644
--- a/flang/lib/Semantics/definable.h
+++ b/flang/lib/Semantics/definable.h
@@ -30,6 +30,7 @@ ENUM_CLASS(DefinabilityFlag,
DuplicatesAreOk, // vector subscript may have duplicates
PointerDefinition, // a pointer is being defined, not its target
AcceptAllocatable, // treat allocatable as if it were a pointer
+ SourcedAllocation, // ALLOCATE(a,SOURCE=)
PolymorphicOkInPure) // don't check for polymorphic type in pure subprogram
using DefinabilityFlags =
diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp
index c09734e5676f3..16f5b53b07be6 100644
--- a/flang/lib/Semantics/semantics.cpp
+++ b/flang/lib/Semantics/semantics.cpp
@@ -168,6 +168,27 @@ using StatementSemanticsPass2 = SemanticsVisitor<AllocateChecker,
ReturnStmtChecker, SelectRankConstructChecker, SelectTypeChecker,
StopChecker>;
+static void WarnUndefinedFunctionResult(
+ SemanticsContext &context, const Scope &scope) {
+ if (const Symbol * symbol{scope.symbol()}) {
+ if (const auto *subp{symbol->detailsIf<SubprogramDetails>()}) {
+ if (!subp->isInterface() && !subp->stmtFunction()) {
+ if (const Symbol * result{FindFunctionResult(*symbol)}) {
+ if (!context.IsSymbolDefined(*result)) {
+ context.Say(
+ symbol->name(), "Function result is never defined"_warn_en_US);
+ }
+ }
+ }
+ }
+ }
+ if (!scope.IsModuleFile()) {
+ for (const Scope &child : scope.children()) {
+ WarnUndefinedFunctionResult(context, child);
+ }
+ }
+}
+
static bool PerformStatementSemantics(
SemanticsContext &context, parser::Program &program) {
ResolveNames(context, program, context.globalScope());
@@ -187,6 +208,9 @@ static bool PerformStatementSemantics(
SemanticsVisitor<CUDAChecker>{context}.Walk(program);
}
if (!context.AnyFatalError()) {
+ if (context.ShouldWarn(common::UsageWarning::UndefinedFunctionResult)) {
+ WarnUndefinedFunctionResult(context, context.globalScope());
+ }
pass2.CompileDataInitializationsIntoInitializers();
}
return !context.AnyFatalError();
@@ -712,4 +736,19 @@ CommonBlockList SemanticsContext::GetCommonBlocks() const {
return {};
}
+void SemanticsContext::NoteDefinedSymbol(const Symbol &symbol) {
+ isDefined_.insert(symbol);
+ if (IsFunctionResult(symbol)) { // FUNCTION or ENTRY
+ if (const Symbol * func{symbol.owner().symbol()}) {
+ if (const Symbol * result{FindFunctionResult(*func)}) {
+ isDefined_.insert(*result);
+ }
+ }
+ }
+}
+
+bool SemanticsContext::IsSymbolDefined(const Symbol &symbol) const {
+ return isDefined_.find(symbol) != isDefined_.end();
+}
+
} // namespace Fortran::semantics
diff --git a/flang/test/Evaluate/folding08.f90 b/flang/test/Evaluate/folding08.f90
index 1b2e5605e85d4..53603474fe1c8 100644
--- a/flang/test/Evaluate/folding08.f90
+++ b/flang/test/Evaluate/folding08.f90
@@ -11,6 +11,11 @@ module m
end type
type(t) :: ta(0:2)
character(len=2) :: ca(-1:1)
+ interface
+ function foo()
+ real :: foo(2:3,4:6)
+ end function
+ end interface
integer, parameter :: lbtadim = lbound(ta,1)
logical, parameter :: test_lbtadim = lbtadim == 0
integer, parameter :: ubtadim = ubound(ta,1)
@@ -47,9 +52,6 @@ module m
logical, parameter :: test_lb_empty_dim = lbound(empty, 1) == 1
logical, parameter :: test_ub_empty_dim = ubound(empty, 1) == 0
contains
- function foo()
- real :: foo(2:3,4:6)
- end function
subroutine test(n1,a1,a2)
integer, intent(in) :: n1
real, intent(in) :: a1(1:n1), a2(0:*)
diff --git a/flang/test/Semantics/call02.f90 b/flang/test/Semantics/call02.f90
index bc3dd6075969c..0ec5530f98089 100644
--- a/flang/test/Semantics/call02.f90
+++ b/flang/test/Semantics/call02.f90
@@ -72,6 +72,7 @@ subroutine callme(f)
contains
elemental real function elem03(x)
real, value :: x
+ elem03 = 0.
end function
subroutine test
intrinsic :: cos
@@ -87,6 +88,7 @@ subroutine test
contains
elemental real function elem04(x)
real, value :: x
+ elem04 = 0.
end function
end subroutine
end module
diff --git a/flang/test/Semantics/call05.f90 b/flang/test/Semantics/call05.f90
index 8a4386e28e2bc..a06fe4f196c8c 100644
--- a/flang/test/Semantics/call05.f90
+++ b/flang/test/Semantics/call05.f90
@@ -155,11 +155,13 @@ subroutine smb(b)
function return_deferred_length_ptr()
character(len=:), pointer :: return_deferred_length_ptr
+ return_deferred_length_ptr => p2
end function
function return_explicit_length_ptr(n)
integer :: n
character(len=n), pointer :: return_explicit_length_ptr
+ return_explicit_length_ptr => p2(1:n)
end function
subroutine test()
diff --git a/flang/test/Semantics/contiguous01.f90 b/flang/test/Semantics/contiguous01.f90
index 0f086624a20ae..89382775261b8 100644
--- a/flang/test/Semantics/contiguous01.f90
+++ b/flang/test/Semantics/contiguous01.f90
@@ -30,8 +30,10 @@ function func(ashape,arank) result(r)
contiguous r2
!PORTABILITY: CONTIGUOUS entity 'e' should be an array pointer, assumed-shape, or assumed-rank
entry e() result(r2)
+ r2 = 0
end
function fp()
real, pointer, contiguous :: fp(:) ! ok
+ nullify(fp)
end
end
diff --git a/flang/test/Semantics/resolve53.f90 b/flang/test/Semantics/resolve53.f90
index 1b0f3f8f8e385..0ab4b7c4d303e 100644
--- a/flang/test/Semantics/resolve53.f90
+++ b/flang/test/Semantics/resolve53.f90
@@ -227,14 +227,17 @@ module m14
real function f1(x, y)
real, intent(in) :: x
logical, intent(in) :: y
+ f1 = 0.
end
integer function f2(x, y)
integer, intent(in) :: x
logical, intent(in) :: y
+ f2 = 0.
end
real function f3(x, y)
real, value :: x
logical, value :: y
+ f3 = 0.
end
end module
@@ -447,12 +450,15 @@ module m19
contains
integer function f1(i)
integer, intent(in) :: i
+ f1 = 0
end
integer function f2(i, j)
integer, value :: i, j
+ f2 = 0
end
integer function f3(i, j)
integer, intent(in) :: i, j
+ f3 = 0
end
end
More information about the flang-commits
mailing list