xref: /llvm-project/flang/lib/Evaluate/fold-character.cpp (revision 0f973ac783aa100cfbce1cd2c6e8a3a8f648fae7)
164ab3302SCarolineConcatto //===-- lib/Evaluate/fold-character.cpp -----------------------------------===//
264ab3302SCarolineConcatto //
364ab3302SCarolineConcatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
464ab3302SCarolineConcatto // See https://llvm.org/LICENSE.txt for license information.
564ab3302SCarolineConcatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
664ab3302SCarolineConcatto //
764ab3302SCarolineConcatto //===----------------------------------------------------------------------===//
864ab3302SCarolineConcatto 
964ab3302SCarolineConcatto #include "fold-implementation.h"
1047f18af5Speter klausler #include "fold-reduction.h"
1164ab3302SCarolineConcatto 
1264ab3302SCarolineConcatto namespace Fortran::evaluate {
1364ab3302SCarolineConcatto 
1447f18af5Speter klausler static std::optional<ConstantSubscript> GetConstantLength(
1547f18af5Speter klausler     FoldingContext &context, Expr<SomeType> &&expr) {
1647f18af5Speter klausler   expr = Fold(context, std::move(expr));
1747f18af5Speter klausler   if (auto *chExpr{UnwrapExpr<Expr<SomeCharacter>>(expr)}) {
1847f18af5Speter klausler     if (auto len{chExpr->LEN()}) {
1947f18af5Speter klausler       return ToInt64(*len);
2047f18af5Speter klausler     }
2147f18af5Speter klausler   }
2247f18af5Speter klausler   return std::nullopt;
2347f18af5Speter klausler }
2447f18af5Speter klausler 
2547f18af5Speter klausler template <typename T>
2647f18af5Speter klausler static std::optional<ConstantSubscript> GetConstantLength(
2747f18af5Speter klausler     FoldingContext &context, FunctionRef<T> &funcRef, int zeroBasedArg) {
2847f18af5Speter klausler   if (auto *expr{funcRef.UnwrapArgExpr(zeroBasedArg)}) {
2947f18af5Speter klausler     return GetConstantLength(context, std::move(*expr));
3047f18af5Speter klausler   } else {
3147f18af5Speter klausler     return std::nullopt;
3247f18af5Speter klausler   }
3347f18af5Speter klausler }
3447f18af5Speter klausler 
3547f18af5Speter klausler template <typename T>
3647f18af5Speter klausler static std::optional<Scalar<T>> Identity(
3747f18af5Speter klausler     Scalar<T> str, std::optional<ConstantSubscript> len) {
3847f18af5Speter klausler   if (len) {
3947f18af5Speter klausler     return CharacterUtils<T::kind>::REPEAT(
4047f18af5Speter klausler         str, std::max<ConstantSubscript>(*len, 0));
4147f18af5Speter klausler   } else {
4247f18af5Speter klausler     return std::nullopt;
4347f18af5Speter klausler   }
4447f18af5Speter klausler }
4547f18af5Speter klausler 
4664ab3302SCarolineConcatto template <int KIND>
4764ab3302SCarolineConcatto Expr<Type<TypeCategory::Character, KIND>> FoldIntrinsicFunction(
4864ab3302SCarolineConcatto     FoldingContext &context,
4964ab3302SCarolineConcatto     FunctionRef<Type<TypeCategory::Character, KIND>> &&funcRef) {
5064ab3302SCarolineConcatto   using T = Type<TypeCategory::Character, KIND>;
5147f18af5Speter klausler   using StringType = Scalar<T>; // std::string or larger
5247f18af5Speter klausler   using SingleCharType = typename StringType::value_type; // char &c.
5364ab3302SCarolineConcatto   auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
5464ab3302SCarolineConcatto   CHECK(intrinsic);
5564ab3302SCarolineConcatto   std::string name{intrinsic->name};
5664ab3302SCarolineConcatto   if (name == "achar" || name == "char") {
5764ab3302SCarolineConcatto     using IntT = SubscriptInteger;
5864ab3302SCarolineConcatto     return FoldElementalIntrinsic<T, IntT>(context, std::move(funcRef),
5973eb5dbdSPeter Klausler         ScalarFunc<T, IntT>([&](const Scalar<IntT> &i) {
6073eb5dbdSPeter Klausler           if (i.IsNegative() || i.BGE(Scalar<IntT>{0}.IBSET(8 * KIND))) {
61505f6da1SPeter Klausler             if (context.languageFeatures().ShouldWarn(
62505f6da1SPeter Klausler                     common::UsageWarning::FoldingValueChecks)) {
63*0f973ac7SPeter Klausler               context.messages().Say(common::UsageWarning::FoldingValueChecks,
6473eb5dbdSPeter Klausler                   "%s(I=%jd) is out of range for CHARACTER(KIND=%d)"_warn_en_US,
6573eb5dbdSPeter Klausler                   parser::ToUpperCaseLetters(name),
6673eb5dbdSPeter Klausler                   static_cast<std::intmax_t>(i.ToInt64()), KIND);
6773eb5dbdSPeter Klausler             }
68505f6da1SPeter Klausler           }
6964ab3302SCarolineConcatto           return CharacterUtils<KIND>::CHAR(i.ToUInt64());
7064ab3302SCarolineConcatto         }));
7164ab3302SCarolineConcatto   } else if (name == "adjustl") {
7264ab3302SCarolineConcatto     return FoldElementalIntrinsic<T, T>(
7364ab3302SCarolineConcatto         context, std::move(funcRef), CharacterUtils<KIND>::ADJUSTL);
7464ab3302SCarolineConcatto   } else if (name == "adjustr") {
7564ab3302SCarolineConcatto     return FoldElementalIntrinsic<T, T>(
7664ab3302SCarolineConcatto         context, std::move(funcRef), CharacterUtils<KIND>::ADJUSTR);
7764ab3302SCarolineConcatto   } else if (name == "max") {
7864ab3302SCarolineConcatto     return FoldMINorMAX(context, std::move(funcRef), Ordering::Greater);
7947f18af5Speter klausler   } else if (name == "maxval") {
8047f18af5Speter klausler     SingleCharType least{0};
8147f18af5Speter klausler     if (auto identity{Identity<T>(
8247f18af5Speter klausler             StringType{least}, GetConstantLength(context, funcRef, 0))}) {
8347f18af5Speter klausler       return FoldMaxvalMinval<T>(
8447f18af5Speter klausler           context, std::move(funcRef), RelationalOperator::GT, *identity);
8547f18af5Speter klausler     }
8664ab3302SCarolineConcatto   } else if (name == "min") {
8764ab3302SCarolineConcatto     return FoldMINorMAX(context, std::move(funcRef), Ordering::Less);
8847f18af5Speter klausler   } else if (name == "minval") {
89fc4f457fSpeter klausler     // Collating sequences correspond to positive integers (3.31)
9082e1e412SPeter Klausler     auto most{static_cast<SingleCharType>(0xffffffff >> (8 * (4 - KIND)))};
9147f18af5Speter klausler     if (auto identity{Identity<T>(
9247f18af5Speter klausler             StringType{most}, GetConstantLength(context, funcRef, 0))}) {
9347f18af5Speter klausler       return FoldMaxvalMinval<T>(
9447f18af5Speter klausler           context, std::move(funcRef), RelationalOperator::LT, *identity);
9547f18af5Speter klausler     }
9664ab3302SCarolineConcatto   } else if (name == "new_line") {
9764ab3302SCarolineConcatto     return Expr<T>{Constant<T>{CharacterUtils<KIND>::NEW_LINE()}};
9864ab3302SCarolineConcatto   } else if (name == "repeat") { // not elemental
9964ab3302SCarolineConcatto     if (auto scalars{GetScalarConstantArguments<T, SubscriptInteger>(
1006f7e715eSPeter Klausler             context, funcRef.arguments(), /*hasOptionalArgument=*/false)}) {
10123f258ceSPeter Klausler       auto str{std::get<Scalar<T>>(*scalars)};
10223f258ceSPeter Klausler       auto n{std::get<Scalar<SubscriptInteger>>(*scalars).ToInt64()};
1038151d6f8SPeter Klausler       if (n < 0) {
1048151d6f8SPeter Klausler         context.messages().Say(
1058151d6f8SPeter Klausler             "NCOPIES= argument to REPEAT() should be nonnegative, but is %jd"_err_en_US,
1068151d6f8SPeter Klausler             static_cast<std::intmax_t>(n));
1078151d6f8SPeter Klausler       } else if (static_cast<double>(n) * str.size() >
10823f258ceSPeter Klausler           (1 << 20)) { // sanity limit of 1MiB
109505f6da1SPeter Klausler         if (context.languageFeatures().ShouldWarn(
110505f6da1SPeter Klausler                 common::UsageWarning::FoldingLimit)) {
111*0f973ac7SPeter Klausler           context.messages().Say(common::UsageWarning::FoldingLimit,
11223f258ceSPeter Klausler               "Result of REPEAT() is too large to compute at compilation time (%g characters)"_port_en_US,
11323f258ceSPeter Klausler               static_cast<double>(n) * str.size());
114505f6da1SPeter Klausler         }
11523f258ceSPeter Klausler       } else {
11623f258ceSPeter Klausler         return Expr<T>{Constant<T>{CharacterUtils<KIND>::REPEAT(str, n)}};
11723f258ceSPeter Klausler       }
11864ab3302SCarolineConcatto     }
11964ab3302SCarolineConcatto   } else if (name == "trim") { // not elemental
1206f7e715eSPeter Klausler     if (auto scalar{GetScalarConstantArguments<T>(
1216f7e715eSPeter Klausler             context, funcRef.arguments(), /*hasOptionalArgument=*/false)}) {
12264ab3302SCarolineConcatto       return Expr<T>{Constant<T>{
12364ab3302SCarolineConcatto           CharacterUtils<KIND>::TRIM(std::get<Scalar<T>>(*scalar))}};
12464ab3302SCarolineConcatto     }
125541f5c4aSHussain Kadhem   } else if (name == "__builtin_compiler_options") {
126541f5c4aSHussain Kadhem     auto &o = context.targetCharacteristics().compilerOptionsString();
127541f5c4aSHussain Kadhem     return Expr<T>{Constant<T>{StringType(o.begin(), o.end())}};
128541f5c4aSHussain Kadhem   } else if (name == "__builtin_compiler_version") {
129541f5c4aSHussain Kadhem     auto &v = context.targetCharacteristics().compilerVersionString();
130541f5c4aSHussain Kadhem     return Expr<T>{Constant<T>{StringType(v.begin(), v.end())}};
13164ab3302SCarolineConcatto   }
13264ab3302SCarolineConcatto   return Expr<T>{std::move(funcRef)};
13364ab3302SCarolineConcatto }
13464ab3302SCarolineConcatto 
13564ab3302SCarolineConcatto template <int KIND>
13664ab3302SCarolineConcatto Expr<Type<TypeCategory::Character, KIND>> FoldOperation(
13764ab3302SCarolineConcatto     FoldingContext &context, Concat<KIND> &&x) {
13864ab3302SCarolineConcatto   if (auto array{ApplyElementwise(context, x)}) {
13964ab3302SCarolineConcatto     return *array;
14064ab3302SCarolineConcatto   }
14164ab3302SCarolineConcatto   using Result = Type<TypeCategory::Character, KIND>;
14264ab3302SCarolineConcatto   if (auto folded{OperandsAreConstants(x)}) {
14364ab3302SCarolineConcatto     return Expr<Result>{Constant<Result>{folded->first + folded->second}};
14464ab3302SCarolineConcatto   }
14564ab3302SCarolineConcatto   return Expr<Result>{std::move(x)};
14664ab3302SCarolineConcatto }
14764ab3302SCarolineConcatto 
14864ab3302SCarolineConcatto template <int KIND>
14964ab3302SCarolineConcatto Expr<Type<TypeCategory::Character, KIND>> FoldOperation(
15064ab3302SCarolineConcatto     FoldingContext &context, SetLength<KIND> &&x) {
15164ab3302SCarolineConcatto   if (auto array{ApplyElementwise(context, x)}) {
15264ab3302SCarolineConcatto     return *array;
15364ab3302SCarolineConcatto   }
15464ab3302SCarolineConcatto   using Result = Type<TypeCategory::Character, KIND>;
15564ab3302SCarolineConcatto   if (auto folded{OperandsAreConstants(x)}) {
15664ab3302SCarolineConcatto     auto oldLength{static_cast<ConstantSubscript>(folded->first.size())};
15764ab3302SCarolineConcatto     auto newLength{folded->second.ToInt64()};
15864ab3302SCarolineConcatto     if (newLength < oldLength) {
15964ab3302SCarolineConcatto       folded->first.erase(newLength);
16064ab3302SCarolineConcatto     } else {
16164ab3302SCarolineConcatto       folded->first.append(newLength - oldLength, ' ');
16264ab3302SCarolineConcatto     }
16364ab3302SCarolineConcatto     CHECK(static_cast<ConstantSubscript>(folded->first.size()) == newLength);
16464ab3302SCarolineConcatto     return Expr<Result>{Constant<Result>{std::move(folded->first)}};
16564ab3302SCarolineConcatto   }
16664ab3302SCarolineConcatto   return Expr<Result>{std::move(x)};
16764ab3302SCarolineConcatto }
16864ab3302SCarolineConcatto 
1695c5bde1bSPeter Klausler #ifdef _MSC_VER // disable bogus warning about missing definitions
1705c5bde1bSPeter Klausler #pragma warning(disable : 4661)
1715c5bde1bSPeter Klausler #endif
17264ab3302SCarolineConcatto FOR_EACH_CHARACTER_KIND(template class ExpressionBase, )
17364ab3302SCarolineConcatto template class ExpressionBase<SomeCharacter>;
1741f879005STim Keith } // namespace Fortran::evaluate
175