[flang-commits] [flang] [flang][HLFIR] Relax verifiers of intrinsic operations (PR #80132)

Tom Eccles via flang-commits flang-commits at lists.llvm.org
Wed Jan 31 03:57:15 PST 2024


https://github.com/tblah created https://github.com/llvm/llvm-project/pull/80132

The verifiers are currently very strict: requiring intrinsic operations to be used only in cases where the Fortran standard permits the intrinsic to be used.

There have now been a lot of cases where these verifiers have caused bugs in corner cases. In a recent ticket, @jeanPerier pointed out that it could be useful for future optimizations if somewhat invalid uses of these operations could be allowed in dead code. See this comment: https://github.com/llvm/llvm-project/issues/79995#issuecomment-1918118234

In response to all of this, I have decided to relax the intrinsic operation verifiers. The intention is now to only disallow operation uses that are likely to crash the compiler. It isn't obvious which checks are more useful than others, and it would not be a good use of time to try lots of illegal (according to Fortran) intrinsic uses to figure out exactly which can crash Flang (as none of these should pass semantics anyway). I have taken an educated guess.

The disadvantage of this approach is that IR can now represent intrinsic invocations which are incorrect. The lowering and implementation of these intrinsic functions is unlikely to do the right thing in all of these cases, and as they should mostly be impossible to generate using normal Fortran code, these edge cases will see very little testing, before some new optimization causes them to become more common.

Fixes #79995

>From 5e3eea4bece9d3700e699035955d930273df2115 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Wed, 31 Jan 2024 11:02:33 +0000
Subject: [PATCH] [flang][HLFIR] Relax verifiers of intrinsic operations

The verifiers are currently very strict: requiring intrinsic operations
to be used only in cases where the Fortran standard permits the
intrinsic to be used.

There have now been a lot of cases where these verifiers have caused
bugs in corner cases. In a recent ticket, @jeanPerier pointed out that
it could be useful for future optimizations if somewhat invalid uses of
these operations could be allowed in dead code. See this comment:
https://github.com/llvm/llvm-project/issues/79995#issuecomment-1918118234

In response to all of this, I have decided to relax the intrinsic
operation verifiers. The intention is now to only disallow operation
uses that are likely to crash the compiler. It isn't obvious which
checks are more useful than others, and it would not be a good use of time
to try lots of illegal (according to Fortran) intrinsic uses to figure out
exactly which can crash Flang (as none of these should pass semantics
anyway). I have taken an educated guess.

The disadvantage of this approach is that IR can now represent intrinsic
invocations which are incorrect. The lowering and implementation of
these intrinsic functions is unlikely to do the right thing in all of
these cases, and as they should mostly be impossible to generate using
normal Fortran code, these edge cases will see very little testing,
before some new optimization causes them to become more common.

Fixes #79995
---
 flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp | 262 +-------------
 flang/test/HLFIR/invalid.fir              | 423 ----------------------
 flang/test/Lower/HLFIR/minval.f90         |  44 +++
 3 files changed, 53 insertions(+), 676 deletions(-)

diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
index 439d106d0bfed..c247b7d486c6c 100644
--- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
+++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
@@ -475,65 +475,11 @@ mlir::LogicalResult hlfir::ParentComponentOp::verify() {
   return mlir::success();
 }
 
-//===----------------------------------------------------------------------===//
-// LogicalReductionOp
-//===----------------------------------------------------------------------===//
-template <typename LogicalReductionOp>
-static mlir::LogicalResult
-verifyLogicalReductionOp(LogicalReductionOp reductionOp) {
-  mlir::Operation *op = reductionOp->getOperation();
-
-  auto results = op->getResultTypes();
-  assert(results.size() == 1);
-
-  mlir::Value mask = reductionOp->getMask();
-  mlir::Value dim = reductionOp->getDim();
-
-  fir::SequenceType maskTy =
-      hlfir::getFortranElementOrSequenceType(mask.getType())
-          .cast<fir::SequenceType>();
-  mlir::Type logicalTy = maskTy.getEleTy();
-  llvm::ArrayRef<int64_t> maskShape = maskTy.getShape();
-
-  mlir::Type resultType = results[0];
-  if (mlir::isa<fir::LogicalType>(resultType)) {
-    // Result is of the same type as MASK
-    if (resultType != logicalTy)
-      return reductionOp->emitOpError(
-          "result must have the same element type as MASK argument");
-
-  } else if (auto resultExpr =
-                 mlir::dyn_cast_or_null<hlfir::ExprType>(resultType)) {
-    // Result should only be in hlfir.expr form if it is an array
-    if (maskShape.size() > 1 && dim != nullptr) {
-      if (!resultExpr.isArray())
-        return reductionOp->emitOpError("result must be an array");
-
-      if (resultExpr.getEleTy() != logicalTy)
-        return reductionOp->emitOpError(
-            "result must have the same element type as MASK argument");
-
-      llvm::ArrayRef<int64_t> resultShape = resultExpr.getShape();
-      // Result has rank n-1
-      if (resultShape.size() != (maskShape.size() - 1))
-        return reductionOp->emitOpError(
-            "result rank must be one less than MASK");
-    } else {
-      return reductionOp->emitOpError("result must be of logical type");
-    }
-  } else {
-    return reductionOp->emitOpError("result must be of logical type");
-  }
-  return mlir::success();
-}
-
 //===----------------------------------------------------------------------===//
 // AllOp
 //===----------------------------------------------------------------------===//
 
-mlir::LogicalResult hlfir::AllOp::verify() {
-  return verifyLogicalReductionOp<hlfir::AllOp *>(this);
-}
+mlir::LogicalResult hlfir::AllOp::verify() { return mlir::success(); }
 
 void hlfir::AllOp::getEffects(
     llvm::SmallVectorImpl<
@@ -546,9 +492,7 @@ void hlfir::AllOp::getEffects(
 // AnyOp
 //===----------------------------------------------------------------------===//
 
-mlir::LogicalResult hlfir::AnyOp::verify() {
-  return verifyLogicalReductionOp<hlfir::AnyOp *>(this);
-}
+mlir::LogicalResult hlfir::AnyOp::verify() { return mlir::success(); }
 
 void hlfir::AnyOp::getEffects(
     llvm::SmallVectorImpl<
@@ -561,38 +505,7 @@ void hlfir::AnyOp::getEffects(
 // CountOp
 //===----------------------------------------------------------------------===//
 
-mlir::LogicalResult hlfir::CountOp::verify() {
-  mlir::Operation *op = getOperation();
-
-  auto results = op->getResultTypes();
-  assert(results.size() == 1);
-  mlir::Value mask = getMask();
-  mlir::Value dim = getDim();
-
-  fir::SequenceType maskTy =
-      hlfir::getFortranElementOrSequenceType(mask.getType())
-          .cast<fir::SequenceType>();
-  llvm::ArrayRef<int64_t> maskShape = maskTy.getShape();
-
-  mlir::Type resultType = results[0];
-  if (auto resultExpr = mlir::dyn_cast_or_null<hlfir::ExprType>(resultType)) {
-    if (maskShape.size() > 1 && dim != nullptr) {
-      if (!resultExpr.isArray())
-        return emitOpError("result must be an array");
-
-      llvm::ArrayRef<int64_t> resultShape = resultExpr.getShape();
-      // Result has rank n-1
-      if (resultShape.size() != (maskShape.size() - 1))
-        return emitOpError("result rank must be one less than MASK");
-    } else {
-      return emitOpError("result must be of numerical scalar type");
-    }
-  } else if (!hlfir::isFortranScalarNumericalType(resultType)) {
-    return emitOpError("result must be of numerical scalar type");
-  }
-
-  return mlir::success();
-}
+mlir::LogicalResult hlfir::CountOp::verify() { return mlir::success(); }
 
 void hlfir::CountOp::getEffects(
     llvm::SmallVectorImpl<
@@ -682,16 +595,6 @@ verifyArrayAndMaskForReductionOp(NumericalReductionOp reductionOp) {
     if (!maskShape.empty()) {
       if (maskShape.size() != arrayShape.size())
         return reductionOp->emitWarning("MASK must be conformable to ARRAY");
-      static_assert(fir::SequenceType::getUnknownExtent() ==
-                    hlfir::ExprType::getUnknownExtent());
-      constexpr int64_t unknownExtent = fir::SequenceType::getUnknownExtent();
-      for (std::size_t i = 0; i < arrayShape.size(); ++i) {
-        int64_t arrayExtent = arrayShape[i];
-        int64_t maskExtent = maskShape[i];
-        if ((arrayExtent != maskExtent) && (arrayExtent != unknownExtent) &&
-            (maskExtent != unknownExtent))
-          return reductionOp->emitWarning("MASK must be conformable to ARRAY");
-      }
     }
   }
   return mlir::success();
@@ -710,40 +613,12 @@ verifyNumericalReductionOp(NumericalReductionOp reductionOp) {
 
   mlir::Value array = reductionOp->getArray();
   mlir::Value dim = reductionOp->getDim();
-  fir::SequenceType arrayTy =
-      hlfir::getFortranElementOrSequenceType(array.getType())
-          .cast<fir::SequenceType>();
-  mlir::Type numTy = arrayTy.getEleTy();
-  llvm::ArrayRef<int64_t> arrayShape = arrayTy.getShape();
 
   mlir::Type resultType = results[0];
-  if (hlfir::isFortranScalarNumericalType(resultType)) {
-    // Result is of the same type as ARRAY
-    if (resultType != numTy)
-      return reductionOp->emitOpError(
-          "result must have the same element type as ARRAY argument");
-
-  } else if (auto resultExpr =
-                 mlir::dyn_cast_or_null<hlfir::ExprType>(resultType)) {
-    if (arrayShape.size() > 1 && dim != nullptr) {
-      if (!resultExpr.isArray())
-        return reductionOp->emitOpError("result must be an array");
-
-      if (resultExpr.getEleTy() != numTy)
-        return reductionOp->emitOpError(
-            "result must have the same element type as ARRAY argument");
-
-      llvm::ArrayRef<int64_t> resultShape = resultExpr.getShape();
-      // Result has rank n-1
-      if (resultShape.size() != (arrayShape.size() - 1))
-        return reductionOp->emitOpError(
-            "result rank must be one less than ARRAY");
-    } else {
-      return reductionOp->emitOpError(
-          "result must be of numerical scalar type");
-    }
-  } else {
-    return reductionOp->emitOpError("result must be of numerical scalar type");
+  if (!hlfir::isFortranScalarNumericalType(resultType) &&
+      !mlir::isa<hlfir::ExprType>(resultType)) {
+    return reductionOp->emitOpError(
+        "result must be of numerical scalar or array type");
   }
   return mlir::success();
 }
@@ -783,7 +658,6 @@ verifyCharacterReductionOp(CharacterReductionOp reductionOp) {
   fir::SequenceType arrayTy =
       hlfir::getFortranElementOrSequenceType(array.getType())
           .cast<fir::SequenceType>();
-  mlir::Type numTy = arrayTy.getEleTy();
   llvm::ArrayRef<int64_t> arrayShape = arrayTy.getShape();
 
   auto resultExpr = results[0].cast<hlfir::ExprType>();
@@ -791,19 +665,9 @@ verifyCharacterReductionOp(CharacterReductionOp reductionOp) {
   assert(mlir::isa<fir::CharacterType>(resultType) &&
          "result must be character");
 
-  // Result is of the same type as ARRAY
-  if (resultType != numTy)
-    return reductionOp->emitOpError(
-        "result must have the same element type as ARRAY argument");
-
   if (arrayShape.size() > 1 && dim != nullptr) {
     if (!resultExpr.isArray())
       return reductionOp->emitOpError("result must be an array");
-    llvm::ArrayRef<int64_t> resultShape = resultExpr.getShape();
-    // Result has rank n-1
-    if (resultShape.size() != (arrayShape.size() - 1))
-      return reductionOp->emitOpError(
-          "result rank must be one less than ARRAY");
   } else if (!resultExpr.isScalar()) {
     return reductionOp->emitOpError("result must be scalar character");
   }
@@ -823,9 +687,8 @@ mlir::LogicalResult hlfir::MaxvalOp::verify() {
   auto resultExpr = mlir::dyn_cast<hlfir::ExprType>(results[0]);
   if (resultExpr && mlir::isa<fir::CharacterType>(resultExpr.getEleTy())) {
     return verifyCharacterReductionOp<hlfir::MaxvalOp *>(this);
-  } else {
-    return verifyNumericalReductionOp<hlfir::MaxvalOp *>(this);
   }
+  return verifyNumericalReductionOp<hlfir::MaxvalOp *>(this);
 }
 
 void hlfir::MaxvalOp::getEffects(
@@ -848,9 +711,8 @@ mlir::LogicalResult hlfir::MinvalOp::verify() {
   auto resultExpr = mlir::dyn_cast<hlfir::ExprType>(results[0]);
   if (resultExpr && mlir::isa<fir::CharacterType>(resultExpr.getEleTy())) {
     return verifyCharacterReductionOp<hlfir::MinvalOp *>(this);
-  } else {
-    return verifyNumericalReductionOp<hlfir::MinvalOp *>(this);
   }
+  return verifyNumericalReductionOp<hlfir::MinvalOp *>(this);
 }
 
 void hlfir::MinvalOp::getEffects(
@@ -889,15 +751,6 @@ verifyResultForMinMaxLoc(NumericalReductionOp reductionOp) {
 
     if (!fir::isa_integer(resultExpr.getEleTy()))
       return reductionOp->emitOpError("result must have integer elements");
-
-    llvm::ArrayRef<int64_t> resultShape = resultExpr.getShape();
-    // With dim the result has rank n-1
-    if (dim && resultShape.size() != (arrayShape.size() - 1))
-      return reductionOp->emitOpError(
-          "result rank must be one less than ARRAY");
-    // With dim the result has rank n
-    if (!dim && resultShape.size() != 1)
-      return reductionOp->emitOpError("result rank must be 1");
   } else {
     return reductionOp->emitOpError("result must be of numerical expr type");
   }
@@ -995,8 +848,6 @@ mlir::LogicalResult hlfir::DotProductOp::verify() {
   llvm::ArrayRef<int64_t> rhsShape = rhsTy.getShape();
   std::size_t lhsRank = lhsShape.size();
   std::size_t rhsRank = rhsShape.size();
-  mlir::Type lhsEleTy = lhsTy.getEleTy();
-  mlir::Type rhsEleTy = rhsTy.getEleTy();
   mlir::Type resultTy = getResult().getType();
 
   if ((lhsRank != 1) || (rhsRank != 1))
@@ -1010,15 +861,6 @@ mlir::LogicalResult hlfir::DotProductOp::verify() {
       (lhsSize != rhsSize))
     return emitOpError("both arrays must have the same size");
 
-  if (mlir::isa<fir::LogicalType>(lhsEleTy) !=
-      mlir::isa<fir::LogicalType>(rhsEleTy))
-    return emitOpError("if one array is logical, so should the other be");
-
-  if (mlir::isa<fir::LogicalType>(lhsEleTy) !=
-      mlir::isa<fir::LogicalType>(resultTy))
-    return emitOpError("the result type should be a logical only if the "
-                       "argument types are logical");
-
   if (!hlfir::isFortranScalarNumericalType(resultTy) &&
       !mlir::isa<fir::LogicalType>(resultTy))
     return emitOpError(
@@ -1051,22 +893,10 @@ mlir::LogicalResult hlfir::MatmulOp::verify() {
   llvm::ArrayRef<int64_t> rhsShape = rhsTy.getShape();
   std::size_t lhsRank = lhsShape.size();
   std::size_t rhsRank = rhsShape.size();
-  mlir::Type lhsEleTy = lhsTy.getEleTy();
-  mlir::Type rhsEleTy = rhsTy.getEleTy();
-  hlfir::ExprType resultTy = getResult().getType().cast<hlfir::ExprType>();
-  llvm::ArrayRef<int64_t> resultShape = resultTy.getShape();
-  mlir::Type resultEleTy = resultTy.getEleTy();
 
   if (((lhsRank != 1) && (lhsRank != 2)) || ((rhsRank != 1) && (rhsRank != 2)))
     return emitOpError("array must have either rank 1 or rank 2");
 
-  if ((lhsRank == 1) && (rhsRank == 1))
-    return emitOpError("at least one array must have rank 2");
-
-  if (mlir::isa<fir::LogicalType>(lhsEleTy) !=
-      mlir::isa<fir::LogicalType>(rhsEleTy))
-    return emitOpError("if one array is logical, so should the other be");
-
   int64_t lastLhsDim = lhsShape[lhsRank - 1];
   int64_t firstRhsDim = rhsShape[0];
   constexpr int64_t unknownExtent = fir::SequenceType::getUnknownExtent();
@@ -1075,34 +905,6 @@ mlir::LogicalResult hlfir::MatmulOp::verify() {
       return emitOpError(
           "the last dimension of LHS should match the first dimension of RHS");
 
-  if (mlir::isa<fir::LogicalType>(lhsEleTy) !=
-      mlir::isa<fir::LogicalType>(resultEleTy))
-    return emitOpError("the result type should be a logical only if the "
-                       "argument types are logical");
-
-  llvm::SmallVector<int64_t, 2> expectedResultShape;
-  if (lhsRank == 2) {
-    if (rhsRank == 2) {
-      expectedResultShape.push_back(lhsShape[0]);
-      expectedResultShape.push_back(rhsShape[1]);
-    } else {
-      // rhsRank == 1
-      expectedResultShape.push_back(lhsShape[0]);
-    }
-  } else {
-    // lhsRank == 1
-    // rhsRank == 2
-    expectedResultShape.push_back(rhsShape[1]);
-  }
-  if (resultShape.size() != expectedResultShape.size())
-    return emitOpError("incorrect result shape");
-  if (resultShape[0] != expectedResultShape[0] &&
-      expectedResultShape[0] != unknownExtent)
-    return emitOpError("incorrect result shape");
-  if (resultShape.size() == 2 && resultShape[1] != expectedResultShape[1] &&
-      expectedResultShape[1] != unknownExtent)
-    return emitOpError("incorrect result shape");
-
   return mlir::success();
 }
 
@@ -1170,25 +972,13 @@ mlir::LogicalResult hlfir::TransposeOp::verify() {
           .cast<fir::SequenceType>();
   llvm::ArrayRef<int64_t> inShape = arrayTy.getShape();
   std::size_t rank = inShape.size();
-  mlir::Type eleTy = arrayTy.getEleTy();
   hlfir::ExprType resultTy = getResult().getType().cast<hlfir::ExprType>();
   llvm::ArrayRef<int64_t> resultShape = resultTy.getShape();
   std::size_t resultRank = resultShape.size();
-  mlir::Type resultEleTy = resultTy.getEleTy();
 
   if (rank != 2 || resultRank != 2)
     return emitOpError("input and output arrays should have rank 2");
 
-  constexpr int64_t unknownExtent = fir::SequenceType::getUnknownExtent();
-  if ((inShape[0] != resultShape[1]) && (inShape[0] != unknownExtent))
-    return emitOpError("output shape does not match input array");
-  if ((inShape[1] != resultShape[0]) && (inShape[1] != unknownExtent))
-    return emitOpError("output shape does not match input array");
-
-  if (eleTy != resultEleTy)
-    return emitOpError(
-        "input and output arrays should have the same element type");
-
   return mlir::success();
 }
 
@@ -1218,9 +1008,6 @@ mlir::LogicalResult hlfir::MatmulTransposeOp::verify() {
   std::size_t rhsRank = rhsShape.size();
   mlir::Type lhsEleTy = lhsTy.getEleTy();
   mlir::Type rhsEleTy = rhsTy.getEleTy();
-  hlfir::ExprType resultTy = getResult().getType().cast<hlfir::ExprType>();
-  llvm::ArrayRef<int64_t> resultShape = resultTy.getShape();
-  mlir::Type resultEleTy = resultTy.getEleTy();
 
   // lhs must have rank 2 for the transpose to be valid
   if ((lhsRank != 2) || ((rhsRank != 1) && (rhsRank != 2)))
@@ -1230,37 +1017,6 @@ mlir::LogicalResult hlfir::MatmulTransposeOp::verify() {
       mlir::isa<fir::LogicalType>(rhsEleTy))
     return emitOpError("if one array is logical, so should the other be");
 
-  // for matmul we compare the last dimension of lhs with the first dimension of
-  // rhs, but for MatmulTranspose, dimensions of lhs are inverted by the
-  // transpose
-  int64_t firstLhsDim = lhsShape[0];
-  int64_t firstRhsDim = rhsShape[0];
-  constexpr int64_t unknownExtent = fir::SequenceType::getUnknownExtent();
-  if (firstLhsDim != firstRhsDim)
-    if ((firstLhsDim != unknownExtent) && (firstRhsDim != unknownExtent))
-      return emitOpError(
-          "the first dimension of LHS should match the first dimension of RHS");
-
-  if (mlir::isa<fir::LogicalType>(lhsEleTy) !=
-      mlir::isa<fir::LogicalType>(resultEleTy))
-    return emitOpError("the result type should be a logical only if the "
-                       "argument types are logical");
-
-  llvm::SmallVector<int64_t, 2> expectedResultShape;
-  if (rhsRank == 2) {
-    expectedResultShape.push_back(lhsShape[1]);
-    expectedResultShape.push_back(rhsShape[1]);
-  } else {
-    // rhsRank == 1
-    expectedResultShape.push_back(lhsShape[1]);
-  }
-  if (resultShape.size() != expectedResultShape.size())
-    return emitOpError("incorrect result shape");
-  if (resultShape[0] != expectedResultShape[0])
-    return emitOpError("incorrect result shape");
-  if (resultShape.size() == 2 && resultShape[1] != expectedResultShape[1])
-    return emitOpError("incorrect result shape");
-
   return mlir::success();
 }
 
diff --git a/flang/test/HLFIR/invalid.fir b/flang/test/HLFIR/invalid.fir
index 56f74d8bf29e6..251f4da300e90 100644
--- a/flang/test/HLFIR/invalid.fir
+++ b/flang/test/HLFIR/invalid.fir
@@ -296,168 +296,18 @@ func.func @bad_concat_4(%arg0: !fir.ref<!fir.char<1,30>>) {
   return
 }
 
-// -----
-func.func @bad_any1(%arg0: !hlfir.expr<?x!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.any' op result must have the same element type as MASK argument}}
-  %0 = hlfir.any %arg0 : (!hlfir.expr<?x!fir.logical<4>>) -> !fir.logical<8>
-}
-
-// -----
-func.func @bad_any2(%arg0: !hlfir.expr<?x?x!fir.logical<4>>, %arg1: i32) {
-  // expected-error at +1 {{'hlfir.any' op result must have the same element type as MASK argument}}
-  %0 = hlfir.any %arg0 dim %arg1 : (!hlfir.expr<?x?x!fir.logical<4>>, i32) -> !hlfir.expr<?x!fir.logical<8>>
-}
-
-// -----
-func.func @bad_any3(%arg0: !hlfir.expr<?x?x!fir.logical<4>>, %arg1: i32){
-  // expected-error at +1 {{'hlfir.any' op result rank must be one less than MASK}}
-  %0 = hlfir.any %arg0 dim %arg1 : (!hlfir.expr<?x?x!fir.logical<4>>, i32) -> !hlfir.expr<?x?x!fir.logical<4>>
-}
-
-// -----
-func.func @bad_any4(%arg0: !hlfir.expr<?x?x!fir.logical<4>>, %arg1: i32) {
-  // expected-error at +1 {{'hlfir.any' op result must be an array}}
-  %0 = hlfir.any %arg0 dim %arg1 : (!hlfir.expr<?x?x!fir.logical<4>>, i32) -> !hlfir.expr<!fir.logical<4>>
-}
-
-// -----
-func.func @bad_any5(%arg0: !hlfir.expr<?x!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.any' op result must be of logical type}}
-  %0 = hlfir.any %arg0 : (!hlfir.expr<?x!fir.logical<4>>) -> i32
-}
-
-// -----
-func.func @bad_any6(%arg0: !hlfir.expr<?x!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.any' op result must be of logical type}}
-  %0 = hlfir.any %arg0 : (!hlfir.expr<?x!fir.logical<4>>) -> !hlfir.expr<!fir.logical<4>>
-}
-
-// -----
-func.func @bad_all1(%arg0: !hlfir.expr<?x!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.all' op result must have the same element type as MASK argument}}
-  %0 = hlfir.all %arg0 : (!hlfir.expr<?x!fir.logical<4>>) -> !fir.logical<8>
-}
-
-// -----
-func.func @bad_all2(%arg0: !hlfir.expr<?x?x!fir.logical<4>>, %arg1: i32) {
-  // expected-error at +1 {{'hlfir.all' op result must have the same element type as MASK argument}}
-  %0 = hlfir.all %arg0 dim %arg1 : (!hlfir.expr<?x?x!fir.logical<4>>, i32) -> !hlfir.expr<?x!fir.logical<8>>
-}
-
-// -----
-func.func @bad_all3(%arg0: !hlfir.expr<?x?x!fir.logical<4>>, %arg1: i32){
-  // expected-error at +1 {{'hlfir.all' op result rank must be one less than MASK}}
-  %0 = hlfir.all %arg0 dim %arg1 : (!hlfir.expr<?x?x!fir.logical<4>>, i32) -> !hlfir.expr<?x?x!fir.logical<4>>
-}
-
-// -----
-func.func @bad_all4(%arg0: !hlfir.expr<?x?x!fir.logical<4>>, %arg1: i32) {
-  // expected-error at +1 {{'hlfir.all' op result must be an array}}
-  %0 = hlfir.all %arg0 dim %arg1 : (!hlfir.expr<?x?x!fir.logical<4>>, i32) -> !hlfir.expr<!fir.logical<4>>
-}
-
-// -----
-func.func @bad_all5(%arg0: !hlfir.expr<?x!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.all' op result must be of logical type}}
-  %0 = hlfir.all %arg0 : (!hlfir.expr<?x!fir.logical<4>>) -> i32
-}
-
-// -----
-func.func @bad_all6(%arg0: !hlfir.expr<?x!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.all' op result must be of logical type}}
-  %0 = hlfir.all %arg0 : (!hlfir.expr<?x!fir.logical<4>>) -> !hlfir.expr<!fir.logical<4>>
-}
-
-// -----
-func.func @bad_count1(%arg0: !hlfir.expr<?x?x!fir.logical<4>>, %arg1: i32) {
-  // expected-error at +1 {{'hlfir.count' op result must be an array}}
-  %0 = hlfir.count %arg0 dim %arg1 : (!hlfir.expr<?x?x!fir.logical<4>>, i32) -> !hlfir.expr<i32>
-}
-
-// -----
-func.func @bad_count2(%arg0: !hlfir.expr<?x?x!fir.logical<4>>, %arg1: i32){
-  // expected-error at +1 {{'hlfir.count' op result rank must be one less than MASK}}
-  %0 = hlfir.count %arg0 dim %arg1 : (!hlfir.expr<?x?x!fir.logical<4>>, i32) -> !hlfir.expr<?x?x!fir.logical<4>>
-}
-
-// -----
-func.func @bad_count3(%arg0: !hlfir.expr<?x!fir.logical<4>>, %arg1: i32) {
-  // expected-error at +1 {{'hlfir.count' op result must be of numerical scalar type}}
-  %0 = hlfir.count %arg0 dim %arg1 : (!hlfir.expr<?x!fir.logical<4>>, i32) -> !hlfir.expr<i32>
-}
-
-// -----
-func.func @bad_count4(%arg0: !hlfir.expr<?x!fir.logical<4>>, %arg1: i32) {
-  // expected-error at +1 {{'hlfir.count' op result must be of numerical scalar type}}
-  %0 = hlfir.count %arg0 dim %arg1 : (!hlfir.expr<?x!fir.logical<4>>, i32) -> !fir.logical<4>
-}
-
-// -----
-func.func @bad_maxval1(%arg0: !hlfir.expr<?xi32>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.maxval' op result must have the same element type as ARRAY argument}}
-  %0 = hlfir.maxval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?xi32>, i32, !fir.box<!fir.logical<4>>) -> f32
-}
-
 // -----
 func.func @bad_maxval2(%arg0: !hlfir.expr<?xi32>, %arg1: i32, %arg2: !fir.box<!fir.array<?x?x?x?x?x!fir.logical<4>>>) {
   // expected-warning at +1 {{MASK must be conformable to ARRAY}}
   %0 = hlfir.maxval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?xi32>, i32, !fir.box<!fir.array<?x?x?x?x?x!fir.logical<4>>>) -> !hlfir.expr<i32>
 }
 
-// -----
-func.func @bad_maxval3(%arg0: !hlfir.expr<?x5x?xi32>, %arg1: i32, %arg2: !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) {
-  // expected-warning at +1 {{MASK must be conformable to ARRAY}}
-  %0 = hlfir.maxval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x5x?xi32>, i32, !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) -> !hlfir.expr<i32>
-}
-
-// -----
-func.func @bad_maxval4(%arg0: !hlfir.expr<?x?xi32>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.maxval' op result rank must be one less than ARRAY}}
-  %0 = hlfir.maxval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x?xi32>, i32, !fir.box<!fir.logical<4>>) -> !hlfir.expr<?x?xi32>
-}
-
-// -----
-func.func @bad_maxval5(%arg0: !hlfir.expr<?xi32>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.maxval' op result must be of numerical scalar type}}
-  %0 = hlfir.maxval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?xi32>, i32, !fir.box<!fir.logical<4>>) -> !fir.logical<4>
-}
-
-// -----
-func.func @bad_maxval6(%arg0: !hlfir.expr<?x?xi32>, %arg1: i32){
-  // expected-error at +1 {{'hlfir.maxval' op result must be an array}}
-  %0 = hlfir.maxval %arg0 dim %arg1 : (!hlfir.expr<?x?xi32>, i32) -> !hlfir.expr<i32>
-}
-
-// -----
-func.func @bad_maxval7(%arg0: !hlfir.expr<?xi32>){
-  // expected-error at +1 {{'hlfir.maxval' op result must be of numerical scalar type}}
-  %0 = hlfir.maxval %arg0 : (!hlfir.expr<?xi32>) -> !hlfir.expr<i32>
-}
-
-// -----
-func.func @bad_maxval8(%arg0: !hlfir.expr<?x!fir.char<1,?>>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.maxval' op result must have the same element type as ARRAY argument}}
-  %0 = hlfir.maxval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x!fir.char<1,?>>, i32, !fir.box<!fir.logical<4>>) -> i32
-}
-
 // -----
 func.func @bad_maxval9(%arg0: !hlfir.expr<?x!fir.char<1,?>>, %arg1: i32, %arg2: !fir.box<!fir.array<?x?x?x?x?x!fir.logical<4>>>) {
   // expected-warning at +1 {{MASK must be conformable to ARRAY}}
   %0 = hlfir.maxval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x!fir.char<1,?>>, i32, !fir.box<!fir.array<?x?x?x?x?x!fir.logical<4>>>) -> !hlfir.expr<!fir.char<1,?>>
 }
 
-// -----
-func.func @bad_maxval10(%arg0: !hlfir.expr<?x5x?x!fir.char<1,?>>, %arg1: i32, %arg2: !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) {
-  // expected-warning at +1 {{MASK must be conformable to ARRAY}}
-  %0 = hlfir.maxval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x5x?x!fir.char<1,?>>, i32, !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) -> !hlfir.expr<!fir.char<1,?>>
-}
-
-// -----
-func.func @bad_maxval11(%arg0: !hlfir.expr<?x?x!fir.char<1,?>>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.maxval' op result rank must be one less than ARRAY}}
-  %0 = hlfir.maxval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x?x!fir.char<1,?>>, i32, !fir.box<!fir.logical<4>>) -> !hlfir.expr<?x?x!fir.char<1,?>>
-}
-
 // -----
 func.func @bad_maxval12(%arg0: !hlfir.expr<?x!fir.char<1,?>>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
   // expected-error at +1 {{'hlfir.maxval' op result must be scalar character}}
@@ -470,72 +320,18 @@ func.func @bad_maxval13(%arg0: !hlfir.expr<?x?x!fir.char<1,?>>, %arg1: i32){
   %0 = hlfir.maxval %arg0 dim %arg1 : (!hlfir.expr<?x?x!fir.char<1,?>>, i32) -> !hlfir.expr<!fir.char<1,?>>
 }
 
-// -----
-func.func @bad_minval1(%arg0: !hlfir.expr<?xi32>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.minval' op result must have the same element type as ARRAY argument}}
-  %0 = hlfir.minval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?xi32>, i32, !fir.box<!fir.logical<4>>) -> f32
-}
-
 // -----
 func.func @bad_minval2(%arg0: !hlfir.expr<?xi32>, %arg1: i32, %arg2: !fir.box<!fir.array<?x?x?x?x?x!fir.logical<4>>>) {
   // expected-warning at +1 {{MASK must be conformable to ARRAY}}
   %0 = hlfir.minval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?xi32>, i32, !fir.box<!fir.array<?x?x?x?x?x!fir.logical<4>>>) -> !hlfir.expr<i32>
 }
 
-// -----
-func.func @bad_minval3(%arg0: !hlfir.expr<?x5x?xi32>, %arg1: i32, %arg2: !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) {
-  // expected-warning at +1 {{MASK must be conformable to ARRAY}}
-  %0 = hlfir.minval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x5x?xi32>, i32, !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) -> !hlfir.expr<i32>
-}
-
-// -----
-func.func @bad_minval4(%arg0: !hlfir.expr<?x?xi32>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.minval' op result rank must be one less than ARRAY}}
-  %0 = hlfir.minval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x?xi32>, i32, !fir.box<!fir.logical<4>>) -> !hlfir.expr<?x?xi32>
-}
-
-// -----
-func.func @bad_minval5(%arg0: !hlfir.expr<?xi32>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.minval' op result must be of numerical scalar type}}
-  %0 = hlfir.minval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?xi32>, i32, !fir.box<!fir.logical<4>>) -> !fir.logical<4>
-}
-
-// -----
-func.func @bad_minval6(%arg0: !hlfir.expr<?x?xi32>, %arg1: i32){
-  // expected-error at +1 {{'hlfir.minval' op result must be an array}}
-  %0 = hlfir.minval %arg0 dim %arg1 : (!hlfir.expr<?x?xi32>, i32) -> !hlfir.expr<i32>
-}
-
-// -----
-func.func @bad_minval7(%arg0: !hlfir.expr<?xi32>){
-  // expected-error at +1 {{'hlfir.minval' op result must be of numerical scalar type}}
-  %0 = hlfir.minval %arg0 : (!hlfir.expr<?xi32>) -> !hlfir.expr<i32>
-}
-
-// -----
-func.func @bad_minval8(%arg0: !hlfir.expr<?x!fir.char<1,?>>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.minval' op result must have the same element type as ARRAY argument}}
-  %0 = hlfir.minval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x!fir.char<1,?>>, i32, !fir.box<!fir.logical<4>>) -> i32
-}
-
 // -----
 func.func @bad_minval9(%arg0: !hlfir.expr<?x!fir.char<1,?>>, %arg1: i32, %arg2: !fir.box<!fir.array<?x?x?x?x?x!fir.logical<4>>>) {
   // expected-warning at +1 {{MASK must be conformable to ARRAY}}
   %0 = hlfir.minval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x!fir.char<1,?>>, i32, !fir.box<!fir.array<?x?x?x?x?x!fir.logical<4>>>) -> !hlfir.expr<!fir.char<1,?>>
 }
 
