1 //===-- CharacterExpr.cpp -------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "flang/Lower/CharacterExpr.h"
10 #include "flang/Lower/ConvertType.h"
11 #include "flang/Lower/DoLoopHelper.h"
12 #include "flang/Lower/IntrinsicCall.h"
13 
14 //===----------------------------------------------------------------------===//
15 // CharacterExprHelper implementation
16 //===----------------------------------------------------------------------===//
17 
18 /// Get fir.char<kind> type with the same kind as inside str.
getCharacterType(mlir::Type type)19 static fir::CharacterType getCharacterType(mlir::Type type) {
20   if (auto boxType = type.dyn_cast<fir::BoxCharType>())
21     return boxType.getEleTy();
22   if (auto refType = type.dyn_cast<fir::ReferenceType>())
23     type = refType.getEleTy();
24   if (auto seqType = type.dyn_cast<fir::SequenceType>()) {
25     assert(seqType.getShape().size() == 1 && "rank must be 1");
26     type = seqType.getEleTy();
27   }
28   if (auto charType = type.dyn_cast<fir::CharacterType>())
29     return charType;
30   llvm_unreachable("Invalid character value type");
31 }
32 
getCharacterType(const fir::CharBoxValue & box)33 static fir::CharacterType getCharacterType(const fir::CharBoxValue &box) {
34   return getCharacterType(box.getBuffer().getType());
35 }
36 
needToMaterialize(const fir::CharBoxValue & box)37 static bool needToMaterialize(const fir::CharBoxValue &box) {
38   return box.getBuffer().getType().isa<fir::SequenceType>() ||
39          box.getBuffer().getType().isa<fir::CharacterType>();
40 }
41 
42 static std::optional<fir::SequenceType::Extent>
getCompileTimeLength(const fir::CharBoxValue & box)43 getCompileTimeLength(const fir::CharBoxValue &box) {
44   // FIXME: should this just return box.getLen() ??
45   auto type = box.getBuffer().getType();
46   if (type.isa<fir::CharacterType>())
47     return 1;
48   if (auto refType = type.dyn_cast<fir::ReferenceType>())
49     type = refType.getEleTy();
50   if (auto seqType = type.dyn_cast<fir::SequenceType>()) {
51     auto shape = seqType.getShape();
52     assert(shape.size() == 1 && "only scalar character supported");
53     if (shape[0] != fir::SequenceType::getUnknownExtent())
54       return shape[0];
55   }
56   return {};
57 }
58 
materializeValue(const fir::CharBoxValue & str)59 fir::CharBoxValue Fortran::lower::CharacterExprHelper::materializeValue(
60     const fir::CharBoxValue &str) {
61   if (!needToMaterialize(str))
62     return str;
63   auto variable = builder.create<fir::AllocaOp>(loc, str.getBuffer().getType());
64   builder.create<fir::StoreOp>(loc, str.getBuffer(), variable);
65   return {variable, str.getLen()};
66 }
67 
68 fir::CharBoxValue
toDataLengthPair(mlir::Value character)69 Fortran::lower::CharacterExprHelper::toDataLengthPair(mlir::Value character) {
70   // TODO: get rid of toDataLengthPair when adding support for arrays
71   auto charBox = toExtendedValue(character).getCharBox();
72   assert(charBox && "Array unsupported in character lowering helper");
73   return *charBox;
74 }
75 
76 fir::ExtendedValue
toExtendedValue(mlir::Value character,mlir::Value len)77 Fortran::lower::CharacterExprHelper::toExtendedValue(mlir::Value character,
78                                                      mlir::Value len) {
79   auto lenType = getLengthType();
80   auto type = character.getType();
81   auto base = character;
82   mlir::Value resultLen = len;
83   llvm::SmallVector<mlir::Value, 2> extents;
84 
85   if (auto refType = type.dyn_cast<fir::ReferenceType>())
86     type = refType.getEleTy();
87 
88   if (auto arrayType = type.dyn_cast<fir::SequenceType>()) {
89     type = arrayType.getEleTy();
90     auto shape = arrayType.getShape();
91     auto cstLen = shape[0];
92     if (!resultLen && cstLen != fir::SequenceType::getUnknownExtent())
93       resultLen = builder.createIntegerConstant(loc, lenType, cstLen);
94     // FIXME: only allow `?` in last dimension ?
95     auto typeExtents =
96         llvm::ArrayRef<fir::SequenceType::Extent>{shape}.drop_front();
97     auto indexType = builder.getIndexType();
98     for (auto extent : typeExtents) {
99       if (extent == fir::SequenceType::getUnknownExtent())
100         break;
101       extents.emplace_back(
102           builder.createIntegerConstant(loc, indexType, extent));
103     }
104     // Last extent might be missing in case of assumed-size. If more extents
105     // could not be deduced from type, that's an error (a fir.box should
106     // have been used in the interface).
107     if (extents.size() + 1 < typeExtents.size())
108       mlir::emitError(loc, "cannot retrieve array extents from type");
109   } else if (type.isa<fir::CharacterType>()) {
110     if (!resultLen)
111       resultLen = builder.createIntegerConstant(loc, lenType, 1);
112   } else if (auto boxCharType = type.dyn_cast<fir::BoxCharType>()) {
113     auto refType = builder.getRefType(boxCharType.getEleTy());
114     auto unboxed =
115         builder.create<fir::UnboxCharOp>(loc, refType, lenType, character);
116     base = unboxed.getResult(0);
117     if (!resultLen)
118       resultLen = unboxed.getResult(1);
119   } else if (type.isa<fir::BoxType>()) {
120     mlir::emitError(loc, "descriptor or derived type not yet handled");
121   } else {
122     llvm_unreachable("Cannot translate mlir::Value to character ExtendedValue");
123   }
124 
125   if (!resultLen)
126     mlir::emitError(loc, "no dynamic length found for character");
127   if (!extents.empty())
128     return fir::CharArrayBoxValue{base, resultLen, extents};
129   return fir::CharBoxValue{base, resultLen};
130 }
131 
132 /// Get fir.ref<fir.char<kind>> type.
getReferenceType(const fir::CharBoxValue & box) const133 mlir::Type Fortran::lower::CharacterExprHelper::getReferenceType(
134     const fir::CharBoxValue &box) const {
135   return builder.getRefType(getCharacterType(box));
136 }
137 
138 mlir::Value
createEmbox(const fir::CharBoxValue & box)139 Fortran::lower::CharacterExprHelper::createEmbox(const fir::CharBoxValue &box) {
140   // BoxChar require a reference.
141   auto str = box;
142   if (needToMaterialize(box))
143     str = materializeValue(box);
144   auto kind = getCharacterType(str).getFKind();
145   auto boxCharType = fir::BoxCharType::get(builder.getContext(), kind);
146   auto refType = getReferenceType(str);
147   // So far, fir.emboxChar fails lowering to llvm when it is given
148   // fir.ref<fir.array<len x fir.char<kind>>> types, so convert to
149   // fir.ref<fir.char<kind>> if needed.
150   auto buff = str.getBuffer();
151   buff = builder.createConvert(loc, refType, buff);
152   // Convert in case the provided length is not of the integer type that must
153   // be used in boxchar.
154   auto lenType = getLengthType();
155   auto len = str.getLen();
156   len = builder.createConvert(loc, lenType, len);
157   return builder.create<fir::EmboxCharOp>(loc, boxCharType, buff, len);
158 }
159 
createLoadCharAt(const fir::CharBoxValue & str,mlir::Value index)160 mlir::Value Fortran::lower::CharacterExprHelper::createLoadCharAt(
161     const fir::CharBoxValue &str, mlir::Value index) {
162   // In case this is addressing a length one character scalar simply return
163   // the single character.
164   if (str.getBuffer().getType().isa<fir::CharacterType>())
165     return str.getBuffer();
166   auto addr = builder.create<fir::CoordinateOp>(loc, getReferenceType(str),
167                                                 str.getBuffer(), index);
168   return builder.create<fir::LoadOp>(loc, addr);
169 }
170 
createStoreCharAt(const fir::CharBoxValue & str,mlir::Value index,mlir::Value c)171 void Fortran::lower::CharacterExprHelper::createStoreCharAt(
172     const fir::CharBoxValue &str, mlir::Value index, mlir::Value c) {
173   assert(!needToMaterialize(str) && "not in memory");
174   auto addr = builder.create<fir::CoordinateOp>(loc, getReferenceType(str),
175                                                 str.getBuffer(), index);
176   builder.create<fir::StoreOp>(loc, c, addr);
177 }
178 
createCopy(const fir::CharBoxValue & dest,const fir::CharBoxValue & src,mlir::Value count)179 void Fortran::lower::CharacterExprHelper::createCopy(
180     const fir::CharBoxValue &dest, const fir::CharBoxValue &src,
181     mlir::Value count) {
182   Fortran::lower::DoLoopHelper{builder, loc}.createLoop(
183       count, [&](Fortran::lower::FirOpBuilder &, mlir::Value index) {
184         auto charVal = createLoadCharAt(src, index);
185         createStoreCharAt(dest, index, charVal);
186       });
187 }
188 
createPadding(const fir::CharBoxValue & str,mlir::Value lower,mlir::Value upper)189 void Fortran::lower::CharacterExprHelper::createPadding(
190     const fir::CharBoxValue &str, mlir::Value lower, mlir::Value upper) {
191   auto blank = createBlankConstant(getCharacterType(str));
192   // Always create the loop, if upper < lower, no iteration will be
193   // executed.
194   Fortran::lower::DoLoopHelper{builder, loc}.createLoop(
195       lower, upper, [&](Fortran::lower::FirOpBuilder &, mlir::Value index) {
196         createStoreCharAt(str, index, blank);
197       });
198 }
199 
200 fir::CharBoxValue
createTemp(mlir::Type type,mlir::Value len)201 Fortran::lower::CharacterExprHelper::createTemp(mlir::Type type,
202                                                 mlir::Value len) {
203   assert(type.isa<fir::CharacterType>() && "expected fir character type");
204   llvm::SmallVector<mlir::Value, 3> sizes{len};
205   auto ref = builder.allocateLocal(loc, type, llvm::StringRef{}, sizes);
206   return {ref, len};
207 }
208 
209 // Simple length one character assignment without loops.
createLengthOneAssign(const fir::CharBoxValue & lhs,const fir::CharBoxValue & rhs)210 void Fortran::lower::CharacterExprHelper::createLengthOneAssign(
211     const fir::CharBoxValue &lhs, const fir::CharBoxValue &rhs) {
212   auto addr = lhs.getBuffer();
213   auto val = rhs.getBuffer();
214   // If rhs value resides in memory, load it.
215   if (!needToMaterialize(rhs))
216     val = builder.create<fir::LoadOp>(loc, val);
217   auto valTy = val.getType();
218   // Precondition is rhs is size 1, but it may be wrapped in a fir.array.
219   if (auto seqTy = valTy.dyn_cast<fir::SequenceType>()) {
220     auto zero = builder.createIntegerConstant(loc, builder.getIndexType(), 0);
221     valTy = seqTy.getEleTy();
222     val = builder.create<fir::ExtractValueOp>(loc, valTy, val, zero);
223   }
224   auto addrTy = fir::ReferenceType::get(valTy);
225   addr = builder.createConvert(loc, addrTy, addr);
226   assert(fir::dyn_cast_ptrEleTy(addr.getType()) == val.getType());
227   builder.create<fir::StoreOp>(loc, val, addr);
228 }
229 
createAssign(const fir::CharBoxValue & lhs,const fir::CharBoxValue & rhs)230 void Fortran::lower::CharacterExprHelper::createAssign(
231     const fir::CharBoxValue &lhs, const fir::CharBoxValue &rhs) {
232   auto rhsCstLen = getCompileTimeLength(rhs);
233   auto lhsCstLen = getCompileTimeLength(lhs);
234   bool compileTimeSameLength =
235       lhsCstLen && rhsCstLen && *lhsCstLen == *rhsCstLen;
236 
237   if (compileTimeSameLength && *lhsCstLen == 1) {
238     createLengthOneAssign(lhs, rhs);
239     return;
240   }
241 
242   // Copy the minimum of the lhs and rhs lengths and pad the lhs remainder
243   // if needed.
244   mlir::Value copyCount = lhs.getLen();
245   if (!compileTimeSameLength)
246     copyCount =
247         Fortran::lower::genMin(builder, loc, {lhs.getLen(), rhs.getLen()});
248 
249   fir::CharBoxValue safeRhs = rhs;
250   if (needToMaterialize(rhs)) {
251     // TODO: revisit now that character constant handling changed.
252     // Need to materialize the constant to get its elements.
253     // (No equivalent of fir.coordinate_of for array value).
254     safeRhs = materializeValue(rhs);
255   } else {
256     // If rhs is in memory, always assumes rhs might overlap with lhs
257     // in a way that require a temp for the copy. That can be optimize later.
258     // Only create a temp of copyCount size because we do not need more from
259     // rhs.
260     auto temp = createTemp(getCharacterType(rhs), copyCount);
261     createCopy(temp, rhs, copyCount);
262     safeRhs = temp;
263   }
264 
265   // Actual copy
266   createCopy(lhs, safeRhs, copyCount);
267 
268   // Pad if needed.
269   if (!compileTimeSameLength) {
270     auto one = builder.createIntegerConstant(loc, lhs.getLen().getType(), 1);
271     auto maxPadding = builder.create<mlir::SubIOp>(loc, lhs.getLen(), one);
272     createPadding(lhs, copyCount, maxPadding);
273   }
274 }
275 
createConcatenate(const fir::CharBoxValue & lhs,const fir::CharBoxValue & rhs)276 fir::CharBoxValue Fortran::lower::CharacterExprHelper::createConcatenate(
277     const fir::CharBoxValue &lhs, const fir::CharBoxValue &rhs) {
278   mlir::Value len =
279       builder.create<mlir::AddIOp>(loc, lhs.getLen(), rhs.getLen());
280   auto temp = createTemp(getCharacterType(rhs), len);
281   createCopy(temp, lhs, lhs.getLen());
282   auto one = builder.createIntegerConstant(loc, len.getType(), 1);
283   auto upperBound = builder.create<mlir::SubIOp>(loc, len, one);
284   auto lhsLen =
285       builder.createConvert(loc, builder.getIndexType(), lhs.getLen());
286   Fortran::lower::DoLoopHelper{builder, loc}.createLoop(
287       lhs.getLen(), upperBound, one,
288       [&](Fortran::lower::FirOpBuilder &bldr, mlir::Value index) {
289         auto rhsIndex = bldr.create<mlir::SubIOp>(loc, index, lhsLen);
290         auto charVal = createLoadCharAt(rhs, rhsIndex);
291         createStoreCharAt(temp, index, charVal);
292       });
293   return temp;
294 }
295 
createSubstring(const fir::CharBoxValue & box,llvm::ArrayRef<mlir::Value> bounds)296 fir::CharBoxValue Fortran::lower::CharacterExprHelper::createSubstring(
297     const fir::CharBoxValue &box, llvm::ArrayRef<mlir::Value> bounds) {
298   // Constant need to be materialize in memory to use fir.coordinate_of.
299   auto str = box;
300   if (needToMaterialize(box))
301     str = materializeValue(box);
302 
303   auto nbounds{bounds.size()};
304   if (nbounds < 1 || nbounds > 2) {
305     mlir::emitError(loc, "Incorrect number of bounds in substring");
306     return {mlir::Value{}, mlir::Value{}};
307   }
308   mlir::SmallVector<mlir::Value, 2> castBounds;
309   // Convert bounds to length type to do safe arithmetic on it.
310   for (auto bound : bounds)
311     castBounds.push_back(builder.createConvert(loc, getLengthType(), bound));
312   auto lowerBound = castBounds[0];
313   // FIR CoordinateOp is zero based but Fortran substring are one based.
314   auto one = builder.createIntegerConstant(loc, lowerBound.getType(), 1);
315   auto offset = builder.create<mlir::SubIOp>(loc, lowerBound, one).getResult();
316   auto idxType = builder.getIndexType();
317   if (offset.getType() != idxType)
318     offset = builder.createConvert(loc, idxType, offset);
319   auto substringRef = builder.create<fir::CoordinateOp>(
320       loc, getReferenceType(str), str.getBuffer(), offset);
321 
322   // Compute the length.
323   mlir::Value substringLen{};
324   if (nbounds < 2) {
325     substringLen =
326         builder.create<mlir::SubIOp>(loc, str.getLen(), castBounds[0]);
327   } else {
328     substringLen =
329         builder.create<mlir::SubIOp>(loc, castBounds[1], castBounds[0]);
330   }
331   substringLen = builder.create<mlir::AddIOp>(loc, substringLen, one);
332 
333   // Set length to zero if bounds were reversed (Fortran 2018 9.4.1)
334   auto zero = builder.createIntegerConstant(loc, substringLen.getType(), 0);
335   auto cdt = builder.create<mlir::CmpIOp>(loc, mlir::CmpIPredicate::slt,
336                                           substringLen, zero);
337   substringLen = builder.create<mlir::SelectOp>(loc, cdt, zero, substringLen);
338 
339   return {substringRef, substringLen};
340 }
341 
createLenTrim(const fir::CharBoxValue & str)342 mlir::Value Fortran::lower::CharacterExprHelper::createLenTrim(
343     const fir::CharBoxValue &str) {
344   // Note: Runtime for LEN_TRIM should also be available at some
345   // point. For now use an inlined implementation.
346   auto indexType = builder.getIndexType();
347   auto len = builder.createConvert(loc, indexType, str.getLen());
348   auto one = builder.createIntegerConstant(loc, indexType, 1);
349   auto minusOne = builder.createIntegerConstant(loc, indexType, -1);
350   auto zero = builder.createIntegerConstant(loc, indexType, 0);
351   auto trueVal = builder.createIntegerConstant(loc, builder.getI1Type(), 1);
352   auto blank = createBlankConstantCode(getCharacterType(str));
353   mlir::Value lastChar = builder.create<mlir::SubIOp>(loc, len, one);
354 
355   auto iterWhile = builder.create<fir::IterWhileOp>(
356       loc, lastChar, zero, minusOne, trueVal, lastChar);
357   auto insPt = builder.saveInsertionPoint();
358   builder.setInsertionPointToStart(iterWhile.getBody());
359   auto index = iterWhile.getInductionVar();
360   // Look for first non-blank from the right of the character.
361   auto c = createLoadCharAt(str, index);
362   c = builder.createConvert(loc, blank.getType(), c);
363   auto isBlank =
364       builder.create<mlir::CmpIOp>(loc, mlir::CmpIPredicate::eq, blank, c);
365   llvm::SmallVector<mlir::Value, 2> results = {isBlank, index};
366   builder.create<fir::ResultOp>(loc, results);
367   builder.restoreInsertionPoint(insPt);
368   // Compute length after iteration (zero if all blanks)
369   mlir::Value newLen =
370       builder.create<mlir::AddIOp>(loc, iterWhile.getResult(1), one);
371   auto result =
372       builder.create<SelectOp>(loc, iterWhile.getResult(0), zero, newLen);
373   return builder.createConvert(loc, getLengthType(), result);
374 }
375 
createTemp(mlir::Type type,int len)376 mlir::Value Fortran::lower::CharacterExprHelper::createTemp(mlir::Type type,
377                                                             int len) {
378   assert(type.isa<fir::CharacterType>() && "expected fir character type");
379   assert(len >= 0 && "expected positive length");
380   fir::SequenceType::Shape shape{len};
381   auto seqType = fir::SequenceType::get(shape, type);
382   return builder.create<fir::AllocaOp>(loc, seqType);
383 }
384 
385 // Returns integer with code for blank. The integer has the same
386 // size as the character. Blank has ascii space code for all kinds.
createBlankConstantCode(fir::CharacterType type)387 mlir::Value Fortran::lower::CharacterExprHelper::createBlankConstantCode(
388     fir::CharacterType type) {
389   auto bits = builder.getKindMap().getCharacterBitsize(type.getFKind());
390   auto intType = builder.getIntegerType(bits);
391   return builder.createIntegerConstant(loc, intType, ' ');
392 }
393 
createBlankConstant(fir::CharacterType type)394 mlir::Value Fortran::lower::CharacterExprHelper::createBlankConstant(
395     fir::CharacterType type) {
396   return builder.createConvert(loc, type, createBlankConstantCode(type));
397 }
398 
createCopy(mlir::Value dest,mlir::Value src,mlir::Value count)399 void Fortran::lower::CharacterExprHelper::createCopy(mlir::Value dest,
400                                                      mlir::Value src,
401                                                      mlir::Value count) {
402   createCopy(toDataLengthPair(dest), toDataLengthPair(src), count);
403 }
404 
createPadding(mlir::Value str,mlir::Value lower,mlir::Value upper)405 void Fortran::lower::CharacterExprHelper::createPadding(mlir::Value str,
406                                                         mlir::Value lower,
407                                                         mlir::Value upper) {
408   createPadding(toDataLengthPair(str), lower, upper);
409 }
410 
createSubstring(mlir::Value str,llvm::ArrayRef<mlir::Value> bounds)411 mlir::Value Fortran::lower::CharacterExprHelper::createSubstring(
412     mlir::Value str, llvm::ArrayRef<mlir::Value> bounds) {
413   return createEmbox(createSubstring(toDataLengthPair(str), bounds));
414 }
415 
createAssign(mlir::Value lhs,mlir::Value rhs)416 void Fortran::lower::CharacterExprHelper::createAssign(mlir::Value lhs,
417                                                        mlir::Value rhs) {
418   createAssign(toDataLengthPair(lhs), toDataLengthPair(rhs));
419 }
420 
421 mlir::Value
createLenTrim(mlir::Value str)422 Fortran::lower::CharacterExprHelper::createLenTrim(mlir::Value str) {
423   return createLenTrim(toDataLengthPair(str));
424 }
425 
createAssign(mlir::Value lptr,mlir::Value llen,mlir::Value rptr,mlir::Value rlen)426 void Fortran::lower::CharacterExprHelper::createAssign(mlir::Value lptr,
427                                                        mlir::Value llen,
428                                                        mlir::Value rptr,
429                                                        mlir::Value rlen) {
430   createAssign(fir::CharBoxValue{lptr, llen}, fir::CharBoxValue{rptr, rlen});
431 }
432 
433 mlir::Value
createConcatenate(mlir::Value lhs,mlir::Value rhs)434 Fortran::lower::CharacterExprHelper::createConcatenate(mlir::Value lhs,
435                                                        mlir::Value rhs) {
436   return createEmbox(
437       createConcatenate(toDataLengthPair(lhs), toDataLengthPair(rhs)));
438 }
439 
440 mlir::Value
createEmboxChar(mlir::Value addr,mlir::Value len)441 Fortran::lower::CharacterExprHelper::createEmboxChar(mlir::Value addr,
442                                                      mlir::Value len) {
443   return createEmbox(fir::CharBoxValue{addr, len});
444 }
445 
446 std::pair<mlir::Value, mlir::Value>
createUnboxChar(mlir::Value boxChar)447 Fortran::lower::CharacterExprHelper::createUnboxChar(mlir::Value boxChar) {
448   auto box = toDataLengthPair(boxChar);
449   return {box.getBuffer(), box.getLen()};
450 }
451 
452 mlir::Value
createCharacterTemp(mlir::Type type,mlir::Value len)453 Fortran::lower::CharacterExprHelper::createCharacterTemp(mlir::Type type,
454                                                          mlir::Value len) {
455   return createEmbox(createTemp(type, len));
456 }
457 
458 std::pair<mlir::Value, mlir::Value>
materializeCharacter(mlir::Value str)459 Fortran::lower::CharacterExprHelper::materializeCharacter(mlir::Value str) {
460   auto box = toDataLengthPair(str);
461   if (needToMaterialize(box))
462     box = materializeValue(box);
463   return {box.getBuffer(), box.getLen()};
464 }
465 
isCharacterLiteral(mlir::Type type)466 bool Fortran::lower::CharacterExprHelper::isCharacterLiteral(mlir::Type type) {
467   if (auto seqType = type.dyn_cast<fir::SequenceType>())
468     return (seqType.getShape().size() == 1) &&
469            seqType.getEleTy().isa<fir::CharacterType>();
470   return false;
471 }
472 
isCharacter(mlir::Type type)473 bool Fortran::lower::CharacterExprHelper::isCharacter(mlir::Type type) {
474   if (type.isa<fir::BoxCharType>())
475     return true;
476   if (auto refType = type.dyn_cast<fir::ReferenceType>())
477     type = refType.getEleTy();
478   if (auto seqType = type.dyn_cast<fir::SequenceType>())
479     if (seqType.getShape().size() == 1)
480       type = seqType.getEleTy();
481   return type.isa<fir::CharacterType>();
482 }
483 
getCharacterKind(mlir::Type type)484 int Fortran::lower::CharacterExprHelper::getCharacterKind(mlir::Type type) {
485   return getCharacterType(type).getFKind();
486 }
487