Skip to content

Commit 6ac392b

Browse files
authored
[flang] Get base objects right in definability checker (#78854)
The utility function GetRelevantObject() seems to be just wrong for definability checks for the "base object" of a designator, and that's all for which it is (now?) used. This leads to some false error messages in Whizard when data-refs with multiple pointer components are defined. Simplify, and add more test cases.
1 parent df7d2b2 commit 6ac392b

File tree

2 files changed

+63
-47
lines changed

2 files changed

+63
-47
lines changed

flang/lib/Semantics/definable.cpp

Lines changed: 48 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -60,37 +60,44 @@ static std::optional<parser::Message> CheckDefinabilityInPureScope(
6060
return std::nullopt;
6161
}
6262

63-
// When a DataRef contains pointers, gets the rightmost one (unless it is
64-
// the entity being defined, in which case the last pointer above it);
65-
// otherwise, returns the leftmost symbol. The resulting symbol is the
66-
// relevant base object for definabiliy checking. Examples:
67-
// ptr1%ptr2 => ... -> ptr1
68-
// nonptr%ptr => ... -> nonptr
69-
// nonptr%ptr = ... -> ptr
70-
// ptr1%ptr2 = ... -> ptr2
71-
// ptr1%ptr2%nonptr = ... -> ptr2
72-
// nonptr1%nonptr2 = ... -> nonptr1
73-
static const Symbol &GetRelevantSymbol(const evaluate::DataRef &dataRef,
74-
bool isPointerDefinition, bool acceptAllocatable) {
75-
if (isPointerDefinition) {
76-
if (const auto *component{std::get_if<evaluate::Component>(&dataRef.u)}) {
77-
if (IsPointer(component->GetLastSymbol()) ||
78-
(acceptAllocatable && IsAllocatable(component->GetLastSymbol()))) {
79-
return GetRelevantSymbol(component->base(), false, false);
63+
// True when the object being defined is not a subobject of the base
64+
// object, e.g. X%PTR = 1., X%PTR%PTR2 => T (but not X%PTR => T).
65+
// F'2023 9.4.2p5
66+
static bool DefinesComponentPointerTarget(
67+
const evaluate::DataRef &dataRef, DefinabilityFlags flags) {
68+
if (const evaluate::Component *
69+
component{common::visit(
70+
common::visitors{
71+
[](const SymbolRef &) -> const evaluate::Component * {
72+
return nullptr;
73+
},
74+
[](const evaluate::Component &component) { return &component; },
75+
[](const evaluate::ArrayRef &aRef) {
76+
return aRef.base().UnwrapComponent();
77+
},
78+
[](const evaluate::CoarrayRef &aRef)
79+
-> const evaluate::Component * { return nullptr; },
80+
},
81+
dataRef.u)}) {
82+
const Symbol &compSym{component->GetLastSymbol()};
83+
if (IsPointer(compSym) ||
84+
(flags.test(DefinabilityFlag::AcceptAllocatable) &&
85+
IsAllocatable(compSym))) {
86+
if (!flags.test(DefinabilityFlag::PointerDefinition)) {
87+
return true;
8088
}
8189
}
82-
}
83-
if (const Symbol * lastPointer{GetLastPointerSymbol(dataRef)}) {
84-
return *lastPointer;
90+
flags.reset(DefinabilityFlag::PointerDefinition);
91+
return DefinesComponentPointerTarget(component->base(), flags);
8592
} else {
86-
return dataRef.GetFirstSymbol();
93+
return false;
8794
}
8895
}
8996

9097
// Check the leftmost (or only) symbol from a data-ref or expression.
9198
static std::optional<parser::Message> WhyNotDefinableBase(parser::CharBlock at,
9299
const Scope &scope, DefinabilityFlags flags, const Symbol &original,
93-
bool isWholeSymbol) {
100+
bool isWholeSymbol, bool isComponentPointerTarget) {
94101
const Symbol &ultimate{original.GetUltimate()};
95102
bool isPointerDefinition{flags.test(DefinabilityFlag::PointerDefinition)};
96103
bool acceptAllocatable{flags.test(DefinabilityFlag::AcceptAllocatable)};
@@ -104,12 +111,14 @@ static std::optional<parser::Message> WhyNotDefinableBase(parser::CharBlock at,
104111
"Construct association '%s' has a vector subscript"_en_US, original);
105112
} else if (auto dataRef{evaluate::ExtractDataRef(
106113
*association->expr(), true, true)}) {
107-
return WhyNotDefinableBase(at, scope, flags,
108-
GetRelevantSymbol(*dataRef, isPointerDefinition, acceptAllocatable),
109-
isWholeSymbol);
114+
return WhyNotDefinableBase(at, scope, flags, dataRef->GetFirstSymbol(),
115+
isWholeSymbol &&
116+
std::holds_alternative<evaluate::SymbolRef>(dataRef->u),
117+
isComponentPointerTarget ||
118+
DefinesComponentPointerTarget(*dataRef, flags));
110119
}
111120
}
112-
if (isTargetDefinition) {
121+
if (isTargetDefinition || isComponentPointerTarget) {
113122
} else if (!isPointerDefinition && !IsVariableName(ultimate)) {
114123
return BlameSymbol(at, "'%s' is not a variable"_en_US, original);
115124
} else if (IsProtected(ultimate) && IsUseAssociated(original, scope)) {
@@ -121,7 +130,7 @@ static std::optional<parser::Message> WhyNotDefinableBase(parser::CharBlock at,
121130
}
122131
if (const Scope * pure{FindPureProcedureContaining(scope)}) {
123132
// Additional checking for pure subprograms.
124-
if (!isTargetDefinition) {
133+
if (!isTargetDefinition || isComponentPointerTarget) {
125134
if (auto msg{CheckDefinabilityInPureScope(
126135
at, original, ultimate, scope, *pure)}) {
127136
return msg;
@@ -222,35 +231,24 @@ static std::optional<parser::Message> WhyNotDefinableLast(parser::CharBlock at,
222231
static std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at,
223232
const Scope &scope, DefinabilityFlags flags,
224233
const evaluate::DataRef &dataRef) {
225-
const Symbol &base{GetRelevantSymbol(dataRef,
226-
flags.test(DefinabilityFlag::PointerDefinition),
227-
flags.test(DefinabilityFlag::AcceptAllocatable))};
228-
if (auto whyNot{WhyNotDefinableBase(at, scope, flags, base,
229-
std::holds_alternative<evaluate::SymbolRef>(dataRef.u))}) {
234+
if (auto whyNot{
235+
WhyNotDefinableBase(at, scope, flags, dataRef.GetFirstSymbol(),
236+
std::holds_alternative<evaluate::SymbolRef>(dataRef.u),
237+
DefinesComponentPointerTarget(dataRef, flags))}) {
230238
return whyNot;
231239
} else {
232240
return WhyNotDefinableLast(at, scope, flags, dataRef.GetLastSymbol());
233241
}
234242
}
235243

236-
// Checks a NOPASS procedure pointer component
237-
static std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at,
238-
const Scope &scope, DefinabilityFlags flags,
239-
const evaluate::Component &component) {
240-
const evaluate::DataRef &dataRef{component.base()};
241-
const Symbol &base{GetRelevantSymbol(dataRef, false, false)};
242-
DefinabilityFlags baseFlags{flags};
243-
baseFlags.reset(DefinabilityFlag::PointerDefinition);
244-
return WhyNotDefinableBase(at, scope, baseFlags, base,
245-
std::holds_alternative<evaluate::SymbolRef>(dataRef.u));
246-
}
247-
248244
std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at,
249245
const Scope &scope, DefinabilityFlags flags, const Symbol &original) {
250-
if (auto base{WhyNotDefinableBase(at, scope, flags, original, true)}) {
246+
if (auto base{WhyNotDefinableBase(at, scope, flags, original,
247+
/*isWholeSymbol=*/true, /*isComponentPointerTarget=*/false)}) {
251248
return base;
249+
} else {
250+
return WhyNotDefinableLast(at, scope, flags, original);
252251
}
253-
return WhyNotDefinableLast(at, scope, flags, original);
254252
}
255253

256254
class DuplicatedSubscriptFinder
@@ -370,7 +368,10 @@ std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at,
370368
*procSym, expr.AsFortran());
371369
}
372370
if (const auto *component{procDesignator->GetComponent()}) {
373-
return WhyNotDefinable(at, scope, flags, *component);
371+
flags.reset(DefinabilityFlag::PointerDefinition);
372+
return WhyNotDefinableBase(at, scope, flags,
373+
component->base().GetFirstSymbol(), false,
374+
DefinesComponentPointerTarget(component->base(), flags));
374375
} else {
375376
return WhyNotDefinable(at, scope, flags, *procSym);
376377
}

flang/test/Semantics/definable01.f90

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ module m
2525
real :: x2
2626
end type
2727
type(t2) :: t2static
28+
type list
29+
real a
30+
type(list), pointer :: prev, next
31+
end type
2832
character(*), parameter :: internal = '0'
2933
contains
3034
subroutine test1(dummy)
@@ -97,4 +101,15 @@ subroutine test5(np)
97101
!CHECK: because: 'np' is an INTENT(IN) dummy argument
98102
nullify(np%ptr)
99103
end
104+
pure function test6(lp)
105+
type(list), pointer :: lp
106+
!CHECK: error: The left-hand side of a pointer assignment is not definable
107+
!CHECK: because: 'lp' may not be defined in pure subprogram 'test6' because it is a POINTER dummy argument of a pure function
108+
lp%next%next => null()
109+
end
110+
pure subroutine test7(lp)
111+
type(list), pointer :: lp
112+
!CHECK-NOT: error:
113+
lp%next%next => null()
114+
end
100115
end module

0 commit comments

Comments
 (0)