164ab3302SCarolineConcatto //===-- lib/Evaluate/fold-integer.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" 1064ab3302SCarolineConcatto 1164ab3302SCarolineConcatto namespace Fortran::evaluate { 1264ab3302SCarolineConcatto 1364ab3302SCarolineConcatto template<int KIND> 1464ab3302SCarolineConcatto Expr<Type<TypeCategory::Integer, KIND>> LBOUND(FoldingContext &context, 1564ab3302SCarolineConcatto FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) { 1664ab3302SCarolineConcatto using T = Type<TypeCategory::Integer, KIND>; 1764ab3302SCarolineConcatto ActualArguments &args{funcRef.arguments()}; 1864ab3302SCarolineConcatto if (const auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) { 1964ab3302SCarolineConcatto if (int rank{array->Rank()}; rank > 0) { 2064ab3302SCarolineConcatto std::optional<int> dim; 2164ab3302SCarolineConcatto if (funcRef.Rank() == 0) { 2264ab3302SCarolineConcatto // Optional DIM= argument is present: result is scalar. 2364ab3302SCarolineConcatto if (auto dim64{GetInt64Arg(args[1])}) { 2464ab3302SCarolineConcatto if (*dim64 < 1 || *dim64 > rank) { 2564ab3302SCarolineConcatto context.messages().Say("DIM=%jd dimension is out of range for " 2664ab3302SCarolineConcatto "rank-%d array"_en_US, 2764ab3302SCarolineConcatto static_cast<std::intmax_t>(*dim64), rank); 2864ab3302SCarolineConcatto return MakeInvalidIntrinsic<T>(std::move(funcRef)); 2964ab3302SCarolineConcatto } else { 3064ab3302SCarolineConcatto dim = *dim64 - 1; // 1-based to 0-based 3164ab3302SCarolineConcatto } 3264ab3302SCarolineConcatto } else { 3364ab3302SCarolineConcatto // DIM= is present but not constant 3464ab3302SCarolineConcatto return Expr<T>{std::move(funcRef)}; 3564ab3302SCarolineConcatto } 3664ab3302SCarolineConcatto } 3764ab3302SCarolineConcatto bool lowerBoundsAreOne{true}; 3864ab3302SCarolineConcatto if (auto named{ExtractNamedEntity(*array)}) { 3964ab3302SCarolineConcatto const Symbol &symbol{named->GetLastSymbol()}; 4064ab3302SCarolineConcatto if (symbol.Rank() == rank) { 4164ab3302SCarolineConcatto lowerBoundsAreOne = false; 4264ab3302SCarolineConcatto if (dim) { 4364ab3302SCarolineConcatto return Fold(context, 4464ab3302SCarolineConcatto ConvertToType<T>(GetLowerBound(context, *named, *dim))); 4564ab3302SCarolineConcatto } else if (auto extents{ 4664ab3302SCarolineConcatto AsExtentArrayExpr(GetLowerBounds(context, *named))}) { 4764ab3302SCarolineConcatto return Fold(context, 4864ab3302SCarolineConcatto ConvertToType<T>(Expr<ExtentType>{std::move(*extents)})); 4964ab3302SCarolineConcatto } 5064ab3302SCarolineConcatto } else { 5164ab3302SCarolineConcatto lowerBoundsAreOne = symbol.Rank() == 0; // LBOUND(array%component) 5264ab3302SCarolineConcatto } 5364ab3302SCarolineConcatto } 5464ab3302SCarolineConcatto if (lowerBoundsAreOne) { 5564ab3302SCarolineConcatto if (dim) { 5664ab3302SCarolineConcatto return Expr<T>{1}; 5764ab3302SCarolineConcatto } else { 5864ab3302SCarolineConcatto std::vector<Scalar<T>> ones(rank, Scalar<T>{1}); 5964ab3302SCarolineConcatto return Expr<T>{ 6064ab3302SCarolineConcatto Constant<T>{std::move(ones), ConstantSubscripts{rank}}}; 6164ab3302SCarolineConcatto } 6264ab3302SCarolineConcatto } 6364ab3302SCarolineConcatto } 6464ab3302SCarolineConcatto } 6564ab3302SCarolineConcatto return Expr<T>{std::move(funcRef)}; 6664ab3302SCarolineConcatto } 6764ab3302SCarolineConcatto 6864ab3302SCarolineConcatto template<int KIND> 6964ab3302SCarolineConcatto Expr<Type<TypeCategory::Integer, KIND>> UBOUND(FoldingContext &context, 7064ab3302SCarolineConcatto FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) { 7164ab3302SCarolineConcatto using T = Type<TypeCategory::Integer, KIND>; 7264ab3302SCarolineConcatto ActualArguments &args{funcRef.arguments()}; 7364ab3302SCarolineConcatto if (auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) { 7464ab3302SCarolineConcatto if (int rank{array->Rank()}; rank > 0) { 7564ab3302SCarolineConcatto std::optional<int> dim; 7664ab3302SCarolineConcatto if (funcRef.Rank() == 0) { 7764ab3302SCarolineConcatto // Optional DIM= argument is present: result is scalar. 7864ab3302SCarolineConcatto if (auto dim64{GetInt64Arg(args[1])}) { 7964ab3302SCarolineConcatto if (*dim64 < 1 || *dim64 > rank) { 8064ab3302SCarolineConcatto context.messages().Say("DIM=%jd dimension is out of range for " 8164ab3302SCarolineConcatto "rank-%d array"_en_US, 8264ab3302SCarolineConcatto static_cast<std::intmax_t>(*dim64), rank); 8364ab3302SCarolineConcatto return MakeInvalidIntrinsic<T>(std::move(funcRef)); 8464ab3302SCarolineConcatto } else { 8564ab3302SCarolineConcatto dim = *dim64 - 1; // 1-based to 0-based 8664ab3302SCarolineConcatto } 8764ab3302SCarolineConcatto } else { 8864ab3302SCarolineConcatto // DIM= is present but not constant 8964ab3302SCarolineConcatto return Expr<T>{std::move(funcRef)}; 9064ab3302SCarolineConcatto } 9164ab3302SCarolineConcatto } 9264ab3302SCarolineConcatto bool takeBoundsFromShape{true}; 9364ab3302SCarolineConcatto if (auto named{ExtractNamedEntity(*array)}) { 9464ab3302SCarolineConcatto const Symbol &symbol{named->GetLastSymbol()}; 9564ab3302SCarolineConcatto if (symbol.Rank() == rank) { 9664ab3302SCarolineConcatto takeBoundsFromShape = false; 9764ab3302SCarolineConcatto if (dim) { 9864ab3302SCarolineConcatto if (semantics::IsAssumedSizeArray(symbol) && *dim == rank) { 9964ab3302SCarolineConcatto return Expr<T>{-1}; 10064ab3302SCarolineConcatto } else if (auto ub{GetUpperBound(context, *named, *dim)}) { 10164ab3302SCarolineConcatto return Fold(context, ConvertToType<T>(std::move(*ub))); 10264ab3302SCarolineConcatto } 10364ab3302SCarolineConcatto } else { 10464ab3302SCarolineConcatto Shape ubounds{GetUpperBounds(context, *named)}; 10564ab3302SCarolineConcatto if (semantics::IsAssumedSizeArray(symbol)) { 10664ab3302SCarolineConcatto CHECK(!ubounds.back()); 10764ab3302SCarolineConcatto ubounds.back() = ExtentExpr{-1}; 10864ab3302SCarolineConcatto } 10964ab3302SCarolineConcatto if (auto extents{AsExtentArrayExpr(ubounds)}) { 11064ab3302SCarolineConcatto return Fold(context, 11164ab3302SCarolineConcatto ConvertToType<T>(Expr<ExtentType>{std::move(*extents)})); 11264ab3302SCarolineConcatto } 11364ab3302SCarolineConcatto } 11464ab3302SCarolineConcatto } else { 11564ab3302SCarolineConcatto takeBoundsFromShape = symbol.Rank() == 0; // UBOUND(array%component) 11664ab3302SCarolineConcatto } 11764ab3302SCarolineConcatto } 11864ab3302SCarolineConcatto if (takeBoundsFromShape) { 11964ab3302SCarolineConcatto if (auto shape{GetShape(context, *array)}) { 12064ab3302SCarolineConcatto if (dim) { 12164ab3302SCarolineConcatto if (auto &dimSize{shape->at(*dim)}) { 12264ab3302SCarolineConcatto return Fold(context, 12364ab3302SCarolineConcatto ConvertToType<T>(Expr<ExtentType>{std::move(*dimSize)})); 12464ab3302SCarolineConcatto } 12564ab3302SCarolineConcatto } else if (auto shapeExpr{AsExtentArrayExpr(*shape)}) { 12664ab3302SCarolineConcatto return Fold(context, ConvertToType<T>(std::move(*shapeExpr))); 12764ab3302SCarolineConcatto } 12864ab3302SCarolineConcatto } 12964ab3302SCarolineConcatto } 13064ab3302SCarolineConcatto } 13164ab3302SCarolineConcatto } 13264ab3302SCarolineConcatto return Expr<T>{std::move(funcRef)}; 13364ab3302SCarolineConcatto } 13464ab3302SCarolineConcatto 13564ab3302SCarolineConcatto template<int KIND> 13664ab3302SCarolineConcatto Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction( 13764ab3302SCarolineConcatto FoldingContext &context, 13864ab3302SCarolineConcatto FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) { 13964ab3302SCarolineConcatto using T = Type<TypeCategory::Integer, KIND>; 14064ab3302SCarolineConcatto using Int4 = Type<TypeCategory::Integer, 4>; 14164ab3302SCarolineConcatto ActualArguments &args{funcRef.arguments()}; 14264ab3302SCarolineConcatto auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)}; 14364ab3302SCarolineConcatto CHECK(intrinsic); 14464ab3302SCarolineConcatto std::string name{intrinsic->name}; 14564ab3302SCarolineConcatto if (name == "abs") { 14664ab3302SCarolineConcatto return FoldElementalIntrinsic<T, T>(context, std::move(funcRef), 14764ab3302SCarolineConcatto ScalarFunc<T, T>([&context](const Scalar<T> &i) -> Scalar<T> { 14864ab3302SCarolineConcatto typename Scalar<T>::ValueWithOverflow j{i.ABS()}; 14964ab3302SCarolineConcatto if (j.overflow) { 15064ab3302SCarolineConcatto context.messages().Say( 15164ab3302SCarolineConcatto "abs(integer(kind=%d)) folding overflowed"_en_US, KIND); 15264ab3302SCarolineConcatto } 15364ab3302SCarolineConcatto return j.value; 15464ab3302SCarolineConcatto })); 15564ab3302SCarolineConcatto } else if (name == "bit_size") { 15664ab3302SCarolineConcatto return Expr<T>{Scalar<T>::bits}; 15764ab3302SCarolineConcatto } else if (name == "ceiling" || name == "floor" || name == "nint") { 15864ab3302SCarolineConcatto if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 15964ab3302SCarolineConcatto // NINT rounds ties away from zero, not to even 16064ab3302SCarolineConcatto common::RoundingMode mode{name == "ceiling" 16164ab3302SCarolineConcatto ? common::RoundingMode::Up 16264ab3302SCarolineConcatto : name == "floor" ? common::RoundingMode::Down 16364ab3302SCarolineConcatto : common::RoundingMode::TiesAwayFromZero}; 16464ab3302SCarolineConcatto return std::visit( 16564ab3302SCarolineConcatto [&](const auto &kx) { 16664ab3302SCarolineConcatto using TR = ResultType<decltype(kx)>; 16764ab3302SCarolineConcatto return FoldElementalIntrinsic<T, TR>(context, std::move(funcRef), 16864ab3302SCarolineConcatto ScalarFunc<T, TR>([&](const Scalar<TR> &x) { 16964ab3302SCarolineConcatto auto y{x.template ToInteger<Scalar<T>>(mode)}; 17064ab3302SCarolineConcatto if (y.flags.test(RealFlag::Overflow)) { 17164ab3302SCarolineConcatto context.messages().Say( 17264ab3302SCarolineConcatto "%s intrinsic folding overflow"_en_US, name); 17364ab3302SCarolineConcatto } 17464ab3302SCarolineConcatto return y.value; 17564ab3302SCarolineConcatto })); 17664ab3302SCarolineConcatto }, 17764ab3302SCarolineConcatto cx->u); 17864ab3302SCarolineConcatto } 17964ab3302SCarolineConcatto } else if (name == "count") { 18064ab3302SCarolineConcatto if (!args[1]) { // TODO: COUNT(x,DIM=d) 18164ab3302SCarolineConcatto if (const auto *constant{UnwrapConstantValue<LogicalResult>(args[0])}) { 18264ab3302SCarolineConcatto std::int64_t result{0}; 18364ab3302SCarolineConcatto for (const auto &element : constant->values()) { 18464ab3302SCarolineConcatto if (element.IsTrue()) { 18564ab3302SCarolineConcatto ++result; 18664ab3302SCarolineConcatto } 18764ab3302SCarolineConcatto } 18864ab3302SCarolineConcatto return Expr<T>{result}; 18964ab3302SCarolineConcatto } 19064ab3302SCarolineConcatto } 19164ab3302SCarolineConcatto } else if (name == "digits") { 19264ab3302SCarolineConcatto if (const auto *cx{UnwrapExpr<Expr<SomeInteger>>(args[0])}) { 19364ab3302SCarolineConcatto return Expr<T>{std::visit( 19464ab3302SCarolineConcatto [](const auto &kx) { 19564ab3302SCarolineConcatto return Scalar<ResultType<decltype(kx)>>::DIGITS; 19664ab3302SCarolineConcatto }, 19764ab3302SCarolineConcatto cx->u)}; 19864ab3302SCarolineConcatto } else if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 19964ab3302SCarolineConcatto return Expr<T>{std::visit( 20064ab3302SCarolineConcatto [](const auto &kx) { 20164ab3302SCarolineConcatto return Scalar<ResultType<decltype(kx)>>::DIGITS; 20264ab3302SCarolineConcatto }, 20364ab3302SCarolineConcatto cx->u)}; 20464ab3302SCarolineConcatto } else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) { 20564ab3302SCarolineConcatto return Expr<T>{std::visit( 20664ab3302SCarolineConcatto [](const auto &kx) { 20764ab3302SCarolineConcatto return Scalar<typename ResultType<decltype(kx)>::Part>::DIGITS; 20864ab3302SCarolineConcatto }, 20964ab3302SCarolineConcatto cx->u)}; 21064ab3302SCarolineConcatto } 21164ab3302SCarolineConcatto } else if (name == "dim") { 21264ab3302SCarolineConcatto return FoldElementalIntrinsic<T, T, T>( 21364ab3302SCarolineConcatto context, std::move(funcRef), &Scalar<T>::DIM); 21464ab3302SCarolineConcatto } else if (name == "dshiftl" || name == "dshiftr") { 21564ab3302SCarolineConcatto const auto fptr{ 21664ab3302SCarolineConcatto name == "dshiftl" ? &Scalar<T>::DSHIFTL : &Scalar<T>::DSHIFTR}; 21764ab3302SCarolineConcatto // Third argument can be of any kind. However, it must be smaller or equal 21864ab3302SCarolineConcatto // than BIT_SIZE. It can be converted to Int4 to simplify. 21964ab3302SCarolineConcatto return FoldElementalIntrinsic<T, T, T, Int4>(context, std::move(funcRef), 22064ab3302SCarolineConcatto ScalarFunc<T, T, T, Int4>( 22164ab3302SCarolineConcatto [&fptr](const Scalar<T> &i, const Scalar<T> &j, 22264ab3302SCarolineConcatto const Scalar<Int4> &shift) -> Scalar<T> { 22364ab3302SCarolineConcatto return std::invoke(fptr, i, j, static_cast<int>(shift.ToInt64())); 22464ab3302SCarolineConcatto })); 22564ab3302SCarolineConcatto } else if (name == "exponent") { 22664ab3302SCarolineConcatto if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 22764ab3302SCarolineConcatto return std::visit( 22864ab3302SCarolineConcatto [&funcRef, &context](const auto &x) -> Expr<T> { 22964ab3302SCarolineConcatto using TR = typename std::decay_t<decltype(x)>::Result; 23064ab3302SCarolineConcatto return FoldElementalIntrinsic<T, TR>(context, std::move(funcRef), 23164ab3302SCarolineConcatto &Scalar<TR>::template EXPONENT<Scalar<T>>); 23264ab3302SCarolineConcatto }, 23364ab3302SCarolineConcatto sx->u); 23464ab3302SCarolineConcatto } else { 23564ab3302SCarolineConcatto DIE("exponent argument must be real"); 23664ab3302SCarolineConcatto } 23764ab3302SCarolineConcatto } else if (name == "huge") { 23864ab3302SCarolineConcatto return Expr<T>{Scalar<T>::HUGE()}; 23964ab3302SCarolineConcatto } else if (name == "iachar" || name == "ichar") { 24064ab3302SCarolineConcatto auto *someChar{UnwrapExpr<Expr<SomeCharacter>>(args[0])}; 24164ab3302SCarolineConcatto CHECK(someChar); 24264ab3302SCarolineConcatto if (auto len{ToInt64(someChar->LEN())}) { 24364ab3302SCarolineConcatto if (len.value() != 1) { 24464ab3302SCarolineConcatto // Do not die, this was not checked before 24564ab3302SCarolineConcatto context.messages().Say( 24664ab3302SCarolineConcatto "Character in intrinsic function %s must have length one"_en_US, 24764ab3302SCarolineConcatto name); 24864ab3302SCarolineConcatto } else { 24964ab3302SCarolineConcatto return std::visit( 25064ab3302SCarolineConcatto [&funcRef, &context](const auto &str) -> Expr<T> { 25164ab3302SCarolineConcatto using Char = typename std::decay_t<decltype(str)>::Result; 25264ab3302SCarolineConcatto return FoldElementalIntrinsic<T, Char>(context, 25364ab3302SCarolineConcatto std::move(funcRef), 25464ab3302SCarolineConcatto ScalarFunc<T, Char>([](const Scalar<Char> &c) { 25564ab3302SCarolineConcatto return Scalar<T>{CharacterUtils<Char::kind>::ICHAR(c)}; 25664ab3302SCarolineConcatto })); 25764ab3302SCarolineConcatto }, 25864ab3302SCarolineConcatto someChar->u); 25964ab3302SCarolineConcatto } 26064ab3302SCarolineConcatto } 26164ab3302SCarolineConcatto } else if (name == "iand" || name == "ior" || name == "ieor") { 26264ab3302SCarolineConcatto auto fptr{&Scalar<T>::IAND}; 26364ab3302SCarolineConcatto if (name == "iand") { // done in fptr declaration 26464ab3302SCarolineConcatto } else if (name == "ior") { 26564ab3302SCarolineConcatto fptr = &Scalar<T>::IOR; 26664ab3302SCarolineConcatto } else if (name == "ieor") { 26764ab3302SCarolineConcatto fptr = &Scalar<T>::IEOR; 26864ab3302SCarolineConcatto } else { 26964ab3302SCarolineConcatto common::die("missing case to fold intrinsic function %s", name.c_str()); 27064ab3302SCarolineConcatto } 27164ab3302SCarolineConcatto return FoldElementalIntrinsic<T, T, T>( 27264ab3302SCarolineConcatto context, std::move(funcRef), ScalarFunc<T, T, T>(fptr)); 27364ab3302SCarolineConcatto } else if (name == "ibclr" || name == "ibset" || name == "ishft" || 27464ab3302SCarolineConcatto name == "shifta" || name == "shiftr" || name == "shiftl") { 27564ab3302SCarolineConcatto // Second argument can be of any kind. However, it must be smaller or 27664ab3302SCarolineConcatto // equal than BIT_SIZE. It can be converted to Int4 to simplify. 27764ab3302SCarolineConcatto auto fptr{&Scalar<T>::IBCLR}; 27864ab3302SCarolineConcatto if (name == "ibclr") { // done in fprt definition 27964ab3302SCarolineConcatto } else if (name == "ibset") { 28064ab3302SCarolineConcatto fptr = &Scalar<T>::IBSET; 28164ab3302SCarolineConcatto } else if (name == "ishft") { 28264ab3302SCarolineConcatto fptr = &Scalar<T>::ISHFT; 28364ab3302SCarolineConcatto } else if (name == "shifta") { 28464ab3302SCarolineConcatto fptr = &Scalar<T>::SHIFTA; 28564ab3302SCarolineConcatto } else if (name == "shiftr") { 28664ab3302SCarolineConcatto fptr = &Scalar<T>::SHIFTR; 28764ab3302SCarolineConcatto } else if (name == "shiftl") { 28864ab3302SCarolineConcatto fptr = &Scalar<T>::SHIFTL; 28964ab3302SCarolineConcatto } else { 29064ab3302SCarolineConcatto common::die("missing case to fold intrinsic function %s", name.c_str()); 29164ab3302SCarolineConcatto } 29264ab3302SCarolineConcatto return FoldElementalIntrinsic<T, T, Int4>(context, std::move(funcRef), 29364ab3302SCarolineConcatto ScalarFunc<T, T, Int4>( 29464ab3302SCarolineConcatto [&fptr](const Scalar<T> &i, const Scalar<Int4> &pos) -> Scalar<T> { 29564ab3302SCarolineConcatto return std::invoke(fptr, i, static_cast<int>(pos.ToInt64())); 29664ab3302SCarolineConcatto })); 29764ab3302SCarolineConcatto } else if (name == "index" || name == "scan" || name == "verify") { 29864ab3302SCarolineConcatto if (auto *charExpr{UnwrapExpr<Expr<SomeCharacter>>(args[0])}) { 29964ab3302SCarolineConcatto return std::visit( 30064ab3302SCarolineConcatto [&](const auto &kch) -> Expr<T> { 30164ab3302SCarolineConcatto using TC = typename std::decay_t<decltype(kch)>::Result; 30264ab3302SCarolineConcatto if (UnwrapExpr<Expr<SomeLogical>>(args[2])) { // BACK= 30364ab3302SCarolineConcatto return FoldElementalIntrinsic<T, TC, TC, LogicalResult>(context, 30464ab3302SCarolineConcatto std::move(funcRef), 30564ab3302SCarolineConcatto ScalarFunc<T, TC, TC, LogicalResult>{ 30664ab3302SCarolineConcatto [&name](const Scalar<TC> &str, const Scalar<TC> &other, 30764ab3302SCarolineConcatto const Scalar<LogicalResult> &back) -> Scalar<T> { 30864ab3302SCarolineConcatto return name == "index" 30964ab3302SCarolineConcatto ? CharacterUtils<TC::kind>::INDEX( 31064ab3302SCarolineConcatto str, other, back.IsTrue()) 31164ab3302SCarolineConcatto : name == "scan" ? CharacterUtils<TC::kind>::SCAN( 31264ab3302SCarolineConcatto str, other, back.IsTrue()) 31364ab3302SCarolineConcatto : CharacterUtils<TC::kind>::VERIFY( 31464ab3302SCarolineConcatto str, other, back.IsTrue()); 31564ab3302SCarolineConcatto }}); 31664ab3302SCarolineConcatto } else { 31764ab3302SCarolineConcatto return FoldElementalIntrinsic<T, TC, TC>(context, 31864ab3302SCarolineConcatto std::move(funcRef), 31964ab3302SCarolineConcatto ScalarFunc<T, TC, TC>{ 32064ab3302SCarolineConcatto [&name](const Scalar<TC> &str, 32164ab3302SCarolineConcatto const Scalar<TC> &other) -> Scalar<T> { 32264ab3302SCarolineConcatto return name == "index" 32364ab3302SCarolineConcatto ? CharacterUtils<TC::kind>::INDEX(str, other) 32464ab3302SCarolineConcatto : name == "scan" 32564ab3302SCarolineConcatto ? CharacterUtils<TC::kind>::SCAN(str, other) 32664ab3302SCarolineConcatto : CharacterUtils<TC::kind>::VERIFY(str, other); 32764ab3302SCarolineConcatto }}); 32864ab3302SCarolineConcatto } 32964ab3302SCarolineConcatto }, 33064ab3302SCarolineConcatto charExpr->u); 33164ab3302SCarolineConcatto } else { 33264ab3302SCarolineConcatto DIE("first argument must be CHARACTER"); 33364ab3302SCarolineConcatto } 33464ab3302SCarolineConcatto } else if (name == "int") { 33564ab3302SCarolineConcatto if (auto *expr{UnwrapExpr<Expr<SomeType>>(args[0])}) { 33664ab3302SCarolineConcatto return std::visit( 33764ab3302SCarolineConcatto [&](auto &&x) -> Expr<T> { 33864ab3302SCarolineConcatto using From = std::decay_t<decltype(x)>; 33964ab3302SCarolineConcatto if constexpr (std::is_same_v<From, BOZLiteralConstant> || 34064ab3302SCarolineConcatto IsNumericCategoryExpr<From>()) { 34164ab3302SCarolineConcatto return Fold(context, ConvertToType<T>(std::move(x))); 34264ab3302SCarolineConcatto } 34364ab3302SCarolineConcatto DIE("int() argument type not valid"); 34464ab3302SCarolineConcatto }, 34564ab3302SCarolineConcatto std::move(expr->u)); 34664ab3302SCarolineConcatto } 34764ab3302SCarolineConcatto } else if (name == "int_ptr_kind") { 34864ab3302SCarolineConcatto return Expr<T>{8}; 34964ab3302SCarolineConcatto } else if (name == "kind") { 35064ab3302SCarolineConcatto if constexpr (common::HasMember<T, IntegerTypes>) { 35164ab3302SCarolineConcatto return Expr<T>{args[0].value().GetType()->kind()}; 35264ab3302SCarolineConcatto } else { 35364ab3302SCarolineConcatto DIE("kind() result not integral"); 35464ab3302SCarolineConcatto } 35564ab3302SCarolineConcatto } else if (name == "lbound") { 35664ab3302SCarolineConcatto return LBOUND(context, std::move(funcRef)); 35764ab3302SCarolineConcatto } else if (name == "leadz" || name == "trailz" || name == "poppar" || 35864ab3302SCarolineConcatto name == "popcnt") { 35964ab3302SCarolineConcatto if (auto *sn{UnwrapExpr<Expr<SomeInteger>>(args[0])}) { 36064ab3302SCarolineConcatto return std::visit( 36164ab3302SCarolineConcatto [&funcRef, &context, &name](const auto &n) -> Expr<T> { 36264ab3302SCarolineConcatto using TI = typename std::decay_t<decltype(n)>::Result; 36364ab3302SCarolineConcatto if (name == "poppar") { 36464ab3302SCarolineConcatto return FoldElementalIntrinsic<T, TI>(context, std::move(funcRef), 36564ab3302SCarolineConcatto ScalarFunc<T, TI>([](const Scalar<TI> &i) -> Scalar<T> { 36664ab3302SCarolineConcatto return Scalar<T>{i.POPPAR() ? 1 : 0}; 36764ab3302SCarolineConcatto })); 36864ab3302SCarolineConcatto } 36964ab3302SCarolineConcatto auto fptr{&Scalar<TI>::LEADZ}; 37064ab3302SCarolineConcatto if (name == "leadz") { // done in fptr definition 37164ab3302SCarolineConcatto } else if (name == "trailz") { 37264ab3302SCarolineConcatto fptr = &Scalar<TI>::TRAILZ; 37364ab3302SCarolineConcatto } else if (name == "popcnt") { 37464ab3302SCarolineConcatto fptr = &Scalar<TI>::POPCNT; 37564ab3302SCarolineConcatto } else { 37664ab3302SCarolineConcatto common::die( 37764ab3302SCarolineConcatto "missing case to fold intrinsic function %s", name.c_str()); 37864ab3302SCarolineConcatto } 37964ab3302SCarolineConcatto return FoldElementalIntrinsic<T, TI>(context, std::move(funcRef), 38064ab3302SCarolineConcatto ScalarFunc<T, TI>([&fptr](const Scalar<TI> &i) -> Scalar<T> { 38164ab3302SCarolineConcatto return Scalar<T>{std::invoke(fptr, i)}; 38264ab3302SCarolineConcatto })); 38364ab3302SCarolineConcatto }, 38464ab3302SCarolineConcatto sn->u); 38564ab3302SCarolineConcatto } else { 38664ab3302SCarolineConcatto DIE("leadz argument must be integer"); 38764ab3302SCarolineConcatto } 38864ab3302SCarolineConcatto } else if (name == "len") { 38964ab3302SCarolineConcatto if (auto *charExpr{UnwrapExpr<Expr<SomeCharacter>>(args[0])}) { 39064ab3302SCarolineConcatto return std::visit( 39164ab3302SCarolineConcatto [&](auto &kx) { 39264ab3302SCarolineConcatto if (auto len{kx.LEN()}) { 39364ab3302SCarolineConcatto return Fold(context, ConvertToType<T>(*std::move(len))); 39464ab3302SCarolineConcatto } else { 39564ab3302SCarolineConcatto return Expr<T>{std::move(funcRef)}; 39664ab3302SCarolineConcatto } 39764ab3302SCarolineConcatto }, 39864ab3302SCarolineConcatto charExpr->u); 39964ab3302SCarolineConcatto } else { 40064ab3302SCarolineConcatto DIE("len() argument must be of character type"); 40164ab3302SCarolineConcatto } 40264ab3302SCarolineConcatto } else if (name == "len_trim") { 40364ab3302SCarolineConcatto if (auto *charExpr{UnwrapExpr<Expr<SomeCharacter>>(args[0])}) { 40464ab3302SCarolineConcatto return std::visit( 40564ab3302SCarolineConcatto [&](const auto &kch) -> Expr<T> { 40664ab3302SCarolineConcatto using TC = typename std::decay_t<decltype(kch)>::Result; 40764ab3302SCarolineConcatto return FoldElementalIntrinsic<T, TC>(context, std::move(funcRef), 40864ab3302SCarolineConcatto ScalarFunc<T, TC>{[](const Scalar<TC> &str) -> Scalar<T> { 40964ab3302SCarolineConcatto return CharacterUtils<TC::kind>::LEN_TRIM(str); 41064ab3302SCarolineConcatto }}); 41164ab3302SCarolineConcatto }, 41264ab3302SCarolineConcatto charExpr->u); 41364ab3302SCarolineConcatto } else { 41464ab3302SCarolineConcatto DIE("len_trim() argument must be of character type"); 41564ab3302SCarolineConcatto } 41664ab3302SCarolineConcatto } else if (name == "maskl" || name == "maskr") { 41764ab3302SCarolineConcatto // Argument can be of any kind but value has to be smaller than BIT_SIZE. 41864ab3302SCarolineConcatto // It can be safely converted to Int4 to simplify. 41964ab3302SCarolineConcatto const auto fptr{name == "maskl" ? &Scalar<T>::MASKL : &Scalar<T>::MASKR}; 42064ab3302SCarolineConcatto return FoldElementalIntrinsic<T, Int4>(context, std::move(funcRef), 42164ab3302SCarolineConcatto ScalarFunc<T, Int4>([&fptr](const Scalar<Int4> &places) -> Scalar<T> { 42264ab3302SCarolineConcatto return fptr(static_cast<int>(places.ToInt64())); 42364ab3302SCarolineConcatto })); 42464ab3302SCarolineConcatto } else if (name == "max") { 42564ab3302SCarolineConcatto return FoldMINorMAX(context, std::move(funcRef), Ordering::Greater); 42664ab3302SCarolineConcatto } else if (name == "maxexponent") { 42764ab3302SCarolineConcatto if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 42864ab3302SCarolineConcatto return std::visit( 42964ab3302SCarolineConcatto [](const auto &x) { 43064ab3302SCarolineConcatto using TR = typename std::decay_t<decltype(x)>::Result; 43164ab3302SCarolineConcatto return Expr<T>{Scalar<TR>::MAXEXPONENT}; 43264ab3302SCarolineConcatto }, 43364ab3302SCarolineConcatto sx->u); 43464ab3302SCarolineConcatto } 43564ab3302SCarolineConcatto } else if (name == "merge") { 43664ab3302SCarolineConcatto return FoldMerge<T>(context, std::move(funcRef)); 43764ab3302SCarolineConcatto } else if (name == "merge_bits") { 43864ab3302SCarolineConcatto return FoldElementalIntrinsic<T, T, T, T>( 43964ab3302SCarolineConcatto context, std::move(funcRef), &Scalar<T>::MERGE_BITS); 44064ab3302SCarolineConcatto } else if (name == "minexponent") { 44164ab3302SCarolineConcatto if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 44264ab3302SCarolineConcatto return std::visit( 44364ab3302SCarolineConcatto [](const auto &x) { 44464ab3302SCarolineConcatto using TR = typename std::decay_t<decltype(x)>::Result; 44564ab3302SCarolineConcatto return Expr<T>{Scalar<TR>::MINEXPONENT}; 44664ab3302SCarolineConcatto }, 44764ab3302SCarolineConcatto sx->u); 44864ab3302SCarolineConcatto } 44964ab3302SCarolineConcatto } else if (name == "min") { 45064ab3302SCarolineConcatto return FoldMINorMAX(context, std::move(funcRef), Ordering::Less); 45164ab3302SCarolineConcatto } else if (name == "mod") { 45264ab3302SCarolineConcatto return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef), 45364ab3302SCarolineConcatto ScalarFuncWithContext<T, T, T>( 45464ab3302SCarolineConcatto [](FoldingContext &context, const Scalar<T> &x, 45564ab3302SCarolineConcatto const Scalar<T> &y) -> Scalar<T> { 45664ab3302SCarolineConcatto auto quotRem{x.DivideSigned(y)}; 45764ab3302SCarolineConcatto if (quotRem.divisionByZero) { 45864ab3302SCarolineConcatto context.messages().Say("mod() by zero"_en_US); 45964ab3302SCarolineConcatto } else if (quotRem.overflow) { 46064ab3302SCarolineConcatto context.messages().Say("mod() folding overflowed"_en_US); 46164ab3302SCarolineConcatto } 46264ab3302SCarolineConcatto return quotRem.remainder; 46364ab3302SCarolineConcatto })); 46464ab3302SCarolineConcatto } else if (name == "modulo") { 46564ab3302SCarolineConcatto return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef), 46664ab3302SCarolineConcatto ScalarFuncWithContext<T, T, T>( 46764ab3302SCarolineConcatto [](FoldingContext &context, const Scalar<T> &x, 46864ab3302SCarolineConcatto const Scalar<T> &y) -> Scalar<T> { 46964ab3302SCarolineConcatto auto result{x.MODULO(y)}; 47064ab3302SCarolineConcatto if (result.overflow) { 47164ab3302SCarolineConcatto context.messages().Say("modulo() folding overflowed"_en_US); 47264ab3302SCarolineConcatto } 47364ab3302SCarolineConcatto return result.value; 47464ab3302SCarolineConcatto })); 47564ab3302SCarolineConcatto } else if (name == "precision") { 47664ab3302SCarolineConcatto if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 47764ab3302SCarolineConcatto return Expr<T>{std::visit( 47864ab3302SCarolineConcatto [](const auto &kx) { 47964ab3302SCarolineConcatto return Scalar<ResultType<decltype(kx)>>::PRECISION; 48064ab3302SCarolineConcatto }, 48164ab3302SCarolineConcatto cx->u)}; 48264ab3302SCarolineConcatto } else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) { 48364ab3302SCarolineConcatto return Expr<T>{std::visit( 48464ab3302SCarolineConcatto [](const auto &kx) { 48564ab3302SCarolineConcatto return Scalar<typename ResultType<decltype(kx)>::Part>::PRECISION; 48664ab3302SCarolineConcatto }, 48764ab3302SCarolineConcatto cx->u)}; 48864ab3302SCarolineConcatto } 48964ab3302SCarolineConcatto } else if (name == "radix") { 49064ab3302SCarolineConcatto return Expr<T>{2}; 49164ab3302SCarolineConcatto } else if (name == "range") { 49264ab3302SCarolineConcatto if (const auto *cx{UnwrapExpr<Expr<SomeInteger>>(args[0])}) { 49364ab3302SCarolineConcatto return Expr<T>{std::visit( 49464ab3302SCarolineConcatto [](const auto &kx) { 49564ab3302SCarolineConcatto return Scalar<ResultType<decltype(kx)>>::RANGE; 49664ab3302SCarolineConcatto }, 49764ab3302SCarolineConcatto cx->u)}; 49864ab3302SCarolineConcatto } else if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 49964ab3302SCarolineConcatto return Expr<T>{std::visit( 50064ab3302SCarolineConcatto [](const auto &kx) { 50164ab3302SCarolineConcatto return Scalar<ResultType<decltype(kx)>>::RANGE; 50264ab3302SCarolineConcatto }, 50364ab3302SCarolineConcatto cx->u)}; 50464ab3302SCarolineConcatto } else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) { 50564ab3302SCarolineConcatto return Expr<T>{std::visit( 50664ab3302SCarolineConcatto [](const auto &kx) { 50764ab3302SCarolineConcatto return Scalar<typename ResultType<decltype(kx)>::Part>::RANGE; 50864ab3302SCarolineConcatto }, 50964ab3302SCarolineConcatto cx->u)}; 51064ab3302SCarolineConcatto } 51164ab3302SCarolineConcatto } else if (name == "rank") { 512*e0ca7b44SSteve Scalpone if (const auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) { 513*e0ca7b44SSteve Scalpone if (auto named{ExtractNamedEntity(*array)}) { 514*e0ca7b44SSteve Scalpone const Symbol &symbol{named->GetLastSymbol()}; 515*e0ca7b44SSteve Scalpone if (semantics::IsAssumedRankArray(symbol)) { 516*e0ca7b44SSteve Scalpone // DescriptorInquiry can only be placed in expression of kind 517*e0ca7b44SSteve Scalpone // DescriptorInquiry::Result::kind. 518*e0ca7b44SSteve Scalpone return ConvertToType<T>(Expr< 519*e0ca7b44SSteve Scalpone Type<TypeCategory::Integer, DescriptorInquiry::Result::kind>>{ 520*e0ca7b44SSteve Scalpone DescriptorInquiry{*named, DescriptorInquiry::Field::Rank}}); 521*e0ca7b44SSteve Scalpone } 522*e0ca7b44SSteve Scalpone } 523*e0ca7b44SSteve Scalpone return Expr<T>{args[0].value().Rank()}; 524*e0ca7b44SSteve Scalpone } 52564ab3302SCarolineConcatto return Expr<T>{args[0].value().Rank()}; 52664ab3302SCarolineConcatto } else if (name == "selected_char_kind") { 52764ab3302SCarolineConcatto if (const auto *chCon{UnwrapExpr<Constant<TypeOf<std::string>>>(args[0])}) { 52864ab3302SCarolineConcatto if (std::optional<std::string> value{chCon->GetScalarValue()}) { 52964ab3302SCarolineConcatto int defaultKind{ 53064ab3302SCarolineConcatto context.defaults().GetDefaultKind(TypeCategory::Character)}; 53164ab3302SCarolineConcatto return Expr<T>{SelectedCharKind(*value, defaultKind)}; 53264ab3302SCarolineConcatto } 53364ab3302SCarolineConcatto } 53464ab3302SCarolineConcatto } else if (name == "selected_int_kind") { 53564ab3302SCarolineConcatto if (auto p{GetInt64Arg(args[0])}) { 53664ab3302SCarolineConcatto return Expr<T>{SelectedIntKind(*p)}; 53764ab3302SCarolineConcatto } 53864ab3302SCarolineConcatto } else if (name == "selected_real_kind") { 53964ab3302SCarolineConcatto if (auto p{GetInt64ArgOr(args[0], 0)}) { 54064ab3302SCarolineConcatto if (auto r{GetInt64ArgOr(args[1], 0)}) { 54164ab3302SCarolineConcatto if (auto radix{GetInt64ArgOr(args[2], 2)}) { 54264ab3302SCarolineConcatto return Expr<T>{SelectedRealKind(*p, *r, *radix)}; 54364ab3302SCarolineConcatto } 54464ab3302SCarolineConcatto } 54564ab3302SCarolineConcatto } 54664ab3302SCarolineConcatto } else if (name == "shape") { 54764ab3302SCarolineConcatto if (auto shape{GetShape(context, args[0])}) { 54864ab3302SCarolineConcatto if (auto shapeExpr{AsExtentArrayExpr(*shape)}) { 54964ab3302SCarolineConcatto return Fold(context, ConvertToType<T>(std::move(*shapeExpr))); 55064ab3302SCarolineConcatto } 55164ab3302SCarolineConcatto } 55264ab3302SCarolineConcatto } else if (name == "sign") { 55364ab3302SCarolineConcatto return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef), 55464ab3302SCarolineConcatto ScalarFunc<T, T, T>( 55564ab3302SCarolineConcatto [&context](const Scalar<T> &j, const Scalar<T> &k) -> Scalar<T> { 55664ab3302SCarolineConcatto typename Scalar<T>::ValueWithOverflow result{j.SIGN(k)}; 55764ab3302SCarolineConcatto if (result.overflow) { 55864ab3302SCarolineConcatto context.messages().Say( 55964ab3302SCarolineConcatto "sign(integer(kind=%d)) folding overflowed"_en_US, KIND); 56064ab3302SCarolineConcatto } 56164ab3302SCarolineConcatto return result.value; 56264ab3302SCarolineConcatto })); 56364ab3302SCarolineConcatto } else if (name == "size") { 56464ab3302SCarolineConcatto if (auto shape{GetShape(context, args[0])}) { 56564ab3302SCarolineConcatto if (auto &dimArg{args[1]}) { // DIM= is present, get one extent 56664ab3302SCarolineConcatto if (auto dim{GetInt64Arg(args[1])}) { 56764ab3302SCarolineConcatto int rank{GetRank(*shape)}; 56864ab3302SCarolineConcatto if (*dim >= 1 && *dim <= rank) { 56964ab3302SCarolineConcatto if (auto &extent{shape->at(*dim - 1)}) { 57064ab3302SCarolineConcatto return Fold(context, ConvertToType<T>(std::move(*extent))); 57164ab3302SCarolineConcatto } 57264ab3302SCarolineConcatto } else { 57364ab3302SCarolineConcatto context.messages().Say( 57464ab3302SCarolineConcatto "size(array,dim=%jd) dimension is out of range for rank-%d array"_en_US, 57564ab3302SCarolineConcatto static_cast<std::intmax_t>(*dim), static_cast<int>(rank)); 57664ab3302SCarolineConcatto } 57764ab3302SCarolineConcatto } 57864ab3302SCarolineConcatto } else if (auto extents{common::AllElementsPresent(std::move(*shape))}) { 57964ab3302SCarolineConcatto // DIM= is absent; compute PRODUCT(SHAPE()) 58064ab3302SCarolineConcatto ExtentExpr product{1}; 58164ab3302SCarolineConcatto for (auto &&extent : std::move(*extents)) { 58264ab3302SCarolineConcatto product = std::move(product) * std::move(extent); 58364ab3302SCarolineConcatto } 58464ab3302SCarolineConcatto return Expr<T>{ConvertToType<T>(Fold(context, std::move(product)))}; 58564ab3302SCarolineConcatto } 58664ab3302SCarolineConcatto } 58764ab3302SCarolineConcatto } else if (name == "ubound") { 58864ab3302SCarolineConcatto return UBOUND(context, std::move(funcRef)); 58964ab3302SCarolineConcatto } 59064ab3302SCarolineConcatto // TODO: 59164ab3302SCarolineConcatto // cshift, dot_product, eoshift, 59264ab3302SCarolineConcatto // findloc, iall, iany, iparity, ibits, image_status, ishftc, 59364ab3302SCarolineConcatto // matmul, maxloc, maxval, 59464ab3302SCarolineConcatto // minloc, minval, not, pack, product, reduce, 59564ab3302SCarolineConcatto // sign, spread, sum, transfer, transpose, unpack 59664ab3302SCarolineConcatto return Expr<T>{std::move(funcRef)}; 59764ab3302SCarolineConcatto } 59864ab3302SCarolineConcatto 59964ab3302SCarolineConcatto // Substitute a bare type parameter reference with its value if it has one now 60064ab3302SCarolineConcatto template<int KIND> 60164ab3302SCarolineConcatto Expr<Type<TypeCategory::Integer, KIND>> FoldOperation( 60264ab3302SCarolineConcatto FoldingContext &context, TypeParamInquiry<KIND> &&inquiry) { 60364ab3302SCarolineConcatto using IntKIND = Type<TypeCategory::Integer, KIND>; 60464ab3302SCarolineConcatto if (!inquiry.base()) { 60564ab3302SCarolineConcatto // A "bare" type parameter: replace with its value, if that's now known. 60664ab3302SCarolineConcatto if (const auto *pdt{context.pdtInstance()}) { 60764ab3302SCarolineConcatto if (const semantics::Scope * scope{context.pdtInstance()->scope()}) { 60864ab3302SCarolineConcatto auto iter{scope->find(inquiry.parameter().name())}; 60964ab3302SCarolineConcatto if (iter != scope->end()) { 61064ab3302SCarolineConcatto const Symbol &symbol{*iter->second}; 61164ab3302SCarolineConcatto const auto *details{symbol.detailsIf<semantics::TypeParamDetails>()}; 61264ab3302SCarolineConcatto if (details && details->init()) { 61364ab3302SCarolineConcatto Expr<SomeInteger> expr{*details->init()}; 61464ab3302SCarolineConcatto return Fold(context, 61564ab3302SCarolineConcatto Expr<IntKIND>{ 61664ab3302SCarolineConcatto Convert<IntKIND, TypeCategory::Integer>(std::move(expr))}); 61764ab3302SCarolineConcatto } 61864ab3302SCarolineConcatto } 61964ab3302SCarolineConcatto } 62064ab3302SCarolineConcatto if (const auto *value{pdt->FindParameter(inquiry.parameter().name())}) { 62164ab3302SCarolineConcatto if (value->isExplicit()) { 62264ab3302SCarolineConcatto return Fold(context, 62364ab3302SCarolineConcatto Expr<IntKIND>{Convert<IntKIND, TypeCategory::Integer>( 62464ab3302SCarolineConcatto Expr<SomeInteger>{value->GetExplicit().value()})}); 62564ab3302SCarolineConcatto } 62664ab3302SCarolineConcatto } 62764ab3302SCarolineConcatto } 62864ab3302SCarolineConcatto } 62964ab3302SCarolineConcatto return Expr<IntKIND>{std::move(inquiry)}; 63064ab3302SCarolineConcatto } 63164ab3302SCarolineConcatto 63264ab3302SCarolineConcatto std::optional<std::int64_t> ToInt64(const Expr<SomeInteger> &expr) { 63364ab3302SCarolineConcatto return std::visit( 63464ab3302SCarolineConcatto [](const auto &kindExpr) { return ToInt64(kindExpr); }, expr.u); 63564ab3302SCarolineConcatto } 63664ab3302SCarolineConcatto 63764ab3302SCarolineConcatto std::optional<std::int64_t> ToInt64(const Expr<SomeType> &expr) { 63864ab3302SCarolineConcatto if (const auto *intExpr{UnwrapExpr<Expr<SomeInteger>>(expr)}) { 63964ab3302SCarolineConcatto return ToInt64(*intExpr); 64064ab3302SCarolineConcatto } else { 64164ab3302SCarolineConcatto return std::nullopt; 64264ab3302SCarolineConcatto } 64364ab3302SCarolineConcatto } 64464ab3302SCarolineConcatto 64564ab3302SCarolineConcatto FOR_EACH_INTEGER_KIND(template class ExpressionBase, ) 64664ab3302SCarolineConcatto template class ExpressionBase<SomeInteger>; 64764ab3302SCarolineConcatto } 648