Skip to content

Commit 1653cd0

Browse files
committed
[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.
1 parent 4120907 commit 1653cd0

File tree

11 files changed

+67
-6
lines changed

11 files changed

+67
-6
lines changed

flang/include/flang/Common/Fortran-features.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
7070
IgnoredIntrinsicFunctionType, PreviousScalarUse,
7171
RedeclaredInaccessibleComponent, ImplicitShared, IndexVarRedefinition,
7272
IncompatibleImplicitInterfaces, BadTypeForTarget,
73-
VectorSubscriptFinalization)
73+
VectorSubscriptFinalization, UndefinedFunctionResult)
7474

7575
using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
7676
using UsageWarnings = EnumSet<UsageWarning, UsageWarning_enumSize>;
@@ -144,6 +144,7 @@ class LanguageFeatureControl {
144144
warnUsage_.set(UsageWarning::IncompatibleImplicitInterfaces);
145145
warnUsage_.set(UsageWarning::BadTypeForTarget);
146146
warnUsage_.set(UsageWarning::VectorSubscriptFinalization);
147+
warnUsage_.set(UsageWarning::UndefinedFunctionResult);
147148
}
148149
LanguageFeatureControl(const LanguageFeatureControl &) = default;
149150

flang/include/flang/Semantics/semantics.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,9 @@ class SemanticsContext {
254254
// behavior.
255255
CommonBlockList GetCommonBlocks() const;
256256

257+
void NoteDefinedSymbol(const Symbol &);
258+
bool IsSymbolDefined(const Symbol &) const;
259+
257260
private:
258261
struct ScopeIndexComparator {
259262
bool operator()(parser::CharBlock, parser::CharBlock) const;
@@ -303,6 +306,7 @@ class SemanticsContext {
303306
std::unique_ptr<CommonBlockMap> commonBlockMap_;
304307
ModuleDependences moduleDependences_;
305308
std::map<const Symbol *, SourceName> moduleFileOutputRenamings_;
309+
UnorderedSymbolSet isDefined_;
306310
};
307311

308312
class Semantics {

flang/include/flang/Semantics/tools.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ const Symbol *FindPointerComponent(const DeclTypeSpec &);
5252
const Symbol *FindPointerComponent(const Symbol &);
5353
const Symbol *FindInterface(const Symbol &);
5454
const Symbol *FindSubprogram(const Symbol &);
55-
const Symbol *FindFunctionResult(const Symbol &);
5655
const Symbol *FindOverriddenBinding(
5756
const Symbol &, bool &isInaccessibleDeferred);
5857
const Symbol *FindGlobal(const Symbol &);

flang/lib/Semantics/check-purity.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ void PurityChecker::Enter(const parser::FunctionSubprogram &func) {
3131
stmt.source, std::get<std::list<parser::PrefixSpec>>(stmt.statement.t));
3232
}
3333

34-
void PurityChecker::Leave(const parser::FunctionSubprogram &) { Left(); }
34+
void PurityChecker::Leave(const parser::FunctionSubprogram &func) { Left(); }
3535

3636
bool PurityChecker::InPureSubprogram() const {
3737
return pureDepth_ >= 0 && depth_ >= pureDepth_;

flang/lib/Semantics/definable.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ static std::optional<parser::Message> WhyNotDefinableBase(parser::CharBlock at,
127127
(!IsPointer(ultimate) || (isWholeSymbol && isPointerDefinition))) {
128128
return BlameSymbol(
129129
at, "'%s' is an INTENT(IN) dummy argument"_en_US, original);
130+
} else if (acceptAllocatable) {
131+
// allocating a function result doesn't count as a def'n
132+
} else {
133+
scope.context().NoteDefinedSymbol(ultimate);
130134
}
131135
if (const Scope * pure{FindPureProcedureContaining(scope)}) {
132136
// Additional checking for pure subprograms.

flang/lib/Semantics/semantics.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,27 @@ using StatementSemanticsPass2 = SemanticsVisitor<AllocateChecker,
168168
ReturnStmtChecker, SelectRankConstructChecker, SelectTypeChecker,
169169
StopChecker>;
170170

171+
static void WarnUndefinedFunctionResult(
172+
SemanticsContext &context, const Scope &scope) {
173+
if (const Symbol * symbol{scope.symbol()}) {
174+
if (const auto *subp{symbol->detailsIf<SubprogramDetails>()}) {
175+
if (!subp->isInterface() && !subp->stmtFunction()) {
176+
if (const Symbol * result{FindFunctionResult(*symbol)}) {
177+
if (!context.IsSymbolDefined(*result)) {
178+
context.Say(
179+
symbol->name(), "Function result is never defined"_warn_en_US);
180+
}
181+
}
182+
}
183+
}
184+
}
185+
if (!scope.IsModuleFile()) {
186+
for (const Scope &child : scope.children()) {
187+
WarnUndefinedFunctionResult(context, child);
188+
}
189+
}
190+
}
191+
171192
static bool PerformStatementSemantics(
172193
SemanticsContext &context, parser::Program &program) {
173194
ResolveNames(context, program, context.globalScope());
@@ -187,6 +208,9 @@ static bool PerformStatementSemantics(
187208
SemanticsVisitor<CUDAChecker>{context}.Walk(program);
188209
}
189210
if (!context.AnyFatalError()) {
211+
if (context.ShouldWarn(common::UsageWarning::UndefinedFunctionResult)) {
212+
WarnUndefinedFunctionResult(context, context.globalScope());
213+
}
190214
pass2.CompileDataInitializationsIntoInitializers();
191215
}
192216
return !context.AnyFatalError();
@@ -712,4 +736,19 @@ CommonBlockList SemanticsContext::GetCommonBlocks() const {
712736
return {};
713737
}
714738

739+
void SemanticsContext::NoteDefinedSymbol(const Symbol &symbol) {
740+
isDefined_.insert(symbol);
741+
if (IsFunctionResult(symbol)) { // FUNCTION or ENTRY
742+
if (const Symbol * func{symbol.owner().symbol()}) {
743+
if (const Symbol * result{FindFunctionResult(*func)}) {
744+
isDefined_.insert(*result);
745+
}
746+
}
747+
}
748+
}
749+
750+
bool SemanticsContext::IsSymbolDefined(const Symbol &symbol) const {
751+
return isDefined_.find(symbol) != isDefined_.end();
752+
}
753+
715754
} // namespace Fortran::semantics

flang/test/Evaluate/folding08.f90

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ module m
1111
end type
1212
type(t) :: ta(0:2)
1313
character(len=2) :: ca(-1:1)
14+
interface
15+
function foo()
16+
real :: foo(2:3,4:6)
17+
end function
18+
end interface
1419
integer, parameter :: lbtadim = lbound(ta,1)
1520
logical, parameter :: test_lbtadim = lbtadim == 0
1621
integer, parameter :: ubtadim = ubound(ta,1)
@@ -47,9 +52,6 @@ module m
4752
logical, parameter :: test_lb_empty_dim = lbound(empty, 1) == 1
4853
logical, parameter :: test_ub_empty_dim = ubound(empty, 1) == 0
4954
contains
50-
function foo()
51-
real :: foo(2:3,4:6)
52-
end function
5355
subroutine test(n1,a1,a2)
5456
integer, intent(in) :: n1
5557
real, intent(in) :: a1(1:n1), a2(0:*)

flang/test/Semantics/call02.f90

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ subroutine callme(f)
7272
contains
7373
elemental real function elem03(x)
7474
real, value :: x
75+
elem03 = 0.
7576
end function
7677
subroutine test
7778
intrinsic :: cos
@@ -87,6 +88,7 @@ subroutine test
8788
contains
8889
elemental real function elem04(x)
8990
real, value :: x
91+
elem04 = 0.
9092
end function
9193
end subroutine
9294
end module

flang/test/Semantics/call05.f90

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,13 @@ subroutine smb(b)
155155

156156
function return_deferred_length_ptr()
157157
character(len=:), pointer :: return_deferred_length_ptr
158+
return_deferred_length_ptr => p2
158159
end function
159160

160161
function return_explicit_length_ptr(n)
161162
integer :: n
162163
character(len=n), pointer :: return_explicit_length_ptr
164+
return_explicit_length_ptr => p2(1:n)
163165
end function
164166

165167
subroutine test()

flang/test/Semantics/contiguous01.f90

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@ function func(ashape,arank) result(r)
3030
contiguous r2
3131
!PORTABILITY: CONTIGUOUS entity 'e' should be an array pointer, assumed-shape, or assumed-rank
3232
entry e() result(r2)
33+
r2 = 0
3334
end
3435
function fp()
3536
real, pointer, contiguous :: fp(:) ! ok
37+
nullify(fp)
3638
end
3739
end

flang/test/Semantics/resolve53.f90

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,14 +227,17 @@ module m14
227227
real function f1(x, y)
228228
real, intent(in) :: x
229229
logical, intent(in) :: y
230+
f1 = 0.
230231
end
231232
integer function f2(x, y)
232233
integer, intent(in) :: x
233234
logical, intent(in) :: y
235+
f2 = 0.
234236
end
235237
real function f3(x, y)
236238
real, value :: x
237239
logical, value :: y
240+
f3 = 0.
238241
end
239242
end module
240243

@@ -447,12 +450,15 @@ module m19
447450
contains
448451
integer function f1(i)
449452
integer, intent(in) :: i
453+
f1 = 0
450454
end
451455
integer function f2(i, j)
452456
integer, value :: i, j
457+
f2 = 0
453458
end
454459
integer function f3(i, j)
455460
integer, intent(in) :: i, j
461+
f3 = 0
456462
end
457463
end
458464

0 commit comments

Comments
 (0)