-// -----
-func.func @bad_minval10(%arg0: !hlfir.expr<?x5x?x!fir.char<1,?>>, %arg1: i32, %arg2: !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) {
-  // expected-warning at +1 {{MASK must be conformable to ARRAY}}
-  %0 = hlfir.minval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x5x?x!fir.char<1,?>>, i32, !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) -> !hlfir.expr<!fir.char<1,?>>
-}
-
-// -----
-func.func @bad_minval11(%arg0: !hlfir.expr<?x?x!fir.char<1,?>>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.minval' op result rank must be one less than ARRAY}}
-  %0 = hlfir.minval %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x?x!fir.char<1,?>>, i32, !fir.box<!fir.logical<4>>) -> !hlfir.expr<?x?x!fir.char<1,?>>
-}
-
 // -----
 func.func @bad_minval12(%arg0: !hlfir.expr<?x!fir.char<1,?>>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
   // expected-error at +1 {{'hlfir.minval' op result must be scalar character}}
@@ -560,18 +356,6 @@ func.func @bad_minloc2(%arg0: !hlfir.expr<?xi32>, %arg1: i32, %arg2: !fir.box<!f
   %0 = hlfir.minloc %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?xi32>, i32, !fir.box<!fir.array<?x?x?x?x?x!fir.logical<4>>>) -> !hlfir.expr<i32>
 }
 
