[flang] rearrange arguments into dummy argument order

Original-commit: flang-compiler/f18@3bab0f55de
Reviewed-on: https://github.com/flang-compiler/f18/pull/219
Tree-same-pre-rewrite: false
This commit is contained in:
peter klausler 2018-10-18 10:50:55 -07:00
parent ef9dd9d1fe
commit 84ea49d062
4 changed files with 84 additions and 29 deletions

View file

@ -29,13 +29,14 @@ struct ActualArgument {
explicit ActualArgument(Expr<SomeType> &&x) : value{std::move(x)} {}
explicit ActualArgument(CopyableIndirection<Expr<SomeType>> &&v)
: value{std::move(v)} {}
std::optional<DynamicType> GetType() const;
int Rank() const;
std::ostream &Dump(std::ostream &) const;
std::optional<int> VectorSize() const;
std::optional<parser::CharBlock> keyword;
bool isAssumedRank{false};
bool isAssumedRank{false}; // TODO: make into a function of the value
bool isAlternateReturn{false};
// Subtlety: There is a distinction that must be maintained here between an

View file

@ -655,6 +655,9 @@ public:
private:
using Others = std::variant<BOZLiteralConstant>;
// TODO pmk: Consider creating the union over the specifically typed
// Expr<> instances rather than the category unions. This would
// be a big refactor and slow down compiles but would simplify usage.
using Categories = common::MapTemplate<Expr, SomeCategory>;
public:

View file

@ -19,6 +19,7 @@
#include "../common/fortran.h"
#include "../common/idioms.h"
#include "../semantics/default-kinds.h"
#include <algorithm>
#include <map>
#include <ostream>
#include <sstream>
@ -196,7 +197,7 @@ static constexpr IntrinsicDummyArgument OptionalMASK{
"mask", AnyLogical, Rank::conformable, Optionality::optional};
struct IntrinsicInterface {
static constexpr int maxArguments{7};
static constexpr int maxArguments{7}; // if not a MAX/MIN(...)
const char *name{nullptr};
IntrinsicDummyArgument dummy[maxArguments];
TypePattern result;
@ -760,14 +761,24 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
// Attempt to construct a 1-1 correspondence between the dummy arguments in
// a particular intrinsic procedure's generic interface and the actual
// arguments in a procedure reference.
ActualArgument *actualForDummy[maxArguments];
int dummies{0};
for (; dummies < maxArguments && dummy[dummies].keyword != nullptr;
++dummies) {
actualForDummy[dummies] = nullptr;
std::size_t dummyArgPatterns{0};
for (; dummyArgPatterns < maxArguments &&
dummy[dummyArgPatterns].keyword != nullptr;
++dummyArgPatterns) {
}
std::vector<ActualArgument *> actualForDummy(dummyArgPatterns, nullptr);
// MAX and MIN (and others that map to them) allow their last argument to
// be repeated indefinitely. The actualForDummy vector is sized
// and null-initialized to the non-repeated dummy argument count,
// but additional actual argument pointers can be pushed on it
// when this flag is set.
bool repeatLastDummy{dummyArgPatterns > 0 &&
dummy[dummyArgPatterns - 1].optionality == Optionality::repeats};
int missingActualArguments{0};
for (std::optional<ActualArgument> &arg : arguments) {
if (arg.has_value()) {
if (!arg.has_value()) {
++missingActualArguments;
} else {
if (arg->isAlternateReturn) {
messages.Say(
"alternate return specifier not acceptable on call to intrinsic '%s'"_err_en_US,
@ -775,29 +786,52 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
return std::nullopt;
}
bool found{false};
for (int dummyArgIndex{0}; dummyArgIndex < dummies; ++dummyArgIndex) {
if (actualForDummy[dummyArgIndex] == nullptr) {
if (!arg->keyword.has_value() ||
*arg->keyword == dummy[dummyArgIndex].keyword) {
actualForDummy[dummyArgIndex] = &*arg;
found = true;
break;
int slot{missingActualArguments};
for (std::size_t j{0}; j < dummyArgPatterns && !found; ++j) {
if (arg->keyword.has_value()) {
found = *arg->keyword == dummy[j].keyword;
if (found) {
if (const auto *previous{actualForDummy[j]}) {
if (previous->keyword.has_value()) {
messages.Say(*arg->keyword,
"repeated keyword argument to intrinsic '%s'"_err_en_US,
name);
} else {
messages.Say(*arg->keyword,
"keyword argument to intrinsic '%s' was supplied "
"positionally by an earlier actual argument"_err_en_US,
name);
}
return std::nullopt;
}
}
} else {
found = actualForDummy[j] == nullptr && slot-- == 0;
}
if (found) {
actualForDummy[j] = &*arg;
}
}
if (!found) {
if (arg->keyword.has_value()) {
messages.Say(*arg->keyword,
"unknown keyword argument to intrinsic '%s'"_err_en_US, name);
if (repeatLastDummy && !arg->keyword.has_value()) {
// MAX/MIN argument after the 2nd
actualForDummy.push_back(&*arg);
} else {
messages.Say(
"too many actual arguments for intrinsic '%s'"_err_en_US, name);
if (arg->keyword.has_value()) {
messages.Say(*arg->keyword,
"unknown keyword argument to intrinsic '%s'"_err_en_US, name);
} else {
messages.Say(
"too many actual arguments for intrinsic '%s'"_err_en_US, name);
}
return std::nullopt;
}
return std::nullopt;
}
}
}
std::size_t dummies{actualForDummy.size()};
// Check types and kinds of the actual arguments against the intrinsic's
// interface. Ensure that two or more arguments that have to have the same
// type and kind do so. Check for missing non-optional arguments now, too.
@ -805,13 +839,13 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
const IntrinsicDummyArgument *kindDummyArg{nullptr};
const ActualArgument *kindArg{nullptr};
bool hasDimArg{false};
for (int dummyArgIndex{0}; dummyArgIndex < dummies; ++dummyArgIndex) {
const IntrinsicDummyArgument &d{dummy[dummyArgIndex]};
for (std::size_t j{0}; j < dummies; ++j) {
const IntrinsicDummyArgument &d{dummy[std::min(j, dummyArgPatterns - 1)]};
if (d.typePattern.kindCode == KindCode::kindArg) {
CHECK(kindDummyArg == nullptr);
kindDummyArg = &d;
}
const ActualArgument *arg{actualForDummy[dummyArgIndex]};
const ActualArgument *arg{actualForDummy[j]};
if (!arg) {
if (d.optionality == Optionality::required) {
messages.Say("missing mandatory '%s=' argument"_err_en_US, d.keyword);
@ -895,9 +929,9 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
const ActualArgument *knownArg{nullptr};
const ActualArgument *shapeArg{nullptr};
int elementalRank{0};
for (int dummyArgIndex{0}; dummyArgIndex < dummies; ++dummyArgIndex) {
const IntrinsicDummyArgument &d{dummy[dummyArgIndex]};
if (const ActualArgument * arg{actualForDummy[dummyArgIndex]}) {
for (std::size_t j{0}; j < dummies; ++j) {
const IntrinsicDummyArgument &d{dummy[std::min(j, dummyArgPatterns - 1)]};
if (const ActualArgument * arg{actualForDummy[j]}) {
if (arg->isAssumedRank && d.rank != Rank::anyOrAssumedRank) {
messages.Say(
"assumed-rank array cannot be used for '%s=' argument"_err_en_US,
@ -1118,9 +1152,9 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
// Rearrange the actual arguments into dummy argument order.
ActualArguments rearranged(dummies);
for (int j{0}; j < dummies; ++j) {
for (std::size_t j{0}; j < dummies; ++j) {
if (ActualArgument * arg{actualForDummy[j]}) {
rearranged[j] = std::make_optional(std::move(*arg));
rearranged[j] = std::move(*arg);
}
}

View file

@ -191,6 +191,23 @@ void TestIntrinsics() {
.DoCall(Real8::dynamicType);
TestCall{table, "abs"}.Push(Const(Scalar<Char>{})).DoCall();
TestCall{table, "abs"}.Push(Const(Scalar<Log4>{})).DoCall();
TestCall maxCall{table, "max"}, max0Call{table, "max0"},
max1Call{table, "max1"};
TestCall amin0Call{table, "amin0"}, amin1Call{table, "amin1"};
for (int j{0}; j < 10; ++j) {
maxCall.Push(Const(Scalar<Real4>{}));
max0Call.Push(Const(Scalar<Real4>{}));
max1Call.Push(Const(Scalar<Real4>{}));
amin0Call.Push(Const(Scalar<Int4>{}));
amin1Call.Push(Const(Scalar<Int4>{}));
}
maxCall.DoCall(Real4::dynamicType);
max0Call.DoCall();
max1Call.DoCall(Int4::dynamicType);
amin0Call.DoCall(Real4::dynamicType);
amin1Call.DoCall();
// TODO: test other intrinsics
}
}