Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9d0043a

Browse files
authored
[flang] Refactor "unused"/"used without definition" warnings (#179539)
I'm emitting a false warning "used without definition" warning for variable cited in an inquiry intrinsic (e.g. LBOUND), and failing to emit an "unused local" warning for allocatables that only appear in ALLOCATE statements. This patch makes the analysis more context-aware by rewriting it in terms of typed expression traversal, and moves the analysis part into Evaluate/check-expression.cpp from Semantics/expression.cpp.
1 parent 621fe67 commit 9d0043a

12 files changed

Lines changed: 174 additions & 114 deletions

File tree

flang/include/flang/Evaluate/check-expression.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,5 +180,14 @@ std::optional<parser::Message> CheckStatementFunction(
180180
std::optional<bool> ActualArgNeedsCopy(const ActualArgument *,
181181
const characteristics::DummyArgument *, FoldingContext &, bool forCopyOut);
182182

183+
// Scan expressions and note uses of values of symbols.
184+
semantics::UnorderedSymbolSet CollectUsedSymbolValues(
185+
semantics::SemanticsContext &, const Expr<SomeType> &,
186+
bool isDefinition = false);
187+
semantics::UnorderedSymbolSet CollectUsedSymbolValues(
188+
semantics::SemanticsContext &, const ProcedureRef &);
189+
semantics::UnorderedSymbolSet CollectUsedSymbolValues(
190+
semantics::SemanticsContext &, const Assignment &);
191+
183192
} // namespace Fortran::evaluate
184193
#endif

flang/include/flang/Evaluate/tools.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1638,6 +1638,9 @@ std::optional<int> GetDummyArgumentNumber(const Symbol *);
16381638

16391639
const Symbol *FindAncestorModuleProcedure(const Symbol *symInSubmodule);
16401640

1641+
// Given a Cray pointee symbol, returns the related Cray pointer symbol.
1642+
const Symbol &GetCrayPointer(const Symbol &crayPointee);
1643+
16411644
} // namespace Fortran::semantics
16421645

16431646
#endif // FORTRAN_EVALUATE_TOOLS_H_

flang/include/flang/Semantics/expression.h

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -465,12 +465,8 @@ evaluate::Expr<evaluate::SubscriptInteger> AnalyzeKindSelector(
465465
SemanticsContext &, common::TypeCategory,
466466
const std::optional<parser::KindSelector> &);
467467

468-
void NoteUsedSymbols(SemanticsContext &, const SomeExpr &);
469-
void NoteUsedSymbols(SemanticsContext &, const evaluate::ProcedureRef &);
470-
void NoteUsedSymbols(SemanticsContext &, const evaluate::Assignment &);
471-
void NoteUsedSymbols(SemanticsContext &, const parser::TypedExpr &);
472-
void NoteUsedSymbols(SemanticsContext &, const parser::TypedCall &);
473-
void NoteUsedSymbols(SemanticsContext &, const parser::TypedAssignment &);
468+
void NoteUsedSymbols(
469+
SemanticsContext &, const SomeExpr &, bool isDefinition = false);
474470

475471
// Semantic analysis of all expressions in a parse tree, which becomes
476472
// decorated with typed representations for top-level expressions.
@@ -499,11 +495,11 @@ class ExprChecker {
499495
return false;
500496
}
501497
bool Pre(const parser::AllocateObject &x) {
502-
AnalyzeAndNoteUses(x);
498+
AnalyzeAndNoteUses(x, /*isDefinition=*/true);
503499
return false;
504500
}
505501
bool Pre(const parser::PointerObject &x) {
506-
AnalyzeAndNoteUses(x);
502+
AnalyzeAndNoteUses(x, /*isDefinition=*/true);
507503
return false;
508504
}
509505
bool Pre(const parser::DataStmtObject &);
@@ -583,14 +579,23 @@ class ExprChecker {
583579
}
584580

