Skip to content

Commit 4d6ca36

Browse files
committed
[flang] Get base objects right in definability checker
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 345c1ea commit 4d6ca36

File tree

2 files changed

+50
-47
lines changed

2 files changed

+50
-47
lines changed

flang/lib/Semantics/definable.cpp

Lines changed: 35 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -60,37 +60,31 @@ 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 auto *component{std::get_if<evaluate::Component>(&dataRef.u)}) {
69+
const Symbol &compSym{component->GetLastSymbol()};
70+
if (IsPointer(compSym) ||
71+
(flags.test(DefinabilityFlag::AcceptAllocatable) &&
72+
IsAllocatable(compSym))) {
73+
if (!flags.test(DefinabilityFlag::PointerDefinition)) {
74+
return true;
8075
}
8176
}
82-
}
83-
if (const Symbol * lastPointer{GetLastPointerSymbol(dataRef)}) {
84-
return *lastPointer;
77+
flags.reset(DefinabilityFlag::PointerDefinition);
78+
return DefinesComponentPointerTarget(component->base(), flags);
8579
} else {
86-
return dataRef.GetFirstSymbol();
80+
return false;
8781
}
8882
}
8983

9084
// Check the leftmost (or only) symbol from a data-ref or expression.
9185
static std::optional<parser::Message> WhyNotDefinableBase(parser::CharBlock at,
9286
const Scope &scope, DefinabilityFlags flags, const Symbol &original,
93-
bool isWholeSymbol) {
87+
bool isWholeSymbol, bool isComponentPointerTarget) {
9488
const Symbol &ultimate{original.GetUltimate()};
9589
bool isPointerDefinition{flags.test(DefinabilityFlag::PointerDefinition)};
9690
bool acceptAllocatable{flags.test(DefinabilityFlag::AcceptAllocatable)};
@@ -104,12 +98,14 @@ static std::optional<parser::Message> WhyNotDefinableBase(parser::CharBlock at,
10498
"Construct association '%s' has a vector subscript"_en_US, original);
10599
} else if (auto dataRef{evaluate::ExtractDataRef(
106100
*association->expr(), true, true)}) {
107-
return WhyNotDefinableBase(at, scope, flags,
108-
GetRelevantSymbol(*dataRef, isPointerDefinition, acceptAllocatable),
109-
isWholeSymbol);
101+
return WhyNotDefinableBase(at, scope, flags, dataRef->GetFirstSymbol(),
102+
isWholeSymbol &&
103+
std::holds_alternative<evaluate::SymbolRef>(dataRef->u),
104+
isComponentPointerTarget ||
105+
DefinesComponentPointerTarget(*dataRef, flags));
110106
}
111107
}
112-
if (isTargetDefinition) {
108+
if (isTargetDefinition || isComponentPointerTarget) {
113109
} else if (!isPointerDefinition && !IsVariableName(ultimate)) {
114110
return BlameSymbol(at, "'%s' is not a variable"_en_US, original);
115111
} else if (IsProtected(ultimate) && IsUseAssociated(original, scope)) {
@@ -121,7 +117,7 @@ static std::optional<parser::Message> WhyNotDefinableBase(parser::CharBlock at,
121117
}
122118
if (const Scope * pure{FindPureProcedureContaining(scope)}) {
123119
// Additional checking for pure subprograms.
124-
if (!isTargetDefinition) {
120+
if (!isTargetDefinition || isComponentPointerTarget) {
125121
if (auto msg{CheckDefinabilityInPureScope(
126122
at, original, ultimate, scope, *pure)}) {
127123
return msg;
@@ -222,35 +218,24 @@ static std::optional<parser::Message> WhyNotDefinableLast(parser::CharBlock at,
222218
static std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at,
223219
const Scope &scope, DefinabilityFlags flags,
224220
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))}) {
221+
if (auto whyNot{
222+
WhyNotDefinableBase(at, scope, flags, dataRef.GetFirstSymbol(),
223+
std::holds_alternative<evaluate::SymbolRef>(dataRef.u),
224+
DefinesComponentPointerTarget(dataRef, flags))}) {
230225
return whyNot;
231226
} else {
232227
return WhyNotDefinableLast(at, scope, flags, dataRef.GetLastSymbol());
233228
}
234229
}
235230

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-
248231
std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at,
249232
const Scope &scope, DefinabilityFlags flags, const Symbol &original) {
250-
if (auto base{WhyNotDefinableBase(at, scope, flags, original, true)}) {
233+
if (auto base{WhyNotDefinableBase(at, scope, flags, original,
234+
/*isWholeSymbol=*/true, /*isComponentPointerTarget=*/false)}) {
251235
return base;
236+
} else {
237+
return WhyNotDefinableLast(at, scope, flags, original);
252238
}
253-
return WhyNotDefinableLast(at, scope, flags, original);
254239
}
255240

256241
class DuplicatedSubscriptFinder
@@ -370,7 +355,10 @@ std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at,
370355
*procSym, expr.AsFortran());
371356
}
372357
if (const auto *component{procDesignator->GetComponent()}) {
373-
return WhyNotDefinable(at, scope, flags, *component);
358+
flags.reset(DefinabilityFlag::PointerDefinition);
359+
return WhyNotDefinableBase(at, scope, flags,
360+
component->base().GetFirstSymbol(), false,
361+
DefinesComponentPointerTarget(component->base(), flags));
374362
} else {
375363
return WhyNotDefinable(at, scope, flags, *procSym);
376364
}

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)