[flang] refactor a bit, and rearrange actual arguments into dummy order on intrinsic calls
Original-commit: flang-compiler/f18@1f50ace68b00a7db4b75ebce5616032501e48457
Reviewed-on: https://github.com/flang-compiler/f18/pull/219
Tree-same-pre-rewrite: false
diff --git a/flang/lib/evaluate/intrinsics.cc b/flang/lib/evaluate/intrinsics.cc
index 0488d46..b4406ca 100644
--- a/flang/lib/evaluate/intrinsics.cc
+++ b/flang/lib/evaluate/intrinsics.cc
@@ -201,8 +201,8 @@
IntrinsicDummyArgument dummy[maxArguments];
TypePattern result;
Rank rank{Rank::elemental};
- std::optional<SpecificIntrinsic> Match(const CallCharacteristics &,
- const semantics::IntrinsicTypeDefaultKinds &,
+ std::optional<SpecificCall> Match(const CallCharacteristics &,
+ const semantics::IntrinsicTypeDefaultKinds &, ActualArguments &,
parser::ContextualMessages &messages) const;
std::ostream &Dump(std::ostream &) const;
};
@@ -285,6 +285,7 @@
{{"array", SameType, Rank::array}, {"shift", AnyInt, Rank::dimRemoved},
OptionalDIM},
SameType, Rank::array},
+ {"dble", {{"a", AnyNumeric, Rank::elementalOrBOZ}}, DoublePrecision},
{"dim", {{"x", SameIntOrReal}, {"y", SameIntOrReal}}, SameIntOrReal},
{"dot_product",
{{"vector_a", AnyLogical, Rank::vector},
@@ -665,8 +666,6 @@
{{"datan2", {{"y", DoublePrecision}, {"x", DoublePrecision}},
DoublePrecision},
"atan2"},
- {{"dble", {{"a", DefaultReal}, DefaultingKIND}, DoublePrecision}, "real",
- true},
{{"dcos", {{"x", DoublePrecision}}, DoublePrecision}, "cos"},
{{"dcosh", {{"x", DoublePrecision}}, DoublePrecision}, "cosh"},
{{"ddim", {{"x", DoublePrecision}, {"y", DoublePrecision}},
@@ -754,46 +753,48 @@
// Intrinsic interface matching against the arguments of a particular
// procedure reference.
-std::optional<SpecificIntrinsic> IntrinsicInterface::Match(
+std::optional<SpecificCall> IntrinsicInterface::Match(
const CallCharacteristics &call,
const semantics::IntrinsicTypeDefaultKinds &defaults,
- parser::ContextualMessages &messages) const {
+ ActualArguments &arguments, parser::ContextualMessages &messages) const {
// 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.
- const ActualArgument *actualForDummy[maxArguments];
+ ActualArgument *actualForDummy[maxArguments];
int dummies{0};
for (; dummies < maxArguments && dummy[dummies].keyword != nullptr;
++dummies) {
actualForDummy[dummies] = nullptr;
}
- for (const ActualArgument &arg : call.arguments) {
- if (arg.isAlternateReturn) {
- messages.Say(
- "alternate return specifier not acceptable on call to intrinsic '%s'"_err_en_US,
- name);
- 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;
+ for (std::optional<ActualArgument> &arg : arguments) {
+ if (arg.has_value()) {
+ if (arg->isAlternateReturn) {
+ messages.Say(
+ "alternate return specifier not acceptable on call to intrinsic '%s'"_err_en_US,
+ name);
+ 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;
+ }
}
}
- }
- if (!found) {
- 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);
+ if (!found) {
+ 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;
}
}
@@ -970,96 +971,103 @@
}
// Calculate the characteristics of the function result, if any
+ std::optional<DynamicType> resultType;
if (result.categorySet.empty()) {
- CHECK(result.kindCode == KindCode::none);
- return std::make_optional<SpecificIntrinsic>(name);
- }
- // Determine the result type.
- DynamicType resultType{*result.categorySet.LeastElement(), 0};
- switch (result.kindCode) {
- case KindCode::defaultIntegerKind:
- CHECK(result.categorySet == IntType);
- CHECK(resultType.category == TypeCategory::Integer);
- resultType.kind = defaults.GetDefaultKind(TypeCategory::Integer);
- break;
- case KindCode::defaultRealKind:
- CHECK(result.categorySet == CategorySet{resultType.category});
- CHECK(FloatingType.test(resultType.category));
- resultType.kind = defaults.GetDefaultKind(TypeCategory::Real);
- break;
- case KindCode::doublePrecision:
- CHECK(result.categorySet == RealType);
- CHECK(resultType.category == TypeCategory::Real);
- resultType.kind = defaults.doublePrecisionKind();
- break;
- case KindCode::defaultCharKind:
- CHECK(result.categorySet == CharType);
- CHECK(resultType.category == TypeCategory::Character);
- resultType.kind = defaults.GetDefaultKind(TypeCategory::Character);
- break;
- case KindCode::defaultLogicalKind:
- CHECK(result.categorySet == LogicalType);
- CHECK(resultType.category == TypeCategory::Logical);
- resultType.kind = defaults.GetDefaultKind(TypeCategory::Logical);
- break;
- case KindCode::same:
- CHECK(sameArg != nullptr);
- if (std::optional<DynamicType> aType{sameArg->GetType()}) {
- if (result.categorySet.test(aType->category)) {
- resultType = *aType;
- } else {
- resultType.kind = aType->kind;
- }
+ if (!call.isSubroutineCall) {
+ return std::nullopt;
}
- break;
- case KindCode::effectiveKind:
- CHECK(kindDummyArg != nullptr);
- CHECK(result.categorySet == CategorySet{resultType.category});
- if (kindArg != nullptr) {
- if (auto *jExpr{std::get_if<Expr<SomeInteger>>(&kindArg->value->u)}) {
- CHECK(jExpr->Rank() == 0);
- if (auto value{jExpr->ScalarValue()}) {
- if (auto code{value->ToInt64()}) {
- if (IsValidKindOfIntrinsicType(resultType.category, *code)) {
- resultType.kind = *code;
- break;
+ CHECK(result.kindCode == KindCode::none);
+ } else {
+ // Determine the result type.
+ if (call.isSubroutineCall) {
+ return std::nullopt;
+ }
+ resultType = DynamicType{*result.categorySet.LeastElement(), 0};
+ switch (result.kindCode) {
+ case KindCode::defaultIntegerKind:
+ CHECK(result.categorySet == IntType);
+ CHECK(resultType->category == TypeCategory::Integer);
+ resultType->kind = defaults.GetDefaultKind(TypeCategory::Integer);
+ break;
+ case KindCode::defaultRealKind:
+ CHECK(result.categorySet == CategorySet{resultType->category});
+ CHECK(FloatingType.test(resultType->category));
+ resultType->kind = defaults.GetDefaultKind(TypeCategory::Real);
+ break;
+ case KindCode::doublePrecision:
+ CHECK(result.categorySet == RealType);
+ CHECK(resultType->category == TypeCategory::Real);
+ resultType->kind = defaults.doublePrecisionKind();
+ break;
+ case KindCode::defaultCharKind:
+ CHECK(result.categorySet == CharType);
+ CHECK(resultType->category == TypeCategory::Character);
+ resultType->kind = defaults.GetDefaultKind(TypeCategory::Character);
+ break;
+ case KindCode::defaultLogicalKind:
+ CHECK(result.categorySet == LogicalType);
+ CHECK(resultType->category == TypeCategory::Logical);
+ resultType->kind = defaults.GetDefaultKind(TypeCategory::Logical);
+ break;
+ case KindCode::same:
+ CHECK(sameArg != nullptr);
+ if (std::optional<DynamicType> aType{sameArg->GetType()}) {
+ if (result.categorySet.test(aType->category)) {
+ resultType = *aType;
+ } else {
+ resultType->kind = aType->kind;
+ }
+ }
+ break;
+ case KindCode::effectiveKind:
+ CHECK(kindDummyArg != nullptr);
+ CHECK(result.categorySet == CategorySet{resultType->category});
+ if (kindArg != nullptr) {
+ if (auto *jExpr{std::get_if<Expr<SomeInteger>>(&kindArg->value->u)}) {
+ CHECK(jExpr->Rank() == 0);
+ if (auto value{jExpr->ScalarValue()}) {
+ if (auto code{value->ToInt64()}) {
+ if (IsValidKindOfIntrinsicType(resultType->category, *code)) {
+ resultType->kind = *code;
+ break;
+ }
}
}
}
+ messages.Say("'kind=' argument must be a constant scalar integer "
+ "whose value is a supported kind for the "
+ "intrinsic result type"_err_en_US);
+ return std::nullopt;
+ } else if (kindDummyArg->optionality == Optionality::defaultsToSameKind) {
+ CHECK(sameArg != nullptr);
+ resultType = *sameArg->GetType();
+ } else if (kindDummyArg->optionality ==
+ Optionality::defaultsToSubscriptKind) {
+ CHECK(resultType->category == TypeCategory::Integer);
+ resultType->kind = defaults.subscriptIntegerKind();
+ } else {
+ CHECK(kindDummyArg->optionality ==
+ Optionality::defaultsToDefaultForResult);
+ resultType->kind = defaults.GetDefaultKind(resultType->category);
}
- messages.Say("'kind=' argument must be a constant scalar integer "
- "whose value is a supported kind for the "
- "intrinsic result type"_err_en_US);
- return std::nullopt;
- } else if (kindDummyArg->optionality == Optionality::defaultsToSameKind) {
- CHECK(sameArg != nullptr);
- resultType = *sameArg->GetType();
- } else if (kindDummyArg->optionality ==
- Optionality::defaultsToSubscriptKind) {
- CHECK(resultType.category == TypeCategory::Integer);
- resultType.kind = defaults.subscriptIntegerKind();
- } else {
- CHECK(
- kindDummyArg->optionality == Optionality::defaultsToDefaultForResult);
- resultType.kind = defaults.GetDefaultKind(resultType.category);
+ break;
+ case KindCode::likeMultiply:
+ CHECK(dummies >= 2);
+ CHECK(actualForDummy[0] != nullptr);
+ CHECK(actualForDummy[1] != nullptr);
+ resultType = actualForDummy[0]->GetType()->ResultTypeForMultiply(
+ *actualForDummy[1]->GetType());
+ break;
+ case KindCode::typeless:
+ case KindCode::teamType:
+ case KindCode::any:
+ case KindCode::kindArg:
+ case KindCode::dimArg:
+ common::die(
+ "INTERNAL: bad KindCode appears on intrinsic '%s' result", name);
+ break;
+ default: CRASH_NO_CASE;
}
- break;
- case KindCode::likeMultiply:
- CHECK(dummies >= 2);
- CHECK(actualForDummy[0] != nullptr);
- CHECK(actualForDummy[1] != nullptr);
- resultType = actualForDummy[0]->GetType()->ResultTypeForMultiply(
- *actualForDummy[1]->GetType());
- break;
- case KindCode::typeless:
- case KindCode::teamType:
- case KindCode::any:
- case KindCode::kindArg:
- case KindCode::dimArg:
- common::die(
- "INTERNAL: bad KindCode appears on intrinsic '%s' result", name);
- break;
- default: CRASH_NO_CASE;
}
// At this point, the call is acceptable.
@@ -1108,8 +1116,17 @@
attrs.set(semantics::Attr::ELEMENTAL);
}
- return std::make_optional<SpecificIntrinsic>(
- name, resultType, resultRank, attrs);
+ // Rearrange the actual arguments into dummy argument order.
+ ActualArguments rearranged(dummies);
+ for (int j{0}; j < dummies; ++j) {
+ if (ActualArgument * arg{actualForDummy[j]}) {
+ rearranged[j] = std::make_optional(std::move(*arg));
+ }
+ }
+
+ return {SpecificCall{
+ SpecificIntrinsic{name, std::move(resultType), resultRank, attrs},
+ std::move(rearranged)}};
}
struct IntrinsicProcTable::Implementation {
@@ -1123,8 +1140,8 @@
}
}
- std::optional<SpecificIntrinsic> Probe(
- const CallCharacteristics &, parser::ContextualMessages *) const;
+ std::optional<SpecificCall> Probe(const CallCharacteristics &,
+ ActualArguments &, parser::ContextualMessages *) const;
semantics::IntrinsicTypeDefaultKinds defaults;
std::multimap<std::string, const IntrinsicInterface *> genericFuncs;
@@ -1134,8 +1151,8 @@
// Probe the configured intrinsic procedure pattern tables in search of a
// match for a given procedure reference.
-std::optional<SpecificIntrinsic> IntrinsicProcTable::Implementation::Probe(
- const CallCharacteristics &call,
+std::optional<SpecificCall> IntrinsicProcTable::Implementation::Probe(
+ const CallCharacteristics &call, ActualArguments &arguments,
parser::ContextualMessages *messages) const {
if (call.isSubroutineCall) {
return std::nullopt; // TODO
@@ -1149,12 +1166,14 @@
std::string name{call.name.ToString()};
auto specificRange{specificFuncs.equal_range(name)};
for (auto iter{specificRange.first}; iter != specificRange.second; ++iter) {
- if (auto specific{iter->second->Match(call, defaults, specificErrors)}) {
+ if (auto specificCall{
+ iter->second->Match(call, defaults, arguments, specificErrors)}) {
if (const char *genericName{iter->second->generic}) {
- specific->name = genericName;
+ specificCall->specificIntrinsic.name = genericName;
}
- specific->isRestrictedSpecific = iter->second->isRestrictedSpecific;
- return specific;
+ specificCall->specificIntrinsic.isRestrictedSpecific =
+ iter->second->isRestrictedSpecific;
+ return specificCall;
}
}
// Probe the generic intrinsic function table next.
@@ -1164,22 +1183,23 @@
finalBuffer ? &genericBuffer : nullptr};
auto genericRange{genericFuncs.equal_range(name)};
for (auto iter{genericRange.first}; iter != genericRange.second; ++iter) {
- if (auto specific{iter->second->Match(call, defaults, genericErrors)}) {
- return specific;
+ if (auto specificCall{
+ iter->second->Match(call, defaults, arguments, genericErrors)}) {
+ return specificCall;
}
}
// Special cases of intrinsic functions
if (call.name.ToString() == "null") {
- if (call.arguments.size() == 0) {
+ if (arguments.size() == 0) {
// TODO: NULL() result type is determined by context
// Can pass that context in, or return a token distinguishing
// NULL, or represent NULL as a new kind of top-level expression
- } else if (call.arguments.size() > 1) {
+ } else if (arguments.size() > 1) {
genericErrors.Say("too many arguments to NULL()"_err_en_US);
- } else if (call.arguments[0].keyword.has_value() &&
- call.arguments[0].keyword->ToString() != "mold") {
+ } else if (arguments[0].has_value() && arguments[0]->keyword.has_value() &&
+ arguments[0]->keyword->ToString() != "mold") {
genericErrors.Say("unknown argument '%s' to NULL()"_err_en_US,
- call.arguments[0].keyword->ToString().data());
+ arguments[0]->keyword->ToString().data());
} else {
// TODO: Argument must be pointer, procedure pointer, or allocatable.
// Characteristics, including dynamic length type parameter values,
@@ -1211,11 +1231,11 @@
return result;
}
-std::optional<SpecificIntrinsic> IntrinsicProcTable::Probe(
- const CallCharacteristics &call,
+std::optional<SpecificCall> IntrinsicProcTable::Probe(
+ const CallCharacteristics &call, ActualArguments &arguments,
parser::ContextualMessages *messages) const {
CHECK(impl_ != nullptr || !"IntrinsicProcTable: not configured");
- return impl_->Probe(call, messages);
+ return impl_->Probe(call, arguments, messages);
}
std::ostream &SpecificIntrinsic::Dump(std::ostream &o) const {