1 //===-- lib/Evaluate/characteristics.cpp ----------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "flang/Evaluate/characteristics.h"
10 #include "flang/Common/indirection.h"
11 #include "flang/Evaluate/check-expression.h"
12 #include "flang/Evaluate/fold.h"
13 #include "flang/Evaluate/intrinsics.h"
14 #include "flang/Evaluate/tools.h"
15 #include "flang/Evaluate/type.h"
16 #include "flang/Parser/message.h"
17 #include "flang/Semantics/scope.h"
18 #include "flang/Semantics/symbol.h"
19 #include "llvm/Support/raw_ostream.h"
20 #include <initializer_list>
21
22 using namespace Fortran::parser::literals;
23
24 namespace Fortran::evaluate::characteristics {
25
26 // Copy attributes from a symbol to dst based on the mapping in pairs.
27 template <typename A, typename B>
CopyAttrs(const semantics::Symbol & src,A & dst,const std::initializer_list<std::pair<semantics::Attr,B>> & pairs)28 static void CopyAttrs(const semantics::Symbol &src, A &dst,
29 const std::initializer_list<std::pair<semantics::Attr, B>> &pairs) {
30 for (const auto &pair : pairs) {
31 if (src.attrs().test(pair.first)) {
32 dst.attrs.set(pair.second);
33 }
34 }
35 }
36
37 // Shapes of function results and dummy arguments have to have
38 // the same rank, the same deferred dimensions, and the same
39 // values for explicit dimensions when constant.
ShapesAreCompatible(const Shape & x,const Shape & y)40 bool ShapesAreCompatible(const Shape &x, const Shape &y) {
41 if (x.size() != y.size()) {
42 return false;
43 }
44 auto yIter{y.begin()};
45 for (const auto &xDim : x) {
46 const auto &yDim{*yIter++};
47 if (xDim) {
48 if (!yDim || ToInt64(*xDim) != ToInt64(*yDim)) {
49 return false;
50 }
51 } else if (yDim) {
52 return false;
53 }
54 }
55 return true;
56 }
57
operator ==(const TypeAndShape & that) const58 bool TypeAndShape::operator==(const TypeAndShape &that) const {
59 return type_ == that.type_ && ShapesAreCompatible(shape_, that.shape_) &&
60 attrs_ == that.attrs_ && corank_ == that.corank_;
61 }
62
Characterize(const semantics::Symbol & symbol,FoldingContext & context)63 std::optional<TypeAndShape> TypeAndShape::Characterize(
64 const semantics::Symbol &symbol, FoldingContext &context) {
65 return std::visit(
66 common::visitors{
67 [&](const semantics::ObjectEntityDetails &object) {
68 auto result{Characterize(object, context)};
69 if (result &&
70 result->type().category() == TypeCategory::Character) {
71 if (auto len{DataRef{symbol}.LEN()}) {
72 result->set_LEN(Fold(context, std::move(*len)));
73 }
74 }
75 return result;
76 },
77 [&](const semantics::ProcEntityDetails &proc) {
78 const semantics::ProcInterface &interface{proc.interface()};
79 if (interface.type()) {
80 return Characterize(*interface.type());
81 } else if (interface.symbol()) {
82 return Characterize(*interface.symbol(), context);
83 } else {
84 return std::optional<TypeAndShape>{};
85 }
86 },
87 [&](const semantics::TypeParamDetails &tp) {
88 if (auto type{DynamicType::From(tp.type())}) {
89 return std::optional<TypeAndShape>{std::move(*type)};
90 } else {
91 return std::optional<TypeAndShape>{};
92 }
93 },
94 [&](const semantics::UseDetails &use) {
95 return Characterize(use.symbol(), context);
96 },
97 [&](const semantics::HostAssocDetails &assoc) {
98 return Characterize(assoc.symbol(), context);
99 },
100 [&](const semantics::AssocEntityDetails &assoc) {
101 return Characterize(assoc, context);
102 },
103 [](const auto &) { return std::optional<TypeAndShape>{}; },
104 },
105 symbol.details());
106 }
107
Characterize(const semantics::ObjectEntityDetails & object,FoldingContext & context)108 std::optional<TypeAndShape> TypeAndShape::Characterize(
109 const semantics::ObjectEntityDetails &object, FoldingContext &context) {
110 if (auto type{DynamicType::From(object.type())}) {
111 TypeAndShape result{std::move(*type)};
112 result.AcquireShape(object, context);
113 return result;
114 } else {
115 return std::nullopt;
116 }
117 }
118
Characterize(const semantics::AssocEntityDetails & assoc,FoldingContext & context)119 std::optional<TypeAndShape> TypeAndShape::Characterize(
120 const semantics::AssocEntityDetails &assoc, FoldingContext &context) {
121 if (auto type{DynamicType::From(assoc.type())}) {
122 if (auto shape{GetShape(context, assoc.expr())}) {
123 TypeAndShape result{std::move(*type), std::move(*shape)};
124 if (type->category() == TypeCategory::Character) {
125 if (const auto *chExpr{UnwrapExpr<Expr<SomeCharacter>>(assoc.expr())}) {
126 if (auto len{chExpr->LEN()}) {
127 result.set_LEN(Fold(context, std::move(*len)));
128 }
129 }
130 }
131 return std::move(result);
132 }
133 }
134 return std::nullopt;
135 }
136
Characterize(const semantics::DeclTypeSpec & spec)137 std::optional<TypeAndShape> TypeAndShape::Characterize(
138 const semantics::DeclTypeSpec &spec) {
139 if (auto type{DynamicType::From(spec)}) {
140 return TypeAndShape{std::move(*type)};
141 } else {
142 return std::nullopt;
143 }
144 }
145
Characterize(const ActualArgument & arg,FoldingContext & context)146 std::optional<TypeAndShape> TypeAndShape::Characterize(
147 const ActualArgument &arg, FoldingContext &context) {
148 return Characterize(arg.UnwrapExpr(), context);
149 }
150
IsCompatibleWith(parser::ContextualMessages & messages,const TypeAndShape & that,const char * thisIs,const char * thatIs,bool isElemental) const151 bool TypeAndShape::IsCompatibleWith(parser::ContextualMessages &messages,
152 const TypeAndShape &that, const char *thisIs, const char *thatIs,
153 bool isElemental) const {
154 if (!type_.IsTkCompatibleWith(that.type_)) {
155 const auto &len{that.LEN()};
156 messages.Say(
157 "%1$s type '%2$s' is not compatible with %3$s type '%4$s'"_err_en_US,
158 thatIs, that.type_.AsFortran(len ? len->AsFortran() : ""), thisIs,
159 type_.AsFortran(LEN_ ? LEN_->AsFortran() : ""));
160 return false;
161 }
162 return isElemental ||
163 CheckConformance(messages, shape_, that.shape_, thisIs, thatIs, false,
164 false /* no scalar expansion */);
165 }
166
MeasureSizeInBytes(FoldingContext * foldingContext) const167 std::optional<Expr<SubscriptInteger>> TypeAndShape::MeasureSizeInBytes(
168 FoldingContext *foldingContext) const {
169 if (type_.category() == TypeCategory::Character && LEN_) {
170 Expr<SubscriptInteger> result{
171 common::Clone(*LEN_) * Expr<SubscriptInteger>{type_.kind()}};
172 if (foldingContext) {
173 result = Fold(*foldingContext, std::move(result));
174 }
175 return result;
176 } else {
177 return type_.MeasureSizeInBytes(foldingContext);
178 }
179 }
180
AcquireShape(const semantics::ObjectEntityDetails & object,FoldingContext & context)181 void TypeAndShape::AcquireShape(
182 const semantics::ObjectEntityDetails &object, FoldingContext &context) {
183 CHECK(shape_.empty() && !attrs_.test(Attr::AssumedRank));
184 corank_ = object.coshape().Rank();
185 if (object.IsAssumedRank()) {
186 attrs_.set(Attr::AssumedRank);
187 return;
188 }
189 if (object.IsAssumedShape()) {
190 attrs_.set(Attr::AssumedShape);
191 }
192 if (object.IsAssumedSize()) {
193 attrs_.set(Attr::AssumedSize);
194 }
195 if (object.IsDeferredShape()) {
196 attrs_.set(Attr::DeferredShape);
197 }
198 if (object.IsCoarray()) {
199 attrs_.set(Attr::Coarray);
200 }
201 for (const semantics::ShapeSpec &dim : object.shape()) {
202 if (dim.ubound().GetExplicit()) {
203 Expr<SubscriptInteger> extent{*dim.ubound().GetExplicit()};
204 if (auto lbound{dim.lbound().GetExplicit()}) {
205 extent =
206 std::move(extent) + Expr<SubscriptInteger>{1} - std::move(*lbound);
207 }
208 shape_.emplace_back(Fold(context, std::move(extent)));
209 } else {
210 shape_.push_back(std::nullopt);
211 }
212 }
213 }
214
AcquireLEN()215 void TypeAndShape::AcquireLEN() {
216 if (type_.category() == TypeCategory::Character) {
217 if (const auto *param{type_.charLength()}) {
218 if (const auto &intExpr{param->GetExplicit()}) {
219 LEN_ = ConvertToType<SubscriptInteger>(common::Clone(*intExpr));
220 }
221 }
222 }
223 }
224
Dump(llvm::raw_ostream & o) const225 llvm::raw_ostream &TypeAndShape::Dump(llvm::raw_ostream &o) const {
226 o << type_.AsFortran(LEN_ ? LEN_->AsFortran() : "");
227 attrs_.Dump(o, EnumToString);
228 if (!shape_.empty()) {
229 o << " dimension";
230 char sep{'('};
231 for (const auto &expr : shape_) {
232 o << sep;
233 sep = ',';
234 if (expr) {
235 expr->AsFortran(o);
236 } else {
237 o << ':';
238 }
239 }
240 o << ')';
241 }
242 return o;
243 }
244
operator ==(const DummyDataObject & that) const245 bool DummyDataObject::operator==(const DummyDataObject &that) const {
246 return type == that.type && attrs == that.attrs && intent == that.intent &&
247 coshape == that.coshape;
248 }
249
GetIntent(const semantics::Attrs & attrs)250 static common::Intent GetIntent(const semantics::Attrs &attrs) {
251 if (attrs.test(semantics::Attr::INTENT_IN)) {
252 return common::Intent::In;
253 } else if (attrs.test(semantics::Attr::INTENT_OUT)) {
254 return common::Intent::Out;
255 } else if (attrs.test(semantics::Attr::INTENT_INOUT)) {
256 return common::Intent::InOut;
257 } else {
258 return common::Intent::Default;
259 }
260 }
261
Characterize(const semantics::Symbol & symbol,FoldingContext & context)262 std::optional<DummyDataObject> DummyDataObject::Characterize(
263 const semantics::Symbol &symbol, FoldingContext &context) {
264 if (const auto *obj{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
265 if (auto type{TypeAndShape::Characterize(*obj, context)}) {
266 std::optional<DummyDataObject> result{std::move(*type)};
267 using semantics::Attr;
268 CopyAttrs<DummyDataObject, DummyDataObject::Attr>(symbol, *result,
269 {
270 {Attr::OPTIONAL, DummyDataObject::Attr::Optional},
271 {Attr::ALLOCATABLE, DummyDataObject::Attr::Allocatable},
272 {Attr::ASYNCHRONOUS, DummyDataObject::Attr::Asynchronous},
273 {Attr::CONTIGUOUS, DummyDataObject::Attr::Contiguous},
274 {Attr::VALUE, DummyDataObject::Attr::Value},
275 {Attr::VOLATILE, DummyDataObject::Attr::Volatile},
276 {Attr::POINTER, DummyDataObject::Attr::Pointer},
277 {Attr::TARGET, DummyDataObject::Attr::Target},
278 });
279 result->intent = GetIntent(symbol.attrs());
280 return result;
281 }
282 }
283 return std::nullopt;
284 }
285
CanBePassedViaImplicitInterface() const286 bool DummyDataObject::CanBePassedViaImplicitInterface() const {
287 if ((attrs &
288 Attrs{Attr::Allocatable, Attr::Asynchronous, Attr::Optional,
289 Attr::Pointer, Attr::Target, Attr::Value, Attr::Volatile})
290 .any()) {
291 return false; // 15.4.2.2(3)(a)
292 } else if ((type.attrs() &
293 TypeAndShape::Attrs{TypeAndShape::Attr::AssumedShape,
294 TypeAndShape::Attr::AssumedRank,
295 TypeAndShape::Attr::Coarray})
296 .any()) {
297 return false; // 15.4.2.2(3)(b-d)
298 } else if (type.type().IsPolymorphic()) {
299 return false; // 15.4.2.2(3)(f)
300 } else if (const auto *derived{GetDerivedTypeSpec(type.type())}) {
301 return derived->parameters().empty(); // 15.4.2.2(3)(e)
302 } else {
303 return true;
304 }
305 }
306
Dump(llvm::raw_ostream & o) const307 llvm::raw_ostream &DummyDataObject::Dump(llvm::raw_ostream &o) const {
308 attrs.Dump(o, EnumToString);
309 if (intent != common::Intent::Default) {
310 o << "INTENT(" << common::EnumToString(intent) << ')';
311 }
312 type.Dump(o);
313 if (!coshape.empty()) {
314 char sep{'['};
315 for (const auto &expr : coshape) {
316 expr.AsFortran(o << sep);
317 sep = ',';
318 }
319 }
320 return o;
321 }
322
DummyProcedure(Procedure && p)323 DummyProcedure::DummyProcedure(Procedure &&p)
324 : procedure{new Procedure{std::move(p)}} {}
325
operator ==(const DummyProcedure & that) const326 bool DummyProcedure::operator==(const DummyProcedure &that) const {
327 return attrs == that.attrs && intent == that.intent &&
328 procedure.value() == that.procedure.value();
329 }
330
Characterize(const semantics::Symbol & symbol,FoldingContext & context)331 std::optional<DummyProcedure> DummyProcedure::Characterize(
332 const semantics::Symbol &symbol, FoldingContext &context) {
333 if (auto procedure{Procedure::Characterize(symbol, context)}) {
334 // Dummy procedures may not be elemental. Elemental dummy procedure
335 // interfaces are errors when the interface is not intrinsic, and that
336 // error is caught elsewhere. Elemental intrinsic interfaces are
337 // made non-elemental.
338 procedure->attrs.reset(Procedure::Attr::Elemental);
339 DummyProcedure result{std::move(procedure.value())};
340 CopyAttrs<DummyProcedure, DummyProcedure::Attr>(symbol, result,
341 {
342 {semantics::Attr::OPTIONAL, DummyProcedure::Attr::Optional},
343 {semantics::Attr::POINTER, DummyProcedure::Attr::Pointer},
344 });
345 result.intent = GetIntent(symbol.attrs());
346 return result;
347 } else {
348 return std::nullopt;
349 }
350 }
351
Dump(llvm::raw_ostream & o) const352 llvm::raw_ostream &DummyProcedure::Dump(llvm::raw_ostream &o) const {
353 attrs.Dump(o, EnumToString);
354 if (intent != common::Intent::Default) {
355 o << "INTENT(" << common::EnumToString(intent) << ')';
356 }
357 procedure.value().Dump(o);
358 return o;
359 }
360
Dump(llvm::raw_ostream & o) const361 llvm::raw_ostream &AlternateReturn::Dump(llvm::raw_ostream &o) const {
362 return o << '*';
363 }
364
~DummyArgument()365 DummyArgument::~DummyArgument() {}
366
operator ==(const DummyArgument & that) const367 bool DummyArgument::operator==(const DummyArgument &that) const {
368 return u == that.u; // name and passed-object usage are not characteristics
369 }
370
Characterize(const semantics::Symbol & symbol,FoldingContext & context)371 std::optional<DummyArgument> DummyArgument::Characterize(
372 const semantics::Symbol &symbol, FoldingContext &context) {
373 auto name{symbol.name().ToString()};
374 if (symbol.has<semantics::ObjectEntityDetails>()) {
375 if (auto obj{DummyDataObject::Characterize(symbol, context)}) {
376 return DummyArgument{std::move(name), std::move(obj.value())};
377 }
378 } else if (auto proc{DummyProcedure::Characterize(symbol, context)}) {
379 return DummyArgument{std::move(name), std::move(proc.value())};
380 }
381 return std::nullopt;
382 }
383
FromActual(std::string && name,const Expr<SomeType> & expr,FoldingContext & context)384 std::optional<DummyArgument> DummyArgument::FromActual(
385 std::string &&name, const Expr<SomeType> &expr, FoldingContext &context) {
386 return std::visit(
387 common::visitors{
388 [&](const BOZLiteralConstant &) {
389 return std::make_optional<DummyArgument>(std::move(name),
390 DummyDataObject{
391 TypeAndShape{DynamicType::TypelessIntrinsicArgument()}});
392 },
393 [&](const NullPointer &) {
394 return std::make_optional<DummyArgument>(std::move(name),
395 DummyDataObject{
396 TypeAndShape{DynamicType::TypelessIntrinsicArgument()}});
397 },
398 [&](const ProcedureDesignator &designator) {
399 if (auto proc{Procedure::Characterize(designator, context)}) {
400 return std::make_optional<DummyArgument>(
401 std::move(name), DummyProcedure{std::move(*proc)});
402 } else {
403 return std::optional<DummyArgument>{};
404 }
405 },
406 [&](const ProcedureRef &call) {
407 if (auto proc{Procedure::Characterize(call, context)}) {
408 return std::make_optional<DummyArgument>(
409 std::move(name), DummyProcedure{std::move(*proc)});
410 } else {
411 return std::optional<DummyArgument>{};
412 }
413 },
414 [&](const auto &) {
415 if (auto type{TypeAndShape::Characterize(expr, context)}) {
416 return std::make_optional<DummyArgument>(
417 std::move(name), DummyDataObject{std::move(*type)});
418 } else {
419 return std::optional<DummyArgument>{};
420 }
421 },
422 },
423 expr.u);
424 }
425
IsOptional() const426 bool DummyArgument::IsOptional() const {
427 return std::visit(
428 common::visitors{
429 [](const DummyDataObject &data) {
430 return data.attrs.test(DummyDataObject::Attr::Optional);
431 },
432 [](const DummyProcedure &proc) {
433 return proc.attrs.test(DummyProcedure::Attr::Optional);
434 },
435 [](const AlternateReturn &) { return false; },
436 },
437 u);
438 }
439
SetOptional(bool value)440 void DummyArgument::SetOptional(bool value) {
441 std::visit(common::visitors{
442 [value](DummyDataObject &data) {
443 data.attrs.set(DummyDataObject::Attr::Optional, value);
444 },
445 [value](DummyProcedure &proc) {
446 proc.attrs.set(DummyProcedure::Attr::Optional, value);
447 },
448 [](AlternateReturn &) { DIE("cannot set optional"); },
449 },
450 u);
451 }
452
SetIntent(common::Intent intent)453 void DummyArgument::SetIntent(common::Intent intent) {
454 std::visit(common::visitors{
455 [intent](DummyDataObject &data) { data.intent = intent; },
456 [intent](DummyProcedure &proc) { proc.intent = intent; },
457 [](AlternateReturn &) { DIE("cannot set intent"); },
458 },
459 u);
460 }
461
GetIntent() const462 common::Intent DummyArgument::GetIntent() const {
463 return std::visit(common::visitors{
464 [](const DummyDataObject &data) { return data.intent; },
465 [](const DummyProcedure &proc) { return proc.intent; },
466 [](const AlternateReturn &) -> common::Intent {
467 DIE("Alternate return have no intent");
468 },
469 },
470 u);
471 }
472
CanBePassedViaImplicitInterface() const473 bool DummyArgument::CanBePassedViaImplicitInterface() const {
474 if (const auto *object{std::get_if<DummyDataObject>(&u)}) {
475 return object->CanBePassedViaImplicitInterface();
476 } else {
477 return true;
478 }
479 }
480
IsTypelessIntrinsicDummy() const481 bool DummyArgument::IsTypelessIntrinsicDummy() const {
482 const auto *argObj{std::get_if<characteristics::DummyDataObject>(&u)};
483 return argObj && argObj->type.type().IsTypelessIntrinsicArgument();
484 }
485
Dump(llvm::raw_ostream & o) const486 llvm::raw_ostream &DummyArgument::Dump(llvm::raw_ostream &o) const {
487 if (!name.empty()) {
488 o << name << '=';
489 }
490 if (pass) {
491 o << " PASS";
492 }
493 std::visit([&](const auto &x) { x.Dump(o); }, u);
494 return o;
495 }
496
FunctionResult(DynamicType t)497 FunctionResult::FunctionResult(DynamicType t) : u{TypeAndShape{t}} {}
FunctionResult(TypeAndShape && t)498 FunctionResult::FunctionResult(TypeAndShape &&t) : u{std::move(t)} {}
FunctionResult(Procedure && p)499 FunctionResult::FunctionResult(Procedure &&p) : u{std::move(p)} {}
~FunctionResult()500 FunctionResult::~FunctionResult() {}
501
operator ==(const FunctionResult & that) const502 bool FunctionResult::operator==(const FunctionResult &that) const {
503 return attrs == that.attrs && u == that.u;
504 }
505
Characterize(const Symbol & symbol,FoldingContext & context)506 std::optional<FunctionResult> FunctionResult::Characterize(
507 const Symbol &symbol, FoldingContext &context) {
508 if (const auto *object{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
509 if (auto type{TypeAndShape::Characterize(*object, context)}) {
510 FunctionResult result{std::move(*type)};
511 CopyAttrs<FunctionResult, FunctionResult::Attr>(symbol, result,
512 {
513 {semantics::Attr::ALLOCATABLE, FunctionResult::Attr::Allocatable},
514 {semantics::Attr::CONTIGUOUS, FunctionResult::Attr::Contiguous},
515 {semantics::Attr::POINTER, FunctionResult::Attr::Pointer},
516 });
517 return result;
518 }
519 } else if (auto maybeProc{Procedure::Characterize(symbol, context)}) {
520 FunctionResult result{std::move(*maybeProc)};
521 result.attrs.set(FunctionResult::Attr::Pointer);
522 return result;
523 }
524 return std::nullopt;
525 }
526
IsAssumedLengthCharacter() const527 bool FunctionResult::IsAssumedLengthCharacter() const {
528 if (const auto *ts{std::get_if<TypeAndShape>(&u)}) {
529 return ts->type().IsAssumedLengthCharacter();
530 } else {
531 return false;
532 }
533 }
534
CanBeReturnedViaImplicitInterface() const535 bool FunctionResult::CanBeReturnedViaImplicitInterface() const {
536 if (attrs.test(Attr::Pointer) || attrs.test(Attr::Allocatable)) {
537 return false; // 15.4.2.2(4)(b)
538 } else if (const auto *typeAndShape{GetTypeAndShape()}) {
539 if (typeAndShape->Rank() > 0) {
540 return false; // 15.4.2.2(4)(a)
541 } else {
542 const DynamicType &type{typeAndShape->type()};
543 switch (type.category()) {
544 case TypeCategory::Character:
545 if (const auto *param{type.charLength()}) {
546 if (const auto &expr{param->GetExplicit()}) {
547 return IsConstantExpr(*expr); // 15.4.2.2(4)(c)
548 } else if (param->isAssumed()) {
549 return true;
550 }
551 }
552 return false;
553 case TypeCategory::Derived:
554 if (!type.IsPolymorphic()) {
555 const auto &spec{type.GetDerivedTypeSpec()};
556 for (const auto &pair : spec.parameters()) {
557 if (const auto &expr{pair.second.GetExplicit()}) {
558 if (!IsConstantExpr(*expr)) {
559 return false; // 15.4.2.2(4)(c)
560 }
561 }
562 }
563 return true;
564 }
565 return false;
566 default:
567 return true;
568 }
569 }
570 } else {
571 return false; // 15.4.2.2(4)(b) - procedure pointer
572 }
573 }
574
Dump(llvm::raw_ostream & o) const575 llvm::raw_ostream &FunctionResult::Dump(llvm::raw_ostream &o) const {
576 attrs.Dump(o, EnumToString);
577 std::visit(common::visitors{
578 [&](const TypeAndShape &ts) { ts.Dump(o); },
579 [&](const CopyableIndirection<Procedure> &p) {
580 p.value().Dump(o << " procedure(") << ')';
581 },
582 },
583 u);
584 return o;
585 }
586
Procedure(FunctionResult && fr,DummyArguments && args,Attrs a)587 Procedure::Procedure(FunctionResult &&fr, DummyArguments &&args, Attrs a)
588 : functionResult{std::move(fr)}, dummyArguments{std::move(args)}, attrs{a} {
589 }
Procedure(DummyArguments && args,Attrs a)590 Procedure::Procedure(DummyArguments &&args, Attrs a)
591 : dummyArguments{std::move(args)}, attrs{a} {}
~Procedure()592 Procedure::~Procedure() {}
593
operator ==(const Procedure & that) const594 bool Procedure::operator==(const Procedure &that) const {
595 return attrs == that.attrs && functionResult == that.functionResult &&
596 dummyArguments == that.dummyArguments;
597 }
598
FindPassIndex(std::optional<parser::CharBlock> name) const599 int Procedure::FindPassIndex(std::optional<parser::CharBlock> name) const {
600 int argCount{static_cast<int>(dummyArguments.size())};
601 int index{0};
602 if (name) {
603 while (index < argCount && *name != dummyArguments[index].name.c_str()) {
604 ++index;
605 }
606 }
607 CHECK(index < argCount);
608 return index;
609 }
610
CanOverride(const Procedure & that,std::optional<int> passIndex) const611 bool Procedure::CanOverride(
612 const Procedure &that, std::optional<int> passIndex) const {
613 // A pure procedure may override an impure one (7.5.7.3(2))
614 if ((that.attrs.test(Attr::Pure) && !attrs.test(Attr::Pure)) ||
615 that.attrs.test(Attr::Elemental) != attrs.test(Attr::Elemental) ||
616 functionResult != that.functionResult) {
617 return false;
618 }
619 int argCount{static_cast<int>(dummyArguments.size())};
620 if (argCount != static_cast<int>(that.dummyArguments.size())) {
621 return false;
622 }
623 for (int j{0}; j < argCount; ++j) {
624 if ((!passIndex || j != *passIndex) &&
625 dummyArguments[j] != that.dummyArguments[j]) {
626 return false;
627 }
628 }
629 return true;
630 }
631
Characterize(const semantics::Symbol & original,FoldingContext & context)632 std::optional<Procedure> Procedure::Characterize(
633 const semantics::Symbol &original, FoldingContext &context) {
634 Procedure result;
635 const auto &symbol{ResolveAssociations(original)};
636 CopyAttrs<Procedure, Procedure::Attr>(symbol, result,
637 {
638 {semantics::Attr::PURE, Procedure::Attr::Pure},
639 {semantics::Attr::ELEMENTAL, Procedure::Attr::Elemental},
640 {semantics::Attr::BIND_C, Procedure::Attr::BindC},
641 });
642 if (result.attrs.test(Attr::Elemental) &&
643 !symbol.attrs().test(semantics::Attr::IMPURE)) {
644 result.attrs.set(Attr::Pure); // explicitly flag pure procedures
645 }
646 return std::visit(
647 common::visitors{
648 [&](const semantics::SubprogramDetails &subp)
649 -> std::optional<Procedure> {
650 if (subp.isFunction()) {
651 if (auto fr{
652 FunctionResult::Characterize(subp.result(), context)}) {
653 result.functionResult = std::move(fr);
654 } else {
655 return std::nullopt;
656 }
657 } else {
658 result.attrs.set(Attr::Subroutine);
659 }
660 for (const semantics::Symbol *arg : subp.dummyArgs()) {
661 if (!arg) {
662 result.dummyArguments.emplace_back(AlternateReturn{});
663 } else if (auto argCharacteristics{
664 DummyArgument::Characterize(*arg, context)}) {
665 result.dummyArguments.emplace_back(
666 std::move(argCharacteristics.value()));
667 } else {
668 return std::nullopt;
669 }
670 }
671 return result;
672 },
673 [&](const semantics::ProcEntityDetails &proc)
674 -> std::optional<Procedure> {
675 if (symbol.attrs().test(semantics::Attr::INTRINSIC)) {
676 return context.intrinsics().IsSpecificIntrinsicFunction(
677 symbol.name().ToString());
678 }
679 const semantics::ProcInterface &interface{proc.interface()};
680 if (const semantics::Symbol * interfaceSymbol{interface.symbol()}) {
681 return Characterize(*interfaceSymbol, context);
682 } else {
683 result.attrs.set(Attr::ImplicitInterface);
684 const semantics::DeclTypeSpec *type{interface.type()};
685 if (symbol.test(semantics::Symbol::Flag::Subroutine)) {
686 // ignore any implicit typing
687 result.attrs.set(Attr::Subroutine);
688 } else if (type) {
689 if (auto resultType{DynamicType::From(*type)}) {
690 result.functionResult = FunctionResult{*resultType};
691 } else {
692 return std::nullopt;
693 }
694 } else if (symbol.test(semantics::Symbol::Flag::Function)) {
695 return std::nullopt;
696 }
697 // The PASS name, if any, is not a characteristic.
698 return result;
699 }
700 },
701 [&](const semantics::ProcBindingDetails &binding) {
702 if (auto result{Characterize(binding.symbol(), context)}) {
703 if (!symbol.attrs().test(semantics::Attr::NOPASS)) {
704 auto passName{binding.passName()};
705 for (auto &dummy : result->dummyArguments) {
706 if (!passName || dummy.name.c_str() == *passName) {
707 dummy.pass = true;
708 return result;
709 }
710 }
711 DIE("PASS argument missing");
712 }
713 return result;
714 } else {
715 return std::optional<Procedure>{};
716 }
717 },
718 [&](const semantics::UseDetails &use) {
719 return Characterize(use.symbol(), context);
720 },
721 [&](const semantics::HostAssocDetails &assoc) {
722 return Characterize(assoc.symbol(), context);
723 },
724 [](const auto &) { return std::optional<Procedure>{}; },
725 },
726 symbol.details());
727 }
728
Characterize(const ProcedureDesignator & proc,FoldingContext & context)729 std::optional<Procedure> Procedure::Characterize(
730 const ProcedureDesignator &proc, FoldingContext &context) {
731 if (const auto *symbol{proc.GetSymbol()}) {
732 if (auto result{characteristics::Procedure::Characterize(
733 ResolveAssociations(*symbol), context)}) {
734 return result;
735 }
736 } else if (const auto *intrinsic{proc.GetSpecificIntrinsic()}) {
737 return intrinsic->characteristics.value();
738 }
739 return std::nullopt;
740 }
741
Characterize(const ProcedureRef & ref,FoldingContext & context)742 std::optional<Procedure> Procedure::Characterize(
743 const ProcedureRef &ref, FoldingContext &context) {
744 if (auto callee{Characterize(ref.proc(), context)}) {
745 if (callee->functionResult) {
746 if (const Procedure *
747 proc{callee->functionResult->IsProcedurePointer()}) {
748 return {*proc};
749 }
750 }
751 }
752 return std::nullopt;
753 }
754
CanBeCalledViaImplicitInterface() const755 bool Procedure::CanBeCalledViaImplicitInterface() const {
756 if (attrs.test(Attr::Elemental) || attrs.test(Attr::BindC)) {
757 return false; // 15.4.2.2(5,6)
758 } else if (IsFunction() &&
759 !functionResult->CanBeReturnedViaImplicitInterface()) {
760 return false;
761 } else {
762 for (const DummyArgument &arg : dummyArguments) {
763 if (!arg.CanBePassedViaImplicitInterface()) {
764 return false;
765 }
766 }
767 return true;
768 }
769 }
770
Dump(llvm::raw_ostream & o) const771 llvm::raw_ostream &Procedure::Dump(llvm::raw_ostream &o) const {
772 attrs.Dump(o, EnumToString);
773 if (functionResult) {
774 functionResult->Dump(o << "TYPE(") << ") FUNCTION";
775 } else {
776 o << "SUBROUTINE";
777 }
778 char sep{'('};
779 for (const auto &dummy : dummyArguments) {
780 dummy.Dump(o << sep);
781 sep = ',';
782 }
783 return o << (sep == '(' ? "()" : ")");
784 }
785
786 // Utility class to determine if Procedures, etc. are distinguishable
787 class DistinguishUtils {
788 public:
789 // Are these procedures distinguishable for a generic name?
790 static bool Distinguishable(const Procedure &, const Procedure &);
791 // Are these procedures distinguishable for a generic operator or assignment?
792 static bool DistinguishableOpOrAssign(const Procedure &, const Procedure &);
793
794 private:
795 struct CountDummyProcedures {
CountDummyProceduresFortran::evaluate::characteristics::DistinguishUtils::CountDummyProcedures796 CountDummyProcedures(const DummyArguments &args) {
797 for (const DummyArgument &arg : args) {
798 if (std::holds_alternative<DummyProcedure>(arg.u)) {
799 total += 1;
800 notOptional += !arg.IsOptional();
801 }
802 }
803 }
804 int total{0};
805 int notOptional{0};
806 };
807
808 static bool Rule3Distinguishable(const Procedure &, const Procedure &);
809 static const DummyArgument *Rule1DistinguishingArg(
810 const DummyArguments &, const DummyArguments &);
811 static int FindFirstToDistinguishByPosition(
812 const DummyArguments &, const DummyArguments &);
813 static int FindLastToDistinguishByName(
814 const DummyArguments &, const DummyArguments &);
815 static int CountCompatibleWith(const DummyArgument &, const DummyArguments &);
816 static int CountNotDistinguishableFrom(
817 const DummyArgument &, const DummyArguments &);
818 static bool Distinguishable(const DummyArgument &, const DummyArgument &);
819 static bool Distinguishable(const DummyDataObject &, const DummyDataObject &);
820 static bool Distinguishable(const DummyProcedure &, const DummyProcedure &);
821 static bool Distinguishable(const FunctionResult &, const FunctionResult &);
822 static bool Distinguishable(const TypeAndShape &, const TypeAndShape &);
823 static bool IsTkrCompatible(const DummyArgument &, const DummyArgument &);
824 static bool IsTkrCompatible(const TypeAndShape &, const TypeAndShape &);
825 static const DummyArgument *GetAtEffectivePosition(
826 const DummyArguments &, int);
827 static const DummyArgument *GetPassArg(const Procedure &);
828 };
829
830 // Simpler distinguishability rules for operators and assignment
DistinguishableOpOrAssign(const Procedure & proc1,const Procedure & proc2)831 bool DistinguishUtils::DistinguishableOpOrAssign(
832 const Procedure &proc1, const Procedure &proc2) {
833 auto &args1{proc1.dummyArguments};
834 auto &args2{proc2.dummyArguments};
835 if (args1.size() != args2.size()) {
836 return true; // C1511: distinguishable based on number of arguments
837 }
838 for (std::size_t i{0}; i < args1.size(); ++i) {
839 if (Distinguishable(args1[i], args2[i])) {
840 return true; // C1511, C1512: distinguishable based on this arg
841 }
842 }
843 return false;
844 }
845
Distinguishable(const Procedure & proc1,const Procedure & proc2)846 bool DistinguishUtils::Distinguishable(
847 const Procedure &proc1, const Procedure &proc2) {
848 auto &args1{proc1.dummyArguments};
849 auto &args2{proc2.dummyArguments};
850 auto count1{CountDummyProcedures(args1)};
851 auto count2{CountDummyProcedures(args2)};
852 if (count1.notOptional > count2.total || count2.notOptional > count1.total) {
853 return true; // distinguishable based on C1514 rule 2
854 }
855 if (Rule3Distinguishable(proc1, proc2)) {
856 return true; // distinguishable based on C1514 rule 3
857 }
858 if (Rule1DistinguishingArg(args1, args2)) {
859 return true; // distinguishable based on C1514 rule 1
860 }
861 int pos1{FindFirstToDistinguishByPosition(args1, args2)};
862 int name1{FindLastToDistinguishByName(args1, args2)};
863 if (pos1 >= 0 && pos1 <= name1) {
864 return true; // distinguishable based on C1514 rule 4
865 }
866 int pos2{FindFirstToDistinguishByPosition(args2, args1)};
867 int name2{FindLastToDistinguishByName(args2, args1)};
868 if (pos2 >= 0 && pos2 <= name2) {
869 return true; // distinguishable based on C1514 rule 4
870 }
871 return false;
872 }
873
874 // C1514 rule 3: Procedures are distinguishable if both have a passed-object
875 // dummy argument and those are distinguishable.
Rule3Distinguishable(const Procedure & proc1,const Procedure & proc2)876 bool DistinguishUtils::Rule3Distinguishable(
877 const Procedure &proc1, const Procedure &proc2) {
878 const DummyArgument *pass1{GetPassArg(proc1)};
879 const DummyArgument *pass2{GetPassArg(proc2)};
880 return pass1 && pass2 && Distinguishable(*pass1, *pass2);
881 }
882
883 // Find a non-passed-object dummy data object in one of the argument lists
884 // that satisfies C1514 rule 1. I.e. x such that:
885 // - m is the number of dummy data objects in one that are nonoptional,
886 // are not passed-object, that x is TKR compatible with
887 // - n is the number of non-passed-object dummy data objects, in the other
888 // that are not distinguishable from x
889 // - m is greater than n
Rule1DistinguishingArg(const DummyArguments & args1,const DummyArguments & args2)890 const DummyArgument *DistinguishUtils::Rule1DistinguishingArg(
891 const DummyArguments &args1, const DummyArguments &args2) {
892 auto size1{args1.size()};
893 auto size2{args2.size()};
894 for (std::size_t i{0}; i < size1 + size2; ++i) {
895 const DummyArgument &x{i < size1 ? args1[i] : args2[i - size1]};
896 if (!x.pass && std::holds_alternative<DummyDataObject>(x.u)) {
897 if (CountCompatibleWith(x, args1) >
898 CountNotDistinguishableFrom(x, args2) ||
899 CountCompatibleWith(x, args2) >
900 CountNotDistinguishableFrom(x, args1)) {
901 return &x;
902 }
903 }
904 }
905 return nullptr;
906 }
907
908 // Find the index of the first nonoptional non-passed-object dummy argument
909 // in args1 at an effective position such that either:
910 // - args2 has no dummy argument at that effective position
911 // - the dummy argument at that position is distinguishable from it
FindFirstToDistinguishByPosition(const DummyArguments & args1,const DummyArguments & args2)912 int DistinguishUtils::FindFirstToDistinguishByPosition(
913 const DummyArguments &args1, const DummyArguments &args2) {
914 int effective{0}; // position of arg1 in list, ignoring passed arg
915 for (std::size_t i{0}; i < args1.size(); ++i) {
916 const DummyArgument &arg1{args1.at(i)};
917 if (!arg1.pass && !arg1.IsOptional()) {
918 const DummyArgument *arg2{GetAtEffectivePosition(args2, effective)};
919 if (!arg2 || Distinguishable(arg1, *arg2)) {
920 return i;
921 }
922 }
923 effective += !arg1.pass;
924 }
925 return -1;
926 }
927
928 // Find the index of the last nonoptional non-passed-object dummy argument
929 // in args1 whose name is such that either:
930 // - args2 has no dummy argument with that name
931 // - the dummy argument with that name is distinguishable from it
FindLastToDistinguishByName(const DummyArguments & args1,const DummyArguments & args2)932 int DistinguishUtils::FindLastToDistinguishByName(
933 const DummyArguments &args1, const DummyArguments &args2) {
934 std::map<std::string, const DummyArgument *> nameToArg;
935 for (const auto &arg2 : args2) {
936 nameToArg.emplace(arg2.name, &arg2);
937 }
938 for (int i = args1.size() - 1; i >= 0; --i) {
939 const DummyArgument &arg1{args1.at(i)};
940 if (!arg1.pass && !arg1.IsOptional()) {
941 auto it{nameToArg.find(arg1.name)};
942 if (it == nameToArg.end() || Distinguishable(arg1, *it->second)) {
943 return i;
944 }
945 }
946 }
947 return -1;
948 }
949
950 // Count the dummy data objects in args that are nonoptional, are not
951 // passed-object, and that x is TKR compatible with
CountCompatibleWith(const DummyArgument & x,const DummyArguments & args)952 int DistinguishUtils::CountCompatibleWith(
953 const DummyArgument &x, const DummyArguments &args) {
954 return std::count_if(args.begin(), args.end(), [&](const DummyArgument &y) {
955 return !y.pass && !y.IsOptional() && IsTkrCompatible(x, y);
956 });
957 }
958
959 // Return the number of dummy data objects in args that are not
960 // distinguishable from x and not passed-object.
CountNotDistinguishableFrom(const DummyArgument & x,const DummyArguments & args)961 int DistinguishUtils::CountNotDistinguishableFrom(
962 const DummyArgument &x, const DummyArguments &args) {
963 return std::count_if(args.begin(), args.end(), [&](const DummyArgument &y) {
964 return !y.pass && std::holds_alternative<DummyDataObject>(y.u) &&
965 !Distinguishable(y, x);
966 });
967 }
968
Distinguishable(const DummyArgument & x,const DummyArgument & y)969 bool DistinguishUtils::Distinguishable(
970 const DummyArgument &x, const DummyArgument &y) {
971 if (x.u.index() != y.u.index()) {
972 return true; // different kind: data/proc/alt-return
973 }
974 return std::visit(
975 common::visitors{
976 [&](const DummyDataObject &z) {
977 return Distinguishable(z, std::get<DummyDataObject>(y.u));
978 },
979 [&](const DummyProcedure &z) {
980 return Distinguishable(z, std::get<DummyProcedure>(y.u));
981 },
982 [&](const AlternateReturn &) { return false; },
983 },
984 x.u);
985 }
986
Distinguishable(const DummyDataObject & x,const DummyDataObject & y)987 bool DistinguishUtils::Distinguishable(
988 const DummyDataObject &x, const DummyDataObject &y) {
989 using Attr = DummyDataObject::Attr;
990 if (Distinguishable(x.type, y.type)) {
991 return true;
992 } else if (x.attrs.test(Attr::Allocatable) && y.attrs.test(Attr::Pointer) &&
993 y.intent != common::Intent::In) {
994 return true;
995 } else if (y.attrs.test(Attr::Allocatable) && x.attrs.test(Attr::Pointer) &&
996 x.intent != common::Intent::In) {
997 return true;
998 } else {
999 return false;
1000 }
1001 }
1002
Distinguishable(const DummyProcedure & x,const DummyProcedure & y)1003 bool DistinguishUtils::Distinguishable(
1004 const DummyProcedure &x, const DummyProcedure &y) {
1005 const Procedure &xProc{x.procedure.value()};
1006 const Procedure &yProc{y.procedure.value()};
1007 if (Distinguishable(xProc, yProc)) {
1008 return true;
1009 } else {
1010 const std::optional<FunctionResult> &xResult{xProc.functionResult};
1011 const std::optional<FunctionResult> &yResult{yProc.functionResult};
1012 return xResult ? !yResult || Distinguishable(*xResult, *yResult)
1013 : yResult.has_value();
1014 }
1015 }
1016
Distinguishable(const FunctionResult & x,const FunctionResult & y)1017 bool DistinguishUtils::Distinguishable(
1018 const FunctionResult &x, const FunctionResult &y) {
1019 if (x.u.index() != y.u.index()) {
1020 return true; // one is data object, one is procedure
1021 }
1022 return std::visit(
1023 common::visitors{
1024 [&](const TypeAndShape &z) {
1025 return Distinguishable(z, std::get<TypeAndShape>(y.u));
1026 },
1027 [&](const CopyableIndirection<Procedure> &z) {
1028 return Distinguishable(z.value(),
1029 std::get<CopyableIndirection<Procedure>>(y.u).value());
1030 },
1031 },
1032 x.u);
1033 }
1034
Distinguishable(const TypeAndShape & x,const TypeAndShape & y)1035 bool DistinguishUtils::Distinguishable(
1036 const TypeAndShape &x, const TypeAndShape &y) {
1037 return !IsTkrCompatible(x, y) && !IsTkrCompatible(y, x);
1038 }
1039
1040 // Compatibility based on type, kind, and rank
IsTkrCompatible(const DummyArgument & x,const DummyArgument & y)1041 bool DistinguishUtils::IsTkrCompatible(
1042 const DummyArgument &x, const DummyArgument &y) {
1043 const auto *obj1{std::get_if<DummyDataObject>(&x.u)};
1044 const auto *obj2{std::get_if<DummyDataObject>(&y.u)};
1045 return obj1 && obj2 && IsTkrCompatible(obj1->type, obj2->type);
1046 }
IsTkrCompatible(const TypeAndShape & x,const TypeAndShape & y)1047 bool DistinguishUtils::IsTkrCompatible(
1048 const TypeAndShape &x, const TypeAndShape &y) {
1049 return x.type().IsTkCompatibleWith(y.type()) &&
1050 (x.attrs().test(TypeAndShape::Attr::AssumedRank) ||
1051 y.attrs().test(TypeAndShape::Attr::AssumedRank) ||
1052 x.Rank() == y.Rank());
1053 }
1054
1055 // Return the argument at the given index, ignoring the passed arg
GetAtEffectivePosition(const DummyArguments & args,int index)1056 const DummyArgument *DistinguishUtils::GetAtEffectivePosition(
1057 const DummyArguments &args, int index) {
1058 for (const DummyArgument &arg : args) {
1059 if (!arg.pass) {
1060 if (index == 0) {
1061 return &arg;
1062 }
1063 --index;
1064 }
1065 }
1066 return nullptr;
1067 }
1068
1069 // Return the passed-object dummy argument of this procedure, if any
GetPassArg(const Procedure & proc)1070 const DummyArgument *DistinguishUtils::GetPassArg(const Procedure &proc) {
1071 for (const auto &arg : proc.dummyArguments) {
1072 if (arg.pass) {
1073 return &arg;
1074 }
1075 }
1076 return nullptr;
1077 }
1078
Distinguishable(const Procedure & x,const Procedure & y)1079 bool Distinguishable(const Procedure &x, const Procedure &y) {
1080 return DistinguishUtils::Distinguishable(x, y);
1081 }
1082
DistinguishableOpOrAssign(const Procedure & x,const Procedure & y)1083 bool DistinguishableOpOrAssign(const Procedure &x, const Procedure &y) {
1084 return DistinguishUtils::DistinguishableOpOrAssign(x, y);
1085 }
1086
1087 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyArgument)
1088 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyProcedure)
1089 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(FunctionResult)
1090 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(Procedure)
1091 } // namespace Fortran::evaluate::characteristics
1092
1093 template class Fortran::common::Indirection<
1094 Fortran::evaluate::characteristics::Procedure, true>;
1095