-// -----
-func.func @bad_minloc3(%arg0: !hlfir.expr<?x5x?xi32>, %arg1: i32, %arg2: !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) {
-  // expected-warning at +1 {{MASK must be conformable to ARRAY}}
-  %0 = hlfir.minloc %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x5x?xi32>, i32, !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) -> !hlfir.expr<i32>
-}
-
-// -----
-func.func @bad_minloc4(%arg0: !hlfir.expr<?x?xi32>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.minloc' op result rank must be one less than ARRAY}}
-  %0 = hlfir.minloc %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x?xi32>, i32, !fir.box<!fir.logical<4>>) -> !hlfir.expr<?x?xi32>
-}
-
 // -----
 func.func @bad_minloc5(%arg0: !hlfir.expr<?xi32>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
   // expected-error at +1 {{'hlfir.minloc' op result must be scalar integer}}
@@ -602,18 +386,6 @@ func.func @bad_minloc9(%arg0: !hlfir.expr<?x!fir.char<1,?>>, %arg1: i32, %arg2:
   %0 = hlfir.minloc %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x!fir.char<1,?>>, i32, !fir.box<!fir.array<?x?x?x?x?x!fir.logical<4>>>) -> !hlfir.expr<!fir.char<1,?>>
 }
 
-// -----
-func.func @bad_minloc10(%arg0: !hlfir.expr<?x5x?x!fir.char<1,?>>, %arg1: i32, %arg2: !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) {
-  // expected-warning at +1 {{MASK must be conformable to ARRAY}}
-  %0 = hlfir.minloc %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x5x?x!fir.char<1,?>>, i32, !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) -> !hlfir.expr<!fir.char<1,?>>
-}
-
-// -----
-func.func @bad_minloc11(%arg0: !hlfir.expr<?x?x!fir.char<1,?>>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.minloc' op result rank must be one less than ARRAY}}
-  %0 = hlfir.minloc %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x?x!fir.char<1,?>>, i32, !fir.box<!fir.logical<4>>) -> !hlfir.expr<?x?xi32>
-}
-
 // -----
 func.func @bad_maxloc1(%arg0: !hlfir.expr<?xi32>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
   // expected-error at +1 {{'hlfir.maxloc' op result must be scalar integer}}
@@ -626,18 +398,6 @@ func.func @bad_maxloc2(%arg0: !hlfir.expr<?xi32>, %arg1: i32, %arg2: !fir.box<!f
   %0 = hlfir.maxloc %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?xi32>, i32, !fir.box<!fir.array<?x?x?x?x?x!fir.logical<4>>>) -> !hlfir.expr<i32>
 }
 
