forked from OSchip/llvm-project
[flang] Use passed-object dummy in distinguishability checks
Complete the checks for distinguishable specifics procedure in a generic by considering any passed-object dummy arguments. C1514 rule 3 is implemented and the checks for the other rules are extended to consider the PASS attribute, including the concept of the "effective" position of an argument in an argument list, computed by ignoring passed-object arguments. Add `pass` to `characteristics::DummyArgument` to mark each passed-object dummy argument. Change symbols to store the index of the passed-object dummy argument rather than its symbol. Check that specifics of a type-bound generic are distinguishable only after all of the procedure bindings have been processed. They don't have to be before the generic. Original-commit: flang-compiler/f18@2751490f95 Reviewed-on: https://github.com/flang-compiler/f18/pull/567
This commit is contained in:
parent
507cc50866
commit
714d3be8bf
|
@ -271,6 +271,9 @@ std::ostream &DummyArgument::Dump(std::ostream &o) const {
|
|||
if (!name.empty()) {
|
||||
o << name << '=';
|
||||
}
|
||||
if (pass) {
|
||||
o << " PASS";
|
||||
}
|
||||
std::visit([&](const auto &x) { x.Dump(o); }, u);
|
||||
return o;
|
||||
}
|
||||
|
@ -405,7 +408,17 @@ std::optional<Procedure> Procedure::Characterize(
|
|||
return result;
|
||||
},
|
||||
[&](const semantics::ProcBindingDetails &binding) {
|
||||
return Characterize(binding.symbol(), intrinsics);
|
||||
auto result{Characterize(binding.symbol(), intrinsics)};
|
||||
if (result) {
|
||||
if (const auto passIndex{binding.passIndex()}) {
|
||||
auto &passArg{result->dummyArguments.at(*passIndex)};
|
||||
passArg.pass = true;
|
||||
if (const auto *passName{binding.passName()}) {
|
||||
CHECK(passArg.name == passName->ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
[](const semantics::GenericDetails &) -> std::optional<Procedure> {
|
||||
return std::nullopt;
|
||||
|
@ -452,6 +465,7 @@ private:
|
|||
int notOptional{0};
|
||||
};
|
||||
|
||||
static bool Rule3Distinguishable(const Procedure &, const Procedure &);
|
||||
static const DummyArgument *Rule1DistinguishingArg(
|
||||
const DummyArguments &, const DummyArguments &);
|
||||
static int FindFirstToDistinguishByPosition(
|
||||
|
@ -468,17 +482,23 @@ private:
|
|||
static bool Distinguishable(const TypeAndShape &, const TypeAndShape &);
|
||||
static bool IsTkrCompatible(const DummyArgument &, const DummyArgument &);
|
||||
static bool IsTkrCompatible(const TypeAndShape &, const TypeAndShape &);
|
||||
static const DummyArgument *GetAtEffectivePosition(
|
||||
const DummyArguments &, int);
|
||||
static const DummyArgument *GetPassArg(const Procedure &);
|
||||
};
|
||||
|
||||
bool DistinguishUtils::Distinguishable(const Procedure &x, const Procedure &y) {
|
||||
auto &args1{x.dummyArguments};
|
||||
auto &args2{y.dummyArguments};
|
||||
bool DistinguishUtils::Distinguishable(
|
||||
const Procedure &proc1, const Procedure &proc2) {
|
||||
auto &args1{proc1.dummyArguments};
|
||||
auto &args2{proc2.dummyArguments};
|
||||
auto count1{CountDummyProcedures(args1)};
|
||||
auto count2{CountDummyProcedures(args2)};
|
||||
if (count1.notOptional > count2.total || count2.notOptional > count1.total) {
|
||||
return true; // distinguishable based on C1514 rule 2
|
||||
}
|
||||
// C1514 rule 3: TODO - depends on passed-object dummies
|
||||
if (Rule3Distinguishable(proc1, proc2)) {
|
||||
return true; // distinguishable based on C1514 rule 3
|
||||
}
|
||||
if (Rule1DistinguishingArg(args1, args2)) {
|
||||
return true; // distinguishable based on C1514 rule 1
|
||||
}
|
||||
|
@ -495,6 +515,15 @@ bool DistinguishUtils::Distinguishable(const Procedure &x, const Procedure &y) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// C1514 rule 3: Procedures are distinguishable if both have a passed-object
|
||||
// dummy argument and those are distinguishable.
|
||||
bool DistinguishUtils::Rule3Distinguishable(
|
||||
const Procedure &proc1, const Procedure &proc2) {
|
||||
const DummyArgument *pass1{GetPassArg(proc1)};
|
||||
const DummyArgument *pass2{GetPassArg(proc2)};
|
||||
return pass1 && pass2 && Distinguishable(*pass1, *pass2);
|
||||
}
|
||||
|
||||
// Find a non-passed-object dummy data object in one of the argument lists
|
||||
// that satisfies C1514 rule 1. I.e. x such that:
|
||||
// - m is the number of dummy data objects in one that are nonoptional,
|
||||
|
@ -508,7 +537,7 @@ const DummyArgument *DistinguishUtils::Rule1DistinguishingArg(
|
|||
auto size2{args2.size()};
|
||||
for (std::size_t i{0}; i < size1 + size2; ++i) {
|
||||
const DummyArgument &x{i < size1 ? args1[i] : args2[i - size1]};
|
||||
if (std::holds_alternative<DummyDataObject>(x.u)) {
|
||||
if (!x.pass && std::holds_alternative<DummyDataObject>(x.u)) {
|
||||
if (CountCompatibleWith(x, args1) >
|
||||
CountNotDistinguishableFrom(x, args2) ||
|
||||
CountCompatibleWith(x, args2) >
|
||||
|
@ -526,13 +555,16 @@ const DummyArgument *DistinguishUtils::Rule1DistinguishingArg(
|
|||
// - the dummy argument at that position is distinguishable from it
|
||||
int DistinguishUtils::FindFirstToDistinguishByPosition(
|
||||
const DummyArguments &args1, const DummyArguments &args2) {
|
||||
int effective{0}; // position of arg1 in list, ignoring passed arg
|
||||
for (std::size_t i{0}; i < args1.size(); ++i) {
|
||||
const DummyArgument &arg1{args1.at(i)};
|
||||
if (!arg1.IsOptional()) {
|
||||
if (i >= args2.size() || Distinguishable(arg1, args2.at(i))) {
|
||||
if (!arg1.pass && !arg1.IsOptional()) {
|
||||
const DummyArgument *arg2{GetAtEffectivePosition(args2, effective)};
|
||||
if (!arg2 || Distinguishable(arg1, *arg2)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
effective += !arg1.pass;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
@ -549,9 +581,11 @@ int DistinguishUtils::FindLastToDistinguishByName(
|
|||
}
|
||||
for (int i = args1.size() - 1; i >= 0; --i) {
|
||||
const DummyArgument &arg1{args1.at(i)};
|
||||
auto it{nameToArg.find(arg1.name)};
|
||||
if (it == nameToArg.end() || Distinguishable(arg1, *it->second)) {
|
||||
return i;
|
||||
if (!arg1.pass && !arg1.IsOptional()) {
|
||||
auto it{nameToArg.find(arg1.name)};
|
||||
if (it == nameToArg.end() || Distinguishable(arg1, *it->second)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
@ -562,7 +596,7 @@ int DistinguishUtils::FindLastToDistinguishByName(
|
|||
int DistinguishUtils::CountCompatibleWith(
|
||||
const DummyArgument &x, const DummyArguments &args) {
|
||||
return std::count_if(args.begin(), args.end(), [&](const DummyArgument &y) {
|
||||
return !y.IsOptional() && IsTkrCompatible(x, y);
|
||||
return !y.pass && !y.IsOptional() && IsTkrCompatible(x, y);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -571,7 +605,7 @@ int DistinguishUtils::CountCompatibleWith(
|
|||
int DistinguishUtils::CountNotDistinguishableFrom(
|
||||
const DummyArgument &x, const DummyArguments &args) {
|
||||
return std::count_if(args.begin(), args.end(), [&](const DummyArgument &y) {
|
||||
return std::holds_alternative<DummyDataObject>(y.u) &&
|
||||
return !y.pass && std::holds_alternative<DummyDataObject>(y.u) &&
|
||||
!Distinguishable(y, x);
|
||||
});
|
||||
}
|
||||
|
@ -660,6 +694,30 @@ bool DistinguishUtils::IsTkrCompatible(
|
|||
(x.IsAssumedRank() || y.IsAssumedRank() || x.Rank() == y.Rank());
|
||||
}
|
||||
|
||||
// Return the argument at the given index, ignoring the passed arg
|
||||
const DummyArgument *DistinguishUtils::GetAtEffectivePosition(
|
||||
const DummyArguments &args, int index) {
|
||||
for (const DummyArgument &arg : args) {
|
||||
if (!arg.pass) {
|
||||
if (index == 0) {
|
||||
return &arg;
|
||||
}
|
||||
--index;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Return the passed-object dummy argument of this procedure, if any
|
||||
const DummyArgument *DistinguishUtils::GetPassArg(const Procedure &proc) {
|
||||
for (const auto &arg : proc.dummyArguments) {
|
||||
if (arg.pass) {
|
||||
return &arg;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Distinguishable(const Procedure &x, const Procedure &y) {
|
||||
return DistinguishUtils::Distinguishable(x, y);
|
||||
}
|
||||
|
|
|
@ -145,9 +145,10 @@ struct DummyArgument {
|
|||
bool IsOptional() const;
|
||||
void SetOptional(bool = true);
|
||||
std::ostream &Dump(std::ostream &) const;
|
||||
// name is not a characteristic and so does not participate in operator==
|
||||
// but it is needed to determine if procedures are distinguishable
|
||||
// name and pass are not a characteristics and so does not participate in
|
||||
// operator== but are needed to determine if procedures are distinguishable
|
||||
std::string name;
|
||||
bool pass{false}; // is this the PASS argument of its procedure
|
||||
std::variant<DummyDataObject, DummyProcedure, AlternateReturn> u;
|
||||
};
|
||||
|
||||
|
|
|
@ -1062,8 +1062,7 @@ private:
|
|||
bool BeginScope(const ProgramTree &);
|
||||
void FinishSpecificationParts(const ProgramTree &);
|
||||
void FinishDerivedType(Scope &);
|
||||
const Symbol *CheckPassArg(
|
||||
const Symbol &, const Symbol *, const SourceName *);
|
||||
void SetPassArg(const Symbol &, const Symbol *, WithPassArg &);
|
||||
void ResolveExecutionParts(const ProgramTree &);
|
||||
};
|
||||
|
||||
|
@ -5174,12 +5173,17 @@ void ResolveNamesVisitor::FinishDerivedType(Scope &scope) {
|
|||
std::visit(
|
||||
common::visitors{
|
||||
[&](ProcEntityDetails &x) {
|
||||
x.set_passArg(
|
||||
CheckPassArg(comp, x.interface().symbol(), x.passName()));
|
||||
},
|
||||
[&](ProcBindingDetails &x) {
|
||||
x.set_passArg(CheckPassArg(comp, &x.symbol(), x.passName()));
|
||||
SetPassArg(comp, x.interface().symbol(), x);
|
||||
},
|
||||
[&](ProcBindingDetails &x) { SetPassArg(comp, &x.symbol(), x); },
|
||||
[](auto &x) {},
|
||||
},
|
||||
comp.details());
|
||||
}
|
||||
for (auto &pair : scope) {
|
||||
Symbol &comp{*pair.second};
|
||||
std::visit(
|
||||
common::visitors{
|
||||
[&](GenericBindingDetails &x) {
|
||||
CheckSpecificsAreDistinguishable(comp, x.specificProcs());
|
||||
},
|
||||
|
@ -5190,19 +5194,20 @@ void ResolveNamesVisitor::FinishDerivedType(Scope &scope) {
|
|||
}
|
||||
|
||||
// Check C760, constraints on the passed-object dummy argument
|
||||
// If they all pass, return the Symbol for that argument.
|
||||
const Symbol *ResolveNamesVisitor::CheckPassArg(
|
||||
const Symbol &proc, const Symbol *interface, const SourceName *passName) {
|
||||
// If they all pass, set the passIndex in details.
|
||||
void ResolveNamesVisitor::SetPassArg(
|
||||
const Symbol &proc, const Symbol *interface, WithPassArg &details) {
|
||||
if (proc.attrs().test(Attr::NOPASS)) {
|
||||
return nullptr;
|
||||
return;
|
||||
}
|
||||
const auto &name{proc.name()};
|
||||
if (!interface) {
|
||||
Say(name,
|
||||
"Procedure component '%s' must have NOPASS attribute or explicit interface"_err_en_US,
|
||||
name);
|
||||
return nullptr;
|
||||
return;
|
||||
}
|
||||
const SourceName *passName{details.passName()};
|
||||
const auto &dummyArgs{interface->get<SubprogramDetails>().dummyArgs()};
|
||||
if (!passName && dummyArgs.empty()) {
|
||||
Say(name,
|
||||
|
@ -5212,7 +5217,7 @@ const Symbol *ResolveNamesVisitor::CheckPassArg(
|
|||
: "Procedure binding '%s' with no dummy arguments"
|
||||
" must have NOPASS attribute"_err_en_US,
|
||||
name);
|
||||
return nullptr;
|
||||
return;
|
||||
}
|
||||
int passArgIndex{0};
|
||||
if (!passName) {
|
||||
|
@ -5223,7 +5228,7 @@ const Symbol *ResolveNamesVisitor::CheckPassArg(
|
|||
Say(*passName,
|
||||
"'%s' is not a dummy argument of procedure interface '%s'"_err_en_US,
|
||||
*passName, interface->name());
|
||||
return nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
const Symbol &passArg{*dummyArgs[passArgIndex]};
|
||||
|
@ -5246,11 +5251,11 @@ const Symbol *ResolveNamesVisitor::CheckPassArg(
|
|||
}
|
||||
if (msg) {
|
||||
Say(name, std::move(*msg), *passName, name);
|
||||
return nullptr;
|
||||
return;
|
||||
}
|
||||
const DeclTypeSpec *type{passArg.GetType()};
|
||||
if (!type) {
|
||||
return nullptr; // an error already occurred
|
||||
return; // an error already occurred
|
||||
}
|
||||
const Symbol &typeSymbol{*proc.owner().GetSymbol()};
|
||||
const DerivedTypeSpec *derived{type->AsDerived()};
|
||||
|
@ -5259,7 +5264,7 @@ const Symbol *ResolveNamesVisitor::CheckPassArg(
|
|||
"Passed-object dummy argument '%s' of procedure '%s'"
|
||||
" must be of type '%s' but is '%s'"_err_en_US,
|
||||
*passName, name, typeSymbol.name(), type->AsFortran());
|
||||
return nullptr;
|
||||
return;
|
||||
}
|
||||
if (IsExtensibleType(derived) != type->IsPolymorphic()) {
|
||||
Say(name,
|
||||
|
@ -5269,7 +5274,7 @@ const Symbol *ResolveNamesVisitor::CheckPassArg(
|
|||
: "Passed-object dummy argument '%s' of procedure '%s'"
|
||||
" must polymorphic because '%s' is extensible"_err_en_US,
|
||||
*passName, name, typeSymbol.name());
|
||||
return nullptr;
|
||||
return;
|
||||
}
|
||||
for (const auto &[paramName, paramValue] : derived->parameters()) {
|
||||
if (paramValue.isLen() && !paramValue.isAssumed()) {
|
||||
|
@ -5279,7 +5284,7 @@ const Symbol *ResolveNamesVisitor::CheckPassArg(
|
|||
*passName, name, paramName);
|
||||
}
|
||||
}
|
||||
return &passArg;
|
||||
details.set_passIndex(passArgIndex);
|
||||
}
|
||||
|
||||
// Resolve names in the execution part of this node and its children
|
||||
|
|
|
@ -190,16 +190,17 @@ private:
|
|||
};
|
||||
|
||||
// Mixin for details with passed-object dummy argument.
|
||||
// passIndex is set based on passName or the PASS attr.
|
||||
class WithPassArg {
|
||||
public:
|
||||
const SourceName *passName() const { return passName_; }
|
||||
void set_passName(const SourceName &passName) { passName_ = &passName; }
|
||||
const Symbol *passArg() const { return passArg_; }
|
||||
void set_passArg(const Symbol *passArg) { passArg_ = passArg; }
|
||||
std::optional<int> passIndex() const { return passIndex_; }
|
||||
void set_passIndex(int index) { passIndex_ = index; }
|
||||
|
||||
private:
|
||||
const SourceName *passName_{nullptr};
|
||||
const Symbol *passArg_{nullptr};
|
||||
std::optional<int> passIndex_;
|
||||
};
|
||||
|
||||
// A procedure pointer, dummy procedure, or external procedure
|
||||
|
|
|
@ -357,3 +357,23 @@ contains
|
|||
type(*) :: x
|
||||
end
|
||||
end
|
||||
|
||||
! Test C1514 rule 3 -- distinguishable passed-object dummy arguments
|
||||
module m18
|
||||
type :: t(k)
|
||||
integer, kind :: k
|
||||
contains
|
||||
procedure, pass(x) :: p1 => s
|
||||
procedure, pass :: p2 => s
|
||||
procedure :: p3 => s
|
||||
procedure, pass(y) :: p4 => s
|
||||
generic :: g1 => p1, p4
|
||||
generic :: g2 => p2, p4
|
||||
generic :: g3 => p3, p4
|
||||
end type
|
||||
contains
|
||||
subroutine s(x, y)
|
||||
class(t(1)) :: x
|
||||
class(t(2)) :: y
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue