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