-// -----
-func.func @bad_maxloc3(%arg0: !hlfir.expr<?x5x?xi32>, %arg1: i32, %arg2: !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) {
-  // expected-warning at +1 {{MASK must be conformable to ARRAY}}
-  %0 = hlfir.maxloc %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x5x?xi32>, i32, !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) -> !hlfir.expr<i32>
-}
-
-// -----
-func.func @bad_maxloc4(%arg0: !hlfir.expr<?x?xi32>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.maxloc' op result rank must be one less than ARRAY}}
-  %0 = hlfir.maxloc %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x?xi32>, i32, !fir.box<!fir.logical<4>>) -> !hlfir.expr<?x?xi32>
-}
-
 // -----
 func.func @bad_maxloc5(%arg0: !hlfir.expr<?xi32>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
   // expected-error at +1 {{'hlfir.maxloc' op result must be scalar integer}}
@@ -668,103 +428,18 @@ func.func @bad_maxloc9(%arg0: !hlfir.expr<?x!fir.char<1,?>>, %arg1: i32, %arg2:
   %0 = hlfir.maxloc %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x!fir.char<1,?>>, i32, !fir.box<!fir.array<?x?x?x?x?x!fir.logical<4>>>) -> !hlfir.expr<!fir.char<1,?>>
 }
 
