[flang] Adding NOTIFY specifier in image selector and add notify type checks (#148810)

This PR adds support for the NOTIFY specifier in the image selector as
described in the 2023 standard, and add checks for the NOTIFY_TYPE type.
This commit is contained in:
Jean-Didier PAILLEUX 2025-11-05 16:35:25 +01:00 committed by GitHub
parent 338fb02c98
commit 87fb7b002d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 118 additions and 6 deletions

View file

@ -348,6 +348,7 @@ public:
READ_FEATURE(TeamValue)
READ_FEATURE(ImageSelector)
READ_FEATURE(ImageSelectorSpec)
READ_FEATURE(ImageSelectorSpec::Notify)
READ_FEATURE(ImageSelectorSpec::Stat)
READ_FEATURE(ImageSelectorSpec::Team_Number)
READ_FEATURE(ImplicitPart)

View file

@ -146,7 +146,7 @@ public:
return Combine(x.base(), x.subscript());
}
Result operator()(const CoarrayRef &x) const {
return Combine(x.base(), x.cosubscript(), x.stat(), x.team());
return Combine(x.base(), x.cosubscript(), x.notify(), x.stat(), x.team());
}
Result operator()(const DataRef &x) const { return visitor_(x.u); }
Result operator()(const Substring &x) const {

View file

@ -260,6 +260,9 @@ public:
// it's TEAM=.
std::optional<Expr<SomeType>> team() const;
CoarrayRef &set_team(Expr<SomeType> &&);
// When notify() is Expr<Some>, it's NOTIFY=.
std::optional<Expr<SomeType>> notify() const;
CoarrayRef &set_notify(Expr<SomeType> &&);
int Rank() const;
int Corank() const { return 0; }
@ -272,6 +275,7 @@ public:
private:
common::CopyableIndirection<DataRef> base_;
std::vector<Expr<SubscriptInteger>> cosubscript_;
std::optional<common::CopyableIndirection<Expr<SomeType>>> notify_;
std::optional<common::CopyableIndirection<Expr<SomeInteger>>> stat_;
std::optional<common::CopyableIndirection<Expr<SomeType>>> team_;
};

View file

@ -387,6 +387,7 @@ public:
NODE(parser, TeamValue)
NODE(parser, ImageSelector)
NODE(parser, ImageSelectorSpec)
NODE(ImageSelectorSpec, Notify)
NODE(ImageSelectorSpec, Stat)
NODE(ImageSelectorSpec, Team_Number)
NODE(parser, ImplicitPart)

View file

@ -1684,13 +1684,15 @@ using Cosubscript = ScalarIntExpr;
WRAPPER_CLASS(TeamValue, Scalar<common::Indirection<Expr>>);
// R926 image-selector-spec ->
// NOTIFY = notify-variable |
// STAT = stat-variable | TEAM = team-value |
// TEAM_NUMBER = scalar-int-expr
struct ImageSelectorSpec {
WRAPPER_CLASS(Stat, Scalar<Integer<common::Indirection<Variable>>>);
WRAPPER_CLASS(Team_Number, ScalarIntExpr);
WRAPPER_CLASS(Notify, Scalar<common::Indirection<Variable>>);
UNION_CLASS_BOILERPLATE(ImageSelectorSpec);
std::variant<Stat, TeamValue, Team_Number> u;
std::variant<Notify, Stat, TeamValue, Team_Number> u;
};
// R924 image-selector ->

View file

@ -107,6 +107,7 @@ bool IsBindCProcedure(const Scope &);
// Returns a pointer to the function's symbol when true, else null
const Symbol *IsFunctionResultWithSameNameAsFunction(const Symbol &);
bool IsOrContainsEventOrLockComponent(const Symbol &);
bool IsOrContainsNotifyComponent(const Symbol &);
bool CanBeTypeBoundProc(const Symbol &);
// Does a non-PARAMETER symbol have explicit initialization with =value or
// =>target in its declaration (but not in a DATA statement)? (Being
@ -652,6 +653,8 @@ using PotentialAndPointerComponentIterator =
// dereferenced.
PotentialComponentIterator::const_iterator FindEventOrLockPotentialComponent(
const DerivedTypeSpec &, bool ignoreCoarrays = false);
PotentialComponentIterator::const_iterator FindNotifyPotentialComponent(
const DerivedTypeSpec &, bool ignoreCoarrays = false);
PotentialComponentIterator::const_iterator FindCoarrayPotentialComponent(
const DerivedTypeSpec &);
PotentialAndPointerComponentIterator::const_iterator

View file

