Skip to content

Commit c11b445

Browse files
committed
[flang] Selectors whose expressions are pointers returned from functions are valid targets
An ASSOCIATE or SELECT TYPE statement's selector whose "right-hand side" is the result of a reference to a function that returns a pointer must be usable as a valid target (but not as a pointer). Differential Revision: https://reviews.llvm.org/D135211
1 parent 7ff9064 commit c11b445

File tree

5 files changed

+99
-17
lines changed

5 files changed

+99
-17
lines changed

flang/docs/Extensions.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,13 @@ end
343343
This Fortran 2008 feature might as well be viewed like an
344344
extension; no other compiler that we've tested can handle
345345
it yet.
346+
* According to 11.1.3.3p1, if a selector of an `ASSOCIATE` or
347+
related construct is defined by a variable, it has the `TARGET`
348+
attribute if the variable was a `POINTER` or `TARGET`.
349+
We read this to include the case of the variable being a
350+
pointer-valued function reference.
351+
No other Fortran compiler seems to handle this correctly for
352+
`ASSOCIATE`, though NAG gets it right for `SELECT TYPE`.
346353

347354
## Behavior in cases where the standard is ambiguous or indefinite
348355

flang/include/flang/Evaluate/tools.h

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -893,8 +893,13 @@ template <typename A> const Symbol *GetLastSymbol(const A &x) {
893893
}
894894
}
895895