-// -----
-func.func @bad_maxloc10(%arg0: !hlfir.expr<?x5x?x!fir.char<1,?>>, %arg1: i32, %arg2: !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) {
-  // expected-warning at +1 {{MASK must be conformable to ARRAY}}
-  %0 = hlfir.maxloc %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x5x?x!fir.char<1,?>>, i32, !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) -> !hlfir.expr<!fir.char<1,?>>
-}
-
-// -----
-func.func @bad_maxloc11(%arg0: !hlfir.expr<?x?x!fir.char<1,?>>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.maxloc' op result rank must be one less than ARRAY}}
-  %0 = hlfir.maxloc %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x?x!fir.char<1,?>>, i32, !fir.box<!fir.logical<4>>) -> !hlfir.expr<?x?xi32>
-}
-
-
-// -----
-func.func @bad_product1(%arg0: !hlfir.expr<?xi32>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.product' op result must have the same element type as ARRAY argument}}
-  %0 = hlfir.product %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?xi32>, i32, !fir.box<!fir.logical<4>>) -> f32
-}
-
 // -----
 func.func @bad_product2(%arg0: !hlfir.expr<?xi32>, %arg1: i32, %arg2: !fir.box<!fir.array<?x?x?x?x?x!fir.logical<4>>>) {
   // expected-warning at +1 {{MASK must be conformable to ARRAY}}
   %0 = hlfir.product %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?xi32>, i32, !fir.box<!fir.array<?x?x?x?x?x!fir.logical<4>>>) -> !hlfir.expr<i32>
 }
 