@ -89,6 +89,14 @@ std::optional<Expr<SomeType>> CoarrayRef::team() const {
}
}
std::optional<Expr<SomeType>> CoarrayRef::notify() const {
if (notify_) {
return notify_.value().value();
} else {
return std::nullopt;
}
}
CoarrayRef &CoarrayRef::set_stat(Expr<SomeInteger> &&v) {
CHECK(IsVariable(v));
stat_.emplace(std::move(v));
@ -100,6 +108,11 @@ CoarrayRef &CoarrayRef::set_team(Expr<SomeType> &&v) {
return *this;
}
CoarrayRef &CoarrayRef::set_notify(Expr<SomeType> &&v) {
notify_.emplace(std::move(v));
return *this;
}
const Symbol &CoarrayRef::GetFirstSymbol() const {
return base().GetFirstSymbol();
}

View file

@ -82,7 +82,7 @@ public:
x.cosubscript())
cosubs -= getHashValue(v);
return getHashValue(x.base()) * 97u - cosubs + getHashValue(x.stat()) +
257u + getHashValue(x.team());
257u + getHashValue(x.team()) + getHashValue(x.notify());
}
static unsigned getHashValue(const Fortran::evaluate::NamedEntity &x) {
if (x.IsSymbol())
@ -341,7 +341,8 @@ public:
const Fortran::evaluate::CoarrayRef &y) {
return isEqual(x.base(), y.base()) &&
isEqual(x.cosubscript(), y.cosubscript()) &&
isEqual(x.stat(), y.stat()) && isEqual(x.team(), y.team());
isEqual(x.stat(), y.stat()) && isEqual(x.team(), y.team()) &&
isEqual(x.notify(), y.notify());
}
static bool isEqual(const Fortran::evaluate::NamedEntity &x,
const Fortran::evaluate::NamedEntity &y) {

View file

@ -1212,12 +1212,15 @@ TYPE_CONTEXT_PARSER("image selector"_en_US,
// R926 image-selector-spec ->
// STAT = stat-variable | TEAM = team-value |
// TEAM_NUMBER = scalar-int-expr
// TEAM_NUMBER = scalar-int-expr |
// NOTIFY = notify-variable
TYPE_PARSER(construct<ImageSelectorSpec>(construct<ImageSelectorSpec::Stat>(
"STAT =" >> scalar(integer(indirect(variable))))) ||
construct<ImageSelectorSpec>(construct<TeamValue>("TEAM =" >> teamValue)) ||
construct<ImageSelectorSpec>(construct<ImageSelectorSpec::Team_Number>(
"TEAM_NUMBER =" >> scalarIntExpr)))
"TEAM_NUMBER =" >> scalarIntExpr)) ||
construct<ImageSelectorSpec>(construct<ImageSelectorSpec::Notify>(
"NOTIFY =" >> scalar(indirect(variable)))))
// R927 allocate-stmt ->
// ALLOCATE ( [type-spec ::] allocation-list [, alloc-opt-list] )

View file

