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