-// -----
-func.func @bad_product3(%arg0: !hlfir.expr<?x5x?xi32>, %arg1: i32, %arg2: !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) {
-  // expected-warning at +1 {{MASK must be conformable to ARRAY}}
-  %0 = hlfir.product %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x5x?xi32>, i32, !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) -> !hlfir.expr<i32>
-}
-
-// -----
-func.func @bad_product4(%arg0: !hlfir.expr<?x?xi32>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.product' op result rank must be one less than ARRAY}}
-  %0 = hlfir.product %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x?xi32>, i32, !fir.box<!fir.logical<4>>) -> !hlfir.expr<?x?xi32>
-}
-
-// -----
-func.func @bad_product5(%arg0: !hlfir.expr<?xi32>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.product' op result must be of numerical scalar type}}
-  %0 = hlfir.product %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?xi32>, i32, !fir.box<!fir.logical<4>>) -> !fir.logical<4>
-}
-
-// -----
-func.func @bad_product6(%arg0: !hlfir.expr<?x?xi32>, %arg1: i32){
-  // expected-error at +1 {{'hlfir.product' op result must be an array}}
-  %0 = hlfir.product %arg0 dim %arg1 : (!hlfir.expr<?x?xi32>, i32) -> !hlfir.expr<i32>
-}
-
-// -----
-func.func @bad_product7(%arg0: !hlfir.expr<?xi32>){
-  // expected-error at +1 {{'hlfir.product' op result must be of numerical scalar type}}
-  %0 = hlfir.product %arg0 : (!hlfir.expr<?xi32>) -> !hlfir.expr<i32>
-}
-
-// -----
-func.func @bad_sum1(%arg0: !hlfir.expr<?xi32>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.sum' op result must have the same element type as ARRAY argument}}
-  %0 = hlfir.sum %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?xi32>, i32, !fir.box<!fir.logical<4>>) -> f32
-}
-
 // -----
 func.func @bad_sum2(%arg0: !hlfir.expr<?xi32>, %arg1: i32, %arg2: !fir.box<!fir.array<?x?x?x?x?x!fir.logical<4>>>) {
   // expected-warning at +1 {{MASK must be conformable to ARRAY}}
   %0 = hlfir.sum %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?xi32>, i32, !fir.box<!fir.array<?x?x?x?x?x!fir.logical<4>>>) -> !hlfir.expr<i32>
 }
 
