[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:
parent
7ff9064b26
commit
c11b4456c2
|
@ -343,6 +343,13 @@ end
|
||||||
This Fortran 2008 feature might as well be viewed like an
|
This Fortran 2008 feature might as well be viewed like an
|
||||||
extension; no other compiler that we've tested can handle
|
extension; no other compiler that we've tested can handle
|
||||||
it yet.
|
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
|
## Behavior in cases where the standard is ambiguous or indefinite
|
||||||
|
|
||||||
|
|
|
@ -893,8 +893,13 @@ template <typename A> const Symbol *GetLastSymbol(const A &x) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convenience: If GetLastSymbol() succeeds on the argument, return its
|
// If a function reference constitutes an entire expression, return a pointer
|
||||||
// set of attributes, otherwise the empty set.
|
// 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) {
|
template <typename A> semantics::Attrs GetAttrs(const A &x) {
|
||||||
if (const Symbol * symbol{GetLastSymbol(x)}) {
|
if (const Symbol * symbol{GetLastSymbol(x)}) {
|
||||||
return symbol->attrs();
|
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()
|
// GetBaseObject()
|
||||||
template <typename A> std::optional<BaseObject> GetBaseObject(const A &) {
|
template <typename A> std::optional<BaseObject> GetBaseObject(const A &) {
|
||||||
return std::nullopt;
|
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
|
// Like IsAllocatableOrPointer, but accepts pointer function results as being
|
||||||
// pointers.
|
// pointers too.
|
||||||
bool IsAllocatableOrPointerObject(const Expr<SomeType> &, FoldingContext &);
|
bool IsAllocatableOrPointerObject(const Expr<SomeType> &, FoldingContext &);
|
||||||
|
|
||||||
bool IsAllocatableDesignator(const Expr<SomeType> &);
|
bool IsAllocatableDesignator(const Expr<SomeType> &);
|
||||||
|
@ -946,8 +976,6 @@ bool IsNullProcedurePointer(const Expr<SomeType> &);
|
||||||
bool IsNullPointer(const Expr<SomeType> &);
|
bool IsNullPointer(const Expr<SomeType> &);
|
||||||
bool IsObjectPointer(const Expr<SomeType> &, FoldingContext &);
|
bool IsObjectPointer(const Expr<SomeType> &, FoldingContext &);
|
||||||
|
|
||||||
const ProcedureRef *GetProcedureRef(const Expr<SomeType> &);
|
|
||||||
|
|
||||||
// Can Expr be passed as absent to an optional dummy argument.
|
// Can Expr be passed as absent to an optional dummy argument.
|
||||||
// See 15.5.2.12 point 1 for more details.
|
// See 15.5.2.12 point 1 for more details.
|
||||||
bool MayBePassedAsAbsentOptional(const Expr<SomeType> &, FoldingContext &);
|
bool MayBePassedAsAbsentOptional(const Expr<SomeType> &, FoldingContext &);
|
||||||
|
|
|
@ -861,10 +861,12 @@ bool IsBareNullPointer(const Expr<SomeType> *expr) {
|
||||||
// GetSymbolVector()
|
// GetSymbolVector()
|
||||||
auto GetSymbolVectorHelper::operator()(const Symbol &x) const -> Result {
|
auto GetSymbolVectorHelper::operator()(const Symbol &x) const -> Result {
|
||||||
if (const auto *details{x.detailsIf<semantics::AssocEntityDetails>()}) {
|
if (const auto *details{x.detailsIf<semantics::AssocEntityDetails>()}) {
|
||||||
return (*this)(details->expr());
|
if (IsVariable(details->expr()) && !GetProcedureRef(*details->expr())) {
|
||||||
} else {
|
// associate(x => variable that is not a pointer returned by a function)
|
||||||
return {x.GetUltimate()};
|
return (*this)(details->expr());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return {x.GetUltimate()};
|
||||||
}
|
}
|
||||||
auto GetSymbolVectorHelper::operator()(const Component &x) const -> Result {
|
auto GetSymbolVectorHelper::operator()(const Component &x) const -> Result {
|
||||||
Result result{(*this)(x.base())};
|
Result result{(*this)(x.base())};
|
||||||
|
@ -1475,14 +1477,14 @@ bool IsAssumedShape(const Symbol &symbol) {
|
||||||
const Symbol &ultimate{ResolveAssociations(symbol)};
|
const Symbol &ultimate{ResolveAssociations(symbol)};
|
||||||
const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
|
const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
|
||||||
return object && object->CanBeAssumedShape() &&
|
return object && object->CanBeAssumedShape() &&
|
||||||
!evaluate::IsAllocatableOrPointer(ultimate);
|
!semantics::IsAllocatableOrPointer(ultimate);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsDeferredShape(const Symbol &symbol) {
|
bool IsDeferredShape(const Symbol &symbol) {
|
||||||
const Symbol &ultimate{ResolveAssociations(symbol)};
|
const Symbol &ultimate{ResolveAssociations(symbol)};
|
||||||
const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
|
const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
|
||||||
return object && object->CanBeDeferredShape() &&
|
return object && object->CanBeDeferredShape() &&
|
||||||
evaluate::IsAllocatableOrPointer(ultimate);
|
semantics::IsAllocatableOrPointer(ultimate);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsFunctionResult(const Symbol &original) {
|
bool IsFunctionResult(const Symbol &original) {
|
||||||
|
|
|
@ -447,7 +447,7 @@ walkCaptureCategories(T visitor, Fortran::lower::AbstractConverter &converter,
|
||||||
if (Fortran::semantics::IsProcedure(sym))
|
if (Fortran::semantics::IsProcedure(sym))
|
||||||
return CapturedProcedure::visit(visitor, converter, sym, ba);
|
return CapturedProcedure::visit(visitor, converter, sym, ba);
|
||||||
ba.analyze(sym);
|
ba.analyze(sym);
|
||||||
if (Fortran::evaluate::IsAllocatableOrPointer(sym))
|
if (Fortran::semantics::IsAllocatableOrPointer(sym))
|
||||||
return CapturedAllocatableAndPointer::visit(visitor, converter, sym, ba);
|
return CapturedAllocatableAndPointer::visit(visitor, converter, sym, ba);
|
||||||
if (ba.isArray())
|
if (ba.isArray())
|
||||||
return CapturedArrays::visit(visitor, converter, sym, ba);
|
return CapturedArrays::visit(visitor, converter, sym, ba);
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue