diff options
author | Peter Klausler <35819229+klausler@users.noreply.github.com> | 2023-11-30 11:44:43 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-30 11:44:43 -0800 |
commit | bf4a876309cdc73e3907801abba02d2f1d2d7b6e (patch) | |
tree | 675ba7e0d5516445cf436db3b07d006141b0979c /flang/lib/Evaluate/intrinsics.cpp | |
parent | f1eddf5c39e7e203fbc8fb5f9293b9baa8bbb04b (diff) | |
download | llvm-bf4a876309cdc73e3907801abba02d2f1d2d7b6e.zip llvm-bf4a876309cdc73e3907801abba02d2f1d2d7b6e.tar.gz llvm-bf4a876309cdc73e3907801abba02d2f1d2d7b6e.tar.bz2 |
[flang] Move and extend REDUCE() compile-time checking (#72570)
Move the code to check the arguments of references to the intrinsic
function REDUCE() into Semantics/check-calls.cpp, and add checks for
several requirements from the standard that weren't yet caught.
Diffstat (limited to 'flang/lib/Evaluate/intrinsics.cpp')
-rw-r--r-- | flang/lib/Evaluate/intrinsics.cpp | 117 |
1 files changed, 30 insertions, 87 deletions
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp index c5faf31..08cec73 100644 --- a/flang/lib/Evaluate/intrinsics.cpp +++ b/flang/lib/Evaluate/intrinsics.cpp @@ -2330,6 +2330,12 @@ std::optional<SpecificCall> IntrinsicInterface::Match( } if (auto dc{characteristics::DummyArgument::FromActual(std::move(kw), *expr, context, /*forImplicitInterface=*/false)}) { + if (auto *dummyProc{ + std::get_if<characteristics::DummyProcedure>(&dc->u)}) { + // Dummy procedures are never elemental. + dummyProc->procedure.value().attrs.reset( + characteristics::Procedure::Attr::Elemental); + } dummyArgs.emplace_back(std::move(*dc)); if (d.typePattern.kindCode == KindCode::same && !sameDummyArg) { sameDummyArg = j; @@ -2874,8 +2880,7 @@ static bool CheckAtomicDefineAndRef(FoldingContext &context, } // Applies any semantic checks peculiar to an intrinsic. -// TODO: Move the rest of these checks to Semantics/check-call.cpp, which is -// where ASSOCIATED() and TRANSFER() are now validated. +// TODO: Move the rest of these checks to Semantics/check-call.cpp. static bool ApplySpecificChecks(SpecificCall &call, FoldingContext &context) { bool ok{true}; const std::string &name{call.specificIntrinsic.name}; @@ -2891,7 +2896,7 @@ static bool ApplySpecificChecks(SpecificCall &call, FoldingContext &context) { arg ? arg->sourceLocation() : context.messages().at(), "Argument of ALLOCATED() must be an ALLOCATABLE object or component"_err_en_US); } - } else if (name == "associated") { + } else if (name == "associated" || name == "reduce") { // Now handled in Semantics/check-call.cpp } else if (name == "atomic_and" || name == "atomic_or" || name == "atomic_xor") { @@ -2967,90 +2972,6 @@ static bool ApplySpecificChecks(SpecificCall &call, FoldingContext &context) { arg ? arg->sourceLocation() : context.messages().at(), "Argument of PRESENT() must be the name of an OPTIONAL dummy argument"_err_en_US); } - } else if (name == "reduce") { // 16.9.161 - std::optional<DynamicType> arrayType; - if (const auto &array{call.arguments[0]}) { - arrayType = array->GetType(); - } - std::optional<characteristics::Procedure> procChars; - parser::CharBlock at{context.messages().at()}; - if (const auto &operation{call.arguments[1]}) { - if (const auto *expr{operation->UnwrapExpr()}) { - if (const auto *designator{ - std::get_if<ProcedureDesignator>(&expr->u)}) { - procChars = - characteristics::Procedure::Characterize(*designator, context); - } else if (const auto *ref{std::get_if<ProcedureRef>(&expr->u)}) { - procChars = characteristics::Procedure::Characterize(*ref, context); - } - } - if (auto operationAt{operation->sourceLocation()}) { - at = *operationAt; - } - } - if (!arrayType || !procChars) { - ok = false; // error recovery - } else { - const auto *result{procChars->functionResult->GetTypeAndShape()}; - if (!procChars->IsPure() || procChars->dummyArguments.size() != 2 || - !procChars->functionResult) { - ok = false; - context.messages().Say(at, - "OPERATION= argument of REDUCE() must be a pure function of two data arguments"_err_en_US); - } else if (!result || result->Rank() != 0) { - ok = false; - context.messages().Say(at, - "OPERATION= argument of REDUCE() must be a scalar function"_err_en_US); - } else if (result->type().IsPolymorphic() || - !arrayType->IsTkLenCompatibleWith(result->type())) { - ok = false; - context.messages().Say(at, - "OPERATION= argument of REDUCE() must have the same type as ARRAY="_err_en_US); - } else { - const characteristics::DummyDataObject *data[2]{}; - for (int j{0}; j < 2; ++j) { - const auto &dummy{procChars->dummyArguments.at(j)}; - data[j] = std::get_if<characteristics::DummyDataObject>(&dummy.u); - ok = ok && data[j]; - } - if (!ok) { - context.messages().Say(at, - "OPERATION= argument of REDUCE() may not have dummy procedure arguments"_err_en_US); - } else { - for (int j{0}; j < 2; ++j) { - ok = ok && - !data[j]->attrs.test( - characteristics::DummyDataObject::Attr::Optional) && - !data[j]->attrs.test( - characteristics::DummyDataObject::Attr::Allocatable) && - !data[j]->attrs.test( - characteristics::DummyDataObject::Attr::Pointer) && - data[j]->type.Rank() == 0 && - !data[j]->type.type().IsPolymorphic() && - data[j]->type.type().IsTkCompatibleWith(*arrayType); - } - if (!ok) { - context.messages().Say(at, - "Arguments of OPERATION= procedure of REDUCE() must be both scalar of the same type as ARRAY=, and neither allocatable, pointer, polymorphic, nor optional"_err_en_US); - } else if (data[0]->attrs.test(characteristics::DummyDataObject:: - Attr::Asynchronous) != - data[1]->attrs.test( - characteristics::DummyDataObject::Attr::Asynchronous) || - data[0]->attrs.test( - characteristics::DummyDataObject::Attr::Volatile) != - data[1]->attrs.test( - characteristics::DummyDataObject::Attr::Volatile) || - data[0]->attrs.test( - characteristics::DummyDataObject::Attr::Target) != - data[1]->attrs.test( - characteristics::DummyDataObject::Attr::Target)) { - ok = false; - context.messages().Say(at, - "If either argument of the OPERATION= procedure of REDUCE() has the ASYNCHRONOUS, VOLATILE, or TARGET attribute, both must have that attribute"_err_en_US); - } - } - } - } } else if (name == "ucobound") { return CheckDimAgainstCorank(call, context); } @@ -3143,6 +3064,28 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::Probe( } else if (buffer.empty()) { buffer.Annex(std::move(localBuffer)); } else { + // When there are multiple entries in the table for an + // intrinsic that has multiple forms depending on the + // presence of DIM=, use messages from a later entry if + // the messages from an earlier entry complain about the + // DIM= argument and it wasn't specified with a keyword. + for (const auto &m : buffer.messages()) { + if (m.ToString().find("'dim='") != std::string::npos) { + bool hadDimKeyword{false}; + for (const auto &a : arguments) { + if (a) { + if (auto kw{a->keyword()}; kw && kw == "dim") { + hadDimKeyword = true; + break; + } + } + } + if (!hadDimKeyword) { + buffer = std::move(localBuffer); + } + break; + } + } localBuffer.clear(); } return std::nullopt; |