diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md index 420b7517922b7..3fef0ab742287 100644 --- a/flang/docs/Extensions.md +++ b/flang/docs/Extensions.md @@ -287,6 +287,9 @@ end * Specific intrinsics AMAX0, AMAX1, AMIN0, AMIN1, DMAX1, DMIN1, MAX0, MAX1, MIN0, and MIN1 accept more argument types than specified. They are replaced by the related generics followed by conversions to the specified result types. + For MAX0 and MIN0, the result type matches the argument types rather than + being converted to default INTEGER, preserving the kind of the arguments. + For example, `MAX0(1_8, 2_8)` returns `INTEGER(8)`, not `INTEGER(4)`. * When a scalar CHARACTER actual argument of the same kind is known to have a length shorter than the associated dummy argument, it is extended on the right with blanks, similar to assignment. diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp index fe679da4ff98b..46375ed0e08f6 100644 --- a/flang/lib/Evaluate/intrinsics.cpp +++ b/flang/lib/Evaluate/intrinsics.cpp @@ -3654,6 +3654,12 @@ std::optional IntrinsicProcTable::Implementation::Probe( .GetTypeAndShape()) .type()}; DynamicType newType{GetReturnType(*specIter->second, defaults_)}; + // For MAX0/MIN0, preserve the generic type rather than forcing + // to default integer. This allows, for example, MAX0(1_8,2_8) to + // return INTEGER(8) instead of INTEGER(4). + if (genericType.category() == newType.category()) { + newType = genericType; + } if (genericType.category() == newType.category() || ((genericType.category() == TypeCategory::Integer || genericType.category() == TypeCategory::Real) && diff --git a/flang/test/Evaluate/folding04.f90 b/flang/test/Evaluate/folding04.f90 index 027db20f608b2..390197eb9cfdd 100644 --- a/flang/test/Evaluate/folding04.f90 +++ b/flang/test/Evaluate/folding04.f90 @@ -79,15 +79,16 @@ module parentheses module specific_extremums ! f18 accepts all type kinds for the arguments of specific extremum intrinsics ! instead of of only default kind (or double precision for DMAX1 and DMIN1). - ! This extensions is implemented by using the related generic intrinsic and - ! converting the result. + ! This extension is implemented by using the related generic intrinsic. + ! For MAX0/MIN0, the result type matches the argument types (preserving kind). + ! For AMAX0/AMIN0/MAX1/MIN1, the result is converted to the specified type. ! The tests below are cases where an implementation that converts the arguments to the ! standard required types instead would give different results than the implementation - ! specified for f18 (converting the result). + ! specified for f18. integer(8), parameter :: max_i32_8 = 2_8**31-1 - integer, parameter :: expected_min0 = int(min(max_i32_8, 2_8*max_i32_8), 4) - !WARN: portability: Argument types do not match specific intrinsic 'min0' requirements; using 'min' generic instead and converting the result to INTEGER(4) if needed [-Wuse-generic-intrinsic-when-specific-doesnt-match] - integer, parameter :: result_min0 = min0(max_i32_8, 2_8*max_i32_8) + integer(8), parameter :: expected_min0 = min(max_i32_8, 2_8*max_i32_8) + !WARN: portability: Argument types do not match specific intrinsic 'min0' requirements; using 'min' generic instead and converting the result to INTEGER(8) if needed [-Wuse-generic-intrinsic-when-specific-doesnt-match] + integer(8), parameter :: result_min0 = min0(max_i32_8, 2_8*max_i32_8) ! result_min0 would be -2 if arguments were converted to default integer. logical, parameter :: test_min0 = expected_min0 .EQ. result_min0