[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
This commit is contained in:
Peter Klausler 2022-10-04 11:10:59 -07:00
parent 7ff9064b26
commit c11b4456c2
5 changed files with 99 additions and 17 deletions

View File

@ -343,6 +343,13 @@ end
This Fortran 2008 feature might as well be viewed like an
extension; no other compiler that we've tested can handle
it yet.
* According to 11.1.3.3p1, if a selector of an `ASSOCIATE` or
related construct is defined by a variable, it has the `TARGET`
attribute if the variable was a `POINTER` or `TARGET`.
We read this to include the case of the variable being a
pointer-valued function reference.
No other Fortran compiler seems to handle this correctly for
`ASSOCIATE`, though NAG gets it right for `SELECT TYPE`.
## Behavior in cases where the standard is ambiguous or indefinite

View File

@ -893,8 +893,13 @@ template <typename A> const Symbol *GetLastSymbol(const A &x) {
}
}
// Convenience: If GetLastSymbol() succeeds on the argument, return its
// set of attributes, otherwise the empty set.
// If a function reference constitutes an entire expression, return a pointer
// to its PrcedureRef.
const ProcedureRef *GetProcedureRef(const Expr<SomeType> &);
// For everyday variables: if GetLastSymbol() succeeds on the argument, return
// its set of attributes, otherwise the empty set. Also works on variables that
// are pointer results of functions.
template <typename A> semantics::Attrs GetAttrs(const A &x) {
if (const Symbol * symbol{GetLastSymbol(x)}) {
return symbol->attrs();
@ -903,6 +908,37 @@ template <typename A> semantics::Attrs GetAttrs(const A &x) {
}
}
template <>
inline semantics::Attrs GetAttrs<Expr<SomeType>>(const Expr<SomeType> &x) {
if (IsVariable(x)) {
if (const auto *procRef{GetProcedureRef(x)}) {
if (const Symbol * interface{procRef->proc().GetInterfaceSymbol()}) {
if (const auto *details{
interface->detailsIf<semantics::SubprogramDetails>()}) {
if (details->isFunction() &&
details->result().attrs().test(semantics::Attr::POINTER)) {
// N.B.: POINTER becomes TARGET in SetAttrsFromAssociation()
return details->result().attrs();
}
}
}
}
}
if (const Symbol * symbol{GetLastSymbol(x)}) {
return symbol->attrs();
} else {
return {};
}
}
template <typename A> semantics::Attrs GetAttrs(const std::optional<A> &x) {
if (x) {
return GetAttrs(*x);
} else {
return {};
}
}
// GetBaseObject()
template <typename A> std::optional<BaseObject> GetBaseObject(const A &) {
return std::nullopt;
@ -924,14 +960,8 @@ std::optional<BaseObject> GetBaseObject(const std::optional<A> &x) {
}
}
// Predicate: IsAllocatableOrPointer()
template <typename A> bool IsAllocatableOrPointer(const A &x) {
return GetAttrs(x).HasAny(
semantics::Attrs{semantics::Attr::POINTER, semantics::Attr::ALLOCATABLE});
}
// Like IsAllocatableOrPointer, but accepts pointer function results as being
// pointers.
// pointers too.
bool IsAllocatableOrPointerObject(const Expr<SomeType> &, FoldingContext &);
bool IsAllocatableDesignator(const Expr<SomeType> &);
@ -946,8 +976,6 @@ bool IsNullProcedurePointer(const Expr<SomeType> &);
bool IsNullPointer(const Expr<SomeType> &);
bool IsObjectPointer(const Expr<SomeType> &, FoldingContext &);
const ProcedureRef *GetProcedureRef(const Expr<SomeType> &);
// Can Expr be passed as absent to an optional dummy argument.
// See 15.5.2.12 point 1 for more details.
bool MayBePassedAsAbsentOptional(const Expr<SomeType> &, FoldingContext &);

View File

@ -861,10 +861,12 @@ bool IsBareNullPointer(const Expr<SomeType> *expr) {
// GetSymbolVector()
auto GetSymbolVectorHelper::operator()(const Symbol &x) const -> Result {
if (const auto *details{x.detailsIf<semantics::AssocEntityDetails>()}) {
return (*this)(details->expr());
} else {
return {x.GetUltimate()};
if (IsVariable(details->expr()) && !GetProcedureRef(*details->expr())) {
// associate(x => variable that is not a pointer returned by a function)
return (*this)(details->expr());
}
}
return {x.GetUltimate()};
}
auto GetSymbolVectorHelper::operator()(const Component &x) const -> Result {
Result result{(*this)(x.base())};
@ -1475,14 +1477,14 @@ bool IsAssumedShape(const Symbol &symbol) {
const Symbol &ultimate{ResolveAssociations(symbol)};
const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
return object && object->CanBeAssumedShape() &&
!evaluate::IsAllocatableOrPointer(ultimate);
!semantics::IsAllocatableOrPointer(ultimate);
}
bool IsDeferredShape(const Symbol &symbol) {
const Symbol &ultimate{ResolveAssociations(symbol)};
const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
return object && object->CanBeDeferredShape() &&
evaluate::IsAllocatableOrPointer(ultimate);
semantics::IsAllocatableOrPointer(ultimate);
}
bool IsFunctionResult(const Symbol &original) {

View File

@ -447,7 +447,7 @@ walkCaptureCategories(T visitor, Fortran::lower::AbstractConverter &converter,
if (Fortran::semantics::IsProcedure(sym))
return CapturedProcedure::visit(visitor, converter, sym, ba);
ba.analyze(sym);
if (Fortran::evaluate::IsAllocatableOrPointer(sym))
if (Fortran::semantics::IsAllocatableOrPointer(sym))
return CapturedAllocatableAndPointer::visit(visitor, converter, sym, ba);
if (ba.isArray())
return CapturedArrays::visit(visitor, converter, sym, ba);

View File

@ -0,0 +1,45 @@
! RUN: %python %S/test_errors.py %s %flang_fc1
! Tests of selectors whose defining expressions are pointer-valued functions;
! they must be valid targets, but not pointers.
! (F'2018 11.1.3.3 p1) "The associating entity does not have the ALLOCATABLE or
! POINTER attributes; it has the TARGET attribute if and only if the selector
! is a variable and has either the TARGET or POINTER attribute."
module m1
type t
contains
procedure, nopass :: iptr
end type
contains
function iptr(n)
integer, intent(in), target :: n
integer, pointer :: iptr
iptr => n
end function
subroutine test
type(t) tv
integer, target :: itarget
integer, pointer :: ip
associate (sel => iptr(itarget))
ip => sel
!ERROR: POINTER= argument of ASSOCIATED() must be a POINTER
if (.not. associated(sel)) stop
end associate
associate (sel => tv%iptr(itarget))
ip => sel
!ERROR: POINTER= argument of ASSOCIATED() must be a POINTER
if (.not. associated(sel)) stop
end associate
associate (sel => (iptr(itarget)))
!ERROR: In assignment to object pointer 'ip', the target 'sel' is not an object with POINTER or TARGET attributes
ip => sel
!ERROR: POINTER= argument of ASSOCIATED() must be a POINTER
if (.not. associated(sel)) stop
end associate
associate (sel => 0 + iptr(itarget))
!ERROR: In assignment to object pointer 'ip', the target 'sel' is not an object with POINTER or TARGET attributes
ip => sel
!ERROR: POINTER= argument of ASSOCIATED() must be a POINTER
if (.not. associated(sel)) stop
end associate
end subroutine
end module