1 //===-- CallInterface.cpp -- Procedure call interface ---------------------===//
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/Lower/CallInterface.h"
10 #include "flang/Evaluate/fold.h"
11 #include "flang/Lower/Bridge.h"
12 #include "flang/Lower/Mangler.h"
13 #include "flang/Lower/PFTBuilder.h"
14 #include "flang/Lower/StatementContext.h"
15 #include "flang/Lower/Support/Utils.h"
16 #include "flang/Lower/Todo.h"
17 #include "flang/Optimizer/Builder/Character.h"
18 #include "flang/Optimizer/Builder/FIRBuilder.h"
19 #include "flang/Optimizer/Dialect/FIRDialect.h"
20 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
21 #include "flang/Optimizer/Support/InternalNames.h"
22 #include "flang/Semantics/symbol.h"
23 #include "flang/Semantics/tools.h"
24 
25 //===----------------------------------------------------------------------===//
26 // BIND(C) mangling helpers
27 //===----------------------------------------------------------------------===//
28 
29 // Return the binding label (from BIND(C...)) or the mangled name of a symbol.
30 static std::string getMangledName(const Fortran::semantics::Symbol &symbol) {
31   const std::string *bindName = symbol.GetBindName();
32   return bindName ? *bindName : Fortran::lower::mangle::mangleName(symbol);
33 }
34 
35 /// Return the type of a dummy procedure given its characteristic (if it has
36 /// one).
37 mlir::Type getProcedureDesignatorType(
38     const Fortran::evaluate::characteristics::Procedure *,
39     Fortran::lower::AbstractConverter &converter) {
40   // TODO: Get actual function type of the dummy procedure, at least when an
41   // interface is given. The result type should be available even if the arity
42   // and type of the arguments is not.
43   llvm::SmallVector<mlir::Type> resultTys;
44   llvm::SmallVector<mlir::Type> inputTys;
45   // In general, that is a nice to have but we cannot guarantee to find the
46   // function type that will match the one of the calls, we may not even know
47   // how many arguments the dummy procedure accepts (e.g. if a procedure
48   // pointer is only transiting through the current procedure without being
49   // called), so a function type cast must always be inserted.
50   auto *context = &converter.getMLIRContext();
51   auto untypedFunc = mlir::FunctionType::get(context, inputTys, resultTys);
52   return fir::BoxProcType::get(context, untypedFunc);
53 }
54 
55 //===----------------------------------------------------------------------===//
56 // Caller side interface implementation
57 //===----------------------------------------------------------------------===//
58 
59 bool Fortran::lower::CallerInterface::hasAlternateReturns() const {
60   return procRef.hasAlternateReturns();
61 }
62 
63 std::string Fortran::lower::CallerInterface::getMangledName() const {
64   const Fortran::evaluate::ProcedureDesignator &proc = procRef.proc();
65   if (const Fortran::semantics::Symbol *symbol = proc.GetSymbol())
66     return ::getMangledName(symbol->GetUltimate());
67   assert(proc.GetSpecificIntrinsic() &&
68          "expected intrinsic procedure in designator");
69   return proc.GetName();
70 }
71 
72 const Fortran::semantics::Symbol *
73 Fortran::lower::CallerInterface::getProcedureSymbol() const {
74   return procRef.proc().GetSymbol();
75 }
76 
77 bool Fortran::lower::CallerInterface::isIndirectCall() const {
78   if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
79     return Fortran::semantics::IsPointer(*symbol) ||
80            Fortran::semantics::IsDummy(*symbol);
81   return false;
82 }
83 
84 const Fortran::semantics::Symbol *
85 Fortran::lower::CallerInterface::getIfIndirectCallSymbol() const {
86   if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
87     if (Fortran::semantics::IsPointer(*symbol) ||
88         Fortran::semantics::IsDummy(*symbol))
89       return symbol;
90   return nullptr;
91 }
92 
93 mlir::Location Fortran::lower::CallerInterface::getCalleeLocation() const {
94   const Fortran::evaluate::ProcedureDesignator &proc = procRef.proc();
95   // FIXME: If the callee is defined in the same file but after the current
96   // unit we cannot get its location here and the funcOp is created at the
97   // wrong location (i.e, the caller location).
98   if (const Fortran::semantics::Symbol *symbol = proc.GetSymbol())
99     return converter.genLocation(symbol->name());
100   // Use current location for intrinsics.
101   return converter.getCurrentLocation();
102 }
103 
104 // Get dummy argument characteristic for a procedure with implicit interface
105 // from the actual argument characteristic. The actual argument may not be a F77
106 // entity. The attribute must be dropped and the shape, if any, must be made
107 // explicit.
108 static Fortran::evaluate::characteristics::DummyDataObject
109 asImplicitArg(Fortran::evaluate::characteristics::DummyDataObject &&dummy) {
110   Fortran::evaluate::Shape shape =
111       dummy.type.attrs().none() ? dummy.type.shape()
112                                 : Fortran::evaluate::Shape(dummy.type.Rank());
113   return Fortran::evaluate::characteristics::DummyDataObject(
114       Fortran::evaluate::characteristics::TypeAndShape(dummy.type.type(),
115                                                        std::move(shape)));
116 }
117 
118 static Fortran::evaluate::characteristics::DummyArgument
119 asImplicitArg(Fortran::evaluate::characteristics::DummyArgument &&dummy) {
120   return std::visit(
121       Fortran::common::visitors{
122           [&](Fortran::evaluate::characteristics::DummyDataObject &obj) {
123             return Fortran::evaluate::characteristics::DummyArgument(
124                 std::move(dummy.name), asImplicitArg(std::move(obj)));
125           },
126           [&](Fortran::evaluate::characteristics::DummyProcedure &proc) {
127             return Fortran::evaluate::characteristics::DummyArgument(
128                 std::move(dummy.name), std::move(proc));
129           },
130           [](Fortran::evaluate::characteristics::AlternateReturn &x) {
131             return Fortran::evaluate::characteristics::DummyArgument(
132                 std::move(x));
133           }},
134       dummy.u);
135 }
136 
137 Fortran::evaluate::characteristics::Procedure
138 Fortran::lower::CallerInterface::characterize() const {
139   Fortran::evaluate::FoldingContext &foldingContext =
140       converter.getFoldingContext();
141   std::optional<Fortran::evaluate::characteristics::Procedure> characteristic =
142       Fortran::evaluate::characteristics::Procedure::Characterize(
143           procRef.proc(), foldingContext);
144   assert(characteristic && "Failed to get characteristic from procRef");
145   // The characteristic may not contain the argument characteristic if the
146   // ProcedureDesignator has no interface.
147   if (!characteristic->HasExplicitInterface()) {
148     for (const std::optional<Fortran::evaluate::ActualArgument> &arg :
149          procRef.arguments()) {
150       if (arg.value().isAlternateReturn()) {
151         characteristic->dummyArguments.emplace_back(
152             Fortran::evaluate::characteristics::AlternateReturn{});
153       } else {
154         // Argument cannot be optional with implicit interface
155         const Fortran::lower::SomeExpr *expr = arg.value().UnwrapExpr();
156         assert(
157             expr &&
158             "argument in call with implicit interface cannot be assumed type");
159         std::optional<Fortran::evaluate::characteristics::DummyArgument>
160             argCharacteristic =
161                 Fortran::evaluate::characteristics::DummyArgument::FromActual(
162                     "actual", *expr, foldingContext);
163         assert(argCharacteristic &&
164                "failed to characterize argument in implicit call");
165         characteristic->dummyArguments.emplace_back(
166             asImplicitArg(std::move(*argCharacteristic)));
167       }
168     }
169   }
170   return *characteristic;
171 }
172 
173 void Fortran::lower::CallerInterface::placeInput(
174     const PassedEntity &passedEntity, mlir::Value arg) {
175   assert(static_cast<int>(actualInputs.size()) > passedEntity.firArgument &&
176          passedEntity.firArgument >= 0 &&
177          passedEntity.passBy != CallInterface::PassEntityBy::AddressAndLength &&
178          "bad arg position");
179   actualInputs[passedEntity.firArgument] = arg;
180 }
181 
182 void Fortran::lower::CallerInterface::placeAddressAndLengthInput(
183     const PassedEntity &passedEntity, mlir::Value addr, mlir::Value len) {
184   assert(static_cast<int>(actualInputs.size()) > passedEntity.firArgument &&
185          static_cast<int>(actualInputs.size()) > passedEntity.firLength &&
186          passedEntity.firArgument >= 0 && passedEntity.firLength >= 0 &&
187          passedEntity.passBy == CallInterface::PassEntityBy::AddressAndLength &&
188          "bad arg position");
189   actualInputs[passedEntity.firArgument] = addr;
190   actualInputs[passedEntity.firLength] = len;
191 }
192 
193 bool Fortran::lower::CallerInterface::verifyActualInputs() const {
194   if (getNumFIRArguments() != actualInputs.size())
195     return false;
196   for (mlir::Value arg : actualInputs) {
197     if (!arg)
198       return false;
199   }
200   return true;
201 }
202 
203 void Fortran::lower::CallerInterface::walkResultLengths(
204     ExprVisitor visitor) const {
205   assert(characteristic && "characteristic was not computed");
206   const Fortran::evaluate::characteristics::FunctionResult &result =
207       characteristic->functionResult.value();
208   const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
209       result.GetTypeAndShape();
210   assert(typeAndShape && "no result type");
211   Fortran::evaluate::DynamicType dynamicType = typeAndShape->type();
212   // Visit result length specification expressions that are explicit.
213   if (dynamicType.category() == Fortran::common::TypeCategory::Character) {
214     if (std::optional<Fortran::evaluate::ExtentExpr> length =
215             dynamicType.GetCharLength())
216       visitor(toEvExpr(*length));
217   } else if (dynamicType.category() == common::TypeCategory::Derived) {
218     TODO(converter.getCurrentLocation(), "walkResultLengths derived type");
219   }
220 }
221 
222 // Compute extent expr from shapeSpec of an explicit shape.
223 // TODO: Allow evaluate shape analysis to work in a mode where it disregards
224 // the non-constant aspects when building the shape to avoid having this here.
225 static Fortran::evaluate::ExtentExpr
226 getExtentExpr(const Fortran::semantics::ShapeSpec &shapeSpec) {
227   const auto &ubound = shapeSpec.ubound().GetExplicit();
228   const auto &lbound = shapeSpec.lbound().GetExplicit();
229   assert(lbound && ubound && "shape must be explicit");
230   return Fortran::common::Clone(*ubound) - Fortran::common::Clone(*lbound) +
231          Fortran::evaluate::ExtentExpr{1};
232 }
233 
234 void Fortran::lower::CallerInterface::walkResultExtents(
235     ExprVisitor visitor) const {
236   // Walk directly the result symbol shape (the characteristic shape may contain
237   // descriptor inquiries to it that would fail to lower on the caller side).
238   const Fortran::semantics::Symbol *interfaceSymbol =
239       procRef.proc().GetInterfaceSymbol();
240   if (interfaceSymbol) {
241     const Fortran::semantics::Symbol &result =
242         interfaceSymbol->get<Fortran::semantics::SubprogramDetails>().result();
243     if (const auto *objectDetails =
244             result.detailsIf<Fortran::semantics::ObjectEntityDetails>())
245       if (objectDetails->shape().IsExplicitShape())
246         for (const Fortran::semantics::ShapeSpec &shapeSpec :
247              objectDetails->shape())
248           visitor(Fortran::evaluate::AsGenericExpr(getExtentExpr(shapeSpec)));
249   } else {
250     if (procRef.Rank() != 0)
251       fir::emitFatalError(
252           converter.getCurrentLocation(),
253           "only scalar functions may not have an interface symbol");
254   }
255 }
256 
257 bool Fortran::lower::CallerInterface::mustMapInterfaceSymbols() const {
258   assert(characteristic && "characteristic was not computed");
259   const std::optional<Fortran::evaluate::characteristics::FunctionResult>
260       &result = characteristic->functionResult;
261   if (!result || result->CanBeReturnedViaImplicitInterface() ||
262       !procRef.proc().GetInterfaceSymbol())
263     return false;
264   bool allResultSpecExprConstant = true;
265   auto visitor = [&](const Fortran::lower::SomeExpr &e) {
266     allResultSpecExprConstant &= Fortran::evaluate::IsConstantExpr(e);
267   };
268   walkResultLengths(visitor);
269   walkResultExtents(visitor);
270   return !allResultSpecExprConstant;
271 }
272 
273 mlir::Value Fortran::lower::CallerInterface::getArgumentValue(
274     const semantics::Symbol &sym) const {
275   mlir::Location loc = converter.getCurrentLocation();
276   const Fortran::semantics::Symbol *iface = procRef.proc().GetInterfaceSymbol();
277   if (!iface)
278     fir::emitFatalError(
279         loc, "mapping actual and dummy arguments requires an interface");
280   const std::vector<Fortran::semantics::Symbol *> &dummies =
281       iface->get<semantics::SubprogramDetails>().dummyArgs();
282   auto it = std::find(dummies.begin(), dummies.end(), &sym);
283   if (it == dummies.end())
284     fir::emitFatalError(loc, "symbol is not a dummy in this call");
285   FirValue mlirArgIndex = passedArguments[it - dummies.begin()].firArgument;
286   return actualInputs[mlirArgIndex];
287 }
288 
289 mlir::Type Fortran::lower::CallerInterface::getResultStorageType() const {
290   if (passedResult)
291     return fir::dyn_cast_ptrEleTy(inputs[passedResult->firArgument].type);
292   assert(saveResult && !outputs.empty());
293   return outputs[0].type;
294 }
295 
296 const Fortran::semantics::Symbol &
297 Fortran::lower::CallerInterface::getResultSymbol() const {
298   mlir::Location loc = converter.getCurrentLocation();
299   const Fortran::semantics::Symbol *iface = procRef.proc().GetInterfaceSymbol();
300   if (!iface)
301     fir::emitFatalError(
302         loc, "mapping actual and dummy arguments requires an interface");
303   return iface->get<semantics::SubprogramDetails>().result();
304 }
305 
306 //===----------------------------------------------------------------------===//
307 // Callee side interface implementation
308 //===----------------------------------------------------------------------===//
309 
310 bool Fortran::lower::CalleeInterface::hasAlternateReturns() const {
311   return !funit.isMainProgram() &&
312          Fortran::semantics::HasAlternateReturns(funit.getSubprogramSymbol());
313 }
314 
315 std::string Fortran::lower::CalleeInterface::getMangledName() const {
316   if (funit.isMainProgram())
317     return fir::NameUniquer::doProgramEntry().str();
318   return ::getMangledName(funit.getSubprogramSymbol());
319 }
320 
321 const Fortran::semantics::Symbol *
322 Fortran::lower::CalleeInterface::getProcedureSymbol() const {
323   if (funit.isMainProgram())
324     return nullptr;
325   return &funit.getSubprogramSymbol();
326 }
327 
328 mlir::Location Fortran::lower::CalleeInterface::getCalleeLocation() const {
329   // FIXME: do NOT use unknown for the anonymous PROGRAM case. We probably
330   // should just stash the location in the funit regardless.
331   return converter.genLocation(funit.getStartingSourceLoc());
332 }
333 
334 Fortran::evaluate::characteristics::Procedure
335 Fortran::lower::CalleeInterface::characterize() const {
336   Fortran::evaluate::FoldingContext &foldingContext =
337       converter.getFoldingContext();
338   std::optional<Fortran::evaluate::characteristics::Procedure> characteristic =
339       Fortran::evaluate::characteristics::Procedure::Characterize(
340           funit.getSubprogramSymbol(), foldingContext);
341   assert(characteristic && "Fail to get characteristic from symbol");
342   return *characteristic;
343 }
344 
345 bool Fortran::lower::CalleeInterface::isMainProgram() const {
346   return funit.isMainProgram();
347 }
348 
349 mlir::FuncOp Fortran::lower::CalleeInterface::addEntryBlockAndMapArguments() {
350   // On the callee side, directly map the mlir::value argument of
351   // the function block to the Fortran symbols.
352   func.addEntryBlock();
353   mapPassedEntities();
354   return func;
355 }
356 
357 bool Fortran::lower::CalleeInterface::hasHostAssociated() const {
358   return funit.parentHasHostAssoc();
359 }
360 
361 mlir::Type Fortran::lower::CalleeInterface::getHostAssociatedTy() const {
362   assert(hasHostAssociated());
363   return funit.parentHostAssoc().getArgumentType(converter);
364 }
365 
366 mlir::Value Fortran::lower::CalleeInterface::getHostAssociatedTuple() const {
367   assert(hasHostAssociated() || !funit.getHostAssoc().empty());
368   return converter.hostAssocTupleValue();
369 }
370 
371 //===----------------------------------------------------------------------===//
372 // CallInterface implementation: this part is common to both caller and caller
373 // sides.
374 //===----------------------------------------------------------------------===//
375 
376 static void addSymbolAttribute(mlir::FuncOp func,
377                                const Fortran::semantics::Symbol &sym,
378                                mlir::MLIRContext &mlirContext) {
379   // Only add this on bind(C) functions for which the symbol is not reflected in
380   // the current context.
381   if (!Fortran::semantics::IsBindCProcedure(sym))
382     return;
383   std::string name =
384       Fortran::lower::mangle::mangleName(sym, /*keepExternalInScope=*/true);
385   func->setAttr(fir::getSymbolAttrName(),
386                 mlir::StringAttr::get(&mlirContext, name));
387 }
388 
389 /// Declare drives the different actions to be performed while analyzing the
390 /// signature and building/finding the mlir::FuncOp.
391 template <typename T>
392 void Fortran::lower::CallInterface<T>::declare() {
393   if (!side().isMainProgram()) {
394     characteristic.emplace(side().characterize());
395     bool isImplicit = characteristic->CanBeCalledViaImplicitInterface();
396     determineInterface(isImplicit, *characteristic);
397   }
398   // No input/output for main program
399 
400   // Create / get funcOp for direct calls. For indirect calls (only meaningful
401   // on the caller side), no funcOp has to be created here. The mlir::Value
402   // holding the indirection is used when creating the fir::CallOp.
403   if (!side().isIndirectCall()) {
404     std::string name = side().getMangledName();
405     mlir::ModuleOp module = converter.getModuleOp();
406     func = fir::FirOpBuilder::getNamedFunction(module, name);
407     if (!func) {
408       mlir::Location loc = side().getCalleeLocation();
409       mlir::FunctionType ty = genFunctionType();
410       func = fir::FirOpBuilder::createFunction(loc, module, name, ty);
411       if (const Fortran::semantics::Symbol *sym = side().getProcedureSymbol())
412         addSymbolAttribute(func, *sym, converter.getMLIRContext());
413       for (const auto &placeHolder : llvm::enumerate(inputs))
414         if (!placeHolder.value().attributes.empty())
415           func.setArgAttrs(placeHolder.index(), placeHolder.value().attributes);
416     }
417   }
418 }
419 
420 /// Once the signature has been analyzed and the mlir::FuncOp was built/found,
421 /// map the fir inputs to Fortran entities (the symbols or expressions).
422 template <typename T>
423 void Fortran::lower::CallInterface<T>::mapPassedEntities() {
424   // map back fir inputs to passed entities
425   if constexpr (std::is_same_v<T, Fortran::lower::CalleeInterface>) {
426     assert(inputs.size() == func.front().getArguments().size() &&
427            "function previously created with different number of arguments");
428     for (auto [fst, snd] : llvm::zip(inputs, func.front().getArguments()))
429       mapBackInputToPassedEntity(fst, snd);
430   } else {
431     // On the caller side, map the index of the mlir argument position
432     // to Fortran ActualArguments.
433     int firPosition = 0;
434     for (const FirPlaceHolder &placeHolder : inputs)
435       mapBackInputToPassedEntity(placeHolder, firPosition++);
436   }
437 }
438 
439 template <typename T>
440 void Fortran::lower::CallInterface<T>::mapBackInputToPassedEntity(
441     const FirPlaceHolder &placeHolder, FirValue firValue) {
442   PassedEntity &passedEntity =
443       placeHolder.passedEntityPosition == FirPlaceHolder::resultEntityPosition
444           ? passedResult.value()
445           : passedArguments[placeHolder.passedEntityPosition];
446   if (placeHolder.property == Property::CharLength)
447     passedEntity.firLength = firValue;
448   else
449     passedEntity.firArgument = firValue;
450 }
451 
452 /// Helpers to access ActualArgument/Symbols
453 static const Fortran::evaluate::ActualArguments &
454 getEntityContainer(const Fortran::evaluate::ProcedureRef &proc) {
455   return proc.arguments();
456 }
457 
458 static const std::vector<Fortran::semantics::Symbol *> &
459 getEntityContainer(Fortran::lower::pft::FunctionLikeUnit &funit) {
460   return funit.getSubprogramSymbol()
461       .get<Fortran::semantics::SubprogramDetails>()
462       .dummyArgs();
463 }
464 
465 static const Fortran::evaluate::ActualArgument *getDataObjectEntity(
466     const std::optional<Fortran::evaluate::ActualArgument> &arg) {
467   if (arg)
468     return &*arg;
469   return nullptr;
470 }
471 
472 static const Fortran::semantics::Symbol &
473 getDataObjectEntity(const Fortran::semantics::Symbol *arg) {
474   assert(arg && "expect symbol for data object entity");
475   return *arg;
476 }
477 
478 static const Fortran::evaluate::ActualArgument *
479 getResultEntity(const Fortran::evaluate::ProcedureRef &) {
480   return nullptr;
481 }
482 
483 static const Fortran::semantics::Symbol &
484 getResultEntity(Fortran::lower::pft::FunctionLikeUnit &funit) {
485   return funit.getSubprogramSymbol()
486       .get<Fortran::semantics::SubprogramDetails>()
487       .result();
488 }
489 
490 /// Bypass helpers to manipulate entities since they are not any symbol/actual
491 /// argument to associate. See SignatureBuilder below.
492 using FakeEntity = bool;
493 using FakeEntities = llvm::SmallVector<FakeEntity>;
494 static FakeEntities
495 getEntityContainer(const Fortran::evaluate::characteristics::Procedure &proc) {
496   FakeEntities enities(proc.dummyArguments.size());
497   return enities;
498 }
499 static const FakeEntity &getDataObjectEntity(const FakeEntity &e) { return e; }
500 static FakeEntity
501 getResultEntity(const Fortran::evaluate::characteristics::Procedure &proc) {
502   return false;
503 }
504 
505 /// This is the actual part that defines the FIR interface based on the
506 /// characteristic. It directly mutates the CallInterface members.
507 template <typename T>
508 class Fortran::lower::CallInterfaceImpl {
509   using CallInterface = Fortran::lower::CallInterface<T>;
510   using PassEntityBy = typename CallInterface::PassEntityBy;
511   using PassedEntity = typename CallInterface::PassedEntity;
512   using FirValue = typename CallInterface::FirValue;
513   using FortranEntity = typename CallInterface::FortranEntity;
514   using FirPlaceHolder = typename CallInterface::FirPlaceHolder;
515   using Property = typename CallInterface::Property;
516   using TypeAndShape = Fortran::evaluate::characteristics::TypeAndShape;
517   using DummyCharacteristics =
518       Fortran::evaluate::characteristics::DummyArgument;
519 
520 public:
521   CallInterfaceImpl(CallInterface &i)
522       : interface(i), mlirContext{i.converter.getMLIRContext()} {}
523 
524   void buildImplicitInterface(
525       const Fortran::evaluate::characteristics::Procedure &procedure) {
526     // Handle result
527     if (const std::optional<Fortran::evaluate::characteristics::FunctionResult>
528             &result = procedure.functionResult)
529       handleImplicitResult(*result);
530     else if (interface.side().hasAlternateReturns())
531       addFirResult(mlir::IndexType::get(&mlirContext),
532                    FirPlaceHolder::resultEntityPosition, Property::Value);
533     // Handle arguments
534     const auto &argumentEntities =
535         getEntityContainer(interface.side().getCallDescription());
536     for (auto pair : llvm::zip(procedure.dummyArguments, argumentEntities)) {
537       const Fortran::evaluate::characteristics::DummyArgument
538           &argCharacteristics = std::get<0>(pair);
539       std::visit(
540           Fortran::common::visitors{
541               [&](const auto &dummy) {
542                 const auto &entity = getDataObjectEntity(std::get<1>(pair));
543                 handleImplicitDummy(&argCharacteristics, dummy, entity);
544               },
545               [&](const Fortran::evaluate::characteristics::AlternateReturn &) {
546                 // nothing to do
547               },
548           },
549           argCharacteristics.u);
550     }
551   }
552 
553   void buildExplicitInterface(
554       const Fortran::evaluate::characteristics::Procedure &procedure) {
555     // Handle result
556     if (const std::optional<Fortran::evaluate::characteristics::FunctionResult>
557             &result = procedure.functionResult) {
558       if (result->CanBeReturnedViaImplicitInterface())
559         handleImplicitResult(*result);
560       else
561         handleExplicitResult(*result);
562     } else if (interface.side().hasAlternateReturns()) {
563       addFirResult(mlir::IndexType::get(&mlirContext),
564                    FirPlaceHolder::resultEntityPosition, Property::Value);
565     }
566     bool isBindC = procedure.IsBindC();
567     // Handle arguments
568     const auto &argumentEntities =
569         getEntityContainer(interface.side().getCallDescription());
570     for (auto pair : llvm::zip(procedure.dummyArguments, argumentEntities)) {
571       const Fortran::evaluate::characteristics::DummyArgument
572           &argCharacteristics = std::get<0>(pair);
573       std::visit(
574           Fortran::common::visitors{
575               [&](const Fortran::evaluate::characteristics::DummyDataObject
576                       &dummy) {
577                 const auto &entity = getDataObjectEntity(std::get<1>(pair));
578                 if (dummy.CanBePassedViaImplicitInterface())
579                   handleImplicitDummy(&argCharacteristics, dummy, entity);
580                 else
581                   handleExplicitDummy(&argCharacteristics, dummy, entity,
582                                       isBindC);
583               },
584               [&](const Fortran::evaluate::characteristics::DummyProcedure
585                       &dummy) {
586                 const auto &entity = getDataObjectEntity(std::get<1>(pair));
587                 handleImplicitDummy(&argCharacteristics, dummy, entity);
588               },
589               [&](const Fortran::evaluate::characteristics::AlternateReturn &) {
590                 // nothing to do
591               },
592           },
593           argCharacteristics.u);
594     }
595   }
596 
597   void appendHostAssocTupleArg(mlir::Type tupTy) {
598     mlir::MLIRContext *ctxt = tupTy.getContext();
599     addFirOperand(tupTy, nextPassedArgPosition(), Property::BaseAddress,
600                   {mlir::NamedAttribute{
601                       mlir::StringAttr::get(ctxt, fir::getHostAssocAttrName()),
602                       mlir::UnitAttr::get(ctxt)}});
603     interface.passedArguments.emplace_back(
604         PassedEntity{PassEntityBy::BaseAddress, std::nullopt,
605                      interface.side().getHostAssociatedTuple(), emptyValue()});
606   }
607 
608   static llvm::Optional<Fortran::evaluate::DynamicType> getResultDynamicType(
609       const Fortran::evaluate::characteristics::Procedure &procedure) {
610     if (const std::optional<Fortran::evaluate::characteristics::FunctionResult>
611             &result = procedure.functionResult)
612       if (const auto *resultTypeAndShape = result->GetTypeAndShape())
613         return resultTypeAndShape->type();
614     return llvm::None;
615   }
616 
617   static bool mustPassLengthWithDummyProcedure(
618       const Fortran::evaluate::characteristics::Procedure &procedure) {
619     // When passing a character function designator `bar` as dummy procedure to
620     // `foo` (e.g. `foo(bar)`), pass the result length of `bar` to `foo` so that
621     // `bar` can be called inside `foo` even if its length is assumed there.
622     // From an ABI perspective, the extra length argument must be handled
623     // exactly as if passing a character object. Using an argument of
624     // fir.boxchar type gives the expected behavior: after codegen, the
625     // fir.boxchar lengths are added after all the arguments as extra value
626     // arguments (the extra arguments order is the order of the fir.boxchar).
627 
628     // This ABI is compatible with ifort, nag, nvfortran, and xlf, but not
629     // gfortran. Gfortran does not pass the length and is therefore unable to
630     // handle later call to `bar` in `foo` where the length would be assumed. If
631     // the result is an array, nag and ifort and xlf still pass the length, but
632     // not nvfortran (and gfortran). It is not clear it is possible to call an
633     // array function with assumed length (f18 forbides defining such
634     // interfaces). Hence, passing the length is most likely useless, but stick
635     // with ifort/nag/xlf interface here.
636     if (llvm::Optional<Fortran::evaluate::DynamicType> type =
637             getResultDynamicType(procedure))
638       return type->category() == Fortran::common::TypeCategory::Character;
639     return false;
640   }
641 
642 private:
643   void handleImplicitResult(
644       const Fortran::evaluate::characteristics::FunctionResult &result) {
645     if (result.IsProcedurePointer())
646       TODO(interface.converter.getCurrentLocation(),
647            "procedure pointer result not yet handled");
648     const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
649         result.GetTypeAndShape();
650     assert(typeAndShape && "expect type for non proc pointer result");
651     Fortran::evaluate::DynamicType dynamicType = typeAndShape->type();
652     // Character result allocated by caller and passed as hidden arguments
653     if (dynamicType.category() == Fortran::common::TypeCategory::Character) {
654       handleImplicitCharacterResult(dynamicType);
655     } else if (dynamicType.category() ==
656                Fortran::common::TypeCategory::Derived) {
657       // Derived result need to be allocated by the caller and the result value
658       // must be saved. Derived type in implicit interface cannot have length
659       // parameters.
660       setSaveResult();
661       mlir::Type mlirType = translateDynamicType(dynamicType);
662       addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
663                    Property::Value);
664     } else {
665       // All result other than characters/derived are simply returned by value
666       // in implicit interfaces
667       mlir::Type mlirType =
668           getConverter().genType(dynamicType.category(), dynamicType.kind());
669       addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
670                    Property::Value);
671     }
672   }
673   void
674   handleImplicitCharacterResult(const Fortran::evaluate::DynamicType &type) {
675     int resultPosition = FirPlaceHolder::resultEntityPosition;
676     setPassedResult(PassEntityBy::AddressAndLength,
677                     getResultEntity(interface.side().getCallDescription()));
678     mlir::Type lenTy = mlir::IndexType::get(&mlirContext);
679     std::optional<std::int64_t> constantLen = type.knownLength();
680     fir::CharacterType::LenType len =
681         constantLen ? *constantLen : fir::CharacterType::unknownLen();
682     mlir::Type charRefTy = fir::ReferenceType::get(
683         fir::CharacterType::get(&mlirContext, type.kind(), len));
684     mlir::Type boxCharTy = fir::BoxCharType::get(&mlirContext, type.kind());
685     addFirOperand(charRefTy, resultPosition, Property::CharAddress);
686     addFirOperand(lenTy, resultPosition, Property::CharLength);
687     /// For now, also return it by boxchar
688     addFirResult(boxCharTy, resultPosition, Property::BoxChar);
689   }
690 
691   /// Return a vector with an attribute with the name of the argument if this
692   /// is a callee interface and the name is available. Otherwise, just return
693   /// an empty vector.
694   llvm::SmallVector<mlir::NamedAttribute>
695   dummyNameAttr(const FortranEntity &entity) {
696     if constexpr (std::is_same_v<FortranEntity,
697                                  std::optional<Fortran::common::Reference<
698                                      const Fortran::semantics::Symbol>>>) {
699       if (entity.has_value()) {
700         const Fortran::semantics::Symbol *argument = &*entity.value();
701         // "fir.bindc_name" is used for arguments for the sake of consistency
702         // with other attributes carrying surface syntax names in FIR.
703         return {mlir::NamedAttribute(
704             mlir::StringAttr::get(&mlirContext, "fir.bindc_name"),
705             mlir::StringAttr::get(&mlirContext,
706                                   toStringRef(argument->name())))};
707       }
708     }
709     return {};
710   }
711 
712   void handleImplicitDummy(
713       const DummyCharacteristics *characteristics,
714       const Fortran::evaluate::characteristics::DummyDataObject &obj,
715       const FortranEntity &entity) {
716     Fortran::evaluate::DynamicType dynamicType = obj.type.type();
717     if (dynamicType.category() == Fortran::common::TypeCategory::Character) {
718       mlir::Type boxCharTy =
719           fir::BoxCharType::get(&mlirContext, dynamicType.kind());
720       addFirOperand(boxCharTy, nextPassedArgPosition(), Property::BoxChar,
721                     dummyNameAttr(entity));
722       addPassedArg(PassEntityBy::BoxChar, entity, characteristics);
723     } else {
724       // non-PDT derived type allowed in implicit interface.
725       mlir::Type type = translateDynamicType(dynamicType);
726       fir::SequenceType::Shape bounds = getBounds(obj.type.shape());
727       if (!bounds.empty())
728         type = fir::SequenceType::get(bounds, type);
729       mlir::Type refType = fir::ReferenceType::get(type);
730       addFirOperand(refType, nextPassedArgPosition(), Property::BaseAddress,
731                     dummyNameAttr(entity));
732       addPassedArg(PassEntityBy::BaseAddress, entity, characteristics);
733     }
734   }
735 
736   // Define when an explicit argument must be passed in a fir.box.
737   bool dummyRequiresBox(
738       const Fortran::evaluate::characteristics::DummyDataObject &obj) {
739     using ShapeAttr = Fortran::evaluate::characteristics::TypeAndShape::Attr;
740     using ShapeAttrs = Fortran::evaluate::characteristics::TypeAndShape::Attrs;
741     constexpr ShapeAttrs shapeRequiringBox = {
742         ShapeAttr::AssumedShape, ShapeAttr::DeferredShape,
743         ShapeAttr::AssumedRank, ShapeAttr::Coarray};
744     if ((obj.type.attrs() & shapeRequiringBox).any())
745       // Need to pass shape/coshape info in fir.box.
746       return true;
747     if (obj.type.type().IsPolymorphic())
748       // Need to pass dynamic type info in fir.box.
749       return true;
750     if (const Fortran::semantics::DerivedTypeSpec *derived =
751             Fortran::evaluate::GetDerivedTypeSpec(obj.type.type()))
752       // Need to pass type parameters in fir.box if any.
753       return derived->parameters().empty();
754     return false;
755   }
756 
757   mlir::Type
758   translateDynamicType(const Fortran::evaluate::DynamicType &dynamicType) {
759     Fortran::common::TypeCategory cat = dynamicType.category();
760     // DERIVED
761     if (cat == Fortran::common::TypeCategory::Derived) {
762       TODO(interface.converter.getCurrentLocation(),
763            "[translateDynamicType] Derived types");
764     }
765     // CHARACTER with compile time constant length.
766     if (cat == Fortran::common::TypeCategory::Character)
767       if (std::optional<std::int64_t> constantLen =
768               toInt64(dynamicType.GetCharLength()))
769         return getConverter().genType(cat, dynamicType.kind(), {*constantLen});
770     // INTEGER, REAL, LOGICAL, COMPLEX, and CHARACTER with dynamic length.
771     return getConverter().genType(cat, dynamicType.kind());
772   }
773 
774   void handleExplicitDummy(
775       const DummyCharacteristics *characteristics,
776       const Fortran::evaluate::characteristics::DummyDataObject &obj,
777       const FortranEntity &entity, bool isBindC) {
778     using Attrs = Fortran::evaluate::characteristics::DummyDataObject::Attr;
779 
780     bool isValueAttr = false;
781     [[maybe_unused]] mlir::Location loc =
782         interface.converter.getCurrentLocation();
783     llvm::SmallVector<mlir::NamedAttribute> attrs = dummyNameAttr(entity);
784     auto addMLIRAttr = [&](llvm::StringRef attr) {
785       attrs.emplace_back(mlir::StringAttr::get(&mlirContext, attr),
786                          mlir::UnitAttr::get(&mlirContext));
787     };
788     if (obj.attrs.test(Attrs::Optional))
789       addMLIRAttr(fir::getOptionalAttrName());
790     if (obj.attrs.test(Attrs::Asynchronous))
791       TODO(loc, "Asynchronous in procedure interface");
792     if (obj.attrs.test(Attrs::Contiguous))
793       addMLIRAttr(fir::getContiguousAttrName());
794     if (obj.attrs.test(Attrs::Value))
795       isValueAttr = true; // TODO: do we want an mlir::Attribute as well?
796     if (obj.attrs.test(Attrs::Volatile))
797       TODO(loc, "Volatile in procedure interface");
798     if (obj.attrs.test(Attrs::Target))
799       addMLIRAttr(fir::getTargetAttrName());
800 
801     // TODO: intents that require special care (e.g finalization)
802 
803     using ShapeAttr = Fortran::evaluate::characteristics::TypeAndShape::Attr;
804     const Fortran::evaluate::characteristics::TypeAndShape::Attrs &shapeAttrs =
805         obj.type.attrs();
806     if (shapeAttrs.test(ShapeAttr::AssumedRank))
807       TODO(loc, "Assumed Rank in procedure interface");
808     if (shapeAttrs.test(ShapeAttr::Coarray))
809       TODO(loc, "Coarray in procedure interface");
810 
811     // So far assume that if the argument cannot be passed by implicit interface
812     // it must be by box. That may no be always true (e.g for simple optionals)
813 
814     Fortran::evaluate::DynamicType dynamicType = obj.type.type();
815     mlir::Type type = translateDynamicType(dynamicType);
816     fir::SequenceType::Shape bounds = getBounds(obj.type.shape());
817     if (!bounds.empty())
818       type = fir::SequenceType::get(bounds, type);
819     if (obj.attrs.test(Attrs::Allocatable))
820       type = fir::HeapType::get(type);
821     if (obj.attrs.test(Attrs::Pointer))
822       type = fir::PointerType::get(type);
823     mlir::Type boxType = fir::BoxType::get(type);
824 
825     if (obj.attrs.test(Attrs::Allocatable) || obj.attrs.test(Attrs::Pointer)) {
826       // Pass as fir.ref<fir.box>
827       mlir::Type boxRefType = fir::ReferenceType::get(boxType);
828       addFirOperand(boxRefType, nextPassedArgPosition(), Property::MutableBox,
829                     attrs);
830       addPassedArg(PassEntityBy::MutableBox, entity, characteristics);
831     } else if (dummyRequiresBox(obj)) {
832       // Pass as fir.box
833       addFirOperand(boxType, nextPassedArgPosition(), Property::Box, attrs);
834       addPassedArg(PassEntityBy::Box, entity, characteristics);
835     } else if (dynamicType.category() ==
836                Fortran::common::TypeCategory::Character) {
837       // Pass as fir.box_char
838       mlir::Type boxCharTy =
839           fir::BoxCharType::get(&mlirContext, dynamicType.kind());
840       addFirOperand(boxCharTy, nextPassedArgPosition(), Property::BoxChar,
841                     attrs);
842       addPassedArg(isValueAttr ? PassEntityBy::CharBoxValueAttribute
843                                : PassEntityBy::BoxChar,
844                    entity, characteristics);
845     } else {
846       // Pass as fir.ref unless it's by VALUE and BIND(C)
847       mlir::Type passType = fir::ReferenceType::get(type);
848       PassEntityBy passBy = PassEntityBy::BaseAddress;
849       Property prop = Property::BaseAddress;
850       if (isValueAttr) {
851         if (isBindC) {
852           passBy = PassEntityBy::Value;
853           prop = Property::Value;
854           passType = type;
855         } else {
856           passBy = PassEntityBy::BaseAddressValueAttribute;
857         }
858       }
859       addFirOperand(passType, nextPassedArgPosition(), prop, attrs);
860       addPassedArg(passBy, entity, characteristics);
861     }
862   }
863 
864   void handleImplicitDummy(
865       const DummyCharacteristics *characteristics,
866       const Fortran::evaluate::characteristics::DummyProcedure &proc,
867       const FortranEntity &entity) {
868     if (proc.attrs.test(
869             Fortran::evaluate::characteristics::DummyProcedure::Attr::Pointer))
870       TODO(interface.converter.getCurrentLocation(),
871            "procedure pointer arguments");
872     // Otherwise, it is a dummy procedure.
873     const Fortran::evaluate::characteristics::Procedure &procedure =
874         proc.procedure.value();
875     mlir::Type funcType =
876         getProcedureDesignatorType(&procedure, interface.converter);
877     llvm::Optional<Fortran::evaluate::DynamicType> resultTy =
878         getResultDynamicType(procedure);
879     if (resultTy && mustPassLengthWithDummyProcedure(procedure)) {
880       // The result length of dummy procedures that are character functions must
881       // be passed so that the dummy procedure can be called if it has assumed
882       // length on the callee side.
883       mlir::Type tupleType =
884           fir::factory::getCharacterProcedureTupleType(funcType);
885       llvm::StringRef charProcAttr = fir::getCharacterProcedureDummyAttrName();
886       addFirOperand(tupleType, nextPassedArgPosition(), Property::CharProcTuple,
887                     {mlir::NamedAttribute{
888                         mlir::StringAttr::get(&mlirContext, charProcAttr),
889                         mlir::UnitAttr::get(&mlirContext)}});
890       addPassedArg(PassEntityBy::CharProcTuple, entity, characteristics);
891       return;
892     }
893     addFirOperand(funcType, nextPassedArgPosition(), Property::BaseAddress);
894     addPassedArg(PassEntityBy::BaseAddress, entity, characteristics);
895   }
896 
897   void handleExplicitResult(
898       const Fortran::evaluate::characteristics::FunctionResult &result) {
899     using Attr = Fortran::evaluate::characteristics::FunctionResult::Attr;
900 
901     if (result.IsProcedurePointer())
902       TODO(interface.converter.getCurrentLocation(),
903            "procedure pointer results");
904     const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
905         result.GetTypeAndShape();
906     assert(typeAndShape && "expect type for non proc pointer result");
907     mlir::Type mlirType = translateDynamicType(typeAndShape->type());
908     fir::SequenceType::Shape bounds = getBounds(typeAndShape->shape());
909     if (!bounds.empty())
910       mlirType = fir::SequenceType::get(bounds, mlirType);
911     if (result.attrs.test(Attr::Allocatable))
912       mlirType = fir::BoxType::get(fir::HeapType::get(mlirType));
913     if (result.attrs.test(Attr::Pointer))
914       mlirType = fir::BoxType::get(fir::PointerType::get(mlirType));
915 
916     if (fir::isa_char(mlirType)) {
917       // Character scalar results must be passed as arguments in lowering so
918       // that an assumed length character function callee can access the result
919       // length. A function with a result requiring an explicit interface does
920       // not have to be compatible with assumed length function, but most
921       // compilers supports it.
922       handleImplicitCharacterResult(typeAndShape->type());
923       return;
924     }
925 
926     addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
927                  Property::Value);
928     // Explicit results require the caller to allocate the storage and save the
929     // function result in the storage with a fir.save_result.
930     setSaveResult();
931   }
932 
933   fir::SequenceType::Shape getBounds(const Fortran::evaluate::Shape &shape) {
934     fir::SequenceType::Shape bounds;
935     for (const std::optional<Fortran::evaluate::ExtentExpr> &extent : shape) {
936       fir::SequenceType::Extent bound = fir::SequenceType::getUnknownExtent();
937       if (std::optional<std::int64_t> i = toInt64(extent))
938         bound = *i;
939       bounds.emplace_back(bound);
940     }
941     return bounds;
942   }
943   std::optional<std::int64_t>
944   toInt64(std::optional<
945           Fortran::evaluate::Expr<Fortran::evaluate::SubscriptInteger>>
946               expr) {
947     if (expr)
948       return Fortran::evaluate::ToInt64(Fortran::evaluate::Fold(
949           getConverter().getFoldingContext(), toEvExpr(*expr)));
950     return std::nullopt;
951   }
952   void
953   addFirOperand(mlir::Type type, int entityPosition, Property p,
954                 llvm::ArrayRef<mlir::NamedAttribute> attributes = llvm::None) {
955     interface.inputs.emplace_back(
956         FirPlaceHolder{type, entityPosition, p, attributes});
957   }
958   void
959   addFirResult(mlir::Type type, int entityPosition, Property p,
960                llvm::ArrayRef<mlir::NamedAttribute> attributes = llvm::None) {
961     interface.outputs.emplace_back(
962         FirPlaceHolder{type, entityPosition, p, attributes});
963   }
964   void addPassedArg(PassEntityBy p, FortranEntity entity,
965                     const DummyCharacteristics *characteristics) {
966     interface.passedArguments.emplace_back(
967         PassedEntity{p, entity, emptyValue(), emptyValue(), characteristics});
968   }
969   void setPassedResult(PassEntityBy p, FortranEntity entity) {
970     interface.passedResult =
971         PassedEntity{p, entity, emptyValue(), emptyValue()};
972   }
973   void setSaveResult() { interface.saveResult = true; }
974   int nextPassedArgPosition() { return interface.passedArguments.size(); }
975 
976   static FirValue emptyValue() {
977     if constexpr (std::is_same_v<Fortran::lower::CalleeInterface, T>) {
978       return {};
979     } else {
980       return -1;
981     }
982   }
983 
984   Fortran::lower::AbstractConverter &getConverter() {
985     return interface.converter;
986   }
987   CallInterface &interface;
988   mlir::MLIRContext &mlirContext;
989 };
990 
991 template <typename T>
992 bool Fortran::lower::CallInterface<T>::PassedEntity::isOptional() const {
993   if (!characteristics)
994     return false;
995   return characteristics->IsOptional();
996 }
997 template <typename T>
998 bool Fortran::lower::CallInterface<T>::PassedEntity::mayBeModifiedByCall()
999     const {
1000   if (!characteristics)
1001     return true;
1002   return characteristics->GetIntent() != Fortran::common::Intent::In;
1003 }
1004 template <typename T>
1005 bool Fortran::lower::CallInterface<T>::PassedEntity::mayBeReadByCall() const {
1006   if (!characteristics)
1007     return true;
1008   return characteristics->GetIntent() != Fortran::common::Intent::Out;
1009 }
1010 
1011 template <typename T>
1012 void Fortran::lower::CallInterface<T>::determineInterface(
1013     bool isImplicit,
1014     const Fortran::evaluate::characteristics::Procedure &procedure) {
1015   CallInterfaceImpl<T> impl(*this);
1016   if (isImplicit)
1017     impl.buildImplicitInterface(procedure);
1018   else
1019     impl.buildExplicitInterface(procedure);
1020   // We only expect the extra host asspciations argument from the callee side as
1021   // the definition of internal procedures will be present, and we'll always
1022   // have a FuncOp definition in the ModuleOp, when lowering.
1023   if constexpr (std::is_same_v<T, Fortran::lower::CalleeInterface>) {
1024     if (side().hasHostAssociated())
1025       impl.appendHostAssocTupleArg(side().getHostAssociatedTy());
1026   }
1027 }
1028 
1029 template <typename T>
1030 mlir::FunctionType Fortran::lower::CallInterface<T>::genFunctionType() {
1031   llvm::SmallVector<mlir::Type> returnTys;
1032   llvm::SmallVector<mlir::Type> inputTys;
1033   for (const FirPlaceHolder &placeHolder : outputs)
1034     returnTys.emplace_back(placeHolder.type);
1035   for (const FirPlaceHolder &placeHolder : inputs)
1036     inputTys.emplace_back(placeHolder.type);
1037   return mlir::FunctionType::get(&converter.getMLIRContext(), inputTys,
1038                                  returnTys);
1039 }
1040 
1041 template <typename T>
1042 llvm::SmallVector<mlir::Type>
1043 Fortran::lower::CallInterface<T>::getResultType() const {
1044   llvm::SmallVector<mlir::Type> types;
1045   for (const FirPlaceHolder &out : outputs)
1046     types.emplace_back(out.type);
1047   return types;
1048 }
1049 
1050 template class Fortran::lower::CallInterface<Fortran::lower::CalleeInterface>;
1051 template class Fortran::lower::CallInterface<Fortran::lower::CallerInterface>;
1052 
1053 //===----------------------------------------------------------------------===//
1054 // Function Type Translation
1055 //===----------------------------------------------------------------------===//
1056 
1057 /// Build signature from characteristics when there is no Fortran entity to
1058 /// associate with the arguments (i.e, this is not a call site or a procedure
1059 /// declaration. This is needed when dealing with function pointers/dummy
1060 /// arguments.
1061 
1062 class SignatureBuilder;
1063 template <>
1064 struct Fortran::lower::PassedEntityTypes<SignatureBuilder> {
1065   using FortranEntity = FakeEntity;
1066   using FirValue = int;
1067 };
1068 
1069 /// SignatureBuilder is a CRTP implementation of CallInterface intended to
1070 /// help translating characteristics::Procedure to mlir::FunctionType using
1071 /// the CallInterface translation.
1072 class SignatureBuilder
1073     : public Fortran::lower::CallInterface<SignatureBuilder> {
1074 public:
1075   SignatureBuilder(const Fortran::evaluate::characteristics::Procedure &p,
1076                    Fortran::lower::AbstractConverter &c, bool forceImplicit)
1077       : CallInterface{c}, proc{p} {
1078     bool isImplicit = forceImplicit || proc.CanBeCalledViaImplicitInterface();
1079     determineInterface(isImplicit, proc);
1080   }
1081   /// Does the procedure characteristics being translated have alternate
1082   /// returns ?
1083   bool hasAlternateReturns() const {
1084     for (const Fortran::evaluate::characteristics::DummyArgument &dummy :
1085          proc.dummyArguments)
1086       if (std::holds_alternative<
1087               Fortran::evaluate::characteristics::AlternateReturn>(dummy.u))
1088         return true;
1089     return false;
1090   };
1091 
1092   /// This is only here to fulfill CRTP dependencies and should not be called.
1093   std::string getMangledName() const {
1094     llvm_unreachable("trying to get name from SignatureBuilder");
1095   }
1096 
1097   /// This is only here to fulfill CRTP dependencies and should not be called.
1098   mlir::Location getCalleeLocation() const {
1099     llvm_unreachable("trying to get callee location from SignatureBuilder");
1100   }
1101 
1102   /// This is only here to fulfill CRTP dependencies and should not be called.
1103   const Fortran::semantics::Symbol *getProcedureSymbol() const {
1104     llvm_unreachable("trying to get callee symbol from SignatureBuilder");
1105   };
1106 
1107   Fortran::evaluate::characteristics::Procedure characterize() const {
1108     return proc;
1109   }
1110   /// SignatureBuilder cannot be used on main program.
1111   static constexpr bool isMainProgram() { return false; }
1112 
1113   /// Return the characteristics::Procedure that is being translated to
1114   /// mlir::FunctionType.
1115   const Fortran::evaluate::characteristics::Procedure &
1116   getCallDescription() const {
1117     return proc;
1118   }
1119 
1120   /// This is not the description of an indirect call.
1121   static constexpr bool isIndirectCall() { return false; }
1122 
1123   /// Return the translated signature.
1124   mlir::FunctionType getFunctionType() { return genFunctionType(); }
1125 
1126   // Copy of base implementation.
1127   static constexpr bool hasHostAssociated() { return false; }
1128   mlir::Type getHostAssociatedTy() const {
1129     llvm_unreachable("getting host associated type in SignatureBuilder");
1130   }
1131 
1132 private:
1133   const Fortran::evaluate::characteristics::Procedure &proc;
1134 };
1135 
1136 mlir::FunctionType Fortran::lower::translateSignature(
1137     const Fortran::evaluate::ProcedureDesignator &proc,
1138     Fortran::lower::AbstractConverter &converter) {
1139   std::optional<Fortran::evaluate::characteristics::Procedure> characteristics =
1140       Fortran::evaluate::characteristics::Procedure::Characterize(
1141           proc, converter.getFoldingContext());
1142   // Most unrestricted intrinsic characteristic has the Elemental attribute
1143   // which triggers CanBeCalledViaImplicitInterface to return false. However,
1144   // using implicit interface rules is just fine here.
1145   bool forceImplicit = proc.GetSpecificIntrinsic();
1146   return SignatureBuilder{characteristics.value(), converter, forceImplicit}
1147       .getFunctionType();
1148 }
1149 
1150 mlir::FuncOp Fortran::lower::getOrDeclareFunction(
1151     llvm::StringRef name, const Fortran::evaluate::ProcedureDesignator &proc,
1152     Fortran::lower::AbstractConverter &converter) {
1153   mlir::ModuleOp module = converter.getModuleOp();
1154   mlir::FuncOp func = fir::FirOpBuilder::getNamedFunction(module, name);
1155   if (func)
1156     return func;
1157 
1158   const Fortran::semantics::Symbol *symbol = proc.GetSymbol();
1159   assert(symbol && "non user function in getOrDeclareFunction");
1160   // getOrDeclareFunction is only used for functions not defined in the current
1161   // program unit, so use the location of the procedure designator symbol, which
1162   // is the first occurrence of the procedure in the program unit.
1163   mlir::Location loc = converter.genLocation(symbol->name());
1164   std::optional<Fortran::evaluate::characteristics::Procedure> characteristics =
1165       Fortran::evaluate::characteristics::Procedure::Characterize(
1166           proc, converter.getFoldingContext());
1167   mlir::FunctionType ty = SignatureBuilder{characteristics.value(), converter,
1168                                            /*forceImplicit=*/false}
1169                               .getFunctionType();
1170   mlir::FuncOp newFunc =
1171       fir::FirOpBuilder::createFunction(loc, module, name, ty);
1172   addSymbolAttribute(newFunc, *symbol, converter.getMLIRContext());
1173   return newFunc;
1174 }
1175 
1176 // Is it required to pass a dummy procedure with \p characteristics as a tuple
1177 // containing the function address and the result length ?
1178 static bool mustPassLengthWithDummyProcedure(
1179     const std::optional<Fortran::evaluate::characteristics::Procedure>
1180         &characteristics) {
1181   return characteristics &&
1182          Fortran::lower::CallInterfaceImpl<SignatureBuilder>::
1183              mustPassLengthWithDummyProcedure(*characteristics);
1184 }
1185 
1186 bool Fortran::lower::mustPassLengthWithDummyProcedure(
1187     const Fortran::evaluate::ProcedureDesignator &procedure,
1188     Fortran::lower::AbstractConverter &converter) {
1189   std::optional<Fortran::evaluate::characteristics::Procedure> characteristics =
1190       Fortran::evaluate::characteristics::Procedure::Characterize(
1191           procedure, converter.getFoldingContext());
1192   return ::mustPassLengthWithDummyProcedure(characteristics);
1193 }
1194 
1195 mlir::Type Fortran::lower::getDummyProcedureType(
1196     const Fortran::semantics::Symbol &dummyProc,
1197     Fortran::lower::AbstractConverter &converter) {
1198   std::optional<Fortran::evaluate::characteristics::Procedure> iface =
1199       Fortran::evaluate::characteristics::Procedure::Characterize(
1200           dummyProc, converter.getFoldingContext());
1201   mlir::Type procType = getProcedureDesignatorType(
1202       iface.has_value() ? &*iface : nullptr, converter);
1203   if (::mustPassLengthWithDummyProcedure(iface))
1204     return fir::factory::getCharacterProcedureTupleType(procType);
1205   return procType;
1206 }
1207