896-
// Convenience: If GetLastSymbol() succeeds on the argument, return its
897-
// set of attributes, otherwise the empty set.
896+
// If a function reference constitutes an entire expression, return a pointer
897+
// to its PrcedureRef.
898+
const ProcedureRef *GetProcedureRef(const Expr<SomeType> &);
899+
900+
// For everyday variables: if GetLastSymbol() succeeds on the argument, return
901+
// its set of attributes, otherwise the empty set. Also works on variables that
902+
// are pointer results of functions.
898903
template <typename A> semantics::Attrs GetAttrs(const A &x) {
899904
if (const Symbol * symbol{GetLastSymbol(x)}) {
900905
return symbol->attrs();
@@ -903,6 +908,37 @@ template <typename A> semantics::Attrs GetAttrs(const A &x) {
903908
}
904909
}
905910

911+
template <>
912+
inline semantics::Attrs GetAttrs<Expr<SomeType>>(const Expr<SomeType> &x) {
913+
if (IsVariable(x)) {
914+
if (const auto *procRef{GetProcedureRef(x)}) {
915+
if (const Symbol * interface{procRef->proc().GetInterfaceSymbol()}) {
916+
if (const auto *details{
917+
interface->detailsIf<semantics::SubprogramDetails>()}) {
918+
if (details->isFunction() &&
919+
details->result().attrs().test(semantics::Attr::POINTER)) {
920+
// N.B.: POINTER becomes TARGET in SetAttrsFromAssociation()
921+
return details->result().attrs();
922+
}
923+
}
924+
}
925+
}
926+
}
927+
if (const Symbol * symbol{GetLastSymbol(x)}) {
928+
return symbol->attrs();
929+
} else {
930+
return {};
931+
}
932+
}
933+
934+
template <typename A> semantics::Attrs GetAttrs(const std::optional<A> &x) {
935+
if (x) {
936+
return GetAttrs(*x);
937+
} else {
938+
return {};
939+
}
940+
}
941+
906942
// GetBaseObject()
907943
template <typename A> std::optional<BaseObject> GetBaseObject(const A &) {
908944
return std::nullopt;
@@ -924,14 +960,8 @@ std::optional<BaseObject> GetBaseObject(const std::optional<A> &x) {
924960
}
925961
}
926962

927-
// Predicate: IsAllocatableOrPointer()
928-
template <typename A> bool IsAllocatableOrPointer(const A &x) {
929-
return GetAttrs(x).HasAny(
930-
semantics::Attrs{semantics::Attr::POINTER, semantics::Attr::ALLOCATABLE});
931-
}
932-
933963
// Like IsAllocatableOrPointer, but accepts pointer function results as being
934-
// pointers.
964+
// pointers too.
935965
bool IsAllocatableOrPointerObject(const Expr<SomeType> &, FoldingContext &);
936966

937967
bool IsAllocatableDesignator(const Expr<SomeType> &);
@@ -946,8 +976,6 @@ bool IsNullProcedurePointer(const Expr<SomeType> &);
946976
bool IsNullPointer(const Expr<SomeType> &);
947977
bool IsObjectPointer(const Expr<SomeType> &, FoldingContext &);
948978

949-
const ProcedureRef *GetProcedureRef(const Expr<SomeType> &);
950-
951979
// Can Expr be passed as absent to an optional dummy argument.
952980
// See 15.5.2.12 point 1 for more details.
953981
bool MayBePassedAsAbsentOptional(const Expr<SomeType> &, FoldingContext &);

flang/lib/Evaluate/tools.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -861,10 +861,12 @@ bool IsBareNullPointer(const Expr<SomeType> *expr) {
861861
// GetSymbolVector()
862862
auto GetSymbolVectorHelper::operator()(const Symbol &x) const -> Result {
863863
if (const auto *details{x.detailsIf<semantics::AssocEntityDetails>()}) {
864-
return (*this)(details->expr());
865-
} else {
866-
return {x.GetUltimate()};
864+
if (IsVariable(details->expr()) && !GetProcedureRef(*details->expr())) {
865+
// associate(x => variable that is not a pointer returned by a function)
866+
return (*this)(details->expr());
867+
}
867868
}
869+
return {x.GetUltimate()};
868870
}
869871
auto GetSymbolVectorHelper::operator()(const Component &x) const -> Result {
870872
Result result{(*this)(x.base())};
@@ -1475,14 +1477,14 @@ bool IsAssumedShape(const Symbol &symbol) {
14751477
const Symbol &ultimate{ResolveAssociations(symbol)};
14761478
const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
14771479
return object && object->CanBeAssumedShape() &&
1478-
!evaluate::IsAllocatableOrPointer(ultimate);
1480+
!semantics::IsAllocatableOrPointer(ultimate);
14791481
}
14801482

14811483
bool IsDeferredShape(const Symbol &symbol) {
14821484
const Symbol &ultimate{ResolveAssociations(symbol)};
14831485
const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
14841486
return object && object->CanBeDeferredShape() &&
1485-
evaluate::IsAllocatableOrPointer(ultimate);
1487+
semantics::IsAllocatableOrPointer(ultimate);
14861488
}
14871489

14881490
bool IsFunctionResult(const Symbol &original) {

flang/lib/Lower/HostAssociations.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ walkCaptureCategories(T visitor, Fortran::lower::AbstractConverter &converter,
447447
if (Fortran::semantics::IsProcedure(sym))
448448
return CapturedProcedure::visit(visitor, converter, sym, ba);
449449
ba.analyze(sym);
450-
if (Fortran::evaluate::IsAllocatableOrPointer(sym))
450+
if (Fortran::semantics::IsAllocatableOrPointer(sym))
451451
return CapturedAllocatableAndPointer::visit(visitor, converter, sym, ba);
452452
if (ba.isArray())
453453
return CapturedArrays::visit(visitor, converter, sym, ba);

flang/test/Semantics/associate01.f90

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
! RUN: %python %S/test_errors.py %s %flang_fc1
2+
! Tests of selectors whose defining expressions are pointer-valued functions;
3+
! they must be valid targets, but not pointers.
4+
! (F'2018 11.1.3.3 p1) "The associating entity does not have the ALLOCATABLE or
5+
! POINTER attributes; it has the TARGET attribute if and only if the selector
6+
! is a variable and has either the TARGET or POINTER attribute."
7+
module m1
8+
type t
9+
contains
10+
procedure, nopass :: iptr
11+
end type
12+
contains
13+
function iptr(n)
14+
integer, intent(in), target :: n
15+
integer, pointer :: iptr
16+
iptr => n
17+
end function
18+
subroutine test
19+
type(t) tv
20+
integer, target :: itarget
21+
integer, pointer :: ip
22+
associate (sel => iptr(itarget))
23+
ip => sel
24+
!ERROR: POINTER= argument of ASSOCIATED() must be a POINTER
25+
if (.not. associated(sel)) stop
26+
end associate
27+
associate (sel => tv%iptr(itarget))
28+
ip => sel
29+
!ERROR: POINTER= argument of ASSOCIATED() must be a POINTER
30+
if (.not. associated(sel)) stop
31+
end associate
32+
associate (sel => (iptr(itarget)))
33+
!ERROR: In assignment to object pointer 'ip', the target 'sel' is not an object with POINTER or TARGET attributes
34+
ip => sel
35+
!ERROR: POINTER= argument of ASSOCIATED() must be a POINTER
36+
if (.not. associated(sel)) stop
37+
end associate
38+
associate (sel => 0 + iptr(itarget))
39+
!ERROR: In assignment to object pointer 'ip', the target 'sel' is not an object with POINTER or TARGET attributes
40+
ip => sel
41+
!ERROR: POINTER= argument of ASSOCIATED() must be a POINTER
42+
if (.not. associated(sel)) stop
43+
end associate
44+
end subroutine
45+
end module

0 commit comments

Comments
 (0)