Skip to content

Commit 3d11570

Browse files
authored
[flang] Restore error status for many indistinguishable specifics (#79927)
A recent patch to allow pFUnit to compile softened the diagnostic about indistinguishable specific procedures to a portability warning. It turns out that this was overkill -- for specific procedures containing no optional or unlimited polymorphic dummy data arguments, a diagnosis of "indistinguishable" can still be a hard error. So adjust the analysis to be tri-state: two procedures are either definitely distinguishable, definitely indistinguishable without optionals or unlimited polymorphics, or indeterminate. Emit errors as before for the definitely indistinguishable cases; continue to emit portability warnings for the indeterminate cases. When this patch is merged, all but one of the dozen or so tests that I disabled in llvm-test-suite can be re-enabled.
1 parent aa1968c commit 3d11570

File tree

10 files changed

+121
-78
lines changed

10 files changed

+121
-78
lines changed

flang/include/flang/Evaluate/characteristics.h

+5-4
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,12 @@ namespace Fortran::evaluate::characteristics {
4545
using common::CopyableIndirection;
4646

4747
// Are these procedures distinguishable for a generic name or FINAL?
48-
bool Distinguishable(const common::LanguageFeatureControl &, const Procedure &,
49-
const Procedure &);
50-
// Are these procedures distinguishable for a generic operator or assignment?
51-
bool DistinguishableOpOrAssign(const common::LanguageFeatureControl &,
48+
std::optional<bool> Distinguishable(const common::LanguageFeatureControl &,
5249
const Procedure &, const Procedure &);
50+
// Are these procedures distinguishable for a generic operator or assignment?
51+
std::optional<bool> DistinguishableOpOrAssign(
52+
const common::LanguageFeatureControl &, const Procedure &,
53+
const Procedure &);
5354

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

flang/lib/Evaluate/characteristics.cpp

+46-10
Original file line numberDiff line numberDiff line change
@@ -1456,9 +1456,11 @@ class DistinguishUtils {
14561456
: features_{features} {}
14571457

14581458
// Are these procedures distinguishable for a generic name?
1459-
bool Distinguishable(const Procedure &, const Procedure &) const;
1459+
std::optional<bool> Distinguishable(
1460+
const Procedure &, const Procedure &) const;
14601461
// Are these procedures distinguishable for a generic operator or assignment?
1461-
bool DistinguishableOpOrAssign(const Procedure &, const Procedure &) const;
1462+
std::optional<bool> DistinguishableOpOrAssign(
1463+
const Procedure &, const Procedure &) const;
14621464

14631465
private:
14641466
struct CountDummyProcedures {
@@ -1474,6 +1476,8 @@ class DistinguishUtils {
14741476
int notOptional{0};
14751477
};
14761478

1479+
bool AnyOptionalData(const DummyArguments &) const;
1480+
bool AnyUnlimitedPolymorphicData(const DummyArguments &) const;
14771481
bool Rule3Distinguishable(const Procedure &, const Procedure &) const;
14781482
const DummyArgument *Rule1DistinguishingArg(
14791483
const DummyArguments &, const DummyArguments &) const;
@@ -1500,7 +1504,7 @@ class DistinguishUtils {
15001504
};
15011505

15021506
// Simpler distinguishability rules for operators and assignment
1503-
bool DistinguishUtils::DistinguishableOpOrAssign(
1507+
std::optional<bool> DistinguishUtils::DistinguishableOpOrAssign(
15041508
const Procedure &proc1, const Procedure &proc2) const {
15051509
if ((proc1.IsFunction() && proc2.IsSubroutine()) ||
15061510
(proc1.IsSubroutine() && proc2.IsFunction())) {
@@ -1519,7 +1523,7 @@ bool DistinguishUtils::DistinguishableOpOrAssign(
15191523
return false;
15201524
}
15211525

1522-
bool DistinguishUtils::Distinguishable(
1526+
std::optional<bool> DistinguishUtils::Distinguishable(
15231527
const Procedure &proc1, const Procedure &proc2) const {
15241528
if ((proc1.IsFunction() && proc2.IsSubroutine()) ||
15251529
(proc1.IsSubroutine() && proc2.IsFunction())) {
@@ -1551,6 +1555,35 @@ bool DistinguishUtils::Distinguishable(
15511555
if (proc1.cudaSubprogramAttrs != proc2.cudaSubprogramAttrs) {
15521556
return true;
15531557
}
1558+
// If there are no optional or unlimited polymorphic dummy arguments,
1559+
// then we know the result for sure; otherwise, it's possible for
1560+
// the procedures to be unambiguous.
1561+
if ((AnyOptionalData(args1) || AnyUnlimitedPolymorphicData(args1)) &&
1562+
(AnyOptionalData(args2) || AnyUnlimitedPolymorphicData(args2))) {
1563+
return std::nullopt; // meaning "maybe"
1564+
} else {
1565+
return false;
1566+
}
1567+
}
1568+
1569+
bool DistinguishUtils::AnyOptionalData(const DummyArguments &args) const {
1570+
for (const auto &arg : args) {
1571+
if (std::holds_alternative<DummyDataObject>(arg.u) && arg.IsOptional()) {
1572+
return true;
1573+
}
1574+
}
1575+
return false;
1576+
}
1577+
1578+
bool DistinguishUtils::AnyUnlimitedPolymorphicData(
1579+
const DummyArguments &args) const {
1580+
for (const auto &arg : args) {
1581+
if (const auto *object{std::get_if<DummyDataObject>(&arg.u)}) {
1582+
if (object->type.type().IsUnlimitedPolymorphic()) {
1583+
return true;
1584+
}
1585+
}
1586+
}
15541587
return false;
15551588
}
15561589

@@ -1704,7 +1737,7 @@ bool DistinguishUtils::Distinguishable(
17041737
const DummyProcedure &x, const DummyProcedure &y) const {
17051738
const Procedure &xProc{x.procedure.value()};
17061739
const Procedure &yProc{y.procedure.value()};
1707-
if (Distinguishable(xProc, yProc)) {
1740+
if (Distinguishable(xProc, yProc).value_or(false)) {
17081741
return true;
17091742
} else {
17101743
const std::optional<FunctionResult> &xResult{xProc.functionResult};
@@ -1730,7 +1763,8 @@ bool DistinguishUtils::Distinguishable(
17301763
},
17311764
[&](const CopyableIndirection<Procedure> &z) {
17321765
return Distinguishable(z.value(),
1733-
std::get<CopyableIndirection<Procedure>>(y.u).value());
1766+
std::get<CopyableIndirection<Procedure>>(y.u).value())
1767+
.value_or(false);
17341768
},
17351769
},
17361770
x.u);
@@ -1795,13 +1829,15 @@ const DummyArgument *DistinguishUtils::GetPassArg(const Procedure &proc) const {
17951829
return nullptr;
17961830
}
17971831

1798-
bool Distinguishable(const common::LanguageFeatureControl &features,
1799-
const Procedure &x, const Procedure &y) {
1832+
std::optional<bool> Distinguishable(
1833+
const common::LanguageFeatureControl &features, const Procedure &x,
1834+
const Procedure &y) {
18001835
return DistinguishUtils{features}.Distinguishable(x, y);
18011836
}
18021837

1803-
bool DistinguishableOpOrAssign(const common::LanguageFeatureControl &features,
1804-
const Procedure &x, const Procedure &y) {
1838+
std::optional<bool> DistinguishableOpOrAssign(
1839+
const common::LanguageFeatureControl &features, const Procedure &x,
1840+
const Procedure &y) {
18051841
return DistinguishUtils{features}.DistinguishableOpOrAssign(x, y);
18061842
}
18071843

flang/lib/Semantics/check-declarations.cpp

+21-15
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ class DistinguishabilityHelper {
192192

193193
private:
194194
void SayNotDistinguishable(const Scope &, const SourceName &, GenericKind,
195-
const Symbol &, const Symbol &);
195+
const Symbol &, const Symbol &, bool isError);
196196
void AttachDeclaration(parser::Message &, const Scope &, const Symbol &);
197197

198198
SemanticsContext &context_;
@@ -1704,8 +1704,9 @@ bool CheckHelper::CheckDistinguishableFinals(const Symbol &f1,
17041704
const Procedure *p1{Characterize(f1)};
17051705
const Procedure *p2{Characterize(f2)};
17061706
if (p1 && p2) {
1707-
if (characteristics::Distinguishable(
1708-
context_.languageFeatures(), *p1, *p2)) {
1707+
std::optional<bool> areDistinct{characteristics::Distinguishable(
1708+
context_.languageFeatures(), *p1, *p2)};
1709+
if (areDistinct.value_or(false)) {
17091710
return true;
17101711
}
17111712
if (auto *msg{messages_.Say(f1Name,
@@ -3519,10 +3520,11 @@ void DistinguishabilityHelper::Check(const Scope &scope) {
35193520
auto distinguishable{kind.IsName()
35203521
? evaluate::characteristics::Distinguishable
35213522
: evaluate::characteristics::DistinguishableOpOrAssign};
3522-
if (!distinguishable(
3523-
context_.languageFeatures(), proc, iter2->second.procedure)) {
3523+
std::optional<bool> distinct{distinguishable(
3524+
context_.languageFeatures(), proc, iter2->second.procedure)};
3525+
if (!distinct.value_or(false)) {
35243526
SayNotDistinguishable(GetTopLevelUnitContaining(scope), name, kind,
3525-
*ultimate, *iter2->first);
3527+
*ultimate, *iter2->first, distinct.has_value());
35263528
}
35273529
}
35283530
}
@@ -3531,15 +3533,15 @@ void DistinguishabilityHelper::Check(const Scope &scope) {
35313533

35323534
void DistinguishabilityHelper::SayNotDistinguishable(const Scope &scope,
35333535
const SourceName &name, GenericKind kind, const Symbol &proc1,
3534-
const Symbol &proc2) {
3535-
if (!context_.ShouldWarn(
3536+
const Symbol &proc2, bool isError) {
3537+
if (!isError &&
3538+
!context_.ShouldWarn(
35363539
common::LanguageFeature::IndistinguishableSpecifics)) {
35373540
// The rules for distinguishing specific procedures (F'2023 15.4.3.4.5)
3538-
// are inadequate for some real-world cases like pFUnit, which has
3539-
// some generic interfaces whose specific procedures are provably
3540-
// unambiguous, but fail all of the standard's criteria for being
3541-
// conformably distinct. So the best we can do here is to emit optional
3542-
// portability warnings for such cases.
3541+
// are inadequate for some real-world cases like pFUnit.
3542+
// When there are optional dummy arguments or unlimited polymorphic
3543+
// dummy data object arguments, the best that we can do is emit an optional
3544+
// portability warning.
35433545
return;
35443546
}
35453547
std::string name1{proc1.name().ToString()};
@@ -3556,11 +3558,15 @@ void DistinguishabilityHelper::SayNotDistinguishable(const Scope &scope,
35563558
parser::Message *msg;
35573559
if (scope.sourceRange().Contains(name)) {
35583560
msg = &context_.Say(name,
3559-
"Generic '%s' should not have specific procedures '%s' and '%s' as their interfaces are not distinguishable by the incomplete rules in the standard"_port_en_US,
3561+
isError
3562+
? "Generic '%s' may not have specific procedures '%s' and '%s' as their interfaces are not distinguishable"_err_en_US
3563+
: "Generic '%s' should not have specific procedures '%s' and '%s' as their interfaces are not distinguishable by the rules in the standard"_port_en_US,
35603564
MakeOpName(name), name1, name2);
35613565
} else {
35623566
msg = &context_.Say(*GetTopLevelUnitContaining(proc1).GetName(),
3563-
"USE-associated generic '%s' should not have specific procedures '%s' and '%s' as their interfaces are not distinguishable by the incomplete rules in the standard"_port_en_US,
3567+
isError
3568+
? "USE-associated generic '%s' may not have specific procedures '%s' and '%s' as their interfaces are not distinguishable"_err_en_US
3569+
: "USE-associated generic '%s' should not have specific procedures '%s' and '%s' as their interfaces are not distinguishable by the incomplete rules in the standard"_port_en_US,
35643570
MakeOpName(name), name1, name2);
35653571
}
35663572
AttachDeclaration(*msg, scope, proc1);

flang/test/Semantics/generic05.F90

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
! RUN: %python %S/test_errors.py %s %flang_fc1 -pedantic
1+
! RUN: %python %S/test_errors.py %s %flang_fc1
22
! Check for distinguishability of defined I/O procedures defined within
33
! and outside their types.
44
module m1
55
type t1
66
integer n
77
contains
88
procedure :: readt1a, readt1b
9-
!PORTABILITY: Generic 'read(unformatted)' should not have specific procedures 'readt1a' and 'readt1b' as their interfaces are not distinguishable by the incomplete rules in the standard
9+
!ERROR: Generic 'read(unformatted)' may not have specific procedures 'readt1a' and 'readt1b' as their interfaces are not distinguishable
1010
generic :: read(unformatted) => readt1a, readt1b
1111
end type
1212
type t2
@@ -15,7 +15,7 @@ module m1
1515
type t3
1616
integer n
1717
end type
18-
!PORTABILITY: Generic 'read(unformatted)' should not have specific procedures 'readt2a' and 'readt2b' as their interfaces are not distinguishable by the incomplete rules in the standard
18+
!ERROR: Generic 'read(unformatted)' may not have specific procedures 'readt2a' and 'readt2b' as their interfaces are not distinguishable
1919
interface read(unformatted)
2020
module procedure :: readt1a, readt2a, readt2b, readt3
2121
end interface

flang/test/Semantics/generic07.f90

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
! RUN: %python %S/test_errors.py %s %flang_fc1 -pedantic -Werror
1+
! RUN: %python %S/test_errors.py %s %flang_fc1
22
module m1
33
type :: t1
44
sequence
@@ -74,7 +74,7 @@ program test
7474
interface distinguishable3
7575
procedure :: s1a, s1b
7676
end interface
77-
!PORTABILITY: Generic 'indistinguishable' should not have specific procedures 's2b' and 's2a' as their interfaces are not distinguishable by the incomplete rules in the standard
77+
!ERROR: Generic 'indistinguishable' may not have specific procedures 's2b' and 's2a' as their interfaces are not distinguishable
7878
interface indistinguishable
7979
procedure :: s2a, s2b
8080
end interface

flang/test/Semantics/resolve17.f90

+3-3
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ subroutine sb(y)
9696
end
9797
end
9898
subroutine s6
99-
!ERROR: Generic 'g' should not have specific procedures 'sa' and 'sb' as their interfaces are not distinguishable by the incomplete rules in the standard
99+
!ERROR: Generic 'g' may not have specific procedures 'sa' and 'sb' as their interfaces are not distinguishable
100100
use m6a, g => gg
101101
use m6b, g => gg
102102
end
@@ -180,7 +180,7 @@ subroutine g()
180180
end
181181
end module
182182
subroutine s9
183-
!PORTABILITY: USE-associated generic 'g' should not have specific procedures 'g' and 'g' as their interfaces are not distinguishable by the incomplete rules in the standard
183+
!ERROR: USE-associated generic 'g' may not have specific procedures 'g' and 'g' as their interfaces are not distinguishable
184184
use m9a
185185
use m9b
186186
end
@@ -197,7 +197,7 @@ subroutine s(x)
197197
end
198198
module m10b
199199
use m10a
200-
!PORTABILITY: Generic 'g' should not have specific procedures 's' and 's' as their interfaces are not distinguishable by the incomplete rules in the standard
200+
!ERROR: Generic 'g' may not have specific procedures 's' and 's' as their interfaces are not distinguishable
201201
interface g
202202
module procedure s
203203
end interface

0 commit comments

Comments
 (0)