1 //===-- lib/Evaluate/fold-character.cpp -----------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "fold-implementation.h"
10 #include "fold-reduction.h"
12 namespace Fortran::evaluate
{
14 static std::optional
<ConstantSubscript
> GetConstantLength(
15 FoldingContext
&context
, Expr
<SomeType
> &&expr
) {
16 expr
= Fold(context
, std::move(expr
));
17 if (auto *chExpr
{UnwrapExpr
<Expr
<SomeCharacter
>>(expr
)}) {
18 if (auto len
{chExpr
->LEN()}) {
26 static std::optional
<ConstantSubscript
> GetConstantLength(
27 FoldingContext
&context
, FunctionRef
<T
> &funcRef
, int zeroBasedArg
) {
28 if (auto *expr
{funcRef
.UnwrapArgExpr(zeroBasedArg
)}) {
29 return GetConstantLength(context
, std::move(*expr
));
36 static std::optional
<Scalar
<T
>> Identity(
37 Scalar
<T
> str
, std::optional
<ConstantSubscript
> len
) {
39 return CharacterUtils
<T::kind
>::REPEAT(
40 str
, std::max
<ConstantSubscript
>(*len
, 0));
47 Expr
<Type
<TypeCategory::Character
, KIND
>> FoldIntrinsicFunction(
48 FoldingContext
&context
,
49 FunctionRef
<Type
<TypeCategory::Character
, KIND
>> &&funcRef
) {
50 using T
= Type
<TypeCategory::Character
, KIND
>;
51 using StringType
= Scalar
<T
>; // std::string or larger
52 using SingleCharType
= typename
StringType::value_type
; // char &c.
53 auto *intrinsic
{std::get_if
<SpecificIntrinsic
>(&funcRef
.proc().u
)};
55 std::string name
{intrinsic
->name
};
56 if (name
== "achar" || name
== "char") {
57 using IntT
= SubscriptInteger
;
58 return FoldElementalIntrinsic
<T
, IntT
>(context
, std::move(funcRef
),
59 ScalarFunc
<T
, IntT
>([&](const Scalar
<IntT
> &i
) {
60 if (i
.IsNegative() || i
.BGE(Scalar
<IntT
>{0}.IBSET(8 * KIND
))) {
61 context
.messages().Say(
62 "%s(I=%jd) is out of range for CHARACTER(KIND=%d)"_warn_en_US
,
63 parser::ToUpperCaseLetters(name
),
64 static_cast<std::intmax_t>(i
.ToInt64()), KIND
);
66 return CharacterUtils
<KIND
>::CHAR(i
.ToUInt64());
68 } else if (name
== "adjustl") {
69 return FoldElementalIntrinsic
<T
, T
>(
70 context
, std::move(funcRef
), CharacterUtils
<KIND
>::ADJUSTL
);
71 } else if (name
== "adjustr") {
72 return FoldElementalIntrinsic
<T
, T
>(
73 context
, std::move(funcRef
), CharacterUtils
<KIND
>::ADJUSTR
);
74 } else if (name
== "max") {
75 return FoldMINorMAX(context
, std::move(funcRef
), Ordering::Greater
);
76 } else if (name
== "maxval") {
77 SingleCharType least
{0};
78 if (auto identity
{Identity
<T
>(
79 StringType
{least
}, GetConstantLength(context
, funcRef
, 0))}) {
80 return FoldMaxvalMinval
<T
>(
81 context
, std::move(funcRef
), RelationalOperator::GT
, *identity
);
83 } else if (name
== "min") {
84 return FoldMINorMAX(context
, std::move(funcRef
), Ordering::Less
);
85 } else if (name
== "minval") {
86 // Collating sequences correspond to positive integers (3.31)
87 auto most
{static_cast<SingleCharType
>(0xffffffff >> (8 * (4 - KIND
)))};
88 if (auto identity
{Identity
<T
>(
89 StringType
{most
}, GetConstantLength(context
, funcRef
, 0))}) {
90 return FoldMaxvalMinval
<T
>(
91 context
, std::move(funcRef
), RelationalOperator::LT
, *identity
);
93 } else if (name
== "new_line") {
94 return Expr
<T
>{Constant
<T
>{CharacterUtils
<KIND
>::NEW_LINE()}};
95 } else if (name
== "repeat") { // not elemental
96 if (auto scalars
{GetScalarConstantArguments
<T
, SubscriptInteger
>(
97 context
, funcRef
.arguments())}) {
98 auto str
{std::get
<Scalar
<T
>>(*scalars
)};
99 auto n
{std::get
<Scalar
<SubscriptInteger
>>(*scalars
).ToInt64()};
101 context
.messages().Say(
102 "NCOPIES= argument to REPEAT() should be nonnegative, but is %jd"_err_en_US
,
103 static_cast<std::intmax_t>(n
));
104 } else if (static_cast<double>(n
) * str
.size() >
105 (1 << 20)) { // sanity limit of 1MiB
106 context
.messages().Say(
107 "Result of REPEAT() is too large to compute at compilation time (%g characters)"_port_en_US
,
108 static_cast<double>(n
) * str
.size());
110 return Expr
<T
>{Constant
<T
>{CharacterUtils
<KIND
>::REPEAT(str
, n
)}};
113 } else if (name
== "trim") { // not elemental
115 GetScalarConstantArguments
<T
>(context
, funcRef
.arguments())}) {
116 return Expr
<T
>{Constant
<T
>{
117 CharacterUtils
<KIND
>::TRIM(std::get
<Scalar
<T
>>(*scalar
))}};
119 } else if (name
== "__builtin_compiler_options") {
120 auto &o
= context
.targetCharacteristics().compilerOptionsString();
121 return Expr
<T
>{Constant
<T
>{StringType(o
.begin(), o
.end())}};
122 } else if (name
== "__builtin_compiler_version") {
123 auto &v
= context
.targetCharacteristics().compilerVersionString();
124 return Expr
<T
>{Constant
<T
>{StringType(v
.begin(), v
.end())}};
126 return Expr
<T
>{std::move(funcRef
)};
130 Expr
<Type
<TypeCategory::Character
, KIND
>> FoldOperation(
131 FoldingContext
&context
, Concat
<KIND
> &&x
) {
132 if (auto array
{ApplyElementwise(context
, x
)}) {
135 using Result
= Type
<TypeCategory::Character
, KIND
>;
136 if (auto folded
{OperandsAreConstants(x
)}) {
137 return Expr
<Result
>{Constant
<Result
>{folded
->first
+ folded
->second
}};
139 return Expr
<Result
>{std::move(x
)};
143 Expr
<Type
<TypeCategory::Character
, KIND
>> FoldOperation(
144 FoldingContext
&context
, SetLength
<KIND
> &&x
) {
145 if (auto array
{ApplyElementwise(context
, x
)}) {
148 using Result
= Type
<TypeCategory::Character
, KIND
>;
149 if (auto folded
{OperandsAreConstants(x
)}) {
150 auto oldLength
{static_cast<ConstantSubscript
>(folded
->first
.size())};
151 auto newLength
{folded
->second
.ToInt64()};
152 if (newLength
< oldLength
) {
153 folded
->first
.erase(newLength
);
155 folded
->first
.append(newLength
- oldLength
, ' ');
157 CHECK(static_cast<ConstantSubscript
>(folded
->first
.size()) == newLength
);
158 return Expr
<Result
>{Constant
<Result
>{std::move(folded
->first
)}};
160 return Expr
<Result
>{std::move(x
)};
163 #ifdef _MSC_VER // disable bogus warning about missing definitions
164 #pragma warning(disable : 4661)
166 FOR_EACH_CHARACTER_KIND(template class ExpressionBase
, )
167 template class ExpressionBase
<SomeCharacter
>;
168 } // namespace Fortran::evaluate