@ -819,6 +819,7 @@ public:
Word("TEAM=");
}
}
void Before(const ImageSelectorSpec::Notify &) { Word("NOTIFY="); }
void Unparse(const AllocateStmt &x) { // R927
Word("ALLOCATE(");
Walk(std::get<std::optional<TypeSpec>>(x.t), "::");

View file

@ -855,6 +855,15 @@ void CheckHelper::CheckObjectEntity(
messages_.Say(
"Variable '%s' with EVENT_TYPE or LOCK_TYPE potential component '%s' must be a coarray"_err_en_US,
symbol.name(), component.BuildResultDesignatorName());
} else if (IsNotifyType(derived)) { // C1612
messages_.Say(
"Variable '%s' with NOTIFY_TYPE must be a coarray"_err_en_US,
symbol.name());
} else if (auto component{FindNotifyPotentialComponent( // C1611
*derived, /*ignoreCoarrays=*/true)}) {
messages_.Say(
"Variable '%s' with NOTIFY_TYPE potential component '%s' must be a coarray"_err_en_US,
symbol.name(), component.BuildResultDesignatorName());
}
}
}
@ -873,6 +882,10 @@ void CheckHelper::CheckObjectEntity(
messages_.Say(
"An INTENT(OUT) dummy argument may not be, or contain, EVENT_TYPE or LOCK_TYPE"_err_en_US);
}
if (IsOrContainsNotifyComponent(symbol)) { // C1613
messages_.Say(
"An INTENT(OUT) dummy argument may not be, or contain, NOTIFY_TYPE"_err_en_US);
}
if (IsAssumedSizeArray(symbol)) { // C834
if (type && type->IsPolymorphic()) {
messages_.Say(

View file

@ -23,6 +23,7 @@ void DumpEvaluateExpr::Show(const evaluate::CoarrayRef &x) {
Indent("coarray ref");
Show(x.base());
Show(x.cosubscript());
Show(x.notify());
Show(x.stat());
Show(x.team());
Outdent();

View file

@ -1579,6 +1579,19 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::CoindexedNamedObject &x) {
std::get<std::list<parser::ImageSelectorSpec>>(x.imageSelector.t)) {
common::visit(
common::visitors{
[&](const parser::ImageSelectorSpec::Notify &x) {
Analyze(x.v);
if (const auto *expr{GetExpr(context_, x.v)}) {
if (coarrayRef.notify()) {
Say("coindexed reference has multiple NOTIFY= specifiers"_err_en_US);
} else if (auto dyType{expr->GetType()};
dyType && IsNotifyType(GetDerivedTypeSpec(*dyType))) {
coarrayRef.set_notify(Expr<SomeType>{*expr});
} else {
Say("NOTIFY= specifier must have type NOTIFY_TYPE from ISO_FORTRAN_ENV"_err_en_US);
}
}
},
[&](const parser::ImageSelectorSpec::Stat &x) {
Analyze(x.v);
if (const auto *expr{GetExpr(context_, x.v)}) {

View file

@ -582,6 +582,18 @@ bool IsOrContainsEventOrLockComponent(const Symbol &original) {
return false;
}
bool IsOrContainsNotifyComponent(const Symbol &original) {
const Symbol &symbol{ResolveAssociations(original, /*stopAtTypeGuard=*/true)};
if (evaluate::IsVariable(symbol)) {
if (const DeclTypeSpec *type{symbol.GetType()}) {
if (const DerivedTypeSpec *derived{type->AsDerived()}) {
return IsNotifyType(derived) || FindNotifyPotentialComponent(*derived);
}
}
}
return false;
}
// Check this symbol suitable as a type-bound procedure - C769
bool CanBeTypeBoundProc(const Symbol &symbol) {
if (IsDummy(symbol) || IsProcedurePointer(symbol)) {
@ -1489,6 +1501,32 @@ PotentialComponentIterator::const_iterator FindEventOrLockPotentialComponent(
return iter;
}
PotentialComponentIterator::const_iterator FindNotifyPotentialComponent(
const DerivedTypeSpec &derived, bool ignoreCoarrays) {
PotentialComponentIterator potentials{derived};
auto iter{potentials.begin()};
for (auto end{potentials.end()}; iter != end; ++iter) {
const Symbol &component{*iter};
if (const auto *object{component.detailsIf<ObjectEntityDetails>()}) {
if (const DeclTypeSpec *type{object->type()}) {
if (IsNotifyType(type->AsDerived())) {
if (!ignoreCoarrays) {
break; // found one
}
auto path{iter.GetComponentPath()};
path.pop_back();
if (std::find_if(path.begin(), path.end(), [](const Symbol &sym) {
return evaluate::IsCoarray(sym);
}) == path.end()) {
break; // found one not in a coarray
}
}
}
}
}
return iter;
}
UltimateComponentIterator::const_iterator FindAllocatableUltimateComponent(
const DerivedTypeSpec &derived) {
UltimateComponentIterator ultimates{derived};

View file

@ -16,6 +16,8 @@ program main
type(event_type) event
!ERROR: Variable 'lock' with EVENT_TYPE or LOCK_TYPE must be a coarray
type(lock_type) lock
!ERROR: Variable 'notify' with NOTIFY_TYPE must be a coarray
type(notify_type) notify
integer :: local[*] ! ok in main
end
@ -120,3 +122,18 @@ subroutine s4
!ERROR: Subscripts must appear in a coindexed reference when its base is an array
print *, ta(1)%a[1]
end
subroutine s5(a, notify, res)
use iso_fortran_env
type t
type(notify_type) :: a
end type
real, intent(in) :: a[*]
type(event_type), intent(in) :: notify[*]
!ERROR: An INTENT(OUT) dummy argument may not be, or contain, NOTIFY_TYPE
type(notify_type), intent(out) :: res[*]
!ERROR: Variable 'bad' with NOTIFY_TYPE potential component '%a' must be a coarray
type(t) :: bad
!ERROR: NOTIFY= specifier must have type NOTIFY_TYPE from ISO_FORTRAN_ENV
print *, a[1, NOTIFY=notify]
end

View file

@ -10,6 +10,7 @@ program test_notify_wait
implicit none
! notify_type variables must be coarrays
!ERROR: Variable 'non_coarray' with NOTIFY_TYPE must be a coarray
type(notify_type) :: non_coarray
type(notify_type) :: notify_var[*], notify_array(2)[*]