[flang] Catch nasty order-of-declarations case (#71881)

It is possible to declare the rank of an object after that object has
been used in the same specification part in a specification function
reference whose result or generic resolution may well have depended on
the object being apparently a scalar.

Catch this case, and emit a warning -- not an error, yet, due to fear of
false positives.

See the new test for examples.
This commit is contained in:
Peter Klausler
2023-11-13 16:24:43 -08:00
committed by GitHub
parent 1c91d9bdea
commit 94d47e6325
2 changed files with 58 additions and 1 deletions

View File

@@ -1078,6 +1078,9 @@ protected:
void EndCheckOnIndexUseInOwnBounds(const std::optional<SourceName> &restore) {
checkIndexUseInOwnBounds_ = restore;
}
void NoteScalarSpecificationArgument(const Symbol &symbol) {
mustBeScalar_.emplace(symbol);
}
private:
// The attribute corresponding to the statement containing an ObjectDecl
@@ -1116,6 +1119,7 @@ private:
std::optional<SourceName> checkIndexUseInOwnBounds_;
bool hasBindCName_{false};
bool isVectorType_{false};
UnorderedSymbolSet mustBeScalar_;
bool HandleAttributeStmt(Attr, const std::list<parser::Name> &);
Symbol &HandleAttributeStmt(Attr, const parser::Name &);
@@ -1195,6 +1199,9 @@ private:
return symbol;
}
bool HasCycle(const Symbol &, const Symbol *interface);
bool MustBeScalar(const Symbol &symbol) const {
return mustBeScalar_.find(symbol) != mustBeScalar_.end();
}
};
// Resolve construct entities and statement entities.
@@ -4886,6 +4893,9 @@ Symbol &DeclarationVisitor::DeclareObjectEntity(
"The dimensions of '%s' have already been declared"_err_en_US);
context().SetError(symbol);
}
} else if (MustBeScalar(symbol)) {
Say(name,
"'%s' appeared earlier as a scalar actual argument to a specification function"_warn_en_US);
} else {
details->set_shape(arraySpec());
}
@@ -7635,7 +7645,36 @@ void ResolveNamesVisitor::HandleCall(
},
},
std::get<parser::ProcedureDesignator>(call.t).u);
Walk(std::get<std::list<parser::ActualArgSpec>>(call.t));
const auto &arguments{std::get<std::list<parser::ActualArgSpec>>(call.t)};
Walk(arguments);
// Once an object has appeared in a specification function reference as
// a whole scalar actual argument, it cannot be (re)dimensioned later.
// The fact that it appeared to be a scalar may determine the resolution
// or the result of an inquiry intrinsic function or generic procedure.
if (inSpecificationPart_) {
for (const auto &argSpec : arguments) {
const auto &actual{std::get<parser::ActualArg>(argSpec.t)};
if (const auto *expr{
std::get_if<common::Indirection<parser::Expr>>(&actual.u)}) {
if (const auto *designator{
std::get_if<common::Indirection<parser::Designator>>(
&expr->value().u)}) {
if (const auto *dataRef{
std::get_if<parser::DataRef>(&designator->value().u)}) {
if (const auto *name{std::get_if<parser::Name>(&dataRef->u)};
name && name->symbol) {
const Symbol &symbol{*name->symbol};
const auto *object{symbol.detailsIf<ObjectEntityDetails>()};
if (symbol.has<EntityDetails>() ||
(object && !object->IsArray())) {
NoteScalarSpecificationArgument(symbol);
}
}
}
}
}
}
}
}
void ResolveNamesVisitor::HandleProcedureName(

View File

@@ -0,0 +1,18 @@
! RUN: %python %S/test_errors.py %s %flang_fc1 -Werror
! A nasty case of a weird order of declarations - a symbol may appear
! as an actual argument to a specification function before its rank
! has been declared.
program main
interface kind
pure integer function mykind(x)
real, intent(in) :: x(:)
end
end interface
real a, b
integer, parameter :: ak = kind(a)
integer, parameter :: br = rank(b)
!WARNING: 'a' appeared earlier as a scalar actual argument to a specification function
dimension a(1)
!WARNING: 'b' appeared earlier as a scalar actual argument to a specification function
dimension b(1)
end