-// -----
-func.func @bad_sum3(%arg0: !hlfir.expr<?x5x?xi32>, %arg1: i32, %arg2: !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) {
-  // expected-warning at +1 {{MASK must be conformable to ARRAY}}
-  %0 = hlfir.sum %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x5x?xi32>, i32, !fir.box<!fir.array<2x6x?x!fir.logical<4>>>) -> !hlfir.expr<i32>
-}
-
-// -----
-func.func @bad_sum4(%arg0: !hlfir.expr<?x?xi32>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.sum' op result rank must be one less than ARRAY}}
-  %0 = hlfir.sum %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?x?xi32>, i32, !fir.box<!fir.logical<4>>) -> !hlfir.expr<?x?xi32>
-}
-
-// -----
-func.func @bad_sum5(%arg0: !hlfir.expr<?xi32>, %arg1: i32, %arg2: !fir.box<!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.sum' op result must be of numerical scalar type}}
-  %0 = hlfir.sum %arg0 dim %arg1 mask %arg2 : (!hlfir.expr<?xi32>, i32, !fir.box<!fir.logical<4>>) -> !fir.logical<4>
-}
-
-// -----
-func.func @bad_sum6(%arg0: !hlfir.expr<?x?xi32>, %arg1: i32){
-  // expected-error at +1 {{'hlfir.sum' op result must be an array}}
-  %0 = hlfir.sum %arg0 dim %arg1 : (!hlfir.expr<?x?xi32>, i32) -> !hlfir.expr<i32>
-}
-
-// -----
-func.func @bad_sum7(%arg0: !hlfir.expr<?xi32>){
-  // expected-error at +1 {{'hlfir.sum' op result must be of numerical scalar type}}
-  %0 = hlfir.sum %arg0 : (!hlfir.expr<?xi32>) -> !hlfir.expr<i32>
-}
-
 // -----
 func.func @bad_matmul1(%arg0: !hlfir.expr<?x?x?xi32>, %arg1: !hlfir.expr<?x?xi32>) {
   // expected-error at +1 {{'hlfir.matmul' op array must have either rank 1 or rank 2}}
@@ -772,20 +447,6 @@ func.func @bad_matmul1(%arg0: !hlfir.expr<?x?x?xi32>, %arg1: !hlfir.expr<?x?xi32
   return
 }
 
-// -----
-func.func @bad_matmul2(%arg0: !hlfir.expr<?xi32>, %arg1: !hlfir.expr<?xi32>) {
-  // expected-error at +1 {{'hlfir.matmul' op at least one array must have rank 2}}
-  %0 = hlfir.matmul %arg0 %arg1 : (!hlfir.expr<?xi32>, !hlfir.expr<?xi32>) -> !hlfir.expr<?x?xi32>
-  return
-}
-
-// -----
-func.func @bad_matmul3(%arg0: !hlfir.expr<?x?x!fir.logical<4>>, %arg1: !hlfir.expr<?x?xi32>) {
-  // expected-error at +1 {{'hlfir.matmul' op if one array is logical, so should the other be}}
-  %0 = hlfir.matmul %arg0 %arg1 : (!hlfir.expr<?x?x!fir.logical<4>>, !hlfir.expr<?x?xi32>) -> !hlfir.expr<?x?xi32>
-  return
-}
-
 // -----
 func.func @bad_matmul4(%arg0: !hlfir.expr<?x2xi32>, %arg1: !hlfir.expr<200x?xi32>) {
   // expected-error at +1 {{'hlfir.matmul' op the last dimension of LHS should match the first dimension of RHS}}
@@ -793,34 +454,6 @@ func.func @bad_matmul4(%arg0: !hlfir.expr<?x2xi32>, %arg1: !hlfir.expr<200x?xi32
   return
 }
 
-// -----
-func.func @bad_matmul5(%arg0: !hlfir.expr<?x?xi32>, %arg1: !hlfir.expr<?x?xi32>) {
-  // expected-error at +1 {{'hlfir.matmul' op the result type should be a logical only if the argument types are logical}}
-  %0 = hlfir.matmul %arg0 %arg1 : (!hlfir.expr<?x?xi32>, !hlfir.expr<?x?xi32>) -> !hlfir.expr<?x?x!fir.logical<4>>
-  return
-}
-
-// -----
-func.func @bad_matmul6(%arg0: !hlfir.expr<1x2xi32>, %arg1: !hlfir.expr<2x3xi32>) {
-  // expected-error at +1 {{'hlfir.matmul' op incorrect result shape}}
-  %0 = hlfir.matmul %arg0 %arg1 : (!hlfir.expr<1x2xi32>, !hlfir.expr<2x3xi32>) -> !hlfir.expr<10x30xi32>
-  return
-}
-
-// -----
-func.func @bad_matmul7(%arg0: !hlfir.expr<1x2xi32>, %arg1: !hlfir.expr<2xi32>) {
-  // expected-error at +1 {{'hlfir.matmul' op incorrect result shape}}
-  %0 = hlfir.matmul %arg0 %arg1 : (!hlfir.expr<1x2xi32>, !hlfir.expr<2xi32>) -> !hlfir.expr<1x3xi32>
-  return
-}
-
-// -----
-func.func @bad_matmul8(%arg0: !hlfir.expr<2xi32>, %arg1: !hlfir.expr<2x3xi32>) {
-  // expected-error at +1 {{'hlfir.matmul' op incorrect result shape}}
-  %0 = hlfir.matmul %arg0 %arg1 : (!hlfir.expr<2xi32>, !hlfir.expr<2x3xi32>) -> !hlfir.expr<1x3xi32>
-  return
-}
-
 // -----
 func.func @bad_dot_product1(%arg0: !hlfir.expr<2xi32>, %arg1: !hlfir.expr<2x3xi32>) {
   // expected-error at +1 {{'hlfir.dot_product' op both arrays must have rank 1}}
@@ -835,27 +468,6 @@ func.func @bad_dot_product2(%arg0: !hlfir.expr<2xi32>, %arg1: !hlfir.expr<3xi32>
   return
 }
 
-// -----
-func.func @bad_dot_product3(%arg0: !hlfir.expr<2xi32>, %arg1: !hlfir.expr<2x!fir.logical<4>>) {
-  // expected-error at +1 {{'hlfir.dot_product' op if one array is logical, so should the other be}}
-  %0 = hlfir.dot_product %arg0 %arg1 : (!hlfir.expr<2xi32>, !hlfir.expr<2x!fir.logical<4>>) -> i32
-  return
-}
-
-// -----
-func.func @bad_dot_product4(%arg0: !hlfir.expr<2xi32>, %arg1: !hlfir.expr<2xi32>) {
-  // expected-error at +1 {{'hlfir.dot_product' op the result type should be a logical only if the argument types are logical}}
-  %0 = hlfir.dot_product %arg0 %arg1 : (!hlfir.expr<2xi32>, !hlfir.expr<2xi32>) -> !fir.logical<4>
-  return
-}
-
-// -----
-func.func @bad_dot_product5(%arg0: !hlfir.expr<2xi32>, %arg1: !hlfir.expr<2xi32>) {
-  // expected-error at +1 {{'hlfir.dot_product' op the result must be of scalar numerical or logical type}}
-  %0 = hlfir.dot_product %arg0 %arg1 : (!hlfir.expr<2xi32>, !hlfir.expr<2xi32>) -> !hlfir.expr<i32>
-  return
-}
-
 // -----
 func.func @bad_transpose1(%arg0: !hlfir.expr<2xi32>) {
   // expected-error at +1 {{'hlfir.transpose' op input and output arrays should have rank 2}}
@@ -863,20 +475,6 @@ func.func @bad_transpose1(%arg0: !hlfir.expr<2xi32>) {
   return
 }
 
-// -----
-func.func @bad_transpose2(%arg0: !hlfir.expr<2x3xi32>) {
-  // expected-error at +1 {{'hlfir.transpose' op output shape does not match input array}}
-  %0 = hlfir.transpose %arg0 : (!hlfir.expr<2x3xi32>) -> !hlfir.expr<2x2xi32>
-  return
-}
-
-// -----
-func.func @bad_transpose3(%arg0: !hlfir.expr<2x3xi32>) {
-  // expected-error at +1 {{'hlfir.transpose' op input and output arrays should have the same element type}}
-  %0 = hlfir.transpose %arg0 : (!hlfir.expr<2x3xi32>) -> !hlfir.expr<3x2xf64>
-  return
-}
-
 // -----
 func.func @bad_matmultranspose1(%arg0: !hlfir.expr<?x?x?xi32>, %arg1: !hlfir.expr<?x?xi32>) {
   // expected-error at +1 {{'hlfir.matmul_transpose' op array must have either rank 1 or rank 2}}
@@ -898,27 +496,6 @@ func.func @bad_matmultranspose3(%arg0: !hlfir.expr<?x?x!fir.logical<4>>, %arg1:
   return
 }
 
-// -----
-func.func @bad_matmultranspose5(%arg0: !hlfir.expr<?x?xi32>, %arg1: !hlfir.expr<?x?xi32>) {
-  // expected-error at +1 {{'hlfir.matmul_transpose' op the result type should be a logical only if the argument types are logical}}
-  %0 = hlfir.matmul_transpose %arg0 %arg1 : (!hlfir.expr<?x?xi32>, !hlfir.expr<?x?xi32>) -> !hlfir.expr<?x?x!fir.logical<4>>
-  return
-}
-
-// -----
-func.func @bad_matmultranspose6(%arg0: !hlfir.expr<2x1xi32>, %arg1: !hlfir.expr<2x3xi32>) {
-  // expected-error at +1 {{'hlfir.matmul_transpose' op incorrect result shape}}
-  %0 = hlfir.matmul_transpose %arg0 %arg1 : (!hlfir.expr<2x1xi32>, !hlfir.expr<2x3xi32>) -> !hlfir.expr<10x30xi32>
-  return
-}
-
-// -----
-func.func @bad_matmultranspose7(%arg0: !hlfir.expr<2x1xi32>, %arg1: !hlfir.expr<2xi32>) {
-  // expected-error at +1 {{'hlfir.matmul_transpose' op incorrect result shape}}
-  %0 = hlfir.matmul_transpose %arg0 %arg1 : (!hlfir.expr<2x1xi32>, !hlfir.expr<2xi32>) -> !hlfir.expr<1x3xi32>
-  return
-}
-
 // -----
 func.func @bad_assign_1(%arg0: !fir.box<!fir.array<?xi32>>, %arg1: !fir.box<!fir.array<?xi32>>) {
   // expected-error at +1 {{'hlfir.assign' op lhs must be an allocatable when `realloc` is set}}
diff --git a/flang/test/Lower/HLFIR/minval.f90 b/flang/test/Lower/HLFIR/minval.f90
index 806c9c2a8a011..01b0ce77e2d30 100644
--- a/flang/test/Lower/HLFIR/minval.f90
+++ b/flang/test/Lower/HLFIR/minval.f90
@@ -260,3 +260,47 @@ end subroutine test_unknown_char_len_result
 ! CHECK-NEXT:      hlfir.destroy %[[EXPR]]
 ! CHECK-NEXT:      return
 ! CHECK-NEXT:    }
+
+! Test edge case with missmatch between argument type !fir.char<1,?> and result
+! type !fir.char<1,4>
+function test_type_mismatch
+  character(:), allocatable :: test_type_mismatch(:)
+  character(3) :: char(3,4)
+  test_type_mismatch = minval(char//' ', dim=1)
+end function
+! CHECK-LABEL:   func.func @_QPtest_type_mismatch() -> !fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>> {
+! CHECK:           %[[VAL_0:.*]] = arith.constant 3 : index
+! CHECK:           %[[VAL_1:.*]] = arith.constant 3 : index
+! CHECK:           %[[VAL_2:.*]] = arith.constant 4 : index
+! CHECK:           %[[VAL_3:.*]] = fir.alloca !fir.array<3x4x!fir.char<1,3>> {bindc_name = "char", uniq_name = "_QFtest_type_mismatchEchar"}
+! CHECK:           %[[VAL_4:.*]] = fir.shape %[[VAL_1]], %[[VAL_2]] : (index, index) -> !fir.shape<2>
+! CHECK:           %[[VAL_5:.*]]:2 = hlfir.declare %[[VAL_3]](%[[VAL_4]]) typeparams %[[VAL_0]] {uniq_name = "_QFtest_type_mismatchEchar"} : (!fir.ref<!fir.array<3x4x!fir.char<1,3>>>, !fir.shape<2>, index) -> (!fir.ref<!fir.array<3x4x!fir.char<1,3>>>, !fir.ref<!fir.array<3x4x!fir.char<1,3>>>)
+! CHECK:           %[[VAL_6:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>> {bindc_name = "test_type_mismatch", uniq_name = "_QFtest_type_mismatchEtest_type_mismatch"}
+! CHECK:           %[[VAL_7:.*]] = fir.zero_bits !fir.heap<!fir.array<?x!fir.char<1,?>>>
+! CHECK:           %[[VAL_8:.*]] = arith.constant 0 : index
+! CHECK:           %[[VAL_9:.*]] = fir.shape %[[VAL_8]] : (index) -> !fir.shape<1>
+! CHECK:           %[[VAL_10:.*]] = arith.constant 0 : index
+! CHECK:           %[[VAL_11:.*]] = fir.embox %[[VAL_7]](%[[VAL_9]]) typeparams %[[VAL_10]] : (!fir.heap<!fir.array<?x!fir.char<1,?>>>, !fir.shape<1>, index) -> !fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>
+! CHECK:           fir.store %[[VAL_11]] to %[[VAL_6]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>
+! CHECK:           %[[VAL_12:.*]]:2 = hlfir.declare %[[VAL_6]] {fortran_attrs = #{{.*}}, uniq_name = "_QFtest_type_mismatchEtest_type_mismatch"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>)
+! CHECK:           %[[VAL_13:.*]] = fir.address_of(@_QQclX20) : !fir.ref<!fir.char<1>>
+! CHECK:           %[[VAL_14:.*]] = arith.constant 1 : index
+! CHECK:           %[[VAL_15:.*]]:2 = hlfir.declare %[[VAL_13]] typeparams %[[VAL_14]] {fortran_attrs = {{.*}}, uniq_name = "_QQclX20"} : (!fir.ref<!fir.char<1>>, index) -> (!fir.ref<!fir.char<1>>, !fir.ref<!fir.char<1>>)
+! CHECK:           %[[VAL_16:.*]] = arith.addi %[[VAL_0]], %[[VAL_14]] : index
+! CHECK:           %[[VAL_17:.*]] = hlfir.elemental %[[VAL_4]] typeparams %[[VAL_16]] unordered : (!fir.shape<2>, index) -> !hlfir.expr<3x4x!fir.char<1,?>> {
+! CHECK:           ^bb0(%[[VAL_18:.*]]: index, %[[VAL_19:.*]]: index):
+! CHECK:             %[[VAL_20:.*]] = hlfir.designate %[[VAL_5]]#0 (%[[VAL_18]], %[[VAL_19]])  typeparams %[[VAL_0]] : (!fir.ref<!fir.array<3x4x!fir.char<1,3>>>, index, index, index) -> !fir.ref<!fir.char<1,3>>
+! CHECK:             %[[VAL_21:.*]] = hlfir.concat %[[VAL_20]], %[[VAL_15]]#0 len %[[VAL_16]] : (!fir.ref<!fir.char<1,3>>, !fir.ref<!fir.char<1>>, index) -> !hlfir.expr<!fir.char<1,4>>
+! CHECK:             hlfir.yield_element %[[VAL_21]] : !hlfir.expr<!fir.char<1,4>>
+! CHECK:           }
+! CHECK:           %[[VAL_22:.*]] = arith.constant 1 : i32
+! CHECK:           %[[VAL_23:.*]] = hlfir.minval %[[VAL_17]] dim %[[VAL_22]] {fastmath = {{.*}}} : (!hlfir.expr<3x4x!fir.char<1,?>>, i32) -> !hlfir.expr<4x!fir.char<1,4>>
+! CHECK:           hlfir.assign %[[VAL_23]] to %[[VAL_12]]#0 realloc : !hlfir.expr<4x!fir.char<1,4>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>
+! CHECK:           hlfir.destroy %[[VAL_23]] : !hlfir.expr<4x!fir.char<1,4>>
+! CHECK:           hlfir.destroy %[[VAL_17]] : !hlfir.expr<3x4x!fir.char<1,?>>
+! CHECK:           %[[VAL_24:.*]] = fir.load %[[VAL_12]]#1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>
+! CHECK:           %[[VAL_25:.*]] = arith.constant 1 : index
+! CHECK:           %[[VAL_26:.*]] = fir.shift %[[VAL_25]] : (index) -> !fir.shift<1>
+! CHECK:           %[[VAL_27:.*]] = fir.rebox %[[VAL_24]](%[[VAL_26]]) : (!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>, !fir.shift<1>) -> !fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>
+! CHECK:           return %[[VAL_27]] : !fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>
+! CHECK:         }



More information about the flang-commits mailing list