1 // Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "characteristics.h"
16 #include "check-expression.h"
17 #include "fold.h"
18 #include "intrinsics.h"
19 #include "tools.h"
20 #include "type.h"
21 #include "../common/indirection.h"
22 #include "../parser/message.h"
23 #include "../semantics/scope.h"
24 #include "../semantics/symbol.h"
25 #include <initializer_list>
26 #include <ostream>
27
28 using namespace Fortran::parser::literals;
29
30 namespace Fortran::evaluate::characteristics {
31
32 // Copy attributes from a symbol to dst based on the mapping in pairs.
33 template<typename A, typename B>
CopyAttrs(const semantics::Symbol & src,A & dst,const std::initializer_list<std::pair<semantics::Attr,B>> & pairs)34 static void CopyAttrs(const semantics::Symbol &src, A &dst,
35 const std::initializer_list<std::pair<semantics::Attr, B>> &pairs) {
36 for (const auto &pair : pairs) {
37 if (src.attrs().test(pair.first)) {
38 dst.attrs.set(pair.second);
39 }
40 }
41 }
42
operator ==(const TypeAndShape & that) const43 bool TypeAndShape::operator==(const TypeAndShape &that) const {
44 return type_ == that.type_ && shape_ == that.shape_ && attrs_ == that.attrs_;
45 }
46
Characterize(const semantics::Symbol & symbol)47 std::optional<TypeAndShape> TypeAndShape::Characterize(
48 const semantics::Symbol &symbol) {
49 return std::visit(
50 common::visitors{
51 [&](const semantics::ObjectEntityDetails &object) {
52 return Characterize(object);
53 },
54 [&](const semantics::ProcEntityDetails &proc) {
55 const semantics::ProcInterface &interface{proc.interface()};
56 if (interface.type()) {
57 return Characterize(*interface.type());
58 } else {
59 return Characterize(*interface.symbol());
60 }
61 },
62 [&](const semantics::UseDetails &use) {
63 return Characterize(use.symbol());
64 },
65 [&](const semantics::HostAssocDetails &assoc) {
66 return Characterize(assoc.symbol());
67 },
68 [](const semantics::AssocEntityDetails &assoc) {
69 if (const semantics::Symbol *
70 nested{UnwrapWholeSymbolDataRef(assoc.expr())}) {
71 return Characterize(*nested);
72 } else {
73 return std::optional<TypeAndShape>{};
74 }
75 },
76 [](const auto &) { return std::optional<TypeAndShape>{}; },
77 },
78 symbol.details());
79 }
80
Characterize(const semantics::ObjectEntityDetails & object)81 std::optional<TypeAndShape> TypeAndShape::Characterize(
82 const semantics::ObjectEntityDetails &object) {
83 if (auto type{DynamicType::From(object.type())}) {
84 TypeAndShape result{std::move(*type)};
85 result.AcquireShape(object);
86 return result;
87 } else {
88 return std::nullopt;
89 }
90 }
91
Characterize(const semantics::DeclTypeSpec & spec)92 std::optional<TypeAndShape> TypeAndShape::Characterize(
93 const semantics::DeclTypeSpec &spec) {
94 if (auto type{DynamicType::From(spec)}) {
95 return TypeAndShape{std::move(*type)};
96 } else {
97 return std::nullopt;
98 }
99 }
100
IsCompatibleWith(parser::ContextualMessages & messages,const TypeAndShape & that) const101 bool TypeAndShape::IsCompatibleWith(
102 parser::ContextualMessages &messages, const TypeAndShape &that) const {
103 const auto &len{that.LEN()};
104 if (!type_.IsTypeCompatibleWith(that.type_)) {
105 std::stringstream lenstr;
106 if (len) {
107 len->AsFortran(lenstr);
108 }
109 messages.Say("Target type '%s' is not compatible with '%s'"_err_en_US,
110 that.type_.AsFortran(lenstr.str()), type_.AsFortran());
111 return false;
112 }
113 if (auto myLEN{ToInt64(LEN())}) {
114 if (auto thatLEN{ToInt64(len)}) {
115 if (*thatLEN < *myLEN) {
116 messages.Say(
117 "Warning: effective length '%jd' is less than expected length '%jd'"_en_US,
118 *thatLEN, *myLEN);
119 }
120 }
121 }
122 return CheckConformance(messages, shape_, that.shape_);
123 }
124
AcquireShape(const semantics::ObjectEntityDetails & object)125 void TypeAndShape::AcquireShape(const semantics::ObjectEntityDetails &object) {
126 CHECK(shape_.empty() && !attrs_.test(Attr::AssumedRank));
127 if (object.IsAssumedRank()) {
128 attrs_.set(Attr::AssumedRank);
129 return;
130 }
131 if (object.IsAssumedShape()) {
132 attrs_.set(Attr::AssumedShape);
133 }
134 if (object.IsAssumedSize()) {
135 attrs_.set(Attr::AssumedSize);
136 }
137 if (object.IsCoarray()) {
138 attrs_.set(Attr::Coarray);
139 }
140 for (const semantics::ShapeSpec &dim : object.shape()) {
141 if (dim.ubound().GetExplicit().has_value()) {
142 Expr<SubscriptInteger> extent{*dim.ubound().GetExplicit()};
143 if (dim.lbound().GetExplicit().has_value()) {
144 extent = std::move(extent) +
145 common::Clone(*dim.lbound().GetExplicit()) -
146 Expr<SubscriptInteger>{1};
147 }
148 shape_.emplace_back(std::move(extent));
149 } else {
150 shape_.push_back(std::nullopt);
151 }
152 }
153 }
154
AcquireLEN()155 void TypeAndShape::AcquireLEN() {
156 if (type_.category() == TypeCategory::Character) {
157 if (const auto *param{type_.charLength()}) {
158 if (const auto &intExpr{param->GetExplicit()}) {
159 LEN_ = *intExpr;
160 }
161 }
162 }
163 }
164
Dump(std::ostream & o) const165 std::ostream &TypeAndShape::Dump(std::ostream &o) const {
166 std::stringstream LENstr;
167 if (LEN_.has_value()) {
168 LEN_->AsFortran(LENstr);
169 }
170 o << type_.AsFortran(LENstr.str());
171 attrs_.Dump(o, EnumToString);
172 if (!shape_.empty()) {
173 o << " dimension(";
174 char sep{'('};
175 for (const auto &expr : shape_) {
176 o << sep;
177 sep = ',';
178 if (expr.has_value()) {
179 expr->AsFortran(o);
180 } else {
181 o << ':';
182 }
183 }
184 o << ')';
185 }
186 return o;
187 }
188
operator ==(const DummyDataObject & that) const189 bool DummyDataObject::operator==(const DummyDataObject &that) const {
190 return type == that.type && attrs == that.attrs && intent == that.intent &&
191 coshape == that.coshape;
192 }
193
Characterize(const semantics::Symbol & symbol)194 std::optional<DummyDataObject> DummyDataObject::Characterize(
195 const semantics::Symbol &symbol) {
196 if (const auto *obj{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
197 if (auto type{TypeAndShape::Characterize(*obj)}) {
198 DummyDataObject result{*type};
199 using semantics::Attr;
200 CopyAttrs<DummyDataObject, DummyDataObject::Attr>(symbol, result,
201 {
202 {Attr::OPTIONAL, DummyDataObject::Attr::Optional},
203 {Attr::ALLOCATABLE, DummyDataObject::Attr::Allocatable},
204 {Attr::ASYNCHRONOUS, DummyDataObject::Attr::Asynchronous},
205 {Attr::CONTIGUOUS, DummyDataObject::Attr::Contiguous},
206 {Attr::VALUE, DummyDataObject::Attr::Value},
207 {Attr::VOLATILE, DummyDataObject::Attr::Volatile},
208 {Attr::POINTER, DummyDataObject::Attr::Pointer},
209 {Attr::TARGET, DummyDataObject::Attr::Target},
210 });
211 if (symbol.attrs().test(semantics::Attr::INTENT_IN)) {
212 result.intent = common::Intent::In;
213 }
214 if (symbol.attrs().test(semantics::Attr::INTENT_OUT)) {
215 CHECK(result.intent == common::Intent::Default);
216 result.intent = common::Intent::Out;
217 }
218 if (symbol.attrs().test(semantics::Attr::INTENT_INOUT)) {
219 CHECK(result.intent == common::Intent::Default);
220 result.intent = common::Intent::InOut;
221 }
222 return result;
223 }
224 }
225 return std::nullopt;
226 }
227
CanBePassedViaImplicitInterface() const228 bool DummyDataObject::CanBePassedViaImplicitInterface() const {
229 if ((attrs &
230 Attrs{Attr::Allocatable, Attr::Asynchronous, Attr::Optional,
231 Attr::Pointer, Attr::Target, Attr::Value, Attr::Volatile})
232 .any()) {
233 return false; // 15.4.2.2(3)(a)
234 } else if ((type.attrs() &
235 TypeAndShape::Attrs{TypeAndShape::Attr::AssumedShape,
236 TypeAndShape::Attr::AssumedRank,
237 TypeAndShape::Attr::Coarray})
238 .any()) {
239 return false; // 15.4.2.2(3)(b-d)
240 } else if (type.type().IsPolymorphic()) {
241 return false; // 15.4.2.2(3)(f)
242 } else if (type.type().category() == TypeCategory::Derived) {
243 if (!type.type().GetDerivedTypeSpec().parameters().empty()) {
244 return false; // 15.4.2.2(3)(e)
245 }
246 }
247 return true;
248 }
249
Dump(std::ostream & o) const250 std::ostream &DummyDataObject::Dump(std::ostream &o) const {
251 attrs.Dump(o, EnumToString);
252 if (intent != common::Intent::Default) {
253 o << "INTENT(" << common::EnumToString(intent) << ')';
254 }
255 type.Dump(o);
256 if (!coshape.empty()) {
257 char sep{'['};
258 for (const auto &expr : coshape) {
259 expr.AsFortran(o << sep);
260 sep = ',';
261 }
262 }
263 return o;
264 }
265
DummyProcedure(Procedure && p)266 DummyProcedure::DummyProcedure(Procedure &&p)
267 : procedure{new Procedure{std::move(p)}} {}
268
operator ==(const DummyProcedure & that) const269 bool DummyProcedure::operator==(const DummyProcedure &that) const {
270 return attrs == that.attrs && procedure.value() == that.procedure.value();
271 }
272
Characterize(const semantics::Symbol & symbol,const IntrinsicProcTable & intrinsics)273 std::optional<DummyProcedure> DummyProcedure::Characterize(
274 const semantics::Symbol &symbol, const IntrinsicProcTable &intrinsics) {
275 if (auto procedure{Procedure::Characterize(symbol, intrinsics)}) {
276 DummyProcedure result{std::move(procedure.value())};
277 CopyAttrs<DummyProcedure, DummyProcedure::Attr>(symbol, result,
278 {
279 {semantics::Attr::OPTIONAL, DummyProcedure::Attr::Optional},
280 {semantics::Attr::POINTER, DummyProcedure::Attr::Pointer},
281 });
282 return result;
283 } else {
284 return std::nullopt;
285 }
286 }
287
Dump(std::ostream & o) const288 std::ostream &DummyProcedure::Dump(std::ostream &o) const {
289 attrs.Dump(o, EnumToString);
290 procedure.value().Dump(o);
291 return o;
292 }
293
Dump(std::ostream & o) const294 std::ostream &AlternateReturn::Dump(std::ostream &o) const { return o << '*'; }
295
operator ==(const DummyArgument & that) const296 bool DummyArgument::operator==(const DummyArgument &that) const {
297 return u == that.u;
298 }
299
Characterize(const semantics::Symbol & symbol,const IntrinsicProcTable & intrinsics)300 std::optional<DummyArgument> DummyArgument::Characterize(
301 const semantics::Symbol &symbol, const IntrinsicProcTable &intrinsics) {
302 auto name{symbol.name().ToString()};
303 if (symbol.has<semantics::ObjectEntityDetails>()) {
304 if (auto obj{DummyDataObject::Characterize(symbol)}) {
305 return DummyArgument{std::move(name), std::move(obj.value())};
306 }
307 } else if (auto proc{DummyProcedure::Characterize(symbol, intrinsics)}) {
308 return DummyArgument{std::move(name), std::move(proc.value())};
309 }
310 return std::nullopt;
311 }
312
IsOptional() const313 bool DummyArgument::IsOptional() const {
314 return std::visit(
315 common::visitors{
316 [](const DummyDataObject &data) {
317 return data.attrs.test(DummyDataObject::Attr::Optional);
318 },
319 [](const DummyProcedure &proc) {
320 return proc.attrs.test(DummyProcedure::Attr::Optional);
321 },
322 [](const AlternateReturn &) { return false; },
323 },
324 u);
325 }
326
SetOptional(bool value)327 void DummyArgument::SetOptional(bool value) {
328 std::visit(
329 common::visitors{
330 [value](DummyDataObject &data) {
331 data.attrs.set(DummyDataObject::Attr::Optional, value);
332 },
333 [value](DummyProcedure &proc) {
334 proc.attrs.set(DummyProcedure::Attr::Optional, value);
335 },
336 [](AlternateReturn &) { DIE("cannot set optional"); },
337 },
338 u);
339 }
340
CanBePassedViaImplicitInterface() const341 bool DummyArgument::CanBePassedViaImplicitInterface() const {
342 if (const auto *object{std::get_if<DummyDataObject>(&u)}) {
343 return object->CanBePassedViaImplicitInterface();
344 } else {
345 return true;
346 }
347 }
348
Dump(std::ostream & o) const349 std::ostream &DummyArgument::Dump(std::ostream &o) const {
350 if (!name.empty()) {
351 o << name << '=';
352 }
353 if (pass) {
354 o << " PASS";
355 }
356 std::visit([&](const auto &x) { x.Dump(o); }, u);
357 return o;
358 }
359
FunctionResult(DynamicType t)360 FunctionResult::FunctionResult(DynamicType t) : u{TypeAndShape{t}} {}
FunctionResult(TypeAndShape && t)361 FunctionResult::FunctionResult(TypeAndShape &&t) : u{std::move(t)} {}
FunctionResult(Procedure && p)362 FunctionResult::FunctionResult(Procedure &&p) : u{std::move(p)} {}
363 FunctionResult::~FunctionResult() = default;
364
operator ==(const FunctionResult & that) const365 bool FunctionResult::operator==(const FunctionResult &that) const {
366 return attrs == that.attrs && u == that.u;
367 }
368
Characterize(const Symbol & symbol,const IntrinsicProcTable & intrinsics)369 std::optional<FunctionResult> FunctionResult::Characterize(
370 const Symbol &symbol, const IntrinsicProcTable &intrinsics) {
371 if (const auto *obj{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
372 if (auto type{TypeAndShape::Characterize(*obj)}) {
373 FunctionResult result{std::move(*type)};
374 CopyAttrs<FunctionResult, FunctionResult::Attr>(symbol, result,
375 {
376 {semantics::Attr::ALLOCATABLE, FunctionResult::Attr::Allocatable},
377 {semantics::Attr::CONTIGUOUS, FunctionResult::Attr::Contiguous},
378 {semantics::Attr::POINTER, FunctionResult::Attr::Pointer},
379 });
380 return result;
381 }
382 } else if (auto maybeProc{Procedure::Characterize(symbol, intrinsics)}) {
383 FunctionResult result{std::move(*maybeProc)};
384 result.attrs.set(FunctionResult::Attr::Pointer);
385 return result;
386 }
387 return std::nullopt;
388 }
389
IsAssumedLengthCharacter() const390 bool FunctionResult::IsAssumedLengthCharacter() const {
391 if (const auto *ts{std::get_if<TypeAndShape>(&u)}) {
392 return ts->type().IsAssumedLengthCharacter();
393 } else {
394 return false;
395 }
396 }
397
CanBeReturnedViaImplicitInterface() const398 bool FunctionResult::CanBeReturnedViaImplicitInterface() const {
399 if (attrs.test(Attr::Pointer) || attrs.test(Attr::Allocatable)) {
400 return false; // 15.4.2.2(4)(b)
401 } else if (const auto *typeAndShape{GetTypeAndShape()}) {
402 if (typeAndShape->Rank() > 0) {
403 return false; // 15.4.2.2(4)(a)
404 } else {
405 const DynamicType &type{typeAndShape->type()};
406 switch (type.category()) {
407 case TypeCategory::Character:
408 if (!type.IsAssumedLengthCharacter()) {
409 if (const auto *param{type.charLength()}) {
410 if (const auto &expr{param->GetExplicit()}) {
411 return IsConstantExpr(*expr); // 15.4.2.2(4)(c)
412 }
413 }
414 }
415 return false;
416 case TypeCategory::Derived:
417 if (!type.IsPolymorphic()) {
418 const auto &spec{type.GetDerivedTypeSpec()};
419 for (const auto &pair : spec.parameters()) {
420 if (const auto &expr{pair.second.GetExplicit()}) {
421 if (!IsConstantExpr(*expr)) {
422 return false; // 15.4.2.2(4)(c)
423 }
424 }
425 }
426 return true;
427 }
428 return false;
429 default: return true;
430 }
431 }
432 } else {
433 return false; // 15.4.2.2(4)(b) - procedure pointer
434 }
435 }
436
Dump(std::ostream & o) const437 std::ostream &FunctionResult::Dump(std::ostream &o) const {
438 attrs.Dump(o, EnumToString);
439 std::visit(
440 common::visitors{
441 [&](const TypeAndShape &ts) { ts.Dump(o); },
442 [&](const CopyableIndirection<Procedure> &p) {
443 p.value().Dump(o << " procedure(") << ')';
444 },
445 },
446 u);
447 return o;
448 }
449
Procedure(FunctionResult && fr,DummyArguments && args,Attrs a)450 Procedure::Procedure(FunctionResult &&fr, DummyArguments &&args, Attrs a)
451 : functionResult{std::move(fr)}, dummyArguments{std::move(args)}, attrs{a} {}
Procedure(DummyArguments && args,Attrs a)452 Procedure::Procedure(DummyArguments &&args, Attrs a)
453 : dummyArguments{std::move(args)}, attrs{a} {}
454
operator ==(const Procedure & that) const455 bool Procedure::operator==(const Procedure &that) const {
456 return attrs == that.attrs && dummyArguments == that.dummyArguments &&
457 functionResult == that.functionResult;
458 }
459
Characterize(const semantics::Symbol & symbol,const IntrinsicProcTable & intrinsics)460 std::optional<Procedure> Procedure::Characterize(
461 const semantics::Symbol &symbol, const IntrinsicProcTable &intrinsics) {
462 Procedure result;
463 CopyAttrs<Procedure, Procedure::Attr>(symbol, result,
464 {
465 {semantics::Attr::PURE, Procedure::Attr::Pure},
466 {semantics::Attr::ELEMENTAL, Procedure::Attr::Elemental},
467 {semantics::Attr::BIND_C, Procedure::Attr::BindC},
468 });
469 auto SetFunctionResult{[&](const semantics::DeclTypeSpec *type) {
470 if (type != nullptr) {
471 if (auto resultType{DynamicType::From(*type)}) {
472 result.functionResult = FunctionResult{*resultType};
473 return true;
474 }
475 }
476 return false;
477 }};
478 return std::visit(
479 common::visitors{
480 [&](const semantics::SubprogramDetails &subp)
481 -> std::optional<Procedure> {
482 if (subp.isFunction()) {
483 auto fr{FunctionResult::Characterize(subp.result(), intrinsics)};
484 if (!fr) {
485 return std::nullopt;
486 }
487 result.functionResult = std::move(fr);
488 }
489 for (const semantics::Symbol *arg : subp.dummyArgs()) {
490 if (arg == nullptr) {
491 result.dummyArguments.emplace_back(AlternateReturn{});
492 } else if (auto argCharacteristics{
493 DummyArgument::Characterize(*arg, intrinsics)}) {
494 result.dummyArguments.emplace_back(
495 std::move(argCharacteristics.value()));
496 } else {
497 return std::nullopt;
498 }
499 }
500 return result;
501 },
502 [&](const semantics::ProcEntityDetails &proc)
503 -> std::optional<Procedure> {
504 if (symbol.attrs().test(semantics::Attr::INTRINSIC)) {
505 return intrinsics.IsUnrestrictedSpecificIntrinsicFunction(
506 symbol.name().ToString());
507 }
508 const semantics::ProcInterface &interface{proc.interface()};
509 if (const semantics::Symbol * interfaceSymbol{interface.symbol()}) {
510 auto characterized{Characterize(*interfaceSymbol, intrinsics)};
511 if (!characterized) {
512 return std::nullopt;
513 }
514 result = *characterized;
515 } else {
516 result.attrs.set(Procedure::Attr::ImplicitInterface);
517 if (symbol.test(semantics::Symbol::Flag::Function)) {
518 if (!SetFunctionResult(interface.type())) {
519 return std::nullopt;
520 }
521 } else {
522 // subroutine, not function
523 if (interface.type() != nullptr) {
524 return std::nullopt;
525 }
526 }
527 }
528 // The PASS name, if any, is not a characteristic.
529 return result;
530 },
531 [&](const semantics::ProcBindingDetails &binding) {
532 if (auto result{Characterize(binding.symbol(), intrinsics)}) {
533 if (const auto passIndex{binding.passIndex()}) {
534 auto &passArg{result->dummyArguments.at(*passIndex)};
535 passArg.pass = true;
536 if (const auto passName{binding.passName()}) {
537 CHECK(passArg.name == passName->ToString());
538 }
539 }
540 return result;
541 }
542 return std::optional<Procedure>{};
543 },
544 [&](const semantics::UseDetails &use) {
545 return Characterize(use.symbol(), intrinsics);
546 },
547 [&](const semantics::HostAssocDetails &assoc) {
548 return Characterize(assoc.symbol(), intrinsics);
549 },
550 [](const auto &) { return std::optional<Procedure>{}; },
551 },
552 symbol.details());
553 }
554
Characterize(const ProcedureDesignator & proc,const IntrinsicProcTable & intrinsics)555 std::optional<Procedure> Procedure::Characterize(
556 const ProcedureDesignator &proc, const IntrinsicProcTable &intrinsics) {
557 if (const auto *symbol{proc.GetSymbol()}) {
558 if (auto result{characteristics::Procedure::Characterize(
559 symbol->GetUltimate(), intrinsics)}) {
560 return result;
561 }
562 } else if (const auto *intrinsic{proc.GetSpecificIntrinsic()}) {
563 return intrinsic->characteristics.value();
564 }
565 return std::nullopt;
566 }
567
Characterize(const ProcedureRef & ref,const IntrinsicProcTable & intrinsics)568 std::optional<Procedure> Procedure::Characterize(
569 const ProcedureRef &ref, const IntrinsicProcTable &intrinsics) {
570 return Characterize(ref.proc(), intrinsics);
571 }
572
CanBeCalledViaImplicitInterface() const573 bool Procedure::CanBeCalledViaImplicitInterface() const {
574 if (attrs.test(Attr::Elemental) || attrs.test(Attr::BindC)) {
575 return false; // 15.4.2.2(5,6)
576 } else if (IsFunction() &&
577 !functionResult->CanBeReturnedViaImplicitInterface()) {
578 return false;
579 } else {
580 for (const DummyArgument &arg : dummyArguments) {
581 if (!arg.CanBePassedViaImplicitInterface()) {
582 return false;
583 }
584 }
585 return true;
586 }
587 }
588
Dump(std::ostream & o) const589 std::ostream &Procedure::Dump(std::ostream &o) const {
590 attrs.Dump(o, EnumToString);
591 if (functionResult.has_value()) {
592 functionResult->Dump(o << "TYPE(") << ") FUNCTION";
593 } else {
594 o << "SUBROUTINE";
595 }
596 char sep{'('};
597 for (const auto &dummy : dummyArguments) {
598 dummy.Dump(o << sep);
599 sep = ',';
600 }
601 return o << (sep == '(' ? "()" : ")");
602 }
603
604 // Utility class to determine if Procedures, etc. are distinguishable
605 class DistinguishUtils {
606 public:
607 // Are these procedures distinguishable for a generic name?
608 static bool Distinguishable(const Procedure &, const Procedure &);
609 // Are these procedures distinguishable for a generic operator or assignment?
610 static bool DistinguishableOpOrAssign(const Procedure &, const Procedure &);
611
612 private:
613 struct CountDummyProcedures {
CountDummyProceduresFortran::evaluate::characteristics::DistinguishUtils::CountDummyProcedures614 CountDummyProcedures(const DummyArguments &args) {
615 for (const DummyArgument &arg : args) {
616 if (std::holds_alternative<DummyProcedure>(arg.u)) {
617 total += 1;
618 notOptional += !arg.IsOptional();
619 }
620 }
621 }
622 int total{0};
623 int notOptional{0};
624 };
625
626 static bool Rule3Distinguishable(const Procedure &, const Procedure &);
627 static const DummyArgument *Rule1DistinguishingArg(
628 const DummyArguments &, const DummyArguments &);
629 static int FindFirstToDistinguishByPosition(
630 const DummyArguments &, const DummyArguments &);
631 static int FindLastToDistinguishByName(
632 const DummyArguments &, const DummyArguments &);
633 static int CountCompatibleWith(const DummyArgument &, const DummyArguments &);
634 static int CountNotDistinguishableFrom(
635 const DummyArgument &, const DummyArguments &);
636 static bool Distinguishable(const DummyArgument &, const DummyArgument &);
637 static bool Distinguishable(const DummyDataObject &, const DummyDataObject &);
638 static bool Distinguishable(const DummyProcedure &, const DummyProcedure &);
639 static bool Distinguishable(const FunctionResult &, const FunctionResult &);
640 static bool Distinguishable(const TypeAndShape &, const TypeAndShape &);
641 static bool IsTkrCompatible(const DummyArgument &, const DummyArgument &);
642 static bool IsTkrCompatible(const TypeAndShape &, const TypeAndShape &);
643 static const DummyArgument *GetAtEffectivePosition(
644 const DummyArguments &, int);
645 static const DummyArgument *GetPassArg(const Procedure &);
646 };
647
648 // Simpler distinguishability rules for operators and assignment
DistinguishableOpOrAssign(const Procedure & proc1,const Procedure & proc2)649 bool DistinguishUtils::DistinguishableOpOrAssign(
650 const Procedure &proc1, const Procedure &proc2) {
651 auto &args1{proc1.dummyArguments};
652 auto &args2{proc2.dummyArguments};
653 if (args1.size() != args2.size()) {
654 return true; // C1511: distinguishable based on number of arguments
655 }
656 for (std::size_t i{0}; i < args1.size(); ++i) {
657 if (Distinguishable(args1[i], args2[i])) {
658 return true; // C1511, C1512: distinguishable based on this arg
659 }
660 }
661 return false;
662 }
663
Distinguishable(const Procedure & proc1,const Procedure & proc2)664 bool DistinguishUtils::Distinguishable(
665 const Procedure &proc1, const Procedure &proc2) {
666 auto &args1{proc1.dummyArguments};
667 auto &args2{proc2.dummyArguments};
668 auto count1{CountDummyProcedures(args1)};
669 auto count2{CountDummyProcedures(args2)};
670 if (count1.notOptional > count2.total || count2.notOptional > count1.total) {
671 return true; // distinguishable based on C1514 rule 2
672 }
673 if (Rule3Distinguishable(proc1, proc2)) {
674 return true; // distinguishable based on C1514 rule 3
675 }
676 if (Rule1DistinguishingArg(args1, args2)) {
677 return true; // distinguishable based on C1514 rule 1
678 }
679 int pos1{FindFirstToDistinguishByPosition(args1, args2)};
680 int name1{FindLastToDistinguishByName(args1, args2)};
681 if (pos1 >= 0 && pos1 <= name1) {
682 return true; // distinguishable based on C1514 rule 4
683 }
684 int pos2{FindFirstToDistinguishByPosition(args2, args1)};
685 int name2{FindLastToDistinguishByName(args2, args1)};
686 if (pos2 >= 0 && pos2 <= name2) {
687 return true; // distinguishable based on C1514 rule 4
688 }
689 return false;
690 }
691
692 // C1514 rule 3: Procedures are distinguishable if both have a passed-object
693 // dummy argument and those are distinguishable.
Rule3Distinguishable(const Procedure & proc1,const Procedure & proc2)694 bool DistinguishUtils::Rule3Distinguishable(
695 const Procedure &proc1, const Procedure &proc2) {
696 const DummyArgument *pass1{GetPassArg(proc1)};
697 const DummyArgument *pass2{GetPassArg(proc2)};
698 return pass1 && pass2 && Distinguishable(*pass1, *pass2);
699 }
700
701 // Find a non-passed-object dummy data object in one of the argument lists
702 // that satisfies C1514 rule 1. I.e. x such that:
703 // - m is the number of dummy data objects in one that are nonoptional,
704 // are not passed-object, that x is TKR compatible with
705 // - n is the number of non-passed-object dummy data objects, in the other
706 // that are not distinguishable from x
707 // - m is greater than n
Rule1DistinguishingArg(const DummyArguments & args1,const DummyArguments & args2)708 const DummyArgument *DistinguishUtils::Rule1DistinguishingArg(
709 const DummyArguments &args1, const DummyArguments &args2) {
710 auto size1{args1.size()};
711 auto size2{args2.size()};
712 for (std::size_t i{0}; i < size1 + size2; ++i) {
713 const DummyArgument &x{i < size1 ? args1[i] : args2[i - size1]};
714 if (!x.pass && std::holds_alternative<DummyDataObject>(x.u)) {
715 if (CountCompatibleWith(x, args1) >
716 CountNotDistinguishableFrom(x, args2) ||
717 CountCompatibleWith(x, args2) >
718 CountNotDistinguishableFrom(x, args1)) {
719 return &x;
720 }
721 }
722 }
723 return nullptr;
724 }
725
726 // Find the index of the first nonoptional non-passed-object dummy argument
727 // in args1 at an effective position such that either:
728 // - args2 has no dummy argument at that effective position
729 // - the dummy argument at that position is distinguishable from it
FindFirstToDistinguishByPosition(const DummyArguments & args1,const DummyArguments & args2)730 int DistinguishUtils::FindFirstToDistinguishByPosition(
731 const DummyArguments &args1, const DummyArguments &args2) {
732 int effective{0}; // position of arg1 in list, ignoring passed arg
733 for (std::size_t i{0}; i < args1.size(); ++i) {
734 const DummyArgument &arg1{args1.at(i)};
735 if (!arg1.pass && !arg1.IsOptional()) {
736 const DummyArgument *arg2{GetAtEffectivePosition(args2, effective)};
737 if (!arg2 || Distinguishable(arg1, *arg2)) {
738 return i;
739 }
740 }
741 effective += !arg1.pass;
742 }
743 return -1;
744 }
745
746 // Find the index of the last nonoptional non-passed-object dummy argument
747 // in args1 whose name is such that either:
748 // - args2 has no dummy argument with that name
749 // - the dummy argument with that name is distinguishable from it
FindLastToDistinguishByName(const DummyArguments & args1,const DummyArguments & args2)750 int DistinguishUtils::FindLastToDistinguishByName(
751 const DummyArguments &args1, const DummyArguments &args2) {
752 std::map<std::string, const DummyArgument *> nameToArg;
753 for (const auto &arg2 : args2) {
754 nameToArg.emplace(arg2.name, &arg2);
755 }
756 for (int i = args1.size() - 1; i >= 0; --i) {
757 const DummyArgument &arg1{args1.at(i)};
758 if (!arg1.pass && !arg1.IsOptional()) {
759 auto it{nameToArg.find(arg1.name)};
760 if (it == nameToArg.end() || Distinguishable(arg1, *it->second)) {
761 return i;
762 }
763 }
764 }
765 return -1;
766 }
767
768 // Count the dummy data objects in args that are nonoptional, are not
769 // passed-object, and that x is TKR compatible with
CountCompatibleWith(const DummyArgument & x,const DummyArguments & args)770 int DistinguishUtils::CountCompatibleWith(
771 const DummyArgument &x, const DummyArguments &args) {
772 return std::count_if(args.begin(), args.end(), [&](const DummyArgument &y) {
773 return !y.pass && !y.IsOptional() && IsTkrCompatible(x, y);
774 });
775 }
776
777 // Return the number of dummy data objects in args that are not
778 // distinguishable from x and not passed-object.
CountNotDistinguishableFrom(const DummyArgument & x,const DummyArguments & args)779 int DistinguishUtils::CountNotDistinguishableFrom(
780 const DummyArgument &x, const DummyArguments &args) {
781 return std::count_if(args.begin(), args.end(), [&](const DummyArgument &y) {
782 return !y.pass && std::holds_alternative<DummyDataObject>(y.u) &&
783 !Distinguishable(y, x);
784 });
785 }
786
Distinguishable(const DummyArgument & x,const DummyArgument & y)787 bool DistinguishUtils::Distinguishable(
788 const DummyArgument &x, const DummyArgument &y) {
789 if (x.u.index() != y.u.index()) {
790 return true; // different kind: data/proc/alt-return
791 }
792 return std::visit(
793 common::visitors{
794 [&](const DummyDataObject &z) {
795 return Distinguishable(z, std::get<DummyDataObject>(y.u));
796 },
797 [&](const DummyProcedure &z) {
798 return Distinguishable(z, std::get<DummyProcedure>(y.u));
799 },
800 [&](const AlternateReturn &) { return false; },
801 },
802 x.u);
803 }
804
Distinguishable(const DummyDataObject & x,const DummyDataObject & y)805 bool DistinguishUtils::Distinguishable(
806 const DummyDataObject &x, const DummyDataObject &y) {
807 using Attr = DummyDataObject::Attr;
808 if (Distinguishable(x.type, y.type)) {
809 return true;
810 } else if (x.attrs.test(Attr::Allocatable) && y.attrs.test(Attr::Pointer) &&
811 y.intent != common::Intent::In) {
812 return true;
813 } else if (y.attrs.test(Attr::Allocatable) && x.attrs.test(Attr::Pointer) &&
814 x.intent != common::Intent::In) {
815 return true;
816 } else {
817 return false;
818 }
819 }
820
Distinguishable(const DummyProcedure & x,const DummyProcedure & y)821 bool DistinguishUtils::Distinguishable(
822 const DummyProcedure &x, const DummyProcedure &y) {
823 const Procedure &xProc{x.procedure.value()};
824 const Procedure &yProc{y.procedure.value()};
825 if (Distinguishable(xProc, yProc)) {
826 return true;
827 } else {
828 const std::optional<FunctionResult> &xResult{xProc.functionResult};
829 const std::optional<FunctionResult> &yResult{yProc.functionResult};
830 return xResult ? !yResult || Distinguishable(*xResult, *yResult)
831 : yResult.has_value();
832 }
833 }
834
Distinguishable(const FunctionResult & x,const FunctionResult & y)835 bool DistinguishUtils::Distinguishable(
836 const FunctionResult &x, const FunctionResult &y) {
837 if (x.u.index() != y.u.index()) {
838 return true; // one is data object, one is procedure
839 }
840 return std::visit(
841 common::visitors{
842 [&](const TypeAndShape &z) {
843 return Distinguishable(z, std::get<TypeAndShape>(y.u));
844 },
845 [&](const CopyableIndirection<Procedure> &z) {
846 return Distinguishable(z.value(),
847 std::get<CopyableIndirection<Procedure>>(y.u).value());
848 },
849 },
850 x.u);
851 }
852
Distinguishable(const TypeAndShape & x,const TypeAndShape & y)853 bool DistinguishUtils::Distinguishable(
854 const TypeAndShape &x, const TypeAndShape &y) {
855 return !IsTkrCompatible(x, y) && !IsTkrCompatible(y, x);
856 }
857
858 // Compatibility based on type, kind, and rank
IsTkrCompatible(const DummyArgument & x,const DummyArgument & y)859 bool DistinguishUtils::IsTkrCompatible(
860 const DummyArgument &x, const DummyArgument &y) {
861 const auto *obj1{std::get_if<DummyDataObject>(&x.u)};
862 const auto *obj2{std::get_if<DummyDataObject>(&y.u)};
863 return obj1 && obj2 && IsTkrCompatible(obj1->type, obj2->type);
864 }
IsTkrCompatible(const TypeAndShape & x,const TypeAndShape & y)865 bool DistinguishUtils::IsTkrCompatible(
866 const TypeAndShape &x, const TypeAndShape &y) {
867 return x.type().IsTkCompatibleWith(y.type()) &&
868 (x.attrs().test(TypeAndShape::Attr::AssumedRank) ||
869 y.attrs().test(TypeAndShape::Attr::AssumedRank) ||
870 x.Rank() == y.Rank());
871 }
872
873 // Return the argument at the given index, ignoring the passed arg
GetAtEffectivePosition(const DummyArguments & args,int index)874 const DummyArgument *DistinguishUtils::GetAtEffectivePosition(
875 const DummyArguments &args, int index) {
876 for (const DummyArgument &arg : args) {
877 if (!arg.pass) {
878 if (index == 0) {
879 return &arg;
880 }
881 --index;
882 }
883 }
884 return nullptr;
885 }
886
887 // Return the passed-object dummy argument of this procedure, if any
GetPassArg(const Procedure & proc)888 const DummyArgument *DistinguishUtils::GetPassArg(const Procedure &proc) {
889 for (const auto &arg : proc.dummyArguments) {
890 if (arg.pass) {
891 return &arg;
892 }
893 }
894 return nullptr;
895 }
896
Distinguishable(const Procedure & x,const Procedure & y)897 bool Distinguishable(const Procedure &x, const Procedure &y) {
898 return DistinguishUtils::Distinguishable(x, y);
899 }
900
DistinguishableOpOrAssign(const Procedure & x,const Procedure & y)901 bool DistinguishableOpOrAssign(const Procedure &x, const Procedure &y) {
902 return DistinguishUtils::DistinguishableOpOrAssign(x, y);
903 }
904
905 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyArgument)
906 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyProcedure)
907 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(FunctionResult)
908 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(Procedure)
909 }
910
911 template class Fortran::common::Indirection<
912 Fortran::evaluate::characteristics::Procedure, true>;
913