Skip to content

Commit c4ba110

Browse files
committed
[flang] Extension to distinguish specific procedures
Allocatable dummy arguments can be used to distinguish two specific procedures in a generic interface when it is the case that exactly one of them is polymorphic or exactly one of them is unlimited polymorphic. The standard requires that an actual argument corresponding to an (unlimited) polymorphic allocatable dummy argument must also be an (unlimited) polymorphic allocatable, so an actual argument that's acceptable to one procedure must necessarily be a bad match for the other. Differential Revision: https://reviews.llvm.org/D112237
1 parent 7d962f9 commit c4ba110

File tree

6 files changed

+108
-48
lines changed

6 files changed

+108
-48
lines changed

flang/docs/Extensions.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,13 @@ end
179179
we also treat scalars as being trivially contiguous, so that they
180180
can be used in contexts like data targets in pointer assignments
181181
with bounds remapping.
182+
* We support some combinations of specific procedures in generic
183+
interfaces that a strict reading of the standard would preclude
184+
when their calls must nonetheless be distinguishable.
185+
Specifically, `ALLOCATABLE` dummy arguments are distinguishing
186+
if an actual argument acceptable to one could not be passed to
187+
the other & vice versa because exactly one is polymorphic or
188+
exactly one is unlimited polymorphic).
182189

183190
### Extensions supported when enabled by options
184191

flang/include/flang/Common/Fortran-features.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
3030
EquivalenceNumericWithCharacter, AdditionalIntrinsics, AnonymousParents,
3131
OldLabelDoEndStatements, LogicalIntegerAssignment, EmptySourceFile,
3232
ProgramReturn, ImplicitNoneTypeNever, ImplicitNoneTypeAlways,
33-
ForwardRefDummyImplicitNone, OpenAccessAppend, BOZAsDefaultInteger)
33+
ForwardRefDummyImplicitNone, OpenAccessAppend, BOZAsDefaultInteger,
34+
DistinguishableSpecifics)
3435

3536
using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
3637

