Skip to content

Commit 3acc69b

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 0684db3 commit 3acc69b

File tree

17 files changed

+254
-20
lines changed

17 files changed

+254
-20
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-allocate.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -600,10 +600,13 @@ bool AllocationCheckerHelper::RunChecks(SemanticsContext &context) {
600600
const Scope &subpScope{
601601
GetProgramUnitContaining(context.FindScope(name_.source))};
602602
if (allocateObject_.typedExpr && allocateObject_.typedExpr->v) {
603-
if (auto whyNot{WhyNotDefinable(name_.source, subpScope,
604-
{DefinabilityFlag::PointerDefinition,
605-
DefinabilityFlag::AcceptAllocatable},
606-
*allocateObject_.typedExpr->v)}) {
603+
DefinabilityFlags flags{DefinabilityFlag::PointerDefinition,
604+
DefinabilityFlag::AcceptAllocatable};
605+
if (allocateInfo_.gotSource) {
606+
flags.set(DefinabilityFlag::SourcedAllocation);
607+
}
608+
if (auto whyNot{WhyNotDefinable(
609+
name_.source, subpScope, flags, *allocateObject_.typedExpr->v)}) {
607610
context
608611
.Say(name_.source,
609612
"Name in ALLOCATE statement is not definable"_err_en_US)

flang/lib/Semantics/check-call.cpp

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ namespace characteristics = Fortran::evaluate::characteristics;
2727
namespace Fortran::semantics {
2828

2929
static void CheckImplicitInterfaceArg(evaluate::ActualArgument &arg,
30-
parser::ContextualMessages &messages, evaluate::FoldingContext &context) {
30+
parser::ContextualMessages &messages, SemanticsContext &context) {
3131
auto restorer{
3232
messages.SetLocation(arg.sourceLocation().value_or(messages.at()))};
3333
if (auto kw{arg.keyword()}) {
@@ -79,8 +79,12 @@ static void CheckImplicitInterfaceArg(evaluate::ActualArgument &arg,
7979
messages.Say(
8080
"VOLATILE argument requires an explicit interface"_err_en_US);
8181
}
82+
if (const Symbol & base{named->GetFirstSymbol()};
83+
IsFunctionResult(base)) {
84+
context.NoteDefinedSymbol(base);
85+
}
8286
} else if (auto argChars{characteristics::DummyArgument::FromActual(
83-
"actual argument", *expr, context,
87+
"actual argument", *expr, context.foldingContext(),
8488
/*forImplicitInterface=*/true)}) {
8589
const auto *argProcDesignator{
8690
std::get_if<evaluate::ProcedureDesignator>(&expr->u)};
@@ -647,8 +651,8 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
647651
actualLastSymbol->name(), dummyName);
648652
}
649653

650-
// Definability
651-
bool actualIsVariable{evaluate::IsVariable(actual)};
654+
// Definability checking
655+
// Problems with polymorphism are caught in the callee's definition.
652656
if (scope) {
653657
std::optional<parser::MessageFixedText> undefinableMessage;
654658
if (dummy.intent == common::Intent::Out) {
@@ -670,7 +674,6 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
670674
}
671675
}
672676
if (undefinableMessage) {
673-
// Problems with polymorphism are caught in the callee's definition.
674677
DefinabilityFlags flags{DefinabilityFlag::PolymorphicOkInPure};
675678
if (isElemental) { // 15.5.2.4(21)
676679
flags.set(DefinabilityFlag::VectorSubscriptIsOk);
@@ -689,6 +692,14 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
689692
messages.Say(std::move(*whyNot));
690693
}
691694
}
695+
} else if (dummy.intent != common::Intent::In ||
696+
(dummyIsPointer && !actualIsPointer)) {
697+
if (auto named{evaluate::ExtractNamedEntity(actual)}) {
698+
if (const Symbol & base{named->GetFirstSymbol()};
699+
IsFunctionResult(base)) {
700+
context.NoteDefinedSymbol(base);
701+
}
702+
}
692703
}
693704
}
694705

@@ -893,6 +904,7 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
893904
// argument
894905
if (dummy.attrs.test(characteristics::DummyDataObject::Attr::Target) &&
895906
context.ShouldWarn(common::UsageWarning::NonTargetPassedToTarget)) {
907+
bool actualIsVariable{evaluate::IsVariable(actual)};
896908
bool actualIsTemp{!actualIsVariable || HasVectorSubscript(actual) ||
897909
evaluate::ExtractCoarrayRef(actual)};
898910
if (actualIsTemp) {
@@ -1416,7 +1428,8 @@ static void CheckAssociated(evaluate::ActualArguments &arguments,
14161428
if (auto whyNot{WhyNotDefinable(
14171429
pointerArg->sourceLocation().value_or(messages.at()),
14181430
*scope,
1419-
DefinabilityFlags{DefinabilityFlag::PointerDefinition},
1431+
DefinabilityFlags{DefinabilityFlag::PointerDefinition,
1432+
DefinabilityFlag::DoNotNoteDefinition},
14201433
*pointerExpr)}) {
14211434
if (whyNot->IsFatal()) {
14221435
if (auto *msg{messages.Say(pointerArg->sourceLocation(),
@@ -2021,7 +2034,7 @@ bool CheckArguments(const characteristics::Procedure &proc,
20212034
auto restorer{messages.SetMessages(buffer)};
20222035
for (auto &actual : actuals) {
20232036
if (actual) {
2024-
CheckImplicitInterfaceArg(*actual, messages, foldingContext);
2037+
CheckImplicitInterfaceArg(*actual, messages, context);
20252038
}
20262039
}
20272040
}

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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ 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+
!flags.test(DefinabilityFlag::SourcedAllocation)) {
132+
// allocating a function result doesn't count as a def'n
133+
// unless there's SOURCE=
134+
} else if (!flags.test(DefinabilityFlag::DoNotNoteDefinition)) {
135+
scope.context().NoteDefinedSymbol(ultimate);
130136
}
131137
if (const Scope * pure{FindPureProcedureContaining(scope)}) {
132138
// Additional checking for pure subprograms.

flang/lib/Semantics/definable.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ ENUM_CLASS(DefinabilityFlag,
3030
DuplicatesAreOk, // vector subscript may have duplicates
3131
PointerDefinition, // a pointer is being defined, not its target
3232
AcceptAllocatable, // treat allocatable as if it were a pointer
33-
PolymorphicOkInPure) // don't check for polymorphic type in pure subprogram
33+
SourcedAllocation, // ALLOCATE(a,SOURCE=)
34+
PolymorphicOkInPure, // don't check for polymorphic type in pure subprogram
35+
DoNotNoteDefinition) // context does not imply definition
3436

3537
using DefinabilityFlags =
3638
common::EnumSet<DefinabilityFlag, DefinabilityFlag_enumSize>;

flang/lib/Semantics/pointer-assignment.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,8 +358,10 @@ bool PointerAssignmentChecker::Check(const evaluate::Designator<T> &d) {
358358
Say(std::get<MessageFormattedText>(*msg));
359359
}
360360
return false;
361+
} else {
362+
context_.NoteDefinedSymbol(*base);
363+
return true;
361364
}
362-
return true;
363365
}
364366

365367
// Common handling for procedure pointer right-hand sides

flang/lib/Semantics/semantics.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,41 @@ class MiscChecker : public virtual BaseChecker {
160160
SemanticsContext &context_;
161161
};
162162

163+
static void WarnUndefinedFunctionResult(
164+
SemanticsContext &context, const Scope &scope) {
165+
auto WasDefined{[&context](const Symbol &symbol) {
166+
return context.IsSymbolDefined(symbol) ||
167+
IsInitialized(symbol, /*ignoreDataStatements=*/true,
168+
/*ignoreAllocatable=*/true, /*ignorePointer=*/true);
169+
}};
170+
if (const Symbol * symbol{scope.symbol()}) {
171+
if (const auto *subp{symbol->detailsIf<SubprogramDetails>()}) {
172+
if (subp->isFunction() && !subp->isInterface() && !subp->stmtFunction()) {
173+
bool wasDefined{WasDefined(subp->result())};
174+
if (!wasDefined) {
175+
// Definitions of ENTRY result variables also count.
176+
for (const auto &pair : scope) {
177+
const Symbol &local{*pair.second};
178+
if (IsFunctionResult(local) && WasDefined(local)) {
179+
wasDefined = true;
180+
break;
181+
}
182+
}
183+
if (!wasDefined) {
184+
context.Say(
185+
symbol->name(), "Function result is never defined"_warn_en_US);
186+
}
187+
}
188+
}
189+
}
190+
}
191+
if (!scope.IsModuleFile()) {
192+
for (const Scope &child : scope.children()) {
193+
WarnUndefinedFunctionResult(context, child);
194+
}
195+
}
196+
}
197+
163198
using StatementSemanticsPass1 = ExprChecker;
164199
using StatementSemanticsPass2 = SemanticsVisitor<AllocateChecker,
165200
ArithmeticIfStmtChecker, AssignmentChecker, CaseChecker, CoarrayChecker,
@@ -187,6 +222,9 @@ static bool PerformStatementSemantics(
187222
SemanticsVisitor<CUDAChecker>{context}.Walk(program);
188223
}
189224
if (!context.AnyFatalError()) {
225+
if (context.ShouldWarn(common::UsageWarning::UndefinedFunctionResult)) {
226+
WarnUndefinedFunctionResult(context, context.globalScope());
227+
}
190228
pass2.CompileDataInitializationsIntoInitializers();
191229
}
192230
return !context.AnyFatalError();
@@ -712,4 +750,12 @@ CommonBlockList SemanticsContext::GetCommonBlocks() const {
712750
return {};
713751
}
714752

753+
void SemanticsContext::NoteDefinedSymbol(const Symbol &symbol) {
754+
isDefined_.insert(symbol);
755+
}
756+
757+
bool SemanticsContext::IsSymbolDefined(const Symbol &symbol) const {
758+
return isDefined_.find(symbol) != isDefined_.end();
759+
}
760+
715761
} // namespace Fortran::semantics

flang/lib/Semantics/tools.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ bool IsInitialized(const Symbol &symbol, bool ignoreDataStatements,
685685
return true;
686686
} else if (IsPointer(symbol)) {
687687
return !ignorePointer;
688-
} else if (IsNamedConstant(symbol) || IsFunctionResult(symbol)) {
688+
} else if (IsNamedConstant(symbol)) {
689689
return false;
690690
} else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
691691
if (!object->isDummy() && object->type()) {

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)