585581
private:
586-
template <typename A> void AnalyzeAndNoteUses(const A &x) {
582+
template <typename A>
583+
void AnalyzeAndNoteUses(const A &x, bool isDefinition = false) {
587584
exprAnalyzer_.Analyze(x);
588585
if constexpr (parser::HasTypedExpr<A>::value) {
589-
NoteUsedSymbols(context_, x.typedExpr);
586+
if (x.typedExpr && x.typedExpr->v) {
587+
NoteUsedSymbols(context_, *x.typedExpr->v, isDefinition);
588+
}
590589
} else if constexpr (parser::HasTypedCall<A>::value) {
591-
NoteUsedSymbols(context_, x.typedCall);
590+
if (x.typedCall) {
591+
context_.NoteUsedSymbols(
592+
evaluate::CollectUsedSymbolValues(context_, *x.typedCall));
593+
}
592594
} else if constexpr (parser::HasTypedAssignment<A>::value) {
593-
NoteUsedSymbols(context_, x.typedAssignment);
595+
if (x.typedAssignment && x.typedAssignment->v) {
596+
context_.NoteUsedSymbols(
597+
evaluate::CollectUsedSymbolValues(context_, *x.typedAssignment->v));
598+
}
594599
}
595600
}
596601
bool InWhereBody() const { return whereDepth_ > 0; }

flang/include/flang/Semantics/semantics.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ class SemanticsContext {
332332
void NoteDefinedSymbol(const Symbol &);
333333
bool IsSymbolDefined(const Symbol &) const;
334334
void NoteUsedSymbol(const Symbol &);
335+
void NoteUsedSymbols(const UnorderedSymbolSet &);
335336
bool IsSymbolUsed(const Symbol &) const;
336337

337338
void DumpSymbols(llvm::raw_ostream &);

flang/include/flang/Semantics/tools.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -334,9 +334,6 @@ const Symbol *FindExternallyVisibleObject(
334334
// specific procedure of the same name, return it instead.
335335
const Symbol &BypassGeneric(const Symbol &);
336336

337-
// Given a cray pointee symbol, returns the related cray pointer symbol.
338-
const Symbol &GetCrayPointer(const Symbol &crayPointee);
339-
340337
using SomeExpr = evaluate::Expr<evaluate::SomeType>;
341338

342339
bool ExprHasTypeCategory(

flang/lib/Evaluate/check-expression.cpp

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,4 +1665,121 @@ std::optional<bool> ActualArgNeedsCopy(const ActualArgument *actual,
16651665
return std::nullopt;
16661666
}
16671667

1668+
// CollectUsedSymbolValues()
1669+
1670+
class CollectUsedSymbolValuesHelper
1671+
: public SetTraverse<CollectUsedSymbolValuesHelper,
1672+
semantics::UnorderedSymbolSet> {
1673+
public:
1674+
using Result = semantics::UnorderedSymbolSet;
1675+
using Base = SetTraverse<CollectUsedSymbolValuesHelper, Result>;
1676+
explicit CollectUsedSymbolValuesHelper(
1677+
semantics::SemanticsContext &c, bool isDefinition = false)
1678+
: Base{*this}, context_{c}, isDefinition_{isDefinition} {}
1679+
using Base::operator();
1680+
1681+
Result operator()(const semantics::Symbol &symbol) const {
1682+
Result result;
1683+
if (!isDefinition_) {
1684+
const Symbol &root{semantics::GetAssociationRoot(symbol)};
1685+
switch (root.owner().kind()) {
1686+
case semantics::Scope::Kind::Subprogram:
1687+
case semantics::Scope::Kind::MainProgram:
1688+
case semantics::Scope::Kind::BlockConstruct:
1689+
if ((root.has<semantics::ObjectEntityDetails>() ||
1690+
IsProcedurePointer(root))) {
1691+
result.insert(root);
1692+
if (root.test(semantics::Symbol::Flag::CrayPointee)) {
1693+
result.insert(semantics::GetCrayPointer(root));
1694+
}
1695+
}
1696+
break;
1697+
default:
1698+
break;
1699+
}
1700+
}
1701+
return result;
1702+
}
1703+
1704+
Result operator()(const Subscript &subscript) {
1705+
auto restorer{common::ScopedSet(isDefinition_, false)};
1706+
return (*this)(subscript.u);
1707+
}
1708+
1709+
template <typename T> Result operator()(const FunctionRef<T> &fRef) {
1710+
return (*this)(static_cast<ProcedureRef>(fRef));
1711+
}
1712+
Result operator()(const ProcedureRef &call) {
1713+
auto restorer{common::ScopedSet(isDefinition_, false)};
1714+
Result result{(*this)(call.proc())};
1715+
int skipLeading{0};
1716+
if (const auto *intrinsic{call.proc().GetSpecificIntrinsic()}) {
1717+
if (context_.intrinsics().GetIntrinsicClass(intrinsic->name) ==
1718+
IntrinsicClass::inquiryFunction) {
1719+
skipLeading = 1; // first argument to inquiry doesn't count as a use
1720+
}
1721+
}
1722+
for (const auto &maybeArg : call.arguments()) {
1723+
if (skipLeading) {
1724+
--skipLeading;
1725+
} else if (maybeArg) {
1726+
if (const auto *expr{maybeArg->UnwrapExpr()}) {
1727+
if (IsBindingUsedAsProcedure(*expr)) {
1728+
// Ignore procedure bindings being used as actual procedures
1729+
// (a local extension).
1730+
} else {
1731+
result = Combine(std::move(result), (*this)(*expr));
1732+
}
1733+
}
1734+
}
1735+
}
1736+
return result;
1737+
}
1738+
1739+
Result operator()(const Assignment &assignment) {
1740+
auto restorer{common::ScopedSet(isDefinition_, true)};
1741+
Result result{(*this)(assignment.lhs)};
1742+
if (IsBindingUsedAsProcedure(assignment.rhs)) {
1743+
// Don't look at the RHS, we're just using its binding (extension).
1744+
} else {
1745+
auto restorer{common::ScopedSet(isDefinition_, false)};
1746+
result = Combine(std::move(result), (*this)(assignment.rhs));
1747+
}
1748+
return result;
1749+
}
1750+
1751+
Result operator()(const TypeParamInquiry &) const {
1752+
return {}; // doesn't count as a use
1753+
}
1754+
Result operator()(const DescriptorInquiry &) const {
1755+
return {}; // doesn't count as a use
1756+
}
1757+
1758+
private:
1759+
static bool IsBindingUsedAsProcedure(const Expr<SomeType> &expr) {
1760+
if (const auto *pd{std::get_if<ProcedureDesignator>(&expr.u)}) {
1761+
if (const Symbol *symbol{pd->GetSymbol()}) {
1762+
return symbol->has<semantics::ProcBindingDetails>();
1763+
}
1764+
}
1765+
return false;
1766+
}
1767+
1768+
semantics::SemanticsContext &context_;
1769+
bool isDefinition_{false};
1770+
};
1771+
1772+
semantics::UnorderedSymbolSet CollectUsedSymbolValues(
1773+
semantics::SemanticsContext &context, const Expr<SomeType> &expr,
1774+
bool isDefinition) {
1775+
return CollectUsedSymbolValuesHelper{context, isDefinition}(expr);
1776+
}
1777+
semantics::UnorderedSymbolSet CollectUsedSymbolValues(
1778+
semantics::SemanticsContext &context, const ProcedureRef &call) {
1779+
return CollectUsedSymbolValuesHelper{context}(call);
1780+
}
1781+
semantics::UnorderedSymbolSet CollectUsedSymbolValues(
1782+
semantics::SemanticsContext &context, const Assignment &assignment) {
1783+
return CollectUsedSymbolValuesHelper{context}(assignment);
1784+
}
16681785
} // namespace Fortran::evaluate

flang/lib/Evaluate/tools.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2645,4 +2645,16 @@ const Symbol *FindAncestorModuleProcedure(const Symbol *symInSubmodule) {
26452645
return nullptr;
26462646
}
26472647

2648+
const Symbol &GetCrayPointer(const Symbol &crayPointee) {
2649+
const Symbol *found{nullptr};
2650+
const Symbol &ultimate{crayPointee.GetUltimate()};
2651+
for (const auto &[pointee, pointer] : ultimate.owner().crayPointers()) {
2652+
if (pointee == ultimate.name()) {
2653+
found = &pointer.get();
2654+
break;
2655+
}
2656+
}
2657+
return DEREF(found);
2658+
}
2659+
26482660
} // namespace Fortran::semantics

flang/lib/Semantics/check-coarray.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "check-coarray.h"
1010
#include "definable.h"
1111
#include "flang/Common/indirection.h"
12+
#include "flang/Evaluate/check-expression.h"
1213
#include "flang/Evaluate/expression.h"
1314
#include "flang/Parser/message.h"
1415
#include "flang/Parser/parse-tree.h"

flang/lib/Semantics/expression.cpp

Lines changed: 3 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -5321,94 +5321,10 @@ evaluate::Expr<evaluate::SubscriptInteger> AnalyzeKindSelector(
53215321
return analyzer.AnalyzeKindSelector(category, selector);
53225322
}
53235323

5324-
// NoteUsedSymbols()
5325-
5326-
static void NoteUsedSymbol(SemanticsContext &context, const Symbol &symbol) {
5327-
const Symbol &root{GetAssociationRoot(symbol)};
5328-
switch (root.owner().kind()) {
5329-
case semantics::Scope::Kind::Subprogram:
5330-
case semantics::Scope::Kind::MainProgram:
5331-
case semantics::Scope::Kind::BlockConstruct:
5332-
if ((root.has<semantics::ObjectEntityDetails>() ||
5333-
IsProcedurePointer(root))) {
5334-
context.NoteUsedSymbol(root);
5335-
if (root.test(Symbol::Flag::CrayPointee)) {
5336-
context.NoteUsedSymbol(GetCrayPointer(root));
5337-
}
5338-
}
5339-
break;
5340-
default:
5341-
break;
5342-
}
5343-
}
5344-
5345-
template <typename A>
5346-
void NoteUsedSymbolsHelper(SemanticsContext &context, const A &x) {
5347-
if (context.ShouldWarn(common::UsageWarning::UnusedVariable)) {
5348-
for (const Symbol &symbol : CollectSymbols(x)) {
5349-
NoteUsedSymbol(context, symbol);
5350-
}
5351-
}
5352-
}
5353-
5354-
void NoteUsedSymbols(SemanticsContext &context, const SomeExpr &expr) {
5355-
NoteUsedSymbolsHelper(context, expr);
5356-
}
5357-
5358-
static bool IsBindingUsedAsProcedure(const SomeExpr &expr) {
5359-
if (const auto *pd{std::get_if<evaluate::ProcedureDesignator>(&expr.u)}) {
5360-
if (const Symbol *symbol{pd->GetSymbol()}) {
5361-
return symbol->has<ProcBindingDetails>();
5362-
}
5363-
}
5364-
return false;
5365-
}
5366-
53675324
void NoteUsedSymbols(
5368-
SemanticsContext &context, const evaluate::ProcedureRef &call) {
5369-
NoteUsedSymbolsHelper(context, call.proc());
5370-
for (const auto &maybeArg : call.arguments()) {
5371-
if (maybeArg) {
5372-
if (const auto *expr{maybeArg->UnwrapExpr()}) {
5373-
if (!IsBindingUsedAsProcedure(*expr)) {
5374-
// Ignore procedure bindings being used as actual procedures
5375-
// (a local extension).
5376-
NoteUsedSymbolsHelper(context, *expr);
5377-
}
5378-
}
5379-
}
5380-
}
5381-
}
5382-
5383-
void NoteUsedSymbols(
5384-
SemanticsContext &context, const evaluate::Assignment &assignment) {
5385-
if (IsBindingUsedAsProcedure(assignment.rhs)) {
5386-
// Don't look at the RHS, we're just using its binding (extension).
5387-
NoteUsedSymbolsHelper(context, assignment.lhs);
5388-
} else {
5389-
NoteUsedSymbolsHelper(context, assignment);
5390-
}
5391-
}
5392-
5393-
void NoteUsedSymbols(
5394-
SemanticsContext &context, const parser::TypedExpr &typedExpr) {
5395-
if (typedExpr && typedExpr->v) {
5396-
NoteUsedSymbols(context, *typedExpr->v);
5397-
}
5398-
}
5399-
5400-
void NoteUsedSymbols(
5401-
SemanticsContext &context, const parser::TypedCall &typedCall) {
5402-
if (typedCall) {
5403-
NoteUsedSymbols(context, *typedCall);
5404-
}
5405-
}
5406-
5407-
void NoteUsedSymbols(
5408-
SemanticsContext &context, const parser::TypedAssignment &typedAssignment) {
5409-
if (typedAssignment && typedAssignment->v) {
5410-
NoteUsedSymbols(context, *typedAssignment->v);
5411-
}
5325+
SemanticsContext &context, const SomeExpr &expr, bool isDefinition) {
5326+
context.NoteUsedSymbols(
5327+
evaluate::CollectUsedSymbolValues(context, expr, isDefinition));
54125328
}
54135329

54145330
ExprChecker::ExprChecker(SemanticsContext &context) : context_{context} {}

flang/lib/Semantics/semantics.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,11 @@ bool SemanticsContext::IsSymbolDefined(const Symbol &symbol) const {
819819
void SemanticsContext::NoteUsedSymbol(const Symbol &symbol) {
820820
isUsed_.insert(symbol);
821821
}
822+
void SemanticsContext::NoteUsedSymbols(const UnorderedSymbolSet &set) {
823+
for (const Symbol &symbol : set) {
824+
NoteUsedSymbol(symbol);
825+
}
826+
}
822827

823828
bool SemanticsContext::IsSymbolUsed(const Symbol &symbol) const {
824829
return isUsed_.find(symbol) != isUsed_.end();

0 commit comments

Comments
 (0)