1e1a12767SValentin Clement //===-- CallInterface.cpp -- Procedure call interface ---------------------===//
2e1a12767SValentin Clement //
3e1a12767SValentin Clement // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e1a12767SValentin Clement // See https://llvm.org/LICENSE.txt for license information.
5e1a12767SValentin Clement // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e1a12767SValentin Clement //
7e1a12767SValentin Clement //===----------------------------------------------------------------------===//
8e1a12767SValentin Clement 
9e1a12767SValentin Clement #include "flang/Lower/CallInterface.h"
10e1a12767SValentin Clement #include "flang/Evaluate/fold.h"
11e1a12767SValentin Clement #include "flang/Lower/Bridge.h"
12e1a12767SValentin Clement #include "flang/Lower/Mangler.h"
13e1a12767SValentin Clement #include "flang/Lower/PFTBuilder.h"
14764f95a8SValentin Clement #include "flang/Lower/StatementContext.h"
15e1a12767SValentin Clement #include "flang/Lower/Support/Utils.h"
16764f95a8SValentin Clement #include "flang/Optimizer/Builder/Character.h"
17e1a12767SValentin Clement #include "flang/Optimizer/Builder/FIRBuilder.h"
185b66cc10SValentin Clement #include "flang/Optimizer/Builder/Todo.h"
19e1a12767SValentin Clement #include "flang/Optimizer/Dialect/FIRDialect.h"
20e1a12767SValentin Clement #include "flang/Optimizer/Dialect/FIROpsSupport.h"
21e1a12767SValentin Clement #include "flang/Optimizer/Support/InternalNames.h"
22e1a12767SValentin Clement #include "flang/Semantics/symbol.h"
23e1a12767SValentin Clement #include "flang/Semantics/tools.h"
24e1a12767SValentin Clement 
25e1a12767SValentin Clement //===----------------------------------------------------------------------===//
26e1a12767SValentin Clement // BIND(C) mangling helpers
27e1a12767SValentin Clement //===----------------------------------------------------------------------===//
28e1a12767SValentin Clement 
29e1a12767SValentin Clement // Return the binding label (from BIND(C...)) or the mangled name of a symbol.
getMangledName(mlir::Location loc,const Fortran::semantics::Symbol & symbol)3010b23ae8SValentin Clement static std::string getMangledName(mlir::Location loc,
3110b23ae8SValentin Clement                                   const Fortran::semantics::Symbol &symbol) {
32e1a12767SValentin Clement   const std::string *bindName = symbol.GetBindName();
3310b23ae8SValentin Clement   // TODO: update GetBindName so that it does not return a label for internal
3410b23ae8SValentin Clement   // procedures.
3510b23ae8SValentin Clement   if (bindName && Fortran::semantics::ClassifyProcedure(symbol) ==
3610b23ae8SValentin Clement                       Fortran::semantics::ProcedureDefinitionClass::Internal)
3710b23ae8SValentin Clement     TODO(loc, "BIND(C) internal procedures");
38e1a12767SValentin Clement   return bindName ? *bindName : Fortran::lower::mangle::mangleName(symbol);
39e1a12767SValentin Clement }
40e1a12767SValentin Clement 
41764f95a8SValentin Clement /// Return the type of a dummy procedure given its characteristic (if it has
42764f95a8SValentin Clement /// one).
getProcedureDesignatorType(const Fortran::evaluate::characteristics::Procedure *,Fortran::lower::AbstractConverter & converter)43764f95a8SValentin Clement mlir::Type getProcedureDesignatorType(
44764f95a8SValentin Clement     const Fortran::evaluate::characteristics::Procedure *,
45764f95a8SValentin Clement     Fortran::lower::AbstractConverter &converter) {
46764f95a8SValentin Clement   // TODO: Get actual function type of the dummy procedure, at least when an
47764f95a8SValentin Clement   // interface is given. The result type should be available even if the arity
48764f95a8SValentin Clement   // and type of the arguments is not.
49764f95a8SValentin Clement   llvm::SmallVector<mlir::Type> resultTys;
50764f95a8SValentin Clement   llvm::SmallVector<mlir::Type> inputTys;
51764f95a8SValentin Clement   // In general, that is a nice to have but we cannot guarantee to find the
52764f95a8SValentin Clement   // function type that will match the one of the calls, we may not even know
53764f95a8SValentin Clement   // how many arguments the dummy procedure accepts (e.g. if a procedure
54764f95a8SValentin Clement   // pointer is only transiting through the current procedure without being
55764f95a8SValentin Clement   // called), so a function type cast must always be inserted.
56764f95a8SValentin Clement   auto *context = &converter.getMLIRContext();
57764f95a8SValentin Clement   auto untypedFunc = mlir::FunctionType::get(context, inputTys, resultTys);
58764f95a8SValentin Clement   return fir::BoxProcType::get(context, untypedFunc);
59764f95a8SValentin Clement }
60764f95a8SValentin Clement 
61e1a12767SValentin Clement //===----------------------------------------------------------------------===//
62d0b70a07SValentin Clement // Caller side interface implementation
63d0b70a07SValentin Clement //===----------------------------------------------------------------------===//
64d0b70a07SValentin Clement 
hasAlternateReturns() const65d0b70a07SValentin Clement bool Fortran::lower::CallerInterface::hasAlternateReturns() const {
66d0b70a07SValentin Clement   return procRef.hasAlternateReturns();
67d0b70a07SValentin Clement }
68d0b70a07SValentin Clement 
getMangledName() const69d0b70a07SValentin Clement std::string Fortran::lower::CallerInterface::getMangledName() const {
70d0b70a07SValentin Clement   const Fortran::evaluate::ProcedureDesignator &proc = procRef.proc();
71d0b70a07SValentin Clement   if (const Fortran::semantics::Symbol *symbol = proc.GetSymbol())
7210b23ae8SValentin Clement     return ::getMangledName(converter.getCurrentLocation(),
7310b23ae8SValentin Clement                             symbol->GetUltimate());
74d0b70a07SValentin Clement   assert(proc.GetSpecificIntrinsic() &&
75d0b70a07SValentin Clement          "expected intrinsic procedure in designator");
76d0b70a07SValentin Clement   return proc.GetName();
77d0b70a07SValentin Clement }
78d0b70a07SValentin Clement 
79d0b70a07SValentin Clement const Fortran::semantics::Symbol *
getProcedureSymbol() const80d0b70a07SValentin Clement Fortran::lower::CallerInterface::getProcedureSymbol() const {
81d0b70a07SValentin Clement   return procRef.proc().GetSymbol();
82d0b70a07SValentin Clement }
83d0b70a07SValentin Clement 
isIndirectCall() const84d0b70a07SValentin Clement bool Fortran::lower::CallerInterface::isIndirectCall() const {
85d0b70a07SValentin Clement   if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
86d0b70a07SValentin Clement     return Fortran::semantics::IsPointer(*symbol) ||
87d0b70a07SValentin Clement            Fortran::semantics::IsDummy(*symbol);
88d0b70a07SValentin Clement   return false;
89d0b70a07SValentin Clement }
90d0b70a07SValentin Clement 
91d0b70a07SValentin Clement const Fortran::semantics::Symbol *
getIfIndirectCallSymbol() const92d0b70a07SValentin Clement Fortran::lower::CallerInterface::getIfIndirectCallSymbol() const {
93d0b70a07SValentin Clement   if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
94d0b70a07SValentin Clement     if (Fortran::semantics::IsPointer(*symbol) ||
95d0b70a07SValentin Clement         Fortran::semantics::IsDummy(*symbol))
96d0b70a07SValentin Clement       return symbol;
97d0b70a07SValentin Clement   return nullptr;
98d0b70a07SValentin Clement }
99d0b70a07SValentin Clement 
getCalleeLocation() const100d0b70a07SValentin Clement mlir::Location Fortran::lower::CallerInterface::getCalleeLocation() const {
101d0b70a07SValentin Clement   const Fortran::evaluate::ProcedureDesignator &proc = procRef.proc();
102d0b70a07SValentin Clement   // FIXME: If the callee is defined in the same file but after the current
103d0b70a07SValentin Clement   // unit we cannot get its location here and the funcOp is created at the
104d0b70a07SValentin Clement   // wrong location (i.e, the caller location).
105d0b70a07SValentin Clement   if (const Fortran::semantics::Symbol *symbol = proc.GetSymbol())
106d0b70a07SValentin Clement     return converter.genLocation(symbol->name());
107d0b70a07SValentin Clement   // Use current location for intrinsics.
108d0b70a07SValentin Clement   return converter.getCurrentLocation();
109d0b70a07SValentin Clement }
110d0b70a07SValentin Clement 
111d0b70a07SValentin Clement // Get dummy argument characteristic for a procedure with implicit interface
112d0b70a07SValentin Clement // from the actual argument characteristic. The actual argument may not be a F77
113d0b70a07SValentin Clement // entity. The attribute must be dropped and the shape, if any, must be made
114d0b70a07SValentin Clement // explicit.
115d0b70a07SValentin Clement static Fortran::evaluate::characteristics::DummyDataObject
asImplicitArg(Fortran::evaluate::characteristics::DummyDataObject && dummy)116d0b70a07SValentin Clement asImplicitArg(Fortran::evaluate::characteristics::DummyDataObject &&dummy) {
117d0b70a07SValentin Clement   Fortran::evaluate::Shape shape =
118d0b70a07SValentin Clement       dummy.type.attrs().none() ? dummy.type.shape()
119d0b70a07SValentin Clement                                 : Fortran::evaluate::Shape(dummy.type.Rank());
120d0b70a07SValentin Clement   return Fortran::evaluate::characteristics::DummyDataObject(
121d0b70a07SValentin Clement       Fortran::evaluate::characteristics::TypeAndShape(dummy.type.type(),
122d0b70a07SValentin Clement                                                        std::move(shape)));
123d0b70a07SValentin Clement }
124d0b70a07SValentin Clement 
125d0b70a07SValentin Clement static Fortran::evaluate::characteristics::DummyArgument
asImplicitArg(Fortran::evaluate::characteristics::DummyArgument && dummy)126d0b70a07SValentin Clement asImplicitArg(Fortran::evaluate::characteristics::DummyArgument &&dummy) {
127d0b70a07SValentin Clement   return std::visit(
128d0b70a07SValentin Clement       Fortran::common::visitors{
129d0b70a07SValentin Clement           [&](Fortran::evaluate::characteristics::DummyDataObject &obj) {
130d0b70a07SValentin Clement             return Fortran::evaluate::characteristics::DummyArgument(
131d0b70a07SValentin Clement                 std::move(dummy.name), asImplicitArg(std::move(obj)));
132d0b70a07SValentin Clement           },
133d0b70a07SValentin Clement           [&](Fortran::evaluate::characteristics::DummyProcedure &proc) {
134d0b70a07SValentin Clement             return Fortran::evaluate::characteristics::DummyArgument(
135d0b70a07SValentin Clement                 std::move(dummy.name), std::move(proc));
136d0b70a07SValentin Clement           },
137d0b70a07SValentin Clement           [](Fortran::evaluate::characteristics::AlternateReturn &x) {
138d0b70a07SValentin Clement             return Fortran::evaluate::characteristics::DummyArgument(
139d0b70a07SValentin Clement                 std::move(x));
140d0b70a07SValentin Clement           }},
141d0b70a07SValentin Clement       dummy.u);
142d0b70a07SValentin Clement }
143d0b70a07SValentin Clement 
144d0b70a07SValentin Clement Fortran::evaluate::characteristics::Procedure
characterize() const145d0b70a07SValentin Clement Fortran::lower::CallerInterface::characterize() const {
146d0b70a07SValentin Clement   Fortran::evaluate::FoldingContext &foldingContext =
147d0b70a07SValentin Clement       converter.getFoldingContext();
148d0b70a07SValentin Clement   std::optional<Fortran::evaluate::characteristics::Procedure> characteristic =
149d0b70a07SValentin Clement       Fortran::evaluate::characteristics::Procedure::Characterize(
150d0b70a07SValentin Clement           procRef.proc(), foldingContext);
151d0b70a07SValentin Clement   assert(characteristic && "Failed to get characteristic from procRef");
152d0b70a07SValentin Clement   // The characteristic may not contain the argument characteristic if the
153d0b70a07SValentin Clement   // ProcedureDesignator has no interface.
154d0b70a07SValentin Clement   if (!characteristic->HasExplicitInterface()) {
155d0b70a07SValentin Clement     for (const std::optional<Fortran::evaluate::ActualArgument> &arg :
156d0b70a07SValentin Clement          procRef.arguments()) {
157d0b70a07SValentin Clement       if (arg.value().isAlternateReturn()) {
158d0b70a07SValentin Clement         characteristic->dummyArguments.emplace_back(
159d0b70a07SValentin Clement             Fortran::evaluate::characteristics::AlternateReturn{});
160d0b70a07SValentin Clement       } else {
161d0b70a07SValentin Clement         // Argument cannot be optional with implicit interface
162d0b70a07SValentin Clement         const Fortran::lower::SomeExpr *expr = arg.value().UnwrapExpr();
163d0b70a07SValentin Clement         assert(
164d0b70a07SValentin Clement             expr &&
165d0b70a07SValentin Clement             "argument in call with implicit interface cannot be assumed type");
166d0b70a07SValentin Clement         std::optional<Fortran::evaluate::characteristics::DummyArgument>
167d0b70a07SValentin Clement             argCharacteristic =
168d0b70a07SValentin Clement                 Fortran::evaluate::characteristics::DummyArgument::FromActual(
169d0b70a07SValentin Clement                     "actual", *expr, foldingContext);
170d0b70a07SValentin Clement         assert(argCharacteristic &&
171d0b70a07SValentin Clement                "failed to characterize argument in implicit call");
172d0b70a07SValentin Clement         characteristic->dummyArguments.emplace_back(
173d0b70a07SValentin Clement             asImplicitArg(std::move(*argCharacteristic)));
174d0b70a07SValentin Clement       }
175d0b70a07SValentin Clement     }
176d0b70a07SValentin Clement   }
177d0b70a07SValentin Clement   return *characteristic;
178d0b70a07SValentin Clement }
179d0b70a07SValentin Clement 
placeInput(const PassedEntity & passedEntity,mlir::Value arg)180d0b70a07SValentin Clement void Fortran::lower::CallerInterface::placeInput(
181d0b70a07SValentin Clement     const PassedEntity &passedEntity, mlir::Value arg) {
182d0b70a07SValentin Clement   assert(static_cast<int>(actualInputs.size()) > passedEntity.firArgument &&
183d0b70a07SValentin Clement          passedEntity.firArgument >= 0 &&
184d0b70a07SValentin Clement          passedEntity.passBy != CallInterface::PassEntityBy::AddressAndLength &&
185d0b70a07SValentin Clement          "bad arg position");
186d0b70a07SValentin Clement   actualInputs[passedEntity.firArgument] = arg;
187d0b70a07SValentin Clement }
188d0b70a07SValentin Clement 
placeAddressAndLengthInput(const PassedEntity & passedEntity,mlir::Value addr,mlir::Value len)189d0b70a07SValentin Clement void Fortran::lower::CallerInterface::placeAddressAndLengthInput(
190d0b70a07SValentin Clement     const PassedEntity &passedEntity, mlir::Value addr, mlir::Value len) {
191d0b70a07SValentin Clement   assert(static_cast<int>(actualInputs.size()) > passedEntity.firArgument &&
192d0b70a07SValentin Clement          static_cast<int>(actualInputs.size()) > passedEntity.firLength &&
193d0b70a07SValentin Clement          passedEntity.firArgument >= 0 && passedEntity.firLength >= 0 &&
194d0b70a07SValentin Clement          passedEntity.passBy == CallInterface::PassEntityBy::AddressAndLength &&
195d0b70a07SValentin Clement          "bad arg position");
196d0b70a07SValentin Clement   actualInputs[passedEntity.firArgument] = addr;
197d0b70a07SValentin Clement   actualInputs[passedEntity.firLength] = len;
198d0b70a07SValentin Clement }
199d0b70a07SValentin Clement 
verifyActualInputs() const200d0b70a07SValentin Clement bool Fortran::lower::CallerInterface::verifyActualInputs() const {
201d0b70a07SValentin Clement   if (getNumFIRArguments() != actualInputs.size())
202d0b70a07SValentin Clement     return false;
203d0b70a07SValentin Clement   for (mlir::Value arg : actualInputs) {
204d0b70a07SValentin Clement     if (!arg)
205d0b70a07SValentin Clement       return false;
206d0b70a07SValentin Clement   }
207d0b70a07SValentin Clement   return true;
208d0b70a07SValentin Clement }
209d0b70a07SValentin Clement 
walkResultLengths(ExprVisitor visitor) const210d0b70a07SValentin Clement void Fortran::lower::CallerInterface::walkResultLengths(
211d0b70a07SValentin Clement     ExprVisitor visitor) const {
212d0b70a07SValentin Clement   assert(characteristic && "characteristic was not computed");
213d0b70a07SValentin Clement   const Fortran::evaluate::characteristics::FunctionResult &result =
214d0b70a07SValentin Clement       characteristic->functionResult.value();
215d0b70a07SValentin Clement   const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
216d0b70a07SValentin Clement       result.GetTypeAndShape();
217d0b70a07SValentin Clement   assert(typeAndShape && "no result type");
218d0b70a07SValentin Clement   Fortran::evaluate::DynamicType dynamicType = typeAndShape->type();
219d0b70a07SValentin Clement   // Visit result length specification expressions that are explicit.
220d0b70a07SValentin Clement   if (dynamicType.category() == Fortran::common::TypeCategory::Character) {
221d0b70a07SValentin Clement     if (std::optional<Fortran::evaluate::ExtentExpr> length =
222d0b70a07SValentin Clement             dynamicType.GetCharLength())
223d0b70a07SValentin Clement       visitor(toEvExpr(*length));
224d0b70a07SValentin Clement   } else if (dynamicType.category() == common::TypeCategory::Derived) {
225589d51eaSValentin Clement     const Fortran::semantics::DerivedTypeSpec &derivedTypeSpec =
226589d51eaSValentin Clement         dynamicType.GetDerivedTypeSpec();
227589d51eaSValentin Clement     if (Fortran::semantics::CountLenParameters(derivedTypeSpec) > 0)
228589d51eaSValentin Clement       TODO(converter.getCurrentLocation(),
229589d51eaSValentin Clement            "function result with derived type length parameters");
230d0b70a07SValentin Clement   }
231d0b70a07SValentin Clement }
232d0b70a07SValentin Clement 
233d0b70a07SValentin Clement // Compute extent expr from shapeSpec of an explicit shape.
234d0b70a07SValentin Clement // TODO: Allow evaluate shape analysis to work in a mode where it disregards
235d0b70a07SValentin Clement // the non-constant aspects when building the shape to avoid having this here.
236d0b70a07SValentin Clement static Fortran::evaluate::ExtentExpr
getExtentExpr(const Fortran::semantics::ShapeSpec & shapeSpec)237d0b70a07SValentin Clement getExtentExpr(const Fortran::semantics::ShapeSpec &shapeSpec) {
238d0b70a07SValentin Clement   const auto &ubound = shapeSpec.ubound().GetExplicit();
239d0b70a07SValentin Clement   const auto &lbound = shapeSpec.lbound().GetExplicit();
240d0b70a07SValentin Clement   assert(lbound && ubound && "shape must be explicit");
241d0b70a07SValentin Clement   return Fortran::common::Clone(*ubound) - Fortran::common::Clone(*lbound) +
242d0b70a07SValentin Clement          Fortran::evaluate::ExtentExpr{1};
243d0b70a07SValentin Clement }
244d0b70a07SValentin Clement 
walkResultExtents(ExprVisitor visitor) const245d0b70a07SValentin Clement void Fortran::lower::CallerInterface::walkResultExtents(
246d0b70a07SValentin Clement     ExprVisitor visitor) const {
247d0b70a07SValentin Clement   // Walk directly the result symbol shape (the characteristic shape may contain
248d0b70a07SValentin Clement   // descriptor inquiries to it that would fail to lower on the caller side).
249fe252f8eSValentin Clement   const Fortran::semantics::SubprogramDetails *interfaceDetails =
250fe252f8eSValentin Clement       getInterfaceDetails();
251fe252f8eSValentin Clement   if (interfaceDetails) {
252fe252f8eSValentin Clement     const Fortran::semantics::Symbol &result = interfaceDetails->result();
253d0b70a07SValentin Clement     if (const auto *objectDetails =
254d0b70a07SValentin Clement             result.detailsIf<Fortran::semantics::ObjectEntityDetails>())
255d0b70a07SValentin Clement       if (objectDetails->shape().IsExplicitShape())
256d0b70a07SValentin Clement         for (const Fortran::semantics::ShapeSpec &shapeSpec :
257d0b70a07SValentin Clement              objectDetails->shape())
258d0b70a07SValentin Clement           visitor(Fortran::evaluate::AsGenericExpr(getExtentExpr(shapeSpec)));
259d0b70a07SValentin Clement   } else {
260d0b70a07SValentin Clement     if (procRef.Rank() != 0)
261d0b70a07SValentin Clement       fir::emitFatalError(
262d0b70a07SValentin Clement           converter.getCurrentLocation(),
263d0b70a07SValentin Clement           "only scalar functions may not have an interface symbol");
264d0b70a07SValentin Clement   }
265d0b70a07SValentin Clement }
266d0b70a07SValentin Clement 
mustMapInterfaceSymbols() const267d0b70a07SValentin Clement bool Fortran::lower::CallerInterface::mustMapInterfaceSymbols() const {
268d0b70a07SValentin Clement   assert(characteristic && "characteristic was not computed");
269d0b70a07SValentin Clement   const std::optional<Fortran::evaluate::characteristics::FunctionResult>
270d0b70a07SValentin Clement       &result = characteristic->functionResult;
271d0b70a07SValentin Clement   if (!result || result->CanBeReturnedViaImplicitInterface() ||
272fe252f8eSValentin Clement       !getInterfaceDetails())
273d0b70a07SValentin Clement     return false;
274d0b70a07SValentin Clement   bool allResultSpecExprConstant = true;
275d0b70a07SValentin Clement   auto visitor = [&](const Fortran::lower::SomeExpr &e) {
276d0b70a07SValentin Clement     allResultSpecExprConstant &= Fortran::evaluate::IsConstantExpr(e);
277d0b70a07SValentin Clement   };
278d0b70a07SValentin Clement   walkResultLengths(visitor);
279d0b70a07SValentin Clement   walkResultExtents(visitor);
280d0b70a07SValentin Clement   return !allResultSpecExprConstant;
281d0b70a07SValentin Clement }
282d0b70a07SValentin Clement 
getArgumentValue(const semantics::Symbol & sym) const283d0b70a07SValentin Clement mlir::Value Fortran::lower::CallerInterface::getArgumentValue(
284d0b70a07SValentin Clement     const semantics::Symbol &sym) const {
285d0b70a07SValentin Clement   mlir::Location loc = converter.getCurrentLocation();
286fe252f8eSValentin Clement   const Fortran::semantics::SubprogramDetails *ifaceDetails =
287fe252f8eSValentin Clement       getInterfaceDetails();
288fe252f8eSValentin Clement   if (!ifaceDetails)
289d0b70a07SValentin Clement     fir::emitFatalError(
290d0b70a07SValentin Clement         loc, "mapping actual and dummy arguments requires an interface");
291d0b70a07SValentin Clement   const std::vector<Fortran::semantics::Symbol *> &dummies =
292fe252f8eSValentin Clement       ifaceDetails->dummyArgs();
293d0b70a07SValentin Clement   auto it = std::find(dummies.begin(), dummies.end(), &sym);
294d0b70a07SValentin Clement   if (it == dummies.end())
295d0b70a07SValentin Clement     fir::emitFatalError(loc, "symbol is not a dummy in this call");
296d0b70a07SValentin Clement   FirValue mlirArgIndex = passedArguments[it - dummies.begin()].firArgument;
297d0b70a07SValentin Clement   return actualInputs[mlirArgIndex];
298d0b70a07SValentin Clement }
299d0b70a07SValentin Clement 
getResultStorageType() const300d0b70a07SValentin Clement mlir::Type Fortran::lower::CallerInterface::getResultStorageType() const {
301d0b70a07SValentin Clement   if (passedResult)
302d0b70a07SValentin Clement     return fir::dyn_cast_ptrEleTy(inputs[passedResult->firArgument].type);
303d0b70a07SValentin Clement   assert(saveResult && !outputs.empty());
304d0b70a07SValentin Clement   return outputs[0].type;
305d0b70a07SValentin Clement }
306d0b70a07SValentin Clement 
307d0b70a07SValentin Clement const Fortran::semantics::Symbol &
getResultSymbol() const308d0b70a07SValentin Clement Fortran::lower::CallerInterface::getResultSymbol() const {
309d0b70a07SValentin Clement   mlir::Location loc = converter.getCurrentLocation();
310fe252f8eSValentin Clement   const Fortran::semantics::SubprogramDetails *ifaceDetails =
311fe252f8eSValentin Clement       getInterfaceDetails();
312fe252f8eSValentin Clement   if (!ifaceDetails)
313d0b70a07SValentin Clement     fir::emitFatalError(
314d0b70a07SValentin Clement         loc, "mapping actual and dummy arguments requires an interface");
315fe252f8eSValentin Clement   return ifaceDetails->result();
316fe252f8eSValentin Clement }
317fe252f8eSValentin Clement 
318fe252f8eSValentin Clement const Fortran::semantics::SubprogramDetails *
getInterfaceDetails() const319fe252f8eSValentin Clement Fortran::lower::CallerInterface::getInterfaceDetails() const {
320fe252f8eSValentin Clement   if (const Fortran::semantics::Symbol *iface =
321fe252f8eSValentin Clement           procRef.proc().GetInterfaceSymbol())
322fe252f8eSValentin Clement     return iface->GetUltimate()
323fe252f8eSValentin Clement         .detailsIf<Fortran::semantics::SubprogramDetails>();
324fe252f8eSValentin Clement   return nullptr;
325d0b70a07SValentin Clement }
326d0b70a07SValentin Clement 
327d0b70a07SValentin Clement //===----------------------------------------------------------------------===//
328e1a12767SValentin Clement // Callee side interface implementation
329e1a12767SValentin Clement //===----------------------------------------------------------------------===//
330e1a12767SValentin Clement 
hasAlternateReturns() const331ad40cc14SValentin Clement bool Fortran::lower::CalleeInterface::hasAlternateReturns() const {
332ad40cc14SValentin Clement   return !funit.isMainProgram() &&
333ad40cc14SValentin Clement          Fortran::semantics::HasAlternateReturns(funit.getSubprogramSymbol());
334ad40cc14SValentin Clement }
335ad40cc14SValentin Clement 
getMangledName() const336e1a12767SValentin Clement std::string Fortran::lower::CalleeInterface::getMangledName() const {
337e1a12767SValentin Clement   if (funit.isMainProgram())
338e1a12767SValentin Clement     return fir::NameUniquer::doProgramEntry().str();
33910b23ae8SValentin Clement   return ::getMangledName(converter.getCurrentLocation(),
34010b23ae8SValentin Clement                           funit.getSubprogramSymbol());
341e1a12767SValentin Clement }
342e1a12767SValentin Clement 
343e1a12767SValentin Clement const Fortran::semantics::Symbol *
getProcedureSymbol() const344e1a12767SValentin Clement Fortran::lower::CalleeInterface::getProcedureSymbol() const {
345e1a12767SValentin Clement   if (funit.isMainProgram())
346e1a12767SValentin Clement     return nullptr;
347e1a12767SValentin Clement   return &funit.getSubprogramSymbol();
348e1a12767SValentin Clement }
349e1a12767SValentin Clement 
getCalleeLocation() const350e1a12767SValentin Clement mlir::Location Fortran::lower::CalleeInterface::getCalleeLocation() const {
351e1a12767SValentin Clement   // FIXME: do NOT use unknown for the anonymous PROGRAM case. We probably
352e1a12767SValentin Clement   // should just stash the location in the funit regardless.
353e1a12767SValentin Clement   return converter.genLocation(funit.getStartingSourceLoc());
354e1a12767SValentin Clement }
355e1a12767SValentin Clement 
356ad40cc14SValentin Clement Fortran::evaluate::characteristics::Procedure
characterize() const357ad40cc14SValentin Clement Fortran::lower::CalleeInterface::characterize() const {
358ad40cc14SValentin Clement   Fortran::evaluate::FoldingContext &foldingContext =
359ad40cc14SValentin Clement       converter.getFoldingContext();
360ad40cc14SValentin Clement   std::optional<Fortran::evaluate::characteristics::Procedure> characteristic =
361ad40cc14SValentin Clement       Fortran::evaluate::characteristics::Procedure::Characterize(
362ad40cc14SValentin Clement           funit.getSubprogramSymbol(), foldingContext);
363ad40cc14SValentin Clement   assert(characteristic && "Fail to get characteristic from symbol");
364ad40cc14SValentin Clement   return *characteristic;
365ad40cc14SValentin Clement }
366ad40cc14SValentin Clement 
isMainProgram() const367ad40cc14SValentin Clement bool Fortran::lower::CalleeInterface::isMainProgram() const {
368ad40cc14SValentin Clement   return funit.isMainProgram();
369ad40cc14SValentin Clement }
370ad40cc14SValentin Clement 
37158ceae95SRiver Riddle mlir::func::FuncOp
addEntryBlockAndMapArguments()37258ceae95SRiver Riddle Fortran::lower::CalleeInterface::addEntryBlockAndMapArguments() {
37310b23ae8SValentin Clement   // Check for bugs in the front end. The front end must not present multiple
37410b23ae8SValentin Clement   // definitions of the same procedure.
37510b23ae8SValentin Clement   if (!func.getBlocks().empty())
37610b23ae8SValentin Clement     fir::emitFatalError(func.getLoc(),
37710b23ae8SValentin Clement                         "cannot process subprogram that was already processed");
37810b23ae8SValentin Clement 
37910b23ae8SValentin Clement   // On the callee side, directly map the mlir::value argument of the function
38010b23ae8SValentin Clement   // block to the Fortran symbols.
381e1a12767SValentin Clement   func.addEntryBlock();
382da7c77b8SValentin Clement   mapPassedEntities();
383e1a12767SValentin Clement   return func;
384e1a12767SValentin Clement }
385e1a12767SValentin Clement 
hasHostAssociated() const386764f95a8SValentin Clement bool Fortran::lower::CalleeInterface::hasHostAssociated() const {
387764f95a8SValentin Clement   return funit.parentHasHostAssoc();
388764f95a8SValentin Clement }
389764f95a8SValentin Clement 
getHostAssociatedTy() const390764f95a8SValentin Clement mlir::Type Fortran::lower::CalleeInterface::getHostAssociatedTy() const {
391764f95a8SValentin Clement   assert(hasHostAssociated());
392764f95a8SValentin Clement   return funit.parentHostAssoc().getArgumentType(converter);
393764f95a8SValentin Clement }
394764f95a8SValentin Clement 
getHostAssociatedTuple() const395764f95a8SValentin Clement mlir::Value Fortran::lower::CalleeInterface::getHostAssociatedTuple() const {
396764f95a8SValentin Clement   assert(hasHostAssociated() || !funit.getHostAssoc().empty());
397764f95a8SValentin Clement   return converter.hostAssocTupleValue();
398764f95a8SValentin Clement }
399764f95a8SValentin Clement 
400e1a12767SValentin Clement //===----------------------------------------------------------------------===//
401764f95a8SValentin Clement // CallInterface implementation: this part is common to both caller and caller
402e1a12767SValentin Clement // sides.
403e1a12767SValentin Clement //===----------------------------------------------------------------------===//
404e1a12767SValentin Clement 
addSymbolAttribute(mlir::func::FuncOp func,const Fortran::semantics::Symbol & sym,mlir::MLIRContext & mlirContext)40558ceae95SRiver Riddle static void addSymbolAttribute(mlir::func::FuncOp func,
406e1a12767SValentin Clement                                const Fortran::semantics::Symbol &sym,
407e1a12767SValentin Clement                                mlir::MLIRContext &mlirContext) {
408e1a12767SValentin Clement   // Only add this on bind(C) functions for which the symbol is not reflected in
409e1a12767SValentin Clement   // the current context.
410e1a12767SValentin Clement   if (!Fortran::semantics::IsBindCProcedure(sym))
411e1a12767SValentin Clement     return;
412e1a12767SValentin Clement   std::string name =
413e1a12767SValentin Clement       Fortran::lower::mangle::mangleName(sym, /*keepExternalInScope=*/true);
414e1a12767SValentin Clement   func->setAttr(fir::getSymbolAttrName(),
415e1a12767SValentin Clement                 mlir::StringAttr::get(&mlirContext, name));
416e1a12767SValentin Clement }
417e1a12767SValentin Clement 
418e1a12767SValentin Clement /// Declare drives the different actions to be performed while analyzing the
41958ceae95SRiver Riddle /// signature and building/finding the mlir::func::FuncOp.
420e1a12767SValentin Clement template <typename T>
declare()421e1a12767SValentin Clement void Fortran::lower::CallInterface<T>::declare() {
422ad40cc14SValentin Clement   if (!side().isMainProgram()) {
423ad40cc14SValentin Clement     characteristic.emplace(side().characterize());
424ad40cc14SValentin Clement     bool isImplicit = characteristic->CanBeCalledViaImplicitInterface();
425ad40cc14SValentin Clement     determineInterface(isImplicit, *characteristic);
426ad40cc14SValentin Clement   }
427ad40cc14SValentin Clement   // No input/output for main program
428ad40cc14SValentin Clement 
429e1a12767SValentin Clement   // Create / get funcOp for direct calls. For indirect calls (only meaningful
430e1a12767SValentin Clement   // on the caller side), no funcOp has to be created here. The mlir::Value
431e1a12767SValentin Clement   // holding the indirection is used when creating the fir::CallOp.
432e1a12767SValentin Clement   if (!side().isIndirectCall()) {
433e1a12767SValentin Clement     std::string name = side().getMangledName();
434e1a12767SValentin Clement     mlir::ModuleOp module = converter.getModuleOp();
435e1a12767SValentin Clement     func = fir::FirOpBuilder::getNamedFunction(module, name);
436e1a12767SValentin Clement     if (!func) {
437e1a12767SValentin Clement       mlir::Location loc = side().getCalleeLocation();
438e1a12767SValentin Clement       mlir::FunctionType ty = genFunctionType();
439e1a12767SValentin Clement       func = fir::FirOpBuilder::createFunction(loc, module, name, ty);
440e1a12767SValentin Clement       if (const Fortran::semantics::Symbol *sym = side().getProcedureSymbol())
441e1a12767SValentin Clement         addSymbolAttribute(func, *sym, converter.getMLIRContext());
442da7c77b8SValentin Clement       for (const auto &placeHolder : llvm::enumerate(inputs))
443da7c77b8SValentin Clement         if (!placeHolder.value().attributes.empty())
444da7c77b8SValentin Clement           func.setArgAttrs(placeHolder.index(), placeHolder.value().attributes);
445e1a12767SValentin Clement     }
446e1a12767SValentin Clement   }
447e1a12767SValentin Clement }
448e1a12767SValentin Clement 
44958ceae95SRiver Riddle /// Once the signature has been analyzed and the mlir::func::FuncOp was
45058ceae95SRiver Riddle /// built/found, map the fir inputs to Fortran entities (the symbols or
45158ceae95SRiver Riddle /// expressions).
452da7c77b8SValentin Clement template <typename T>
mapPassedEntities()453da7c77b8SValentin Clement void Fortran::lower::CallInterface<T>::mapPassedEntities() {
454da7c77b8SValentin Clement   // map back fir inputs to passed entities
455da7c77b8SValentin Clement   if constexpr (std::is_same_v<T, Fortran::lower::CalleeInterface>) {
456da7c77b8SValentin Clement     assert(inputs.size() == func.front().getArguments().size() &&
457da7c77b8SValentin Clement            "function previously created with different number of arguments");
458da7c77b8SValentin Clement     for (auto [fst, snd] : llvm::zip(inputs, func.front().getArguments()))
459da7c77b8SValentin Clement       mapBackInputToPassedEntity(fst, snd);
460da7c77b8SValentin Clement   } else {
461da7c77b8SValentin Clement     // On the caller side, map the index of the mlir argument position
462da7c77b8SValentin Clement     // to Fortran ActualArguments.
463da7c77b8SValentin Clement     int firPosition = 0;
464da7c77b8SValentin Clement     for (const FirPlaceHolder &placeHolder : inputs)
465da7c77b8SValentin Clement       mapBackInputToPassedEntity(placeHolder, firPosition++);
466da7c77b8SValentin Clement   }
467da7c77b8SValentin Clement }
468da7c77b8SValentin Clement 
469da7c77b8SValentin Clement template <typename T>
mapBackInputToPassedEntity(const FirPlaceHolder & placeHolder,FirValue firValue)470da7c77b8SValentin Clement void Fortran::lower::CallInterface<T>::mapBackInputToPassedEntity(
471da7c77b8SValentin Clement     const FirPlaceHolder &placeHolder, FirValue firValue) {
472da7c77b8SValentin Clement   PassedEntity &passedEntity =
473da7c77b8SValentin Clement       placeHolder.passedEntityPosition == FirPlaceHolder::resultEntityPosition
474da7c77b8SValentin Clement           ? passedResult.value()
475da7c77b8SValentin Clement           : passedArguments[placeHolder.passedEntityPosition];
476da7c77b8SValentin Clement   if (placeHolder.property == Property::CharLength)
477da7c77b8SValentin Clement     passedEntity.firLength = firValue;
478da7c77b8SValentin Clement   else
479da7c77b8SValentin Clement     passedEntity.firArgument = firValue;
480da7c77b8SValentin Clement }
481da7c77b8SValentin Clement 
482d0b70a07SValentin Clement /// Helpers to access ActualArgument/Symbols
483d0b70a07SValentin Clement static const Fortran::evaluate::ActualArguments &
getEntityContainer(const Fortran::evaluate::ProcedureRef & proc)484d0b70a07SValentin Clement getEntityContainer(const Fortran::evaluate::ProcedureRef &proc) {
485d0b70a07SValentin Clement   return proc.arguments();
486d0b70a07SValentin Clement }
487d0b70a07SValentin Clement 
488da7c77b8SValentin Clement static const std::vector<Fortran::semantics::Symbol *> &
getEntityContainer(Fortran::lower::pft::FunctionLikeUnit & funit)489da7c77b8SValentin Clement getEntityContainer(Fortran::lower::pft::FunctionLikeUnit &funit) {
490da7c77b8SValentin Clement   return funit.getSubprogramSymbol()
491da7c77b8SValentin Clement       .get<Fortran::semantics::SubprogramDetails>()
492da7c77b8SValentin Clement       .dummyArgs();
493da7c77b8SValentin Clement }
494da7c77b8SValentin Clement 
getDataObjectEntity(const std::optional<Fortran::evaluate::ActualArgument> & arg)495d0b70a07SValentin Clement static const Fortran::evaluate::ActualArgument *getDataObjectEntity(
496d0b70a07SValentin Clement     const std::optional<Fortran::evaluate::ActualArgument> &arg) {
497d0b70a07SValentin Clement   if (arg)
498d0b70a07SValentin Clement     return &*arg;
499d0b70a07SValentin Clement   return nullptr;
500d0b70a07SValentin Clement }
501d0b70a07SValentin Clement 
502da7c77b8SValentin Clement static const Fortran::semantics::Symbol &
getDataObjectEntity(const Fortran::semantics::Symbol * arg)503da7c77b8SValentin Clement getDataObjectEntity(const Fortran::semantics::Symbol *arg) {
504da7c77b8SValentin Clement   assert(arg && "expect symbol for data object entity");
505da7c77b8SValentin Clement   return *arg;
506da7c77b8SValentin Clement }
507da7c77b8SValentin Clement 
50837e84d9bSValentin Clement static const Fortran::evaluate::ActualArgument *
getResultEntity(const Fortran::evaluate::ProcedureRef &)50937e84d9bSValentin Clement getResultEntity(const Fortran::evaluate::ProcedureRef &) {
51037e84d9bSValentin Clement   return nullptr;
51137e84d9bSValentin Clement }
51237e84d9bSValentin Clement 
51337e84d9bSValentin Clement static const Fortran::semantics::Symbol &
getResultEntity(Fortran::lower::pft::FunctionLikeUnit & funit)51437e84d9bSValentin Clement getResultEntity(Fortran::lower::pft::FunctionLikeUnit &funit) {
51537e84d9bSValentin Clement   return funit.getSubprogramSymbol()
51637e84d9bSValentin Clement       .get<Fortran::semantics::SubprogramDetails>()
51737e84d9bSValentin Clement       .result();
51837e84d9bSValentin Clement }
51937e84d9bSValentin Clement 
520764f95a8SValentin Clement /// Bypass helpers to manipulate entities since they are not any symbol/actual
521764f95a8SValentin Clement /// argument to associate. See SignatureBuilder below.
522764f95a8SValentin Clement using FakeEntity = bool;
523764f95a8SValentin Clement using FakeEntities = llvm::SmallVector<FakeEntity>;
524764f95a8SValentin Clement static FakeEntities
getEntityContainer(const Fortran::evaluate::characteristics::Procedure & proc)525764f95a8SValentin Clement getEntityContainer(const Fortran::evaluate::characteristics::Procedure &proc) {
526764f95a8SValentin Clement   FakeEntities enities(proc.dummyArguments.size());
527764f95a8SValentin Clement   return enities;
528764f95a8SValentin Clement }
getDataObjectEntity(const FakeEntity & e)529764f95a8SValentin Clement static const FakeEntity &getDataObjectEntity(const FakeEntity &e) { return e; }
530764f95a8SValentin Clement static FakeEntity
getResultEntity(const Fortran::evaluate::characteristics::Procedure & proc)531764f95a8SValentin Clement getResultEntity(const Fortran::evaluate::characteristics::Procedure &proc) {
532764f95a8SValentin Clement   return false;
533764f95a8SValentin Clement }
534ad40cc14SValentin Clement 
535ad40cc14SValentin Clement /// This is the actual part that defines the FIR interface based on the
536ad40cc14SValentin Clement /// characteristic. It directly mutates the CallInterface members.
537ad40cc14SValentin Clement template <typename T>
538ad40cc14SValentin Clement class Fortran::lower::CallInterfaceImpl {
539ad40cc14SValentin Clement   using CallInterface = Fortran::lower::CallInterface<T>;
540da7c77b8SValentin Clement   using PassEntityBy = typename CallInterface::PassEntityBy;
541da7c77b8SValentin Clement   using PassedEntity = typename CallInterface::PassedEntity;
54237e84d9bSValentin Clement   using FirValue = typename CallInterface::FirValue;
543da7c77b8SValentin Clement   using FortranEntity = typename CallInterface::FortranEntity;
544ad40cc14SValentin Clement   using FirPlaceHolder = typename CallInterface::FirPlaceHolder;
545ad40cc14SValentin Clement   using Property = typename CallInterface::Property;
546ad40cc14SValentin Clement   using TypeAndShape = Fortran::evaluate::characteristics::TypeAndShape;
547da7c77b8SValentin Clement   using DummyCharacteristics =
548da7c77b8SValentin Clement       Fortran::evaluate::characteristics::DummyArgument;
549ad40cc14SValentin Clement 
550ad40cc14SValentin Clement public:
CallInterfaceImpl(CallInterface & i)551ad40cc14SValentin Clement   CallInterfaceImpl(CallInterface &i)
552ad40cc14SValentin Clement       : interface(i), mlirContext{i.converter.getMLIRContext()} {}
553ad40cc14SValentin Clement 
buildImplicitInterface(const Fortran::evaluate::characteristics::Procedure & procedure)554ad40cc14SValentin Clement   void buildImplicitInterface(
555ad40cc14SValentin Clement       const Fortran::evaluate::characteristics::Procedure &procedure) {
556ad40cc14SValentin Clement     // Handle result
557ad40cc14SValentin Clement     if (const std::optional<Fortran::evaluate::characteristics::FunctionResult>
558ad40cc14SValentin Clement             &result = procedure.functionResult)
559ad40cc14SValentin Clement       handleImplicitResult(*result);
560ad40cc14SValentin Clement     else if (interface.side().hasAlternateReturns())
561ad40cc14SValentin Clement       addFirResult(mlir::IndexType::get(&mlirContext),
562ad40cc14SValentin Clement                    FirPlaceHolder::resultEntityPosition, Property::Value);
563da7c77b8SValentin Clement     // Handle arguments
564da7c77b8SValentin Clement     const auto &argumentEntities =
565da7c77b8SValentin Clement         getEntityContainer(interface.side().getCallDescription());
566da7c77b8SValentin Clement     for (auto pair : llvm::zip(procedure.dummyArguments, argumentEntities)) {
567da7c77b8SValentin Clement       const Fortran::evaluate::characteristics::DummyArgument
568da7c77b8SValentin Clement           &argCharacteristics = std::get<0>(pair);
569da7c77b8SValentin Clement       std::visit(
570da7c77b8SValentin Clement           Fortran::common::visitors{
571da7c77b8SValentin Clement               [&](const auto &dummy) {
572da7c77b8SValentin Clement                 const auto &entity = getDataObjectEntity(std::get<1>(pair));
573da7c77b8SValentin Clement                 handleImplicitDummy(&argCharacteristics, dummy, entity);
574da7c77b8SValentin Clement               },
575da7c77b8SValentin Clement               [&](const Fortran::evaluate::characteristics::AlternateReturn &) {
576da7c77b8SValentin Clement                 // nothing to do
577da7c77b8SValentin Clement               },
578da7c77b8SValentin Clement           },
579da7c77b8SValentin Clement           argCharacteristics.u);
580da7c77b8SValentin Clement     }
581ad40cc14SValentin Clement   }
582ad40cc14SValentin Clement 
buildExplicitInterface(const Fortran::evaluate::characteristics::Procedure & procedure)583c807aa53SValentin Clement   void buildExplicitInterface(
584c807aa53SValentin Clement       const Fortran::evaluate::characteristics::Procedure &procedure) {
585c807aa53SValentin Clement     // Handle result
586c807aa53SValentin Clement     if (const std::optional<Fortran::evaluate::characteristics::FunctionResult>
587c807aa53SValentin Clement             &result = procedure.functionResult) {
588c807aa53SValentin Clement       if (result->CanBeReturnedViaImplicitInterface())
589c807aa53SValentin Clement         handleImplicitResult(*result);
590c807aa53SValentin Clement       else
591c807aa53SValentin Clement         handleExplicitResult(*result);
592c807aa53SValentin Clement     } else if (interface.side().hasAlternateReturns()) {
593c807aa53SValentin Clement       addFirResult(mlir::IndexType::get(&mlirContext),
594c807aa53SValentin Clement                    FirPlaceHolder::resultEntityPosition, Property::Value);
595c807aa53SValentin Clement     }
596914061bbSValentin Clement     bool isBindC = procedure.IsBindC();
597914061bbSValentin Clement     // Handle arguments
598914061bbSValentin Clement     const auto &argumentEntities =
599914061bbSValentin Clement         getEntityContainer(interface.side().getCallDescription());
600914061bbSValentin Clement     for (auto pair : llvm::zip(procedure.dummyArguments, argumentEntities)) {
601914061bbSValentin Clement       const Fortran::evaluate::characteristics::DummyArgument
602914061bbSValentin Clement           &argCharacteristics = std::get<0>(pair);
603914061bbSValentin Clement       std::visit(
604914061bbSValentin Clement           Fortran::common::visitors{
605914061bbSValentin Clement               [&](const Fortran::evaluate::characteristics::DummyDataObject
606914061bbSValentin Clement                       &dummy) {
607914061bbSValentin Clement                 const auto &entity = getDataObjectEntity(std::get<1>(pair));
608914061bbSValentin Clement                 if (dummy.CanBePassedViaImplicitInterface())
609914061bbSValentin Clement                   handleImplicitDummy(&argCharacteristics, dummy, entity);
610914061bbSValentin Clement                 else
611914061bbSValentin Clement                   handleExplicitDummy(&argCharacteristics, dummy, entity,
612914061bbSValentin Clement                                       isBindC);
613914061bbSValentin Clement               },
614914061bbSValentin Clement               [&](const Fortran::evaluate::characteristics::DummyProcedure
615914061bbSValentin Clement                       &dummy) {
616914061bbSValentin Clement                 const auto &entity = getDataObjectEntity(std::get<1>(pair));
617914061bbSValentin Clement                 handleImplicitDummy(&argCharacteristics, dummy, entity);
618914061bbSValentin Clement               },
619914061bbSValentin Clement               [&](const Fortran::evaluate::characteristics::AlternateReturn &) {
620914061bbSValentin Clement                 // nothing to do
621914061bbSValentin Clement               },
622914061bbSValentin Clement           },
623914061bbSValentin Clement           argCharacteristics.u);
624914061bbSValentin Clement     }
625c807aa53SValentin Clement   }
626c807aa53SValentin Clement 
appendHostAssocTupleArg(mlir::Type tupTy)627764f95a8SValentin Clement   void appendHostAssocTupleArg(mlir::Type tupTy) {
628092601d4SAndrzej Warzynski     mlir::MLIRContext *ctxt = tupTy.getContext();
629764f95a8SValentin Clement     addFirOperand(tupTy, nextPassedArgPosition(), Property::BaseAddress,
630764f95a8SValentin Clement                   {mlir::NamedAttribute{
631764f95a8SValentin Clement                       mlir::StringAttr::get(ctxt, fir::getHostAssocAttrName()),
632764f95a8SValentin Clement                       mlir::UnitAttr::get(ctxt)}});
633764f95a8SValentin Clement     interface.passedArguments.emplace_back(
634764f95a8SValentin Clement         PassedEntity{PassEntityBy::BaseAddress, std::nullopt,
635764f95a8SValentin Clement                      interface.side().getHostAssociatedTuple(), emptyValue()});
636764f95a8SValentin Clement   }
637764f95a8SValentin Clement 
getResultDynamicType(const Fortran::evaluate::characteristics::Procedure & procedure)638764f95a8SValentin Clement   static llvm::Optional<Fortran::evaluate::DynamicType> getResultDynamicType(
639764f95a8SValentin Clement       const Fortran::evaluate::characteristics::Procedure &procedure) {
640764f95a8SValentin Clement     if (const std::optional<Fortran::evaluate::characteristics::FunctionResult>
641764f95a8SValentin Clement             &result = procedure.functionResult)
642764f95a8SValentin Clement       if (const auto *resultTypeAndShape = result->GetTypeAndShape())
643764f95a8SValentin Clement         return resultTypeAndShape->type();
644764f95a8SValentin Clement     return llvm::None;
645764f95a8SValentin Clement   }
646764f95a8SValentin Clement 
mustPassLengthWithDummyProcedure(const Fortran::evaluate::characteristics::Procedure & procedure)647764f95a8SValentin Clement   static bool mustPassLengthWithDummyProcedure(
648764f95a8SValentin Clement       const Fortran::evaluate::characteristics::Procedure &procedure) {
649764f95a8SValentin Clement     // When passing a character function designator `bar` as dummy procedure to
650764f95a8SValentin Clement     // `foo` (e.g. `foo(bar)`), pass the result length of `bar` to `foo` so that
651764f95a8SValentin Clement     // `bar` can be called inside `foo` even if its length is assumed there.
652764f95a8SValentin Clement     // From an ABI perspective, the extra length argument must be handled
653764f95a8SValentin Clement     // exactly as if passing a character object. Using an argument of
654764f95a8SValentin Clement     // fir.boxchar type gives the expected behavior: after codegen, the
655764f95a8SValentin Clement     // fir.boxchar lengths are added after all the arguments as extra value
656764f95a8SValentin Clement     // arguments (the extra arguments order is the order of the fir.boxchar).
657764f95a8SValentin Clement 
658764f95a8SValentin Clement     // This ABI is compatible with ifort, nag, nvfortran, and xlf, but not
659764f95a8SValentin Clement     // gfortran. Gfortran does not pass the length and is therefore unable to
660764f95a8SValentin Clement     // handle later call to `bar` in `foo` where the length would be assumed. If
661764f95a8SValentin Clement     // the result is an array, nag and ifort and xlf still pass the length, but
662764f95a8SValentin Clement     // not nvfortran (and gfortran). It is not clear it is possible to call an
663764f95a8SValentin Clement     // array function with assumed length (f18 forbides defining such
664764f95a8SValentin Clement     // interfaces). Hence, passing the length is most likely useless, but stick
665764f95a8SValentin Clement     // with ifort/nag/xlf interface here.
666764f95a8SValentin Clement     if (llvm::Optional<Fortran::evaluate::DynamicType> type =
667764f95a8SValentin Clement             getResultDynamicType(procedure))
668764f95a8SValentin Clement       return type->category() == Fortran::common::TypeCategory::Character;
669764f95a8SValentin Clement     return false;
670764f95a8SValentin Clement   }
671764f95a8SValentin Clement 
672ad40cc14SValentin Clement private:
handleImplicitResult(const Fortran::evaluate::characteristics::FunctionResult & result)673ad40cc14SValentin Clement   void handleImplicitResult(
674ad40cc14SValentin Clement       const Fortran::evaluate::characteristics::FunctionResult &result) {
675ad40cc14SValentin Clement     if (result.IsProcedurePointer())
676ad40cc14SValentin Clement       TODO(interface.converter.getCurrentLocation(),
677ad40cc14SValentin Clement            "procedure pointer result not yet handled");
678ad40cc14SValentin Clement     const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
679ad40cc14SValentin Clement         result.GetTypeAndShape();
680ad40cc14SValentin Clement     assert(typeAndShape && "expect type for non proc pointer result");
681ad40cc14SValentin Clement     Fortran::evaluate::DynamicType dynamicType = typeAndShape->type();
68237e84d9bSValentin Clement     // Character result allocated by caller and passed as hidden arguments
683ad40cc14SValentin Clement     if (dynamicType.category() == Fortran::common::TypeCategory::Character) {
68437e84d9bSValentin Clement       handleImplicitCharacterResult(dynamicType);
685ad40cc14SValentin Clement     } else if (dynamicType.category() ==
686ad40cc14SValentin Clement                Fortran::common::TypeCategory::Derived) {
687764f95a8SValentin Clement       // Derived result need to be allocated by the caller and the result value
688764f95a8SValentin Clement       // must be saved. Derived type in implicit interface cannot have length
689764f95a8SValentin Clement       // parameters.
690764f95a8SValentin Clement       setSaveResult();
691764f95a8SValentin Clement       mlir::Type mlirType = translateDynamicType(dynamicType);
692764f95a8SValentin Clement       addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
693764f95a8SValentin Clement                    Property::Value);
694ad40cc14SValentin Clement     } else {
695ad40cc14SValentin Clement       // All result other than characters/derived are simply returned by value
696ad40cc14SValentin Clement       // in implicit interfaces
697ad40cc14SValentin Clement       mlir::Type mlirType =
698ad40cc14SValentin Clement           getConverter().genType(dynamicType.category(), dynamicType.kind());
699ad40cc14SValentin Clement       addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
700ad40cc14SValentin Clement                    Property::Value);
701ad40cc14SValentin Clement     }
702ad40cc14SValentin Clement   }
70337e84d9bSValentin Clement   void
handleImplicitCharacterResult(const Fortran::evaluate::DynamicType & type)70437e84d9bSValentin Clement   handleImplicitCharacterResult(const Fortran::evaluate::DynamicType &type) {
70537e84d9bSValentin Clement     int resultPosition = FirPlaceHolder::resultEntityPosition;
70637e84d9bSValentin Clement     setPassedResult(PassEntityBy::AddressAndLength,
70737e84d9bSValentin Clement                     getResultEntity(interface.side().getCallDescription()));
70837e84d9bSValentin Clement     mlir::Type lenTy = mlir::IndexType::get(&mlirContext);
70937e84d9bSValentin Clement     std::optional<std::int64_t> constantLen = type.knownLength();
71037e84d9bSValentin Clement     fir::CharacterType::LenType len =
71137e84d9bSValentin Clement         constantLen ? *constantLen : fir::CharacterType::unknownLen();
71237e84d9bSValentin Clement     mlir::Type charRefTy = fir::ReferenceType::get(
71337e84d9bSValentin Clement         fir::CharacterType::get(&mlirContext, type.kind(), len));
71437e84d9bSValentin Clement     mlir::Type boxCharTy = fir::BoxCharType::get(&mlirContext, type.kind());
71537e84d9bSValentin Clement     addFirOperand(charRefTy, resultPosition, Property::CharAddress);
71637e84d9bSValentin Clement     addFirOperand(lenTy, resultPosition, Property::CharLength);
71737e84d9bSValentin Clement     /// For now, also return it by boxchar
71837e84d9bSValentin Clement     addFirResult(boxCharTy, resultPosition, Property::BoxChar);
71937e84d9bSValentin Clement   }
72037e84d9bSValentin Clement 
721da7c77b8SValentin Clement   /// Return a vector with an attribute with the name of the argument if this
722da7c77b8SValentin Clement   /// is a callee interface and the name is available. Otherwise, just return
723da7c77b8SValentin Clement   /// an empty vector.
724da7c77b8SValentin Clement   llvm::SmallVector<mlir::NamedAttribute>
dummyNameAttr(const FortranEntity & entity)725da7c77b8SValentin Clement   dummyNameAttr(const FortranEntity &entity) {
726da7c77b8SValentin Clement     if constexpr (std::is_same_v<FortranEntity,
727da7c77b8SValentin Clement                                  std::optional<Fortran::common::Reference<
728da7c77b8SValentin Clement                                      const Fortran::semantics::Symbol>>>) {
729da7c77b8SValentin Clement       if (entity.has_value()) {
730da7c77b8SValentin Clement         const Fortran::semantics::Symbol *argument = &*entity.value();
731da7c77b8SValentin Clement         // "fir.bindc_name" is used for arguments for the sake of consistency
732da7c77b8SValentin Clement         // with other attributes carrying surface syntax names in FIR.
733da7c77b8SValentin Clement         return {mlir::NamedAttribute(
734da7c77b8SValentin Clement             mlir::StringAttr::get(&mlirContext, "fir.bindc_name"),
735da7c77b8SValentin Clement             mlir::StringAttr::get(&mlirContext,
736da7c77b8SValentin Clement                                   toStringRef(argument->name())))};
737ad40cc14SValentin Clement       }
738da7c77b8SValentin Clement     }
739da7c77b8SValentin Clement     return {};
740da7c77b8SValentin Clement   }
741da7c77b8SValentin Clement 
handleImplicitDummy(const DummyCharacteristics * characteristics,const Fortran::evaluate::characteristics::DummyDataObject & obj,const FortranEntity & entity)742764f95a8SValentin Clement   void handleImplicitDummy(
743764f95a8SValentin Clement       const DummyCharacteristics *characteristics,
744764f95a8SValentin Clement       const Fortran::evaluate::characteristics::DummyDataObject &obj,
745764f95a8SValentin Clement       const FortranEntity &entity) {
746764f95a8SValentin Clement     Fortran::evaluate::DynamicType dynamicType = obj.type.type();
747764f95a8SValentin Clement     if (dynamicType.category() == Fortran::common::TypeCategory::Character) {
748764f95a8SValentin Clement       mlir::Type boxCharTy =
749764f95a8SValentin Clement           fir::BoxCharType::get(&mlirContext, dynamicType.kind());
750764f95a8SValentin Clement       addFirOperand(boxCharTy, nextPassedArgPosition(), Property::BoxChar,
751764f95a8SValentin Clement                     dummyNameAttr(entity));
752764f95a8SValentin Clement       addPassedArg(PassEntityBy::BoxChar, entity, characteristics);
753764f95a8SValentin Clement     } else {
754764f95a8SValentin Clement       // non-PDT derived type allowed in implicit interface.
755764f95a8SValentin Clement       mlir::Type type = translateDynamicType(dynamicType);
756764f95a8SValentin Clement       fir::SequenceType::Shape bounds = getBounds(obj.type.shape());
757764f95a8SValentin Clement       if (!bounds.empty())
758764f95a8SValentin Clement         type = fir::SequenceType::get(bounds, type);
759764f95a8SValentin Clement       mlir::Type refType = fir::ReferenceType::get(type);
760764f95a8SValentin Clement       addFirOperand(refType, nextPassedArgPosition(), Property::BaseAddress,
761764f95a8SValentin Clement                     dummyNameAttr(entity));
762764f95a8SValentin Clement       addPassedArg(PassEntityBy::BaseAddress, entity, characteristics);
763764f95a8SValentin Clement     }
764764f95a8SValentin Clement   }
765764f95a8SValentin Clement 
766914061bbSValentin Clement   // Define when an explicit argument must be passed in a fir.box.
dummyRequiresBox(const Fortran::evaluate::characteristics::DummyDataObject & obj)767914061bbSValentin Clement   bool dummyRequiresBox(
768914061bbSValentin Clement       const Fortran::evaluate::characteristics::DummyDataObject &obj) {
769914061bbSValentin Clement     using ShapeAttr = Fortran::evaluate::characteristics::TypeAndShape::Attr;
770914061bbSValentin Clement     using ShapeAttrs = Fortran::evaluate::characteristics::TypeAndShape::Attrs;
771914061bbSValentin Clement     constexpr ShapeAttrs shapeRequiringBox = {
772914061bbSValentin Clement         ShapeAttr::AssumedShape, ShapeAttr::DeferredShape,
773914061bbSValentin Clement         ShapeAttr::AssumedRank, ShapeAttr::Coarray};
774914061bbSValentin Clement     if ((obj.type.attrs() & shapeRequiringBox).any())
775914061bbSValentin Clement       // Need to pass shape/coshape info in fir.box.
776914061bbSValentin Clement       return true;
777914061bbSValentin Clement     if (obj.type.type().IsPolymorphic())
778914061bbSValentin Clement       // Need to pass dynamic type info in fir.box.
779914061bbSValentin Clement       return true;
780914061bbSValentin Clement     if (const Fortran::semantics::DerivedTypeSpec *derived =
781914061bbSValentin Clement             Fortran::evaluate::GetDerivedTypeSpec(obj.type.type()))
7823d63d211SJean Perier       if (const Fortran::semantics::Scope *scope = derived->scope())
7833d63d211SJean Perier         // Need to pass length type parameters in fir.box if any.
7843d63d211SJean Perier         return scope->IsDerivedTypeWithLengthParameter();
785914061bbSValentin Clement     return false;
786914061bbSValentin Clement   }
787914061bbSValentin Clement 
788914061bbSValentin Clement   mlir::Type
translateDynamicType(const Fortran::evaluate::DynamicType & dynamicType)789914061bbSValentin Clement   translateDynamicType(const Fortran::evaluate::DynamicType &dynamicType) {
790914061bbSValentin Clement     Fortran::common::TypeCategory cat = dynamicType.category();
791914061bbSValentin Clement     // DERIVED
792914061bbSValentin Clement     if (cat == Fortran::common::TypeCategory::Derived) {
793589d51eaSValentin Clement       if (dynamicType.IsPolymorphic())
794914061bbSValentin Clement         TODO(interface.converter.getCurrentLocation(),
795*39377d52SValentin Clement              "support for polymorphic types");
796589d51eaSValentin Clement       return getConverter().genType(dynamicType.GetDerivedTypeSpec());
797914061bbSValentin Clement     }
798914061bbSValentin Clement     // CHARACTER with compile time constant length.
799914061bbSValentin Clement     if (cat == Fortran::common::TypeCategory::Character)
80096d9df41SValentin Clement       if (std::optional<std::int64_t> constantLen =
80196d9df41SValentin Clement               toInt64(dynamicType.GetCharLength()))
80296d9df41SValentin Clement         return getConverter().genType(cat, dynamicType.kind(), {*constantLen});
803914061bbSValentin Clement     // INTEGER, REAL, LOGICAL, COMPLEX, and CHARACTER with dynamic length.
804914061bbSValentin Clement     return getConverter().genType(cat, dynamicType.kind());
805914061bbSValentin Clement   }
806914061bbSValentin Clement 
handleExplicitDummy(const DummyCharacteristics * characteristics,const Fortran::evaluate::characteristics::DummyDataObject & obj,const FortranEntity & entity,bool isBindC)807914061bbSValentin Clement   void handleExplicitDummy(
808914061bbSValentin Clement       const DummyCharacteristics *characteristics,
809914061bbSValentin Clement       const Fortran::evaluate::characteristics::DummyDataObject &obj,
810914061bbSValentin Clement       const FortranEntity &entity, bool isBindC) {
811914061bbSValentin Clement     using Attrs = Fortran::evaluate::characteristics::DummyDataObject::Attr;
812914061bbSValentin Clement 
813914061bbSValentin Clement     bool isValueAttr = false;
814914061bbSValentin Clement     [[maybe_unused]] mlir::Location loc =
815914061bbSValentin Clement         interface.converter.getCurrentLocation();
816914061bbSValentin Clement     llvm::SmallVector<mlir::NamedAttribute> attrs = dummyNameAttr(entity);
817914061bbSValentin Clement     auto addMLIRAttr = [&](llvm::StringRef attr) {
818914061bbSValentin Clement       attrs.emplace_back(mlir::StringAttr::get(&mlirContext, attr),
819914061bbSValentin Clement                          mlir::UnitAttr::get(&mlirContext));
820914061bbSValentin Clement     };
821914061bbSValentin Clement     if (obj.attrs.test(Attrs::Optional))
822914061bbSValentin Clement       addMLIRAttr(fir::getOptionalAttrName());
823914061bbSValentin Clement     if (obj.attrs.test(Attrs::Asynchronous))
824331145e6SValentin Clement       TODO(loc, "ASYNCHRONOUS in procedure interface");
825914061bbSValentin Clement     if (obj.attrs.test(Attrs::Contiguous))
826914061bbSValentin Clement       addMLIRAttr(fir::getContiguousAttrName());
827914061bbSValentin Clement     if (obj.attrs.test(Attrs::Value))
828914061bbSValentin Clement       isValueAttr = true; // TODO: do we want an mlir::Attribute as well?
829914061bbSValentin Clement     if (obj.attrs.test(Attrs::Volatile))
830331145e6SValentin Clement       TODO(loc, "VOLATILE in procedure interface");
831914061bbSValentin Clement     if (obj.attrs.test(Attrs::Target))
832914061bbSValentin Clement       addMLIRAttr(fir::getTargetAttrName());
833914061bbSValentin Clement 
834914061bbSValentin Clement     // TODO: intents that require special care (e.g finalization)
835914061bbSValentin Clement 
836914061bbSValentin Clement     using ShapeAttr = Fortran::evaluate::characteristics::TypeAndShape::Attr;
837914061bbSValentin Clement     const Fortran::evaluate::characteristics::TypeAndShape::Attrs &shapeAttrs =
838914061bbSValentin Clement         obj.type.attrs();
839914061bbSValentin Clement     if (shapeAttrs.test(ShapeAttr::AssumedRank))
840331145e6SValentin Clement       TODO(loc, "assumed rank in procedure interface");
841914061bbSValentin Clement     if (shapeAttrs.test(ShapeAttr::Coarray))
842331145e6SValentin Clement       TODO(loc, "coarray in procedure interface");
843914061bbSValentin Clement 
844914061bbSValentin Clement     // So far assume that if the argument cannot be passed by implicit interface
845914061bbSValentin Clement     // it must be by box. That may no be always true (e.g for simple optionals)
846914061bbSValentin Clement 
847914061bbSValentin Clement     Fortran::evaluate::DynamicType dynamicType = obj.type.type();
848914061bbSValentin Clement     mlir::Type type = translateDynamicType(dynamicType);
849914061bbSValentin Clement     fir::SequenceType::Shape bounds = getBounds(obj.type.shape());
850914061bbSValentin Clement     if (!bounds.empty())
851914061bbSValentin Clement       type = fir::SequenceType::get(bounds, type);
852914061bbSValentin Clement     if (obj.attrs.test(Attrs::Allocatable))
853914061bbSValentin Clement       type = fir::HeapType::get(type);
854914061bbSValentin Clement     if (obj.attrs.test(Attrs::Pointer))
855914061bbSValentin Clement       type = fir::PointerType::get(type);
856914061bbSValentin Clement     mlir::Type boxType = fir::BoxType::get(type);
857914061bbSValentin Clement 
858914061bbSValentin Clement     if (obj.attrs.test(Attrs::Allocatable) || obj.attrs.test(Attrs::Pointer)) {
859914061bbSValentin Clement       // Pass as fir.ref<fir.box>
860914061bbSValentin Clement       mlir::Type boxRefType = fir::ReferenceType::get(boxType);
861914061bbSValentin Clement       addFirOperand(boxRefType, nextPassedArgPosition(), Property::MutableBox,
862914061bbSValentin Clement                     attrs);
863914061bbSValentin Clement       addPassedArg(PassEntityBy::MutableBox, entity, characteristics);
864914061bbSValentin Clement     } else if (dummyRequiresBox(obj)) {
865914061bbSValentin Clement       // Pass as fir.box
866f5b29a7aSValentin Clement       if (isValueAttr)
867f5b29a7aSValentin Clement         TODO(loc, "assumed shape dummy argument with VALUE attribute");
868914061bbSValentin Clement       addFirOperand(boxType, nextPassedArgPosition(), Property::Box, attrs);
869914061bbSValentin Clement       addPassedArg(PassEntityBy::Box, entity, characteristics);
870914061bbSValentin Clement     } else if (dynamicType.category() ==
871914061bbSValentin Clement                Fortran::common::TypeCategory::Character) {
872914061bbSValentin Clement       // Pass as fir.box_char
873914061bbSValentin Clement       mlir::Type boxCharTy =
874914061bbSValentin Clement           fir::BoxCharType::get(&mlirContext, dynamicType.kind());
875914061bbSValentin Clement       addFirOperand(boxCharTy, nextPassedArgPosition(), Property::BoxChar,
876914061bbSValentin Clement                     attrs);
877914061bbSValentin Clement       addPassedArg(isValueAttr ? PassEntityBy::CharBoxValueAttribute
878914061bbSValentin Clement                                : PassEntityBy::BoxChar,
879914061bbSValentin Clement                    entity, characteristics);
880914061bbSValentin Clement     } else {
881914061bbSValentin Clement       // Pass as fir.ref unless it's by VALUE and BIND(C)
882914061bbSValentin Clement       mlir::Type passType = fir::ReferenceType::get(type);
883914061bbSValentin Clement       PassEntityBy passBy = PassEntityBy::BaseAddress;
884914061bbSValentin Clement       Property prop = Property::BaseAddress;
885914061bbSValentin Clement       if (isValueAttr) {
886914061bbSValentin Clement         if (isBindC) {
887914061bbSValentin Clement           passBy = PassEntityBy::Value;
888914061bbSValentin Clement           prop = Property::Value;
889914061bbSValentin Clement           passType = type;
890914061bbSValentin Clement         } else {
891914061bbSValentin Clement           passBy = PassEntityBy::BaseAddressValueAttribute;
892914061bbSValentin Clement         }
893914061bbSValentin Clement       }
894914061bbSValentin Clement       addFirOperand(passType, nextPassedArgPosition(), prop, attrs);
895914061bbSValentin Clement       addPassedArg(passBy, entity, characteristics);
896914061bbSValentin Clement     }
897914061bbSValentin Clement   }
898914061bbSValentin Clement 
handleImplicitDummy(const DummyCharacteristics * characteristics,const Fortran::evaluate::characteristics::DummyProcedure & proc,const FortranEntity & entity)899da7c77b8SValentin Clement   void handleImplicitDummy(
900da7c77b8SValentin Clement       const DummyCharacteristics *characteristics,
901da7c77b8SValentin Clement       const Fortran::evaluate::characteristics::DummyProcedure &proc,
902da7c77b8SValentin Clement       const FortranEntity &entity) {
903764f95a8SValentin Clement     if (proc.attrs.test(
904764f95a8SValentin Clement             Fortran::evaluate::characteristics::DummyProcedure::Attr::Pointer))
905da7c77b8SValentin Clement       TODO(interface.converter.getCurrentLocation(),
906764f95a8SValentin Clement            "procedure pointer arguments");
907764f95a8SValentin Clement     // Otherwise, it is a dummy procedure.
908764f95a8SValentin Clement     const Fortran::evaluate::characteristics::Procedure &procedure =
909764f95a8SValentin Clement         proc.procedure.value();
910764f95a8SValentin Clement     mlir::Type funcType =
911764f95a8SValentin Clement         getProcedureDesignatorType(&procedure, interface.converter);
912764f95a8SValentin Clement     llvm::Optional<Fortran::evaluate::DynamicType> resultTy =
913764f95a8SValentin Clement         getResultDynamicType(procedure);
914764f95a8SValentin Clement     if (resultTy && mustPassLengthWithDummyProcedure(procedure)) {
915764f95a8SValentin Clement       // The result length of dummy procedures that are character functions must
916764f95a8SValentin Clement       // be passed so that the dummy procedure can be called if it has assumed
917764f95a8SValentin Clement       // length on the callee side.
918764f95a8SValentin Clement       mlir::Type tupleType =
919764f95a8SValentin Clement           fir::factory::getCharacterProcedureTupleType(funcType);
920764f95a8SValentin Clement       llvm::StringRef charProcAttr = fir::getCharacterProcedureDummyAttrName();
921764f95a8SValentin Clement       addFirOperand(tupleType, nextPassedArgPosition(), Property::CharProcTuple,
922764f95a8SValentin Clement                     {mlir::NamedAttribute{
923764f95a8SValentin Clement                         mlir::StringAttr::get(&mlirContext, charProcAttr),
924764f95a8SValentin Clement                         mlir::UnitAttr::get(&mlirContext)}});
925764f95a8SValentin Clement       addPassedArg(PassEntityBy::CharProcTuple, entity, characteristics);
926764f95a8SValentin Clement       return;
927764f95a8SValentin Clement     }
928764f95a8SValentin Clement     addFirOperand(funcType, nextPassedArgPosition(), Property::BaseAddress);
929764f95a8SValentin Clement     addPassedArg(PassEntityBy::BaseAddress, entity, characteristics);
930da7c77b8SValentin Clement   }
931da7c77b8SValentin Clement 
handleExplicitResult(const Fortran::evaluate::characteristics::FunctionResult & result)932764f95a8SValentin Clement   void handleExplicitResult(
933764f95a8SValentin Clement       const Fortran::evaluate::characteristics::FunctionResult &result) {
934764f95a8SValentin Clement     using Attr = Fortran::evaluate::characteristics::FunctionResult::Attr;
935764f95a8SValentin Clement 
936764f95a8SValentin Clement     if (result.IsProcedurePointer())
937764f95a8SValentin Clement       TODO(interface.converter.getCurrentLocation(),
938764f95a8SValentin Clement            "procedure pointer results");
939764f95a8SValentin Clement     const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
940764f95a8SValentin Clement         result.GetTypeAndShape();
941764f95a8SValentin Clement     assert(typeAndShape && "expect type for non proc pointer result");
942764f95a8SValentin Clement     mlir::Type mlirType = translateDynamicType(typeAndShape->type());
943764f95a8SValentin Clement     fir::SequenceType::Shape bounds = getBounds(typeAndShape->shape());
944764f95a8SValentin Clement     if (!bounds.empty())
945764f95a8SValentin Clement       mlirType = fir::SequenceType::get(bounds, mlirType);
946764f95a8SValentin Clement     if (result.attrs.test(Attr::Allocatable))
947764f95a8SValentin Clement       mlirType = fir::BoxType::get(fir::HeapType::get(mlirType));
948764f95a8SValentin Clement     if (result.attrs.test(Attr::Pointer))
949764f95a8SValentin Clement       mlirType = fir::BoxType::get(fir::PointerType::get(mlirType));
950764f95a8SValentin Clement 
951764f95a8SValentin Clement     if (fir::isa_char(mlirType)) {
952764f95a8SValentin Clement       // Character scalar results must be passed as arguments in lowering so
953764f95a8SValentin Clement       // that an assumed length character function callee can access the result
954764f95a8SValentin Clement       // length. A function with a result requiring an explicit interface does
955764f95a8SValentin Clement       // not have to be compatible with assumed length function, but most
956764f95a8SValentin Clement       // compilers supports it.
957764f95a8SValentin Clement       handleImplicitCharacterResult(typeAndShape->type());
958764f95a8SValentin Clement       return;
959764f95a8SValentin Clement     }
960764f95a8SValentin Clement 
961764f95a8SValentin Clement     addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
962764f95a8SValentin Clement                  Property::Value);
963764f95a8SValentin Clement     // Explicit results require the caller to allocate the storage and save the
964764f95a8SValentin Clement     // function result in the storage with a fir.save_result.
965764f95a8SValentin Clement     setSaveResult();
966764f95a8SValentin Clement   }
967764f95a8SValentin Clement 
getBounds(const Fortran::evaluate::Shape & shape)968764f95a8SValentin Clement   fir::SequenceType::Shape getBounds(const Fortran::evaluate::Shape &shape) {
969764f95a8SValentin Clement     fir::SequenceType::Shape bounds;
970764f95a8SValentin Clement     for (const std::optional<Fortran::evaluate::ExtentExpr> &extent : shape) {
971764f95a8SValentin Clement       fir::SequenceType::Extent bound = fir::SequenceType::getUnknownExtent();
972764f95a8SValentin Clement       if (std::optional<std::int64_t> i = toInt64(extent))
973764f95a8SValentin Clement         bound = *i;
974764f95a8SValentin Clement       bounds.emplace_back(bound);
975764f95a8SValentin Clement     }
976764f95a8SValentin Clement     return bounds;
977764f95a8SValentin Clement   }
978764f95a8SValentin Clement   std::optional<std::int64_t>
toInt64(std::optional<Fortran::evaluate::Expr<Fortran::evaluate::SubscriptInteger>> expr)979764f95a8SValentin Clement   toInt64(std::optional<
980764f95a8SValentin Clement           Fortran::evaluate::Expr<Fortran::evaluate::SubscriptInteger>>
981764f95a8SValentin Clement               expr) {
982764f95a8SValentin Clement     if (expr)
983764f95a8SValentin Clement       return Fortran::evaluate::ToInt64(Fortran::evaluate::Fold(
984764f95a8SValentin Clement           getConverter().getFoldingContext(), toEvExpr(*expr)));
985764f95a8SValentin Clement     return std::nullopt;
986764f95a8SValentin Clement   }
987da7c77b8SValentin Clement   void
addFirOperand(mlir::Type type,int entityPosition,Property p,llvm::ArrayRef<mlir::NamedAttribute> attributes=llvm::None)988da7c77b8SValentin Clement   addFirOperand(mlir::Type type, int entityPosition, Property p,
989da7c77b8SValentin Clement                 llvm::ArrayRef<mlir::NamedAttribute> attributes = llvm::None) {
990da7c77b8SValentin Clement     interface.inputs.emplace_back(
991da7c77b8SValentin Clement         FirPlaceHolder{type, entityPosition, p, attributes});
992da7c77b8SValentin Clement   }
993da7c77b8SValentin Clement   void
addFirResult(mlir::Type type,int entityPosition,Property p,llvm::ArrayRef<mlir::NamedAttribute> attributes=llvm::None)994da7c77b8SValentin Clement   addFirResult(mlir::Type type, int entityPosition, Property p,
995da7c77b8SValentin Clement                llvm::ArrayRef<mlir::NamedAttribute> attributes = llvm::None) {
996da7c77b8SValentin Clement     interface.outputs.emplace_back(
997da7c77b8SValentin Clement         FirPlaceHolder{type, entityPosition, p, attributes});
998da7c77b8SValentin Clement   }
addPassedArg(PassEntityBy p,FortranEntity entity,const DummyCharacteristics * characteristics)999da7c77b8SValentin Clement   void addPassedArg(PassEntityBy p, FortranEntity entity,
1000da7c77b8SValentin Clement                     const DummyCharacteristics *characteristics) {
1001da7c77b8SValentin Clement     interface.passedArguments.emplace_back(
1002764f95a8SValentin Clement         PassedEntity{p, entity, emptyValue(), emptyValue(), characteristics});
1003da7c77b8SValentin Clement   }
setPassedResult(PassEntityBy p,FortranEntity entity)100437e84d9bSValentin Clement   void setPassedResult(PassEntityBy p, FortranEntity entity) {
100537e84d9bSValentin Clement     interface.passedResult =
100637e84d9bSValentin Clement         PassedEntity{p, entity, emptyValue(), emptyValue()};
100737e84d9bSValentin Clement   }
setSaveResult()100837e84d9bSValentin Clement   void setSaveResult() { interface.saveResult = true; }
nextPassedArgPosition()1009da7c77b8SValentin Clement   int nextPassedArgPosition() { return interface.passedArguments.size(); }
1010ad40cc14SValentin Clement 
emptyValue()101137e84d9bSValentin Clement   static FirValue emptyValue() {
101237e84d9bSValentin Clement     if constexpr (std::is_same_v<Fortran::lower::CalleeInterface, T>) {
101337e84d9bSValentin Clement       return {};
101437e84d9bSValentin Clement     } else {
101537e84d9bSValentin Clement       return -1;
101637e84d9bSValentin Clement     }
101737e84d9bSValentin Clement   }
101837e84d9bSValentin Clement 
getConverter()1019ad40cc14SValentin Clement   Fortran::lower::AbstractConverter &getConverter() {
1020ad40cc14SValentin Clement     return interface.converter;
1021ad40cc14SValentin Clement   }
1022ad40cc14SValentin Clement   CallInterface &interface;
1023ad40cc14SValentin Clement   mlir::MLIRContext &mlirContext;
1024ad40cc14SValentin Clement };
1025ad40cc14SValentin Clement 
1026ad40cc14SValentin Clement template <typename T>
isOptional() const1027d0b70a07SValentin Clement bool Fortran::lower::CallInterface<T>::PassedEntity::isOptional() const {
1028d0b70a07SValentin Clement   if (!characteristics)
1029d0b70a07SValentin Clement     return false;
1030d0b70a07SValentin Clement   return characteristics->IsOptional();
1031d0b70a07SValentin Clement }
1032d0b70a07SValentin Clement template <typename T>
mayBeModifiedByCall() const1033d0b70a07SValentin Clement bool Fortran::lower::CallInterface<T>::PassedEntity::mayBeModifiedByCall()
1034d0b70a07SValentin Clement     const {
1035d0b70a07SValentin Clement   if (!characteristics)
1036d0b70a07SValentin Clement     return true;
1037d0b70a07SValentin Clement   return characteristics->GetIntent() != Fortran::common::Intent::In;
1038d0b70a07SValentin Clement }
1039d0b70a07SValentin Clement template <typename T>
mayBeReadByCall() const1040d0b70a07SValentin Clement bool Fortran::lower::CallInterface<T>::PassedEntity::mayBeReadByCall() const {
1041d0b70a07SValentin Clement   if (!characteristics)
1042d0b70a07SValentin Clement     return true;
1043d0b70a07SValentin Clement   return characteristics->GetIntent() != Fortran::common::Intent::Out;
1044d0b70a07SValentin Clement }
1045d0b70a07SValentin Clement 
1046d0b70a07SValentin Clement template <typename T>
determineInterface(bool isImplicit,const Fortran::evaluate::characteristics::Procedure & procedure)1047ad40cc14SValentin Clement void Fortran::lower::CallInterface<T>::determineInterface(
1048ad40cc14SValentin Clement     bool isImplicit,
1049ad40cc14SValentin Clement     const Fortran::evaluate::characteristics::Procedure &procedure) {
1050ad40cc14SValentin Clement   CallInterfaceImpl<T> impl(*this);
1051ad40cc14SValentin Clement   if (isImplicit)
1052ad40cc14SValentin Clement     impl.buildImplicitInterface(procedure);
1053ad40cc14SValentin Clement   else
1054c807aa53SValentin Clement     impl.buildExplicitInterface(procedure);
1055764f95a8SValentin Clement   // We only expect the extra host asspciations argument from the callee side as
1056764f95a8SValentin Clement   // the definition of internal procedures will be present, and we'll always
1057764f95a8SValentin Clement   // have a FuncOp definition in the ModuleOp, when lowering.
1058764f95a8SValentin Clement   if constexpr (std::is_same_v<T, Fortran::lower::CalleeInterface>) {
1059764f95a8SValentin Clement     if (side().hasHostAssociated())
1060764f95a8SValentin Clement       impl.appendHostAssocTupleArg(side().getHostAssociatedTy());
1061764f95a8SValentin Clement   }
1062ad40cc14SValentin Clement }
1063ad40cc14SValentin Clement 
1064e1a12767SValentin Clement template <typename T>
genFunctionType()1065e1a12767SValentin Clement mlir::FunctionType Fortran::lower::CallInterface<T>::genFunctionType() {
1066ad40cc14SValentin Clement   llvm::SmallVector<mlir::Type> returnTys;
1067da7c77b8SValentin Clement   llvm::SmallVector<mlir::Type> inputTys;
1068ad40cc14SValentin Clement   for (const FirPlaceHolder &placeHolder : outputs)
1069ad40cc14SValentin Clement     returnTys.emplace_back(placeHolder.type);
1070da7c77b8SValentin Clement   for (const FirPlaceHolder &placeHolder : inputs)
1071da7c77b8SValentin Clement     inputTys.emplace_back(placeHolder.type);
1072da7c77b8SValentin Clement   return mlir::FunctionType::get(&converter.getMLIRContext(), inputTys,
1073da7c77b8SValentin Clement                                  returnTys);
1074e1a12767SValentin Clement }
1075e1a12767SValentin Clement 
1076764f95a8SValentin Clement template <typename T>
1077764f95a8SValentin Clement llvm::SmallVector<mlir::Type>
getResultType() const1078764f95a8SValentin Clement Fortran::lower::CallInterface<T>::getResultType() const {
1079764f95a8SValentin Clement   llvm::SmallVector<mlir::Type> types;
1080764f95a8SValentin Clement   for (const FirPlaceHolder &out : outputs)
1081764f95a8SValentin Clement     types.emplace_back(out.type);
1082764f95a8SValentin Clement   return types;
1083764f95a8SValentin Clement }
1084764f95a8SValentin Clement 
1085e1a12767SValentin Clement template class Fortran::lower::CallInterface<Fortran::lower::CalleeInterface>;
1086d0b70a07SValentin Clement template class Fortran::lower::CallInterface<Fortran::lower::CallerInterface>;
1087764f95a8SValentin Clement 
1088764f95a8SValentin Clement //===----------------------------------------------------------------------===//
1089764f95a8SValentin Clement // Function Type Translation
1090764f95a8SValentin Clement //===----------------------------------------------------------------------===//
1091764f95a8SValentin Clement 
1092764f95a8SValentin Clement /// Build signature from characteristics when there is no Fortran entity to
1093764f95a8SValentin Clement /// associate with the arguments (i.e, this is not a call site or a procedure
1094764f95a8SValentin Clement /// declaration. This is needed when dealing with function pointers/dummy
1095764f95a8SValentin Clement /// arguments.
1096764f95a8SValentin Clement 
1097764f95a8SValentin Clement class SignatureBuilder;
1098764f95a8SValentin Clement template <>
1099764f95a8SValentin Clement struct Fortran::lower::PassedEntityTypes<SignatureBuilder> {
1100764f95a8SValentin Clement   using FortranEntity = FakeEntity;
1101764f95a8SValentin Clement   using FirValue = int;
1102764f95a8SValentin Clement };
1103764f95a8SValentin Clement 
1104764f95a8SValentin Clement /// SignatureBuilder is a CRTP implementation of CallInterface intended to
1105764f95a8SValentin Clement /// help translating characteristics::Procedure to mlir::FunctionType using
1106764f95a8SValentin Clement /// the CallInterface translation.
1107764f95a8SValentin Clement class SignatureBuilder
1108764f95a8SValentin Clement     : public Fortran::lower::CallInterface<SignatureBuilder> {
1109764f95a8SValentin Clement public:
SignatureBuilder(const Fortran::evaluate::characteristics::Procedure & p,Fortran::lower::AbstractConverter & c,bool forceImplicit)1110764f95a8SValentin Clement   SignatureBuilder(const Fortran::evaluate::characteristics::Procedure &p,
1111764f95a8SValentin Clement                    Fortran::lower::AbstractConverter &c, bool forceImplicit)
1112764f95a8SValentin Clement       : CallInterface{c}, proc{p} {
1113764f95a8SValentin Clement     bool isImplicit = forceImplicit || proc.CanBeCalledViaImplicitInterface();
1114764f95a8SValentin Clement     determineInterface(isImplicit, proc);
1115764f95a8SValentin Clement   }
1116764f95a8SValentin Clement   /// Does the procedure characteristics being translated have alternate
1117764f95a8SValentin Clement   /// returns ?
hasAlternateReturns() const1118764f95a8SValentin Clement   bool hasAlternateReturns() const {
1119764f95a8SValentin Clement     for (const Fortran::evaluate::characteristics::DummyArgument &dummy :
1120764f95a8SValentin Clement          proc.dummyArguments)
1121764f95a8SValentin Clement       if (std::holds_alternative<
1122764f95a8SValentin Clement               Fortran::evaluate::characteristics::AlternateReturn>(dummy.u))
1123764f95a8SValentin Clement         return true;
1124764f95a8SValentin Clement     return false;
1125764f95a8SValentin Clement   };
1126764f95a8SValentin Clement 
1127764f95a8SValentin Clement   /// This is only here to fulfill CRTP dependencies and should not be called.
getMangledName() const1128764f95a8SValentin Clement   std::string getMangledName() const {
1129764f95a8SValentin Clement     llvm_unreachable("trying to get name from SignatureBuilder");
1130764f95a8SValentin Clement   }
1131764f95a8SValentin Clement 
1132764f95a8SValentin Clement   /// This is only here to fulfill CRTP dependencies and should not be called.
getCalleeLocation() const1133764f95a8SValentin Clement   mlir::Location getCalleeLocation() const {
1134764f95a8SValentin Clement     llvm_unreachable("trying to get callee location from SignatureBuilder");
1135764f95a8SValentin Clement   }
1136764f95a8SValentin Clement 
1137764f95a8SValentin Clement   /// This is only here to fulfill CRTP dependencies and should not be called.
getProcedureSymbol() const1138764f95a8SValentin Clement   const Fortran::semantics::Symbol *getProcedureSymbol() const {
1139764f95a8SValentin Clement     llvm_unreachable("trying to get callee symbol from SignatureBuilder");
1140764f95a8SValentin Clement   };
1141764f95a8SValentin Clement 
characterize() const1142764f95a8SValentin Clement   Fortran::evaluate::characteristics::Procedure characterize() const {
1143764f95a8SValentin Clement     return proc;
1144764f95a8SValentin Clement   }
1145764f95a8SValentin Clement   /// SignatureBuilder cannot be used on main program.
isMainProgram()1146764f95a8SValentin Clement   static constexpr bool isMainProgram() { return false; }
1147764f95a8SValentin Clement 
1148764f95a8SValentin Clement   /// Return the characteristics::Procedure that is being translated to
1149764f95a8SValentin Clement   /// mlir::FunctionType.
1150764f95a8SValentin Clement   const Fortran::evaluate::characteristics::Procedure &
getCallDescription() const1151764f95a8SValentin Clement   getCallDescription() const {
1152764f95a8SValentin Clement     return proc;
1153764f95a8SValentin Clement   }
1154764f95a8SValentin Clement 
1155764f95a8SValentin Clement   /// This is not the description of an indirect call.
isIndirectCall()1156764f95a8SValentin Clement   static constexpr bool isIndirectCall() { return false; }
1157764f95a8SValentin Clement 
1158764f95a8SValentin Clement   /// Return the translated signature.
getFunctionType()1159764f95a8SValentin Clement   mlir::FunctionType getFunctionType() { return genFunctionType(); }
1160764f95a8SValentin Clement 
1161764f95a8SValentin Clement   // Copy of base implementation.
hasHostAssociated()1162764f95a8SValentin Clement   static constexpr bool hasHostAssociated() { return false; }
getHostAssociatedTy() const1163764f95a8SValentin Clement   mlir::Type getHostAssociatedTy() const {
1164764f95a8SValentin Clement     llvm_unreachable("getting host associated type in SignatureBuilder");
1165764f95a8SValentin Clement   }
1166764f95a8SValentin Clement 
1167764f95a8SValentin Clement private:
1168764f95a8SValentin Clement   const Fortran::evaluate::characteristics::Procedure &proc;
1169764f95a8SValentin Clement };
1170764f95a8SValentin Clement 
translateSignature(const Fortran::evaluate::ProcedureDesignator & proc,Fortran::lower::AbstractConverter & converter)1171764f95a8SValentin Clement mlir::FunctionType Fortran::lower::translateSignature(
1172764f95a8SValentin Clement     const Fortran::evaluate::ProcedureDesignator &proc,
1173764f95a8SValentin Clement     Fortran::lower::AbstractConverter &converter) {
1174764f95a8SValentin Clement   std::optional<Fortran::evaluate::characteristics::Procedure> characteristics =
1175764f95a8SValentin Clement       Fortran::evaluate::characteristics::Procedure::Characterize(
1176764f95a8SValentin Clement           proc, converter.getFoldingContext());
1177764f95a8SValentin Clement   // Most unrestricted intrinsic characteristic has the Elemental attribute
1178764f95a8SValentin Clement   // which triggers CanBeCalledViaImplicitInterface to return false. However,
1179764f95a8SValentin Clement   // using implicit interface rules is just fine here.
1180764f95a8SValentin Clement   bool forceImplicit = proc.GetSpecificIntrinsic();
1181764f95a8SValentin Clement   return SignatureBuilder{characteristics.value(), converter, forceImplicit}
1182764f95a8SValentin Clement       .getFunctionType();
1183764f95a8SValentin Clement }
1184764f95a8SValentin Clement 
getOrDeclareFunction(llvm::StringRef name,const Fortran::evaluate::ProcedureDesignator & proc,Fortran::lower::AbstractConverter & converter)118558ceae95SRiver Riddle mlir::func::FuncOp Fortran::lower::getOrDeclareFunction(
1186764f95a8SValentin Clement     llvm::StringRef name, const Fortran::evaluate::ProcedureDesignator &proc,
1187764f95a8SValentin Clement     Fortran::lower::AbstractConverter &converter) {
1188764f95a8SValentin Clement   mlir::ModuleOp module = converter.getModuleOp();
118958ceae95SRiver Riddle   mlir::func::FuncOp func = fir::FirOpBuilder::getNamedFunction(module, name);
1190764f95a8SValentin Clement   if (func)
1191764f95a8SValentin Clement     return func;
1192764f95a8SValentin Clement 
1193764f95a8SValentin Clement   const Fortran::semantics::Symbol *symbol = proc.GetSymbol();
1194764f95a8SValentin Clement   assert(symbol && "non user function in getOrDeclareFunction");
1195764f95a8SValentin Clement   // getOrDeclareFunction is only used for functions not defined in the current
1196764f95a8SValentin Clement   // program unit, so use the location of the procedure designator symbol, which
1197764f95a8SValentin Clement   // is the first occurrence of the procedure in the program unit.
1198764f95a8SValentin Clement   mlir::Location loc = converter.genLocation(symbol->name());
1199764f95a8SValentin Clement   std::optional<Fortran::evaluate::characteristics::Procedure> characteristics =
1200764f95a8SValentin Clement       Fortran::evaluate::characteristics::Procedure::Characterize(
1201764f95a8SValentin Clement           proc, converter.getFoldingContext());
1202764f95a8SValentin Clement   mlir::FunctionType ty = SignatureBuilder{characteristics.value(), converter,
1203764f95a8SValentin Clement                                            /*forceImplicit=*/false}
1204764f95a8SValentin Clement                               .getFunctionType();
120558ceae95SRiver Riddle   mlir::func::FuncOp newFunc =
1206764f95a8SValentin Clement       fir::FirOpBuilder::createFunction(loc, module, name, ty);
1207764f95a8SValentin Clement   addSymbolAttribute(newFunc, *symbol, converter.getMLIRContext());
1208764f95a8SValentin Clement   return newFunc;
1209764f95a8SValentin Clement }
1210764f95a8SValentin Clement 
1211764f95a8SValentin Clement // Is it required to pass a dummy procedure with \p characteristics as a tuple
1212764f95a8SValentin Clement // containing the function address and the result length ?
mustPassLengthWithDummyProcedure(const std::optional<Fortran::evaluate::characteristics::Procedure> & characteristics)1213764f95a8SValentin Clement static bool mustPassLengthWithDummyProcedure(
1214764f95a8SValentin Clement     const std::optional<Fortran::evaluate::characteristics::Procedure>
1215764f95a8SValentin Clement         &characteristics) {
1216764f95a8SValentin Clement   return characteristics &&
1217764f95a8SValentin Clement          Fortran::lower::CallInterfaceImpl<SignatureBuilder>::
1218764f95a8SValentin Clement              mustPassLengthWithDummyProcedure(*characteristics);
1219764f95a8SValentin Clement }
1220764f95a8SValentin Clement 
mustPassLengthWithDummyProcedure(const Fortran::evaluate::ProcedureDesignator & procedure,Fortran::lower::AbstractConverter & converter)1221764f95a8SValentin Clement bool Fortran::lower::mustPassLengthWithDummyProcedure(
1222764f95a8SValentin Clement     const Fortran::evaluate::ProcedureDesignator &procedure,
1223764f95a8SValentin Clement     Fortran::lower::AbstractConverter &converter) {
1224764f95a8SValentin Clement   std::optional<Fortran::evaluate::characteristics::Procedure> characteristics =
1225764f95a8SValentin Clement       Fortran::evaluate::characteristics::Procedure::Characterize(
1226764f95a8SValentin Clement           procedure, converter.getFoldingContext());
1227764f95a8SValentin Clement   return ::mustPassLengthWithDummyProcedure(characteristics);
1228764f95a8SValentin Clement }
1229764f95a8SValentin Clement 
getDummyProcedureType(const Fortran::semantics::Symbol & dummyProc,Fortran::lower::AbstractConverter & converter)1230764f95a8SValentin Clement mlir::Type Fortran::lower::getDummyProcedureType(
1231764f95a8SValentin Clement     const Fortran::semantics::Symbol &dummyProc,
1232764f95a8SValentin Clement     Fortran::lower::AbstractConverter &converter) {
1233764f95a8SValentin Clement   std::optional<Fortran::evaluate::characteristics::Procedure> iface =
1234764f95a8SValentin Clement       Fortran::evaluate::characteristics::Procedure::Characterize(
1235764f95a8SValentin Clement           dummyProc, converter.getFoldingContext());
1236764f95a8SValentin Clement   mlir::Type procType = getProcedureDesignatorType(
1237764f95a8SValentin Clement       iface.has_value() ? &*iface : nullptr, converter);
1238764f95a8SValentin Clement   if (::mustPassLengthWithDummyProcedure(iface))
1239764f95a8SValentin Clement     return fir::factory::getCharacterProcedureTupleType(procType);
1240764f95a8SValentin Clement   return procType;
1241764f95a8SValentin Clement }
1242