flang/include/flang/Evaluate/characteristics.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "expression.h"
1818
#include "shape.h"
1919
#include "type.h"
20+
#include "flang/Common/Fortran-features.h"
2021
#include "flang/Common/Fortran.h"
2122
#include "flang/Common/enum-set.h"
2223
#include "flang/Common/idioms.h"
@@ -43,9 +44,11 @@ namespace Fortran::evaluate::characteristics {
4344
using common::CopyableIndirection;
4445

4546
// Are these procedures distinguishable for a generic name or FINAL?
46-
bool Distinguishable(const Procedure &, const Procedure &);
47+
bool Distinguishable(const common::LanguageFeatureControl &, const Procedure &,
48+
const Procedure &);
4749
// Are these procedures distinguishable for a generic operator or assignment?
48-
bool DistinguishableOpOrAssign(const Procedure &, const Procedure &);
50+
bool DistinguishableOpOrAssign(const common::LanguageFeatureControl &,
51+
const Procedure &, const Procedure &);
4952

5053
// Shapes of function results and dummy arguments have to have
5154
// the same rank, the same deferred dimensions, and the same

flang/lib/Evaluate/characteristics.cpp

Lines changed: 64 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -862,10 +862,13 @@ llvm::raw_ostream &Procedure::Dump(llvm::raw_ostream &o) const {
862862
// Utility class to determine if Procedures, etc. are distinguishable
863863
class DistinguishUtils {
864864
public:
865+
explicit DistinguishUtils(const common::LanguageFeatureControl &features)
866+
: features_{features} {}
867+
865868
// Are these procedures distinguishable for a generic name?
866-
static bool Distinguishable(const Procedure &, const Procedure &);
869+
bool Distinguishable(const Procedure &, const Procedure &) const;
867870
// Are these procedures distinguishable for a generic operator or assignment?
868-
static bool DistinguishableOpOrAssign(const Procedure &, const Procedure &);
871+
bool DistinguishableOpOrAssign(const Procedure &, const Procedure &) const;
869872

870873
private:
871874
struct CountDummyProcedures {
@@ -881,31 +884,33 @@ class DistinguishUtils {
881884
int notOptional{0};
882885
};
883886

884-
static bool Rule3Distinguishable(const Procedure &, const Procedure &);
885-
static const DummyArgument *Rule1DistinguishingArg(
886-
const DummyArguments &, const DummyArguments &);
887-
static int FindFirstToDistinguishByPosition(
888-
const DummyArguments &, const DummyArguments &);
889-
static int FindLastToDistinguishByName(
890-
const DummyArguments &, const DummyArguments &);
891-
static int CountCompatibleWith(const DummyArgument &, const DummyArguments &);
892-
static int CountNotDistinguishableFrom(
893-
const DummyArgument &, const DummyArguments &);
894-
static bool Distinguishable(const DummyArgument &, const DummyArgument &);
895-
static bool Distinguishable(const DummyDataObject &, const DummyDataObject &);
896-
static bool Distinguishable(const DummyProcedure &, const DummyProcedure &);
897-
static bool Distinguishable(const FunctionResult &, const FunctionResult &);
898-
static bool Distinguishable(const TypeAndShape &, const TypeAndShape &);
899-
static bool IsTkrCompatible(const DummyArgument &, const DummyArgument &);
900-
static bool IsTkrCompatible(const TypeAndShape &, const TypeAndShape &);
901-
static const DummyArgument *GetAtEffectivePosition(
902-
const DummyArguments &, int);
903-
static const DummyArgument *GetPassArg(const Procedure &);
887+
bool Rule3Distinguishable(const Procedure &, const Procedure &) const;
888+
const DummyArgument *Rule1DistinguishingArg(
889+
const DummyArguments &, const DummyArguments &) const;
890+
int FindFirstToDistinguishByPosition(
891+
const DummyArguments &, const DummyArguments &) const;
892+
int FindLastToDistinguishByName(
893+
const DummyArguments &, const DummyArguments &) const;
894+
int CountCompatibleWith(const DummyArgument &, const DummyArguments &) const;
895+
int CountNotDistinguishableFrom(
896+
const DummyArgument &, const DummyArguments &) const;
897+
bool Distinguishable(const DummyArgument &, const DummyArgument &) const;
898+
bool Distinguishable(const DummyDataObject &, const DummyDataObject &) const;
899+
bool Distinguishable(const DummyProcedure &, const DummyProcedure &) const;
900+
bool Distinguishable(const FunctionResult &, const FunctionResult &) const;
901+
bool Distinguishable(const TypeAndShape &, const TypeAndShape &) const;
902+
bool IsTkrCompatible(const DummyArgument &, const DummyArgument &) const;
903+
bool IsTkrCompatible(const TypeAndShape &, const TypeAndShape &) const;
904+
const DummyArgument *GetAtEffectivePosition(
905+
const DummyArguments &, int) const;
906+
const DummyArgument *GetPassArg(const Procedure &) const;
907+
908+
const common::LanguageFeatureControl &features_;
904909
};
905910

906911
// Simpler distinguishability rules for operators and assignment
907912
bool DistinguishUtils::DistinguishableOpOrAssign(
908-
const Procedure &proc1, const Procedure &proc2) {
913+
const Procedure &proc1, const Procedure &proc2) const {
909914
auto &args1{proc1.dummyArguments};
910915
auto &args2{proc2.dummyArguments};
911916
if (args1.size() != args2.size()) {
@@ -920,7 +925,7 @@ bool DistinguishUtils::DistinguishableOpOrAssign(
920925
}
921926

922927
bool DistinguishUtils::Distinguishable(
923-
const Procedure &proc1, const Procedure &proc2) {
928+
const Procedure &proc1, const Procedure &proc2) const {
924929
auto &args1{proc1.dummyArguments};
925930
auto &args2{proc2.dummyArguments};
926931
auto count1{CountDummyProcedures(args1)};
@@ -950,7 +955,7 @@ bool DistinguishUtils::Distinguishable(
950955
// C1514 rule 3: Procedures are distinguishable if both have a passed-object
951956
// dummy argument and those are distinguishable.
952957
bool DistinguishUtils::Rule3Distinguishable(
953-
const Procedure &proc1, const Procedure &proc2) {
958+
const Procedure &proc1, const Procedure &proc2) const {
954959
const DummyArgument *pass1{GetPassArg(proc1)};
955960
const DummyArgument *pass2{GetPassArg(proc2)};
956961
return pass1 && pass2 && Distinguishable(*pass1, *pass2);
@@ -964,7 +969,7 @@ bool DistinguishUtils::Rule3Distinguishable(
964969
// that are not distinguishable from x
965970
// - m is greater than n
966971
const DummyArgument *DistinguishUtils::Rule1DistinguishingArg(
967-
const DummyArguments &args1, const DummyArguments &args2) {
972+
const DummyArguments &args1, const DummyArguments &args2) const {
968973
auto size1{args1.size()};
969974
auto size2{args2.size()};
970975
for (std::size_t i{0}; i < size1 + size2; ++i) {
@@ -986,7 +991,7 @@ const DummyArgument *DistinguishUtils::Rule1DistinguishingArg(
986991
// - args2 has no dummy argument at that effective position
987992
// - the dummy argument at that position is distinguishable from it
988993
int DistinguishUtils::FindFirstToDistinguishByPosition(
989-
const DummyArguments &args1, const DummyArguments &args2) {
994+
const DummyArguments &args1, const DummyArguments &args2) const {
990995
int effective{0}; // position of arg1 in list, ignoring passed arg
991996
for (std::size_t i{0}; i < args1.size(); ++i) {
992997
const DummyArgument &arg1{args1.at(i)};
@@ -1006,7 +1011,7 @@ int DistinguishUtils::FindFirstToDistinguishByPosition(
10061011
// - args2 has no dummy argument with that name
10071012
// - the dummy argument with that name is distinguishable from it
10081013
int DistinguishUtils::FindLastToDistinguishByName(
1009-
const DummyArguments &args1, const DummyArguments &args2) {
1014+
const DummyArguments &args1, const DummyArguments &args2) const {
10101015
std::map<std::string, const DummyArgument *> nameToArg;
10111016
for (const auto &arg2 : args2) {
10121017
nameToArg.emplace(arg2.name, &arg2);
@@ -1026,7 +1031,7 @@ int DistinguishUtils::FindLastToDistinguishByName(
10261031
// Count the dummy data objects in args that are nonoptional, are not
10271032
// passed-object, and that x is TKR compatible with
10281033
int DistinguishUtils::CountCompatibleWith(
1029-
const DummyArgument &x, const DummyArguments &args) {
1034+
const DummyArgument &x, const DummyArguments &args) const {
10301035
return std::count_if(args.begin(), args.end(), [&](const DummyArgument &y) {
10311036
return !y.pass && !y.IsOptional() && IsTkrCompatible(x, y);
10321037
});
@@ -1035,15 +1040,15 @@ int DistinguishUtils::CountCompatibleWith(
10351040
// Return the number of dummy data objects in args that are not
10361041
// distinguishable from x and not passed-object.
10371042
int DistinguishUtils::CountNotDistinguishableFrom(
1038-
const DummyArgument &x, const DummyArguments &args) {
1043+
const DummyArgument &x, const DummyArguments &args) const {
10391044
return std::count_if(args.begin(), args.end(), [&](const DummyArgument &y) {
10401045
return !y.pass && std::holds_alternative<DummyDataObject>(y.u) &&
10411046
!Distinguishable(y, x);
10421047
});
10431048
}
10441049

10451050
bool DistinguishUtils::Distinguishable(
1046-
const DummyArgument &x, const DummyArgument &y) {
1051+
const DummyArgument &x, const DummyArgument &y) const {
10471052
if (x.u.index() != y.u.index()) {
10481053
return true; // different kind: data/proc/alt-return
10491054
}
@@ -1061,7 +1066,7 @@ bool DistinguishUtils::Distinguishable(
10611066
}
10621067

10631068
bool DistinguishUtils::Distinguishable(
1064-
const DummyDataObject &x, const DummyDataObject &y) {
1069+
const DummyDataObject &x, const DummyDataObject &y) const {
10651070
using Attr = DummyDataObject::Attr;
10661071
if (Distinguishable(x.type, y.type)) {
10671072
return true;
@@ -1071,13 +1076,27 @@ bool DistinguishUtils::Distinguishable(
10711076
} else if (y.attrs.test(Attr::Allocatable) && x.attrs.test(Attr::Pointer) &&
10721077
x.intent != common::Intent::In) {
10731078
return true;
1079+
} else if (features_.IsEnabled(
1080+
common::LanguageFeature::DistinguishableSpecifics) &&
1081+
(x.attrs.test(Attr::Allocatable) || x.attrs.test(Attr::Pointer)) &&
1082+
(y.attrs.test(Attr::Allocatable) || y.attrs.test(Attr::Pointer)) &&
1083+
(x.type.type().IsUnlimitedPolymorphic() !=
1084+
y.type.type().IsUnlimitedPolymorphic() ||
1085+
x.type.type().IsPolymorphic() != y.type.type().IsPolymorphic())) {
1086+
// Extension: Per 15.5.2.5(2), an allocatable/pointer dummy and its
1087+
// corresponding actual argument must both or neither be polymorphic,
1088+
// and must both or neither be unlimited polymorphic. So when exactly
1089+
// one of two dummy arguments is polymorphic or unlimited polymorphic,
1090+
// any actual argument that is admissible to one of them cannot also match
1091+
// the other one.
1092+
return true;
10741093
} else {
10751094
return false;
10761095
}
10771096
}
10781097

10791098
bool DistinguishUtils::Distinguishable(
1080-
const DummyProcedure &x, const DummyProcedure &y) {
1099+
const DummyProcedure &x, const DummyProcedure &y) const {
10811100
const Procedure &xProc{x.procedure.value()};
10821101
const Procedure &yProc{y.procedure.value()};
10831102
if (Distinguishable(xProc, yProc)) {
@@ -1091,7 +1110,7 @@ bool DistinguishUtils::Distinguishable(
10911110
}
10921111

10931112
bool DistinguishUtils::Distinguishable(
1094-
const FunctionResult &x, const FunctionResult &y) {
1113+
const FunctionResult &x, const FunctionResult &y) const {
10951114
if (x.u.index() != y.u.index()) {
10961115
return true; // one is data object, one is procedure
10971116
}
@@ -1109,19 +1128,19 @@ bool DistinguishUtils::Distinguishable(
11091128
}
11101129

11111130
bool DistinguishUtils::Distinguishable(
1112-
const TypeAndShape &x, const TypeAndShape &y) {
1131+
const TypeAndShape &x, const TypeAndShape &y) const {
11131132
return !IsTkrCompatible(x, y) && !IsTkrCompatible(y, x);
11141133
}
11151134

11161135
// Compatibility based on type, kind, and rank
11171136
bool DistinguishUtils::IsTkrCompatible(
1118-
const DummyArgument &x, const DummyArgument &y) {
1137+
const DummyArgument &x, const DummyArgument &y) const {
11191138
const auto *obj1{std::get_if<DummyDataObject>(&x.u)};
11201139
const auto *obj2{std::get_if<DummyDataObject>(&y.u)};
11211140
return obj1 && obj2 && IsTkrCompatible(obj1->type, obj2->type);
11221141
}
11231142
bool DistinguishUtils::IsTkrCompatible(
1124-
const TypeAndShape &x, const TypeAndShape &y) {
1143+
const TypeAndShape &x, const TypeAndShape &y) const {
11251144
return x.type().IsTkCompatibleWith(y.type()) &&
11261145
(x.attrs().test(TypeAndShape::Attr::AssumedRank) ||
11271146
y.attrs().test(TypeAndShape::Attr::AssumedRank) ||
@@ -1130,7 +1149,7 @@ bool DistinguishUtils::IsTkrCompatible(
11301149

11311150
// Return the argument at the given index, ignoring the passed arg
11321151
const DummyArgument *DistinguishUtils::GetAtEffectivePosition(
1133-
const DummyArguments &args, int index) {
1152+
const DummyArguments &args, int index) const {
11341153
for (const DummyArgument &arg : args) {
11351154
if (!arg.pass) {
11361155
if (index == 0) {
@@ -1143,7 +1162,7 @@ const DummyArgument *DistinguishUtils::GetAtEffectivePosition(
11431162
}
11441163

11451164
// Return the passed-object dummy argument of this procedure, if any
1146-
const DummyArgument *DistinguishUtils::GetPassArg(const Procedure &proc) {
1165+
const DummyArgument *DistinguishUtils::GetPassArg(const Procedure &proc) const {
11471166
for (const auto &arg : proc.dummyArguments) {
11481167
if (arg.pass) {
11491168
return &arg;
@@ -1152,12 +1171,14 @@ const DummyArgument *DistinguishUtils::GetPassArg(const Procedure &proc) {
11521171
return nullptr;
11531172
}
11541173

1155-
bool Distinguishable(const Procedure &x, const Procedure &y) {
1156-
return DistinguishUtils::Distinguishable(x, y);
1174+
bool Distinguishable(const common::LanguageFeatureControl &features,
1175+
const Procedure &x, const Procedure &y) {
1176+
return DistinguishUtils{features}.Distinguishable(x, y);
11571177
}
11581178

1159-
bool DistinguishableOpOrAssign(const Procedure &x, const Procedure &y) {
1160-
return DistinguishUtils::DistinguishableOpOrAssign(x, y);
1179+
bool DistinguishableOpOrAssign(const common::LanguageFeatureControl &features,
1180+
const Procedure &x, const Procedure &y) {
1181+
return DistinguishUtils{features}.DistinguishableOpOrAssign(x, y);
11611182
}
11621183

11631184
DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyArgument)

flang/lib/Semantics/check-declarations.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,7 +1097,8 @@ bool CheckHelper::CheckDistinguishableFinals(const Symbol &f1,
10971097
const Procedure *p1{Characterize(f1)};
10981098
const Procedure *p2{Characterize(f2)};
10991099
if (p1 && p2) {
1100-
if (characteristics::Distinguishable(*p1, *p2)) {
1100+
if (characteristics::Distinguishable(
1101+
context_.languageFeatures(), *p1, *p2)) {
11011102
return true;
11021103
}
11031104
if (auto *msg{messages_.Say(f1Name,
@@ -2290,7 +2291,8 @@ void DistinguishabilityHelper::Check(const Scope &scope) {
22902291
auto distinguishable{kind.IsName()
22912292
? evaluate::characteristics::Distinguishable
22922293
: evaluate::characteristics::DistinguishableOpOrAssign};
2293-
if (!distinguishable(proc, info[i2].procedure)) {
2294+
if (!distinguishable(
2295+
context_.languageFeatures(), proc, info[i2].procedure)) {
22942296
SayNotDistinguishable(GetTopLevelUnitContaining(scope), name, kind,
22952297
symbol, info[i2].symbol);
22962298
}

flang/test/Semantics/resolve53.f90

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,3 +479,29 @@ subroutine s1()
479479
procedure f
480480
end interface
481481
end subroutine s1
482+
483+
! Extensions for distinguishable allocatable arguments; these should not
484+
! elicit errors from f18
485+
module m21
486+
type :: t
487+
end type
488+
interface int1
489+
procedure s1a, s1b ! only one is polymorphic
490+
end interface
491+
interface int2
492+
procedure s2a, s2b ! only one is unlimited polymorphic
493+
end interface
494+
contains
495+
subroutine s1a(x)
496+
type(t), allocatable :: x
497+
end subroutine
498+
subroutine s1b(x)
499+
class(t), allocatable :: x
500+
end subroutine
501+
subroutine s2a(x)
502+
class(t), allocatable :: x
503+
end subroutine
504+
subroutine s2b(x)
505+
class(*), allocatable :: x
506+
end subroutine
507+
end module

0 commit comments

Comments
 (0)