@@ -71,6 +71,28 @@ static bool CheckDimArg(const std::optional<ActualArgument> &dimArg,
7171 return true ;
7272}
7373
74+ static bool CheckCoDimArg (const std::optional<ActualArgument> &dimArg,
75+ const Symbol &symbol, parser::ContextualMessages &messages,
76+ std::optional<int > &dimVal) {
77+ dimVal.reset ();
78+ if (int corank{symbol.Corank ()}; corank > 0 ) {
79+ if (auto dim64{ToInt64 (dimArg)}) {
80+ if (*dim64 < 1 ) {
81+ messages.Say (" DIM=%jd dimension must be positive" _err_en_US, *dim64);
82+ return false ;
83+ } else if (*dim64 > corank) {
84+ messages.Say (
85+ " DIM=%jd dimension is out of range for corank-%d coarray" _err_en_US,
86+ *dim64, corank);
87+ return false ;
88+ } else {
89+ dimVal = static_cast <int >(*dim64 - 1 ); // 1-based to 0-based
90+ }
91+ }
92+ }
93+ return true ;
94+ }
95+
7496// Class to retrieve the constant bound of an expression which is an
7597// array that devolves to a type of Constant<T>
7698class GetConstantArrayBoundHelper {
@@ -264,6 +286,37 @@ Expr<Type<TypeCategory::Integer, KIND>> UBOUND(FoldingContext &context,
264286 return Expr<T>{std::move (funcRef)};
265287}
266288
289+ // LCOBOUND() & UCOBOUND()
290+ template <int KIND>
291+ Expr<Type<TypeCategory::Integer, KIND>> COBOUND (FoldingContext &context,
292+ FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef, bool isUCOBOUND) {
293+ using T = Type<TypeCategory::Integer, KIND>;
294+ ActualArguments &args{funcRef.arguments ()};
295+ if (const Symbol * coarray{UnwrapWholeSymbolOrComponentDataRef (args[0 ])}) {
296+ std::optional<int > dim;
297+ if (funcRef.Rank () == 0 ) {
298+ // Optional DIM= argument is present: result is scalar.
299+ if (!CheckCoDimArg (args[1 ], *coarray, context.messages (), dim)) {
300+ return MakeInvalidIntrinsic<T>(std::move (funcRef));
301+ } else if (!dim) {
302+ // DIM= is present but not constant, or error
303+ return Expr<T>{std::move (funcRef)};
304+ }
305+ }
306+ if (dim) {
307+ if (auto cb{isUCOBOUND ? GetUCOBOUND (*coarray, *dim)
308+ : GetLCOBOUND (*coarray, *dim)}) {
309+ return Fold (context, ConvertToType<T>(std::move (*cb)));
310+ }
311+ } else if (auto cbs{
312+ AsExtentArrayExpr (isUCOBOUND ? GetUCOBOUNDs (*coarray)
313+ : GetLCOBOUNDs (*coarray))}) {
314+ return Fold (context, ConvertToType<T>(Expr<ExtentType>{std::move (*cbs)}));
315+ }
316+ }
317+ return Expr<T>{std::move (funcRef)};
318+ }
319+
267320// COUNT()
268321template <typename T, int MASK_KIND> class CountAccumulator {
269322 using MaskT = Type<TypeCategory::Logical, MASK_KIND>;
@@ -1105,6 +1158,8 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
11051158 }
11061159 } else if (name == " lbound" ) {
11071160 return LBOUND (context, std::move (funcRef));
1161+ } else if (name == " lcobound" ) {
1162+ return COBOUND (context, std::move (funcRef), /* isUCOBOUND=*/ false );
11081163 } else if (name == " leadz" || name == " trailz" || name == " poppar" ||
11091164 name == " popcnt" ) {
11101165 if (auto *sn{UnwrapExpr<Expr<SomeKind<T::category>>>(args[0 ])}) {
@@ -1396,6 +1451,8 @@ Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
13961451 }
13971452 } else if (name == " ubound" ) {
13981453 return UBOUND (context, std::move (funcRef));
1454+ } else if (name == " ucobound" ) {
1455+ return COBOUND (context, std::move (funcRef), /* isUCOBOUND=*/ true );
13991456 } else if (name == " __builtin_numeric_storage_size" ) {
14001457 if (!context.moduleFileName ()) {
14011458 // Don't fold this reference until it appears in the module file
0 commit comments