Skip to content

Commit 1fa9ef6

Browse files
committed
[flang] Consolidate and enhance pointer assignment checks
Consolidate aspects of pointer assignment & structure constructor pointer component checking from Semantics/assignment.cpp and /expression.cpp into /pointer-assignment.cpp, and add a warning about data targets that are not definable objects but not hard errors. Specifically, a structure component pointer component data target is not allowed to be a USE-associated object in a pure context by a numbered constraint, but the right-hand side data target of a pointer assignment statement has no such constraint, and that's the new warning. Differential Revision: https://reviews.llvm.org/D146581
1 parent c44d307 commit 1fa9ef6

File tree

13 files changed

+182
-104
lines changed

13 files changed

+182
-104
lines changed

flang/lib/Semantics/assignment.cpp

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ class AssignmentContext {
4444
void Analyze(const parser::ConcurrentControl &);
4545

4646
private:
47-
bool CheckForPureContext(const SomeExpr &rhs, parser::CharBlock rhsSource,
48-
bool isPointerAssignment, bool isDefinedAssignment);
47+
bool CheckForPureContext(const SomeExpr &rhs, parser::CharBlock rhsSource);
4948
void CheckShape(parser::CharBlock, const SomeExpr *);
5049
template <typename... A>
5150
parser::Message *Say(parser::CharBlock at, A &&...args) {
@@ -75,8 +74,11 @@ void AssignmentContext::Analyze(const parser::AssignmentStmt &stmt) {
7574
}
7675
}
7776
auto rhsLoc{std::get<parser::Expr>(stmt.t).source};
78-
CheckForPureContext(rhs, rhsLoc, false /*not a pointer assignment*/,
79-
std::holds_alternative<evaluate::ProcedureRef>(assignment->u));
77+
if (std::holds_alternative<evaluate::ProcedureRef>(assignment->u)) {
78+
// it's a defined ASSIGNMENT(=)
79+
} else {
80+
CheckForPureContext(rhs, rhsLoc);
81+
}
8082
if (whereDepth_ > 0) {
8183
CheckShape(lhsLoc, &lhs);
8284
}
@@ -86,14 +88,10 @@ void AssignmentContext::Analyze(const parser::AssignmentStmt &stmt) {
8688
void AssignmentContext::Analyze(const parser::PointerAssignmentStmt &stmt) {
8789
CHECK(whereDepth_ == 0);
8890
if (const evaluate::Assignment * assignment{GetAssignment(stmt)}) {
89-
const SomeExpr &rhs{assignment->rhs};
90-
CheckForPureContext(rhs, std::get<parser::Expr>(stmt.t).source,
91-
true /*this is a pointer assignment*/,
92-
false /*not a defined assignment*/);
9391
parser::CharBlock at{context_.location().value()};
9492
auto restorer{foldingContext().messages().SetLocation(at)};
95-
const Scope &scope{context_.FindScope(at)};
96-
CheckPointerAssignment(foldingContext(), *assignment, scope);
93+
CheckPointerAssignment(
94+
foldingContext(), *assignment, context_.FindScope(at));
9795
}
9896
}
9997

@@ -128,29 +126,16 @@ bool CheckCopyabilityInPureScope(parser::ContextualMessages &messages,
128126
return true;
129127
}
130128

131-
bool AssignmentContext::CheckForPureContext(const SomeExpr &rhs,
132-
parser::CharBlock rhsSource, bool isPointerAssignment,
133-
bool isDefinedAssignment) {
129+
bool AssignmentContext::CheckForPureContext(
130+
const SomeExpr &rhs, parser::CharBlock rhsSource) {
134131
const Scope &scope{context_.FindScope(rhsSource)};
135-
if (!FindPureProcedureContaining(scope)) {
136-
return true;
137-
}
138-
parser::ContextualMessages messages{
139-
context_.location().value(), &context_.messages()};
140-
if (isPointerAssignment) {
141-
if (const Symbol * base{GetFirstSymbol(rhs)}) {
142-
if (const char *why{WhyBaseObjectIsSuspicious(
143-
base->GetUltimate(), scope)}) { // C1594(3)
144-
evaluate::SayWithDeclaration(messages, *base,
145-
"A pure subprogram may not use '%s' as the target of pointer assignment because it is %s"_err_en_US,
146-
base->name(), why);
147-
return false;
148-
}
149-
}
150-
} else if (!isDefinedAssignment) {
132+
if (FindPureProcedureContaining(scope)) {
133+
parser::ContextualMessages messages{
134+
context_.location().value(), &context_.messages()};
151135
return CheckCopyabilityInPureScope(messages, rhs, scope);
136+
} else {
137+
return true;
152138
}
153-
return true;
154139
}
155140

156141
// 10.2.3.1(2) The masks and LHS of assignments must be arrays of the same shape

flang/lib/Semantics/check-call.cpp

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -494,23 +494,16 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
494494

495495
// 15.5.2.7 -- dummy is POINTER
496496
if (dummyIsPointer) {
497-
if (dummyIsContiguous && !actualIsContiguous) {
497+
if (actualIsPointer || dummy.intent == common::Intent::In) {
498+
if (scope) {
499+
semantics::CheckPointerAssignment(
500+
context, messages.at(), dummyName, dummy, actual, *scope);
501+
}
502+
} else if (!actualIsPointer) {
498503
messages.Say(
499-
"Actual argument associated with CONTIGUOUS POINTER %s must be simply contiguous"_err_en_US,
504+
"Actual argument associated with POINTER %s must also be POINTER unless INTENT(IN)"_err_en_US,
500505
dummyName);
501506
}
502-
if (!actualIsPointer) {
503-
if (dummy.intent == common::Intent::In) {
504-
if (scope) {
505-
semantics::CheckPointerAssignment(
506-
context, messages.at(), dummyName, dummy, actual, *scope);
507-
}
508-
} else {
509-
messages.Say(
510-
"Actual argument associated with POINTER %s must also be POINTER unless INTENT(IN)"_err_en_US,
511-
dummyName);
512-
}
513-
}
514507
}
515508

516509
// 15.5.2.5 -- actual & dummy are both POINTER or both ALLOCATABLE

flang/lib/Semantics/expression.cpp

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1814,6 +1814,8 @@ MaybeExpr ExpressionAnalyzer::Analyze(
18141814
if (!spec.scope() || !typeSymbol.has<semantics::DerivedTypeDetails>()) {
18151815
return std::nullopt; // error recovery
18161816
}
1817+
const semantics::Scope &scope{context_.FindScope(typeName)};
1818+
const semantics::Scope *pureContext{FindPureProcedureContaining(scope)};
18171819
const auto &typeDetails{typeSymbol.get<semantics::DerivedTypeDetails>()};
18181820
const Symbol *parentComponent{typeDetails.GetParentComponent(*spec.scope())};
18191821

@@ -1939,41 +1941,18 @@ MaybeExpr ExpressionAnalyzer::Analyze(
19391941
}
19401942
unavailable.insert(symbol->name());
19411943
if (value) {
1942-
if (symbol->has<semantics::ProcEntityDetails>()) {
1943-
CHECK(IsPointer(*symbol));
1944-
} else if (symbol->has<semantics::ObjectEntityDetails>()) {
1945-
// C1594(4)
1946-
if (const auto *pureProc{FindPureProcedureContaining(innermost)}) {
1947-
if (const Symbol *pointer{FindPointerComponent(*symbol)}) {
1948-
if (const Symbol *object{
1949-
FindExternallyVisibleObject(*value, *pureProc)}) {
1950-
if (auto *msg{Say(expr.source,
1951-
"Externally visible object '%s' may not be "
1952-
"associated with pointer component '%s' in a "
1953-
"pure procedure"_err_en_US,
1954-
object->name(), pointer->name())}) {
1955-
msg->Attach(object->name(), "Object declaration"_en_US)
1956-
.Attach(pointer->name(), "Pointer declaration"_en_US);
1957-
}
1958-
}
1959-
}
1960-
}
1961-
} else if (symbol->has<semantics::TypeParamDetails>()) {
1944+
if (symbol->has<semantics::TypeParamDetails>()) {
19621945
Say(expr.source,
1963-
"Type parameter '%s' may not appear as a component "
1964-
"of a structure constructor"_err_en_US,
1946+
"Type parameter '%s' may not appear as a component of a structure constructor"_err_en_US,
19651947
symbol->name());
1966-
continue;
1967-
} else {
1968-
Say(expr.source,
1969-
"Component '%s' is neither a procedure pointer "
1970-
"nor a data object"_err_en_US,
1971-
symbol->name());
1972-
continue;
19731948
}
1974-
if (IsPointer(*symbol)) {
1949+
if (!(symbol->has<semantics::ProcEntityDetails>() ||
1950+
symbol->has<semantics::ObjectEntityDetails>())) {
1951+
continue; // recovery
1952+
}
1953+
if (IsPointer(*symbol)) { // C7104, C7105, C1594(4)
19751954
semantics::CheckStructConstructorPointerComponent(
1976-
GetFoldingContext(), *symbol, *value, innermost); // C7104, C7105
1955+
GetFoldingContext(), *symbol, *value, innermost);
19771956
result.Add(*symbol, Fold(std::move(*value)));
19781957
continue;
19791958
}
@@ -2008,6 +1987,15 @@ MaybeExpr ExpressionAnalyzer::Analyze(
20081987
*symbol);
20091988
continue;
20101989
}
1990+
} else if (const Symbol * pointer{FindPointerComponent(*symbol)};
1991+
pointer && pureContext) { // C1594(4)
1992+
if (const Symbol *
1993+
visible{semantics::FindExternallyVisibleObject(
1994+
*value, *pureContext)}) {
1995+
Say(expr.source,
1996+
"The externally visible object '%s' may not be used in a pure procedure as the value for component '%s' which has the pointer component '%s'"_err_en_US,
1997+
visible->name(), symbol->name(), pointer->name());
1998+
}
20111999
}
20122000
if (MaybeExpr converted{ConvertToType(*symbol, std::move(*value))}) {
20132001
if (auto componentShape{GetShape(GetFoldingContext(), *symbol)}) {

flang/lib/Semantics/pointer-assignment.cpp

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class PointerAssignmentChecker {
5757
PointerAssignmentChecker &set_isContiguous(bool);
5858
PointerAssignmentChecker &set_isVolatile(bool);
5959
PointerAssignmentChecker &set_isBoundsRemapping(bool);
60+
PointerAssignmentChecker &set_pointerComponentLHS(const Symbol *);
6061
bool CheckLeftHandSide(const SomeExpr &);
6162
bool Check(const SomeExpr &);
6263

@@ -87,6 +88,7 @@ class PointerAssignmentChecker {
8788
bool isContiguous_{false};
8889
bool isVolatile_{false};
8990
bool isBoundsRemapping_{false};
91+
const Symbol *pointerComponentLHS_{nullptr};
9092
};
9193

9294
PointerAssignmentChecker &PointerAssignmentChecker::set_lhsType(
@@ -113,6 +115,12 @@ PointerAssignmentChecker &PointerAssignmentChecker::set_isBoundsRemapping(
113115
return *this;
114116
}
115117

118+
PointerAssignmentChecker &PointerAssignmentChecker::set_pointerComponentLHS(
119+
const Symbol *symbol) {
120+
pointerComponentLHS_ = symbol;
121+
return *this;
122+
}
123+
116124
bool PointerAssignmentChecker::CharacterizeProcedure() {
117125
if (!characterizedProcedure_) {
118126
characterizedProcedure_ = true;
@@ -126,7 +134,7 @@ bool PointerAssignmentChecker::CharacterizeProcedure() {
126134
bool PointerAssignmentChecker::CheckLeftHandSide(const SomeExpr &lhs) {
127135
if (auto whyNot{WhyNotDefinable(context_.messages().at(), scope_,
128136
DefinabilityFlags{DefinabilityFlag::PointerDefinition}, lhs)}) {
129-
if (auto *msg{context_.messages().Say(
137+
if (auto *msg{Say(
130138
"The left-hand side of a pointer assignment is not definable"_err_en_US)}) {
131139
msg->Attach(std::move(*whyNot));
132140
}
@@ -153,12 +161,62 @@ bool PointerAssignmentChecker::Check(const SomeExpr &rhs) {
153161
if (HasVectorSubscript(rhs)) { // C1025
154162
Say("An array section with a vector subscript may not be a pointer target"_err_en_US);
155163
return false;
156-
} else if (ExtractCoarrayRef(rhs)) { // C1026
164+
}
165+
if (ExtractCoarrayRef(rhs)) { // C1026
157166
Say("A coindexed object may not be a pointer target"_err_en_US);
158167
return false;
159-
} else {
160-
return common::visit([&](const auto &x) { return Check(x); }, rhs.u);
161168
}
169+
if (!common::visit([&](const auto &x) { return Check(x); }, rhs.u)) {
170+
return false;
171+
}
172+
if (IsNullPointer(rhs)) {
173+
return true;
174+
}
175+
if (lhs_ && IsProcedure(*lhs_)) {
176+
return true;
177+
}
178+
if (const auto *pureProc{FindPureProcedureContaining(scope_)}) {
179+
if (pointerComponentLHS_) { // C1594(4) is a hard error
180+
if (const Symbol * object{FindExternallyVisibleObject(rhs, *pureProc)}) {
181+
if (auto *msg{Say(
182+
"Externally visible object '%s' may not be associated with pointer component '%s' in a pure procedure"_err_en_US,
183+
object->name(), pointerComponentLHS_->name())}) {
184+
msg->Attach(object->name(), "Object declaration"_en_US)
185+
.Attach(
186+
pointerComponentLHS_->name(), "Pointer declaration"_en_US);
187+
}
188+
return false;
189+
}
190+
} else if (const Symbol * base{GetFirstSymbol(rhs)}) {
191+
if (const char *why{WhyBaseObjectIsSuspicious(
192+
base->GetUltimate(), scope_)}) { // C1594(3)
193+
evaluate::SayWithDeclaration(context_.messages(), *base,
194+
"A pure subprogram may not use '%s' as the target of pointer assignment because it is %s"_err_en_US,
195+
base->name(), why);
196+
return false;
197+
}
198+
}
199+
}
200+
if (isContiguous_) {
201+
if (auto contiguous{evaluate::IsContiguous(rhs, context_)}) {
202+
if (!*contiguous) {
203+
Say("CONTIGUOUS pointer may not be associated with a discontiguous target"_err_en_US);
204+
return false;
205+
}
206+
} else {
207+
Say("Target of CONTIGUOUS pointer association is not known to be contiguous"_warn_en_US);
208+
}
209+
}
210+
// Warn about undefinable data targets
211+
if (auto because{
212+
WhyNotDefinable(context_.messages().at(), scope_, {}, rhs)}) {
213+
if (auto *msg{
214+
Say("Pointer target is not a definable variable"_warn_en_US)}) {
215+
msg->Attach(std::move(*because));
216+
}
217+
return false;
218+
}
219+
return true;
162220
}
163221

164222
bool PointerAssignmentChecker::Check(const evaluate::NullPointer &) {
@@ -221,7 +279,7 @@ bool PointerAssignmentChecker::Check(const evaluate::Designator<T> &d) {
221279
const Symbol *base{d.GetBaseObject().symbol()};
222280
if (!last || !base) {
223281
// P => "character literal"(1:3)
224-
context_.messages().Say("Pointer target is not a named entity"_err_en_US);
282+
Say("Pointer target is not a named entity"_err_en_US);
225283
return false;
226284
}
227285
std::optional<std::variant<MessageFixedText, MessageFormattedText>> msg;
@@ -440,8 +498,9 @@ bool CheckPointerAssignment(evaluate::FoldingContext &context,
440498

441499
bool CheckStructConstructorPointerComponent(evaluate::FoldingContext &context,
442500
const Symbol &lhs, const SomeExpr &rhs, const Scope &scope) {
443-
CHECK(IsPointer(lhs));
444-
return PointerAssignmentChecker{context, scope, lhs}.Check(rhs);
501+
return PointerAssignmentChecker{context, scope, lhs}
502+
.set_pointerComponentLHS(&lhs)
503+
.Check(rhs);
445504
}
446505

447506
bool CheckPointerAssignment(evaluate::FoldingContext &context,

flang/test/Semantics/assign14.f90

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
! RUN: %python %S/test_errors.py %s %flang_fc1
2+
! Can't associate a pointer with a substring of a character literal
3+
program main
4+
character(:), pointer :: cp
5+
!ERROR: Target associated with pointer 'cp' must be a designator or a call to a pointer-valued function
6+
cp => "abcd"(1:4)
7+
end

flang/test/Semantics/associate01.f90

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ module m1
1313
function iptr(n)
1414
integer, intent(in), target :: n
1515
integer, pointer :: iptr
16+
!WARNING: Pointer target is not a definable variable
17+
!BECAUSE: 'n' is an INTENT(IN) dummy argument
1618
iptr => n
1719
end function
1820
subroutine test

flang/test/Semantics/c_f_pointer.f90

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ program test
3030
!ERROR: FPTR= argument to C_F_POINTER() may not have a deferred type parameter
3131
call c_f_pointer(scalarC, charDeferredF)
3232
!ERROR: FPTR= argument to C_F_POINTER() may not be a coindexed object
33+
!ERROR: A coindexed object may not be a pointer target
3334
call c_f_pointer(scalarC, coindexed[0]%p)
3435
!ERROR: FPTR= argument to C_F_POINTER() must have a type
3536
call c_f_pointer(scalarC, null())

flang/test/Semantics/call05.f90

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ subroutine test
8686
!ERROR: If a POINTER or ALLOCATABLE dummy or actual argument is unlimited polymorphic, both must be so
8787
call sua(pa)
8888
!ERROR: Actual argument type 'CLASS(*)' is not compatible with dummy argument type 'CLASS(t)'
89+
!ERROR: Pointer type must be unlimited polymorphic or non-extensible derived type when target is unlimited polymorphic
8990
call spp(up)
9091
!ERROR: Actual argument type 'CLASS(*)' is not compatible with dummy argument type 'CLASS(t)'
9192
call spa(ua)
@@ -94,6 +95,7 @@ subroutine test
9495
!ERROR: POINTER or ALLOCATABLE dummy and actual arguments must have the same declared type and kind
9596
call spa(pa2)
9697
!ERROR: Rank of dummy argument is 1, but actual argument has rank 2
98+
!ERROR: Pointer has rank 1 but target has rank 2
9799
call smp(mpmat)
98100
!ERROR: Rank of dummy argument is 1, but actual argument has rank 2
99101
call sma(mamat)

flang/test/Semantics/call07.f90

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ subroutine test
2525
real, target :: a03(10)
2626
real :: a04(10) ! not TARGET
2727
call s01(a03) ! ok
28-
!ERROR: Actual argument associated with CONTIGUOUS POINTER dummy argument 'p=' must be simply contiguous
28+
!WARNING: Target of CONTIGUOUS pointer association is not known to be contiguous
2929
call s01(a02)
30-
!ERROR: Actual argument associated with CONTIGUOUS POINTER dummy argument 'p=' must be simply contiguous
30+
!ERROR: CONTIGUOUS pointer may not be associated with a discontiguous target
3131
call s01(a03(::2))
3232
call s02(a02) ! ok
3333
call s03(a03) ! ok

flang/test/Semantics/call33.f90

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ program test
4040
!ERROR: Actual argument variable length '2' does not match the expected length '3'
4141
call s5(shortalloc)
4242
!ERROR: Actual argument variable length '2' does not match the expected length '3'
43+
!ERROR: Target type CHARACTER(KIND=1,LEN=2_8) is not compatible with pointer type CHARACTER(KIND=1,LEN=3_8)
4344
call s6(shortptr)
4445
call s1(long) ! ok
4546
call s2(longarr) ! ok
@@ -50,5 +51,6 @@ program test
5051
!ERROR: Actual argument variable length '4' does not match the expected length '3'
5152
call s5(longalloc)
5253
!ERROR: Actual argument variable length '4' does not match the expected length '3'
54+
!ERROR: Target type CHARACTER(KIND=1,LEN=4_8) is not compatible with pointer type CHARACTER(KIND=1,LEN=3_8)
5355
call s6(longptr)
5456
end

0 commit comments

Comments
 (0)