[flang-commits] [flang] 4bb9d28 - [flang] Add hlfir.designate definition

Jean Perier via flang-commits flang-commits at lists.llvm.org
Thu Nov 17 23:41:59 PST 2022


Author: Jean Perier
Date: 2022-11-18T08:41:38+01:00
New Revision: 4bb9d28cc48f9f2dc46222a55b102d69a51170e6

URL: https://github.com/llvm/llvm-project/commit/4bb9d28cc48f9f2dc46222a55b102d69a51170e6
DIFF: https://github.com/llvm/llvm-project/commit/4bb9d28cc48f9f2dc46222a55b102d69a51170e6.diff

LOG: [flang] Add hlfir.designate definition

Add hlfir.designate operation definition. This is the HLFIR building
blocks to represent Fortran designator. An hlfir.designator is a
Fortran "part-ref" and an optional susbstring or part ref.
See the operation added description for more detail, and
https://github.com/llvm/llvm-project/blob/main/flang/docs/HighLevelFIR.md
for the rational of this operation.

Differential Revision: https://reviews.llvm.org/D138121

Added: 
    flang/test/HLFIR/designate.fir

Modified: 
    flang/include/flang/Optimizer/Builder/HLFIRTools.h
    flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h
    flang/include/flang/Optimizer/HLFIR/HLFIROps.td
    flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
    flang/test/HLFIR/invalid.fir

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Optimizer/Builder/HLFIRTools.h b/flang/include/flang/Optimizer/Builder/HLFIRTools.h
index 834c410ecb469..5a693152ae623 100644
--- a/flang/include/flang/Optimizer/Builder/HLFIRTools.h
+++ b/flang/include/flang/Optimizer/Builder/HLFIRTools.h
@@ -67,10 +67,7 @@ class Entity : public mlir::Value {
       : mlir::Value(variable.getBase()) {}
   bool isValue() const { return isFortranValue(*this); }
   bool isVariable() const { return !isValue(); }
-  bool isMutableBox() const {
-    mlir::Type type = fir::dyn_cast_ptrEleTy(getType());
-    return type && type.isa<fir::BaseBoxType>();
-  }
+  bool isMutableBox() const { return hlfir::isBoxAddressType(getType()); }
   bool isArray() const {
     mlir::Type type = fir::unwrapPassByRefType(fir::unwrapRefType(getType()));
     if (type.isa<fir::SequenceType>())
@@ -82,11 +79,7 @@ class Entity : public mlir::Value {
   bool isScalar() const { return !isArray(); }
 
   mlir::Type getFortranElementType() const {
-    mlir::Type type = fir::unwrapSequenceType(
-        fir::unwrapPassByRefType(fir::unwrapRefType(getType())));
-    if (auto exprType = type.dyn_cast<hlfir::ExprType>())
-      return exprType.getEleTy();
-    return type;
+    return hlfir::getFortranElementType(getType());
   }
 
   bool hasLengthParameters() const {

diff  --git a/flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h b/flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h
index dd7708bad195d..5d229dd445799 100644
--- a/flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h
+++ b/flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h
@@ -14,6 +14,7 @@
 #ifndef FORTRAN_OPTIMIZER_HLFIR_HLFIRDIALECT_H
 #define FORTRAN_OPTIMIZER_HLFIR_HLFIRDIALECT_H
 
+#include "flang/Optimizer/Dialect/FIRType.h"
 #include "mlir/IR/Dialect.h"
 
 namespace hlfir {
@@ -29,4 +30,38 @@ bool isFortranVariableType(mlir::Type);
 #define GET_ATTRDEF_CLASSES
 #include "flang/Optimizer/HLFIR/HLFIRAttributes.h.inc"
 
+namespace hlfir {
+/// Get the element type of a Fortran entity type.
+inline mlir::Type getFortranElementType(mlir::Type type) {
+  type = fir::unwrapSequenceType(
+      fir::unwrapPassByRefType(fir::unwrapRefType(type)));
+  if (auto exprType = type.dyn_cast<hlfir::ExprType>())
+    return exprType.getEleTy();
+  if (auto boxCharType = type.dyn_cast<fir::BoxCharType>())
+    return boxCharType.getEleTy();
+  return type;
+}
+
+/// If this is the type of a Fortran array entity, get the related
+/// fir.array type. Otherwise, returns the Fortran element typeof the entity.
+inline mlir::Type getFortranElementOrSequenceType(mlir::Type type) {
+  type = fir::unwrapPassByRefType(fir::unwrapRefType(type));
+  if (auto exprType = type.dyn_cast<hlfir::ExprType>()) {
+    if (exprType.isArray())
+      return fir::SequenceType::get(exprType.getShape(), exprType.getEleTy());
+    return exprType.getEleTy();
+  }
+  if (auto boxCharType = type.dyn_cast<fir::BoxCharType>())
+    return boxCharType.getEleTy();
+  return type;
+}
+
+/// Is this a fir.box or fir.class address type?
+inline bool isBoxAddressType(mlir::Type type) {
+  type = fir::dyn_cast_ptrEleTy(type);
+  return type && type.isa<fir::BaseBoxType>();
+}
+
+} // namespace hlfir
+
 #endif // FORTRAN_OPTIMIZER_HLFIR_HLFIRDIALECT_H

diff  --git a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
index 3a8094721197a..46775b198fdc6 100644
--- a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
+++ b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
@@ -133,4 +133,79 @@ def fir_AssignOp : hlfir_Op<"assign", [MemoryEffects<[MemWrite]>]> {
   }];
 }
 
+def hlfir_DesignateOp : hlfir_Op<"designate", [AttrSizedOperandSegments,
+    DeclareOpInterfaceMethods<fir_FortranVariableOpInterface>]> {
+  let summary = "Designate a Fortran variable";
+
+  let description = [{
+    This operation represents a Fortran "part-ref", except that it can embed a
+    substring or or complex part directly, and that vector subscripts cannot be
+    used. It returns a Fortran variable that is a part of the input variable.
+
+    The operands are as follow:
+      - memref is the variable being designated.
+      - component may be provided if the memref is a derived type to
+        represent a reference to a component. It must be the name of a
+        component of memref derived type.
+      - component_shape represents the shape of the component and must be
+        provided if and only if both component and indices appear.
+      - indices can be provided to index arrays. The indices may be simple
+        indices or triplets.
+        If indices are provided and there is a component, the component must be
+        an array component and the indices index the array component.
+        If memref is an array, and component is provided and is an array
+        component, indices must be provided and must not be triplets. This
+        ensures hlfir.designate does not create arrays of arrays (which is not
+        possible in Fortran).
+      - substring may contain two values to represent a substring lower and
+        upper bounds.
+      - complex_part may be provided to represent a complex part (true
+        represents the imaginary part, and false the real part).
+      - shape represents the shape of the result and must be provided if the
+        result is an array that is not a box address.
+      - typeparams represents the length parameters of the result and must be
+        provided if the result type has length parameters and is not a box
+        address.
+  }];
+
+  let arguments = (ins AnyFortranVariable:$memref,
+                   OptionalAttr<Builtin_StringAttr>:$component,
+                   Optional<AnyShapeOrShiftType>:$component_shape,
+                   Variadic<AnyIntegerType>:$indices,
+                   DenseBoolArrayAttr:$is_triplet,
+                   Variadic<AnyIntegerType>:$substring,
+                   OptionalAttr<BoolAttr>:$complex_part,
+                   Optional<AnyShapeOrShiftType>:$shape,
+                   Variadic<AnyIntegerType>:$typeparams,
+                   OptionalAttr<fir_FortranVariableFlagsAttr>:$fortran_attrs
+                );
+
+  let results = (outs AnyFortranVariable);
+
+  let assemblyFormat = [{
+    $memref (`{` $component^ `}`)? (`<` $component_shape^ `>`)?
+    custom<DesignatorIndices>($indices, $is_triplet)
+    (`substr` $substring^)?
+    custom<DesignatorComplexPart>($complex_part)
+    (`shape` $shape^)? (`typeparams` $typeparams^)?
+    attr-dict `:` functional-type(operands, results)
+  }];
+
+  let extraClassDeclaration = [{
+    using Triplet = std::tuple<mlir::Value, mlir::Value, mlir::Value>;
+    using Subscript = std::variant<mlir::Value, Triplet>;
+  }];
+
+  let builders = [
+    OpBuilder<(ins "mlir::Type":$result_type, "mlir::Value":$memref,
+      "llvm::StringRef":$component, "mlir::Value":$component_shape,
+      "llvm::ArrayRef<std::variant<mlir::Value, std::tuple<mlir::Value, mlir::Value, mlir::Value>>>":$subscripts,
+      CArg<"mlir::ValueRange", "{}">:$substring,
+      CArg<"llvm::Optional<bool>", "{}">:$complex_part,
+      CArg<"mlir::Value", "{}">:$shape, CArg<"mlir::ValueRange", "{}">:$typeparams,
+      CArg<"fir::FortranVariableFlagsAttr", "{}">:$fortran_attrs)>];
+
+  let hasVerifier = 1;
+}
+
 #endif // FORTRAN_DIALECT_HLFIR_OPS

diff  --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
index 1b723d73bc1ce..986553a35451e 100644
--- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
+++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
@@ -79,5 +79,263 @@ mlir::LogicalResult hlfir::DeclareOp::verify() {
   return fortranVar.verifyDeclareLikeOpImpl(getMemref());
 }
 
+//===----------------------------------------------------------------------===//
+// DesignateOp
+//===----------------------------------------------------------------------===//
+
+void hlfir::DesignateOp::build(
+    mlir::OpBuilder &builder, mlir::OperationState &result,
+    mlir::Type result_type, mlir::Value memref, llvm::StringRef component,
+    mlir::Value component_shape, llvm::ArrayRef<Subscript> subscripts,
+    mlir::ValueRange substring, llvm::Optional<bool> complex_part,
+    mlir::Value shape, mlir::ValueRange typeparams,
+    fir::FortranVariableFlagsAttr fortran_attrs) {
+  auto componentAttr =
+      component.empty() ? mlir::StringAttr{} : builder.getStringAttr(component);
+  llvm::SmallVector<mlir::Value> indices;
+  llvm::SmallVector<bool> isTriplet;
+  for (auto subscript : subscripts) {
+    if (auto *triplet = std::get_if<Triplet>(&subscript)) {
+      isTriplet.push_back(true);
+      indices.push_back(std::get<0>(*triplet));
+      indices.push_back(std::get<1>(*triplet));
+      indices.push_back(std::get<2>(*triplet));
+    } else {
+      isTriplet.push_back(false);
+      indices.push_back(std::get<mlir::Value>(subscript));
+    }
+  }
+  auto isTripletAttr =
+      mlir::DenseBoolArrayAttr::get(builder.getContext(), isTriplet);
+  auto complexPartAttr =
+      complex_part.has_value()
+          ? mlir::BoolAttr::get(builder.getContext(), *complex_part)
+          : mlir::BoolAttr{};
+  build(builder, result, result_type, memref, componentAttr, component_shape,
+        indices, isTripletAttr, substring, complexPartAttr, shape, typeparams,
+        fortran_attrs);
+}
+
+static mlir::ParseResult parseDesignatorIndices(
+    mlir::OpAsmParser &parser,
+    llvm::SmallVectorImpl<mlir::OpAsmParser::UnresolvedOperand> &indices,
+    mlir::DenseBoolArrayAttr &isTripletAttr) {
+  llvm::SmallVector<bool> isTriplet;
+  if (mlir::succeeded(parser.parseOptionalLParen())) {
+    do {
+      mlir::OpAsmParser::UnresolvedOperand i1, i2, i3;
+      if (parser.parseOperand(i1))
+        return mlir::failure();
+      indices.push_back(i1);
+      if (mlir::succeeded(parser.parseOptionalColon())) {
+        if (parser.parseOperand(i2) || parser.parseColon() ||
+            parser.parseOperand(i3))
+          return mlir::failure();
+        indices.push_back(i2);
+        indices.push_back(i3);
+        isTriplet.push_back(true);
+      } else {
+        isTriplet.push_back(false);
+      }
+    } while (mlir::succeeded(parser.parseOptionalComma()));
+    if (parser.parseRParen())
+      return mlir::failure();
+  }
+  isTripletAttr = mlir::DenseBoolArrayAttr::get(parser.getContext(), isTriplet);
+  return mlir::success();
+}
+
+static void
+printDesignatorIndices(mlir::OpAsmPrinter &p, hlfir::DesignateOp designateOp,
+                       mlir::OperandRange indices,
+                       const mlir::DenseBoolArrayAttr &isTripletAttr) {
+  if (!indices.empty()) {
+    p << '(';
+    unsigned i = 0;
+    for (auto isTriplet : isTripletAttr.asArrayRef()) {
+      if (isTriplet) {
+        assert(i + 2 < indices.size() && "ill-formed indices");
+        p << indices[i] << ":" << indices[i + 1] << ":" << indices[i + 2];
+        i += 3;
+      } else {
+        p << indices[i++];
+      }
+      if (i != indices.size())
+        p << ", ";
+    }
+    p << ')';
+  }
+}
+
+static mlir::ParseResult
+parseDesignatorComplexPart(mlir::OpAsmParser &parser,
+                           mlir::BoolAttr &complexPart) {
+  if (mlir::succeeded(parser.parseOptionalKeyword("imag")))
+    complexPart = mlir::BoolAttr::get(parser.getContext(), true);
+  else if (mlir::succeeded(parser.parseOptionalKeyword("real")))
+    complexPart = mlir::BoolAttr::get(parser.getContext(), false);
+  return mlir::success();
+}
+
+static void printDesignatorComplexPart(mlir::OpAsmPrinter &p,
+                                       hlfir::DesignateOp designateOp,
+                                       mlir::BoolAttr complexPartAttr) {
+  if (complexPartAttr) {
+    if (complexPartAttr.getValue())
+      p << "imag";
+    else
+      p << "real";
+  }
+}
+
+mlir::LogicalResult hlfir::DesignateOp::verify() {
+  mlir::Type memrefType = getMemref().getType();
+  mlir::Type baseType = getFortranElementOrSequenceType(memrefType);
+  mlir::Type baseElementType = fir::unwrapSequenceType(baseType);
+  unsigned numSubscripts = getIsTriplet().size();
+  unsigned subscriptsRank =
+      llvm::count_if(getIsTriplet(), [](bool isTriplet) { return isTriplet; });
+  unsigned outputRank = 0;
+  mlir::Type outputElementType;
+  bool hasBoxComponent;
+  if (getComponent()) {
+    auto component = getComponent().value();
+    auto recType = baseElementType.dyn_cast<fir::RecordType>();
+    if (!recType)
+      return emitOpError(
+          "component must be provided only when the memref is a derived type");
+    unsigned fieldIdx = recType.getFieldIndex(component);
+    if (fieldIdx > recType.getNumFields()) {
+      return emitOpError("component ")
+             << component << " is not a component of memref element type "
+             << recType;
+    }
+    mlir::Type fieldType = recType.getType(fieldIdx);
+    mlir::Type componentBaseType = getFortranElementOrSequenceType(fieldType);
+    hasBoxComponent = fieldType.isa<fir::BaseBoxType>();
+    if (componentBaseType.isa<fir::SequenceType>() &&
+        baseType.isa<fir::SequenceType>() &&
+        (numSubscripts == 0 || subscriptsRank > 0))
+      return emitOpError("indices must be provided and must not contain "
+                         "triplets when both memref and component are arrays");
+    if (numSubscripts != 0) {
+      if (!componentBaseType.isa<fir::SequenceType>())
+        return emitOpError("indices must not be provided if component appears "
+                           "and is not an array component");
+      if (!getComponentShape())
+        return emitOpError(
+            "component_shape must be provided when indexing a component");
+      mlir::Type compShapeType = getComponentShape().getType();
+      unsigned componentRank =
+          componentBaseType.cast<fir::SequenceType>().getDimension();
+      auto shapeType = compShapeType.dyn_cast<fir::ShapeType>();
+      auto shapeShiftType = compShapeType.dyn_cast<fir::ShapeShiftType>();
+      if (!((shapeType && shapeType.getRank() == componentRank) ||
+            (shapeShiftType && shapeShiftType.getRank() == componentRank)))
+        return emitOpError("component_shape must be a fir.shape or "
+                           "fir.shapeshift with the rank of the component");
+      if (numSubscripts > componentRank)
+        return emitOpError("indices number must match array component rank");
+    }
+    if (auto baseSeqType = baseType.dyn_cast<fir::SequenceType>())
+      // This case must come first to cover "array%array_comp(i, j)" that has
+      // subscripts for the component but whose rank come from the base.
+      outputRank = baseSeqType.getDimension();
+    else if (numSubscripts != 0)
+      outputRank = subscriptsRank;
+    else if (auto componentSeqType =
+                 componentBaseType.dyn_cast<fir::SequenceType>())
+      outputRank = componentSeqType.getDimension();
+    outputElementType = fir::unwrapSequenceType(componentBaseType);
+  } else {
+    outputElementType = baseElementType;
+    unsigned baseTypeRank =
+        baseType.isa<fir::SequenceType>()
+            ? baseType.cast<fir::SequenceType>().getDimension()
+            : 0;
+    if (numSubscripts != 0) {
+      if (baseTypeRank != numSubscripts)
+        return emitOpError("indices number must match memref rank");
+      outputRank = subscriptsRank;
+    } else if (auto baseSeqType = baseType.dyn_cast<fir::SequenceType>()) {
+      outputRank = baseSeqType.getDimension();
+    }
+  }
+
+  if (!getSubstring().empty()) {
+    if (!outputElementType.isa<fir::CharacterType>())
+      return emitOpError("memref or component must have character type if "
+                         "substring indices are provided");
+    if (getSubstring().size() != 2)
+      return emitOpError("substring must contain 2 indices when provided");
+  }
+  if (getComplexPart()) {
+    if (!fir::isa_complex(outputElementType))
+      return emitOpError("memref or component must have complex type if "
+                         "complex_part is provided");
+    if (auto firCplx = outputElementType.dyn_cast<fir::ComplexType>())
+      outputElementType = firCplx.getElementType();
+    else
+      outputElementType =
+          outputElementType.cast<mlir::ComplexType>().getElementType();
+  }
+  mlir::Type resultBaseType =
+      getFortranElementOrSequenceType(getResult().getType());
+  unsigned resultRank = 0;
+  if (auto resultSeqType = resultBaseType.dyn_cast<fir::SequenceType>())
+    resultRank = resultSeqType.getDimension();
+  if (resultRank != outputRank)
+    return emitOpError("result type rank is not consistent with operands, "
+                       "expected rank ")
+           << outputRank;
+  mlir::Type resultElementType = fir::unwrapSequenceType(resultBaseType);
+  // result type must match the one that was inferred here, except the character
+  // length may 
diff er because of substrings.
+  if (resultElementType != outputElementType &&
+      !(resultElementType.isa<fir::CharacterType>() &&
+        outputElementType.isa<fir::CharacterType>()))
+    return emitOpError(
+               "result element type is not consistent with operands, expected ")
+           << outputElementType;
+
+  if (isBoxAddressType(getResult().getType())) {
+    if (!hasBoxComponent || numSubscripts != 0 || !getSubstring().empty() ||
+        getComplexPart())
+      return emitOpError(
+          "result type must only be a box address type if it designates a "
+          "component that is a fir.box or fir.class and if there are no "
+          "indices, substrings, and complex part");
+
+  } else {
+    if ((resultRank == 0) != !getShape())
+      return emitOpError("shape must be provided if and only if the result is "
+                         "an array that is not a box address");
+    if (resultRank != 0) {
+      auto shapeType = getShape().getType().dyn_cast<fir::ShapeType>();
+      auto shapeShiftType =
+          getShape().getType().dyn_cast<fir::ShapeShiftType>();
+      if (!((shapeType && shapeType.getRank() == resultRank) ||
+            (shapeShiftType && shapeShiftType.getRank() == resultRank)))
+        return emitOpError("shape must be a fir.shape or fir.shapeshift with "
+                           "the rank of the result");
+    }
+    auto numLenParam = getTypeparams().size();
+    if (outputElementType.isa<fir::CharacterType>()) {
+      if (numLenParam != 1)
+        return emitOpError("must be provided one length parameter when the "
+                           "result is a character");
+    } else if (fir::isRecordWithTypeParameters(outputElementType)) {
+      if (numLenParam !=
+          outputElementType.cast<fir::RecordType>().getNumLenParams())
+        return emitOpError("must be provided the same number of length "
+                           "parameters as in the result derived type");
+    } else if (numLenParam != 0) {
+      return emitOpError("must not be provided length parameters if the result "
+                         "type does not have length parameters");
+    }
+  }
+  return mlir::success();
+}
+
 #define GET_OP_CLASSES
 #include "flang/Optimizer/HLFIR/HLFIROps.cpp.inc"

diff  --git a/flang/test/HLFIR/designate.fir b/flang/test/HLFIR/designate.fir
new file mode 100644
index 0000000000000..dcc156aed4797
--- /dev/null
+++ b/flang/test/HLFIR/designate.fir
@@ -0,0 +1,135 @@
+// Test hlfir.designate operation parse, verify (no errors), and unparse.
+
+// RUN: fir-opt %s | fir-opt | FileCheck %s
+
+// array(1)
+func.func @array_ref(%arg0 : !fir.ref<!fir.array<10xi32>>) {
+  %c1 = arith.constant 1 : index
+  %0 = hlfir.designate %arg0 (%c1) :  (!fir.ref<!fir.array<10xi32>>, index) -> !fir.ref<i32>
+  return
+}
+// CHECK-LABEL: func.func @array_ref(
+// CHECK-SAME:    %[[VAL_0:.*]]: !fir.ref<!fir.array<10xi32>>) {
+// CHECK:  %[[VAL_1:.*]] = arith.constant 1 : index
+// CHECK:  %[[VAL_2:.*]] = hlfir.designate %[[VAL_0]] (%[[VAL_1]]) :  (!fir.ref<!fir.array<10xi32>>, index) -> !fir.ref<i32>
+
+// array(1, 2:3:4, 5)
+func.func @array_section(%arg0 : !fir.box<!fir.array<10x?x30xi32>>) {
+  %c1 = arith.constant 1 : index
+  %c2 = arith.constant 2 : index
+  %c3 = arith.constant 3 : index
+  %c4 = arith.constant 4 : index
+  %c5 = arith.constant 5 : index
+  %shape = fir.undefined !fir.shape<1>
+  %0 = hlfir.designate %arg0 (%c1, %c2:%c3:%c4, %c5) shape %shape:  (!fir.box<!fir.array<10x?x30xi32>>, index, index, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<?xi32>>
+  return
+}
+// CHECK-LABEL: func.func @array_section(
+// CHECK-SAME:    %[[VAL_0:.*]]: !fir.box<!fir.array<10x?x30xi32>>) {
+// CHECK:  %[[VAL_1:.*]] = arith.constant 1 : index
+// CHECK:  %[[VAL_2:.*]] = arith.constant 2 : index
+// CHECK:  %[[VAL_3:.*]] = arith.constant 3 : index
+// CHECK:  %[[VAL_4:.*]] = arith.constant 4 : index
+// CHECK:  %[[VAL_5:.*]] = arith.constant 5 : index
+// CHECK:  %[[VAL_6:.*]] = fir.undefined !fir.shape<1>
+// CHECK:  %[[VAL_7:.*]] = hlfir.designate %[[VAL_0]] (%[[VAL_1]], %[[VAL_2]]:%[[VAL_3]]:%[[VAL_4]], %[[VAL_5]]) shape %[[VAL_6]] : (!fir.box<!fir.array<10x?x30xi32>>, index, index, index, index, index, !fir.shape<1>) -> !fir.box<!fir.array<?xi32>>
+
+// array%comp
+func.func @component_ref(%arg0 : !fir.box<!fir.array<10x!fir.type<t{i:i32}>>>) {
+  %shape = fir.undefined !fir.shape<1>
+  %0 = hlfir.designate %arg0 {"i"} shape %shape:  (!fir.box<!fir.array<10x!fir.type<t{i:i32}>>>, !fir.shape<1>) -> !fir.box<!fir.array<10xi32>>
+  return
+}
+// CHECK-LABEL: func.func @component_ref(
+// CHECK-SAME:    %[[VAL_0:.*]]: !fir.box<!fir.array<10x!fir.type<t{i:i32}>>>) {
+// CHECK:  %[[VAL_1:.*]] = fir.undefined !fir.shape<1>
+// CHECK:  %[[VAL_2:.*]] = hlfir.designate %[[VAL_0]]{"i"} shape %[[VAL_1]] : (!fir.box<!fir.array<10x!fir.type<t{i:i32}>>>, !fir.shape<1>) -> !fir.box<!fir.array<10xi32>>
+
+// array%array_comp(1)
+func.func @component_array_ref(%arg0 : !fir.box<!fir.array<10x!fir.type<t2{array_comp:!fir.array<100xi32>}>>>) {
+  %c1 = arith.constant 1 : index
+  %component_shape = fir.undefined !fir.shapeshift<1>
+  %result_shape = fir.undefined !fir.shape<1>
+  %0 = hlfir.designate %arg0 {"array_comp"}<%component_shape>(%c1) shape %result_shape : (!fir.box<!fir.array<10x!fir.type<t2{array_comp:!fir.array<100xi32>}>>>, !fir.shapeshift<1>, index, !fir.shape<1>) -> !fir.box<!fir.array<10xi32>>
+  return
+}
+// CHECK-LABEL: func.func @component_array_ref(
+// CHECK-SAME:    %[[VAL_0:.*]]: !fir.box<!fir.array<10x!fir.type<t2{array_comp:!fir.array<100xi32>}>>>) {
+// CHECK:  %[[VAL_1:.*]] = arith.constant 1 : index
+// CHECK:  %[[VAL_2:.*]] = fir.undefined !fir.shapeshift<1>
+// CHECK:  %[[VAL_3:.*]] = fir.undefined !fir.shape<1>
+// CHECK:  %[[VAL_4:.*]] = hlfir.designate %[[VAL_0]]{"array_comp"} <%[[VAL_2]]> (%[[VAL_1]]) shape %[[VAL_3]] : (!fir.box<!fir.array<10x!fir.type<t2{array_comp:!fir.array<100xi32>}>>>, !fir.shapeshift<1>, index, !fir.shape<1>) -> !fir.box<!fir.array<10xi32>>
+
+// char(3:4)
+func.func @substring(%arg0 : !fir.boxchar<1>) {
+  %c2 = arith.constant 2 : index
+  %c3 = arith.constant 3 : index
+  %c4 = arith.constant 4 : index
+  %0 = hlfir.designate %arg0 substr %c3, %c4 typeparams %c2:  (!fir.boxchar<1>, index, index, index) -> !fir.boxchar<1>
+  return
+}
+// CHECK-LABEL: func.func @substring(
+// CHECK-SAME:    %[[VAL_0:.*]]: !fir.boxchar<1>) {
+// CHECK:  %[[VAL_1:.*]] = arith.constant 2 : index
+// CHECK:  %[[VAL_2:.*]] = arith.constant 3 : index
+// CHECK:  %[[VAL_3:.*]] = arith.constant 4 : index
+// CHECK:  %[[VAL_4:.*]] = hlfir.designate %[[VAL_0]] substr %[[VAL_2]], %[[VAL_3]] typeparams %[[VAL_1]] : (!fir.boxchar<1>, index, index, index) -> !fir.boxchar<1>
+
+// char_array(3:4)
+func.func @array_substring(%arg0 : !fir.box<!fir.array<10x!fir.char<1,?>>>) {
+  %c2 = arith.constant 2 : index
+  %c3 = arith.constant 3 : index
+  %c4 = arith.constant 4 : index
+  %shape = fir.undefined !fir.shape<1>
+  %0 = hlfir.designate %arg0 substr %c3, %c4 shape %shape typeparams %c2 : (!fir.box<!fir.array<10x!fir.char<1,?>>>, index, index, !fir.shape<1>, index) -> !fir.box<!fir.array<10x!fir.char<1,?>>>
+  return
+}
+// CHECK-LABEL: func.func @array_substring(
+// CHECK-SAME:    %[[VAL_0:.*]]: !fir.box<!fir.array<10x!fir.char<1,?>>>) {
+// CHECK:  %[[VAL_1:.*]] = arith.constant 2 : index
+// CHECK:  %[[VAL_2:.*]] = arith.constant 3 : index
+// CHECK:  %[[VAL_3:.*]] = arith.constant 4 : index
+// CHECK:  %[[VAL_4:.*]] = fir.undefined !fir.shape<1>
+// CHECK:  %[[VAL_5:.*]] = hlfir.designate %[[VAL_0]] substr %[[VAL_2]], %[[VAL_3]] shape %[[VAL_4]] typeparams %[[VAL_1]] : (!fir.box<!fir.array<10x!fir.char<1,?>>>, index, index, !fir.shape<1>, index) -> !fir.box<!fir.array<10x!fir.char<1,?>>>
+
+func.func @real_part(%arg0 : !fir.ref<!fir.complex<4>>) {
+  %0 = hlfir.designate %arg0 real :  (!fir.ref<!fir.complex<4>>) -> !fir.ref<!fir.real<4>>
+  return
+}
+// CHECK-LABEL: func.func @real_part(
+// CHECK-SAME:    %[[VAL_0:.*]]: !fir.ref<!fir.complex<4>>) {
+// CHECK:  %[[VAL_1:.*]] = hlfir.designate %[[VAL_0]] real :  (!fir.ref<!fir.complex<4>>) -> !fir.ref<!fir.real<4>>
+
+func.func @imag_part_mlir_complex(%arg0 : !fir.ref<complex<f32>>) {
+  %0 = hlfir.designate %arg0 imag :  (!fir.ref<complex<f32>>) -> !fir.ref<f32>
+  return
+}
+// CHECK-LABEL: func.func @imag_part_mlir_complex(
+// CHECK-SAME:    %[[VAL_0:.*]]: !fir.ref<complex<f32>>) {
+// CHECK:  %[[VAL_1:.*]] = hlfir.designate %[[VAL_0]] imag :  (!fir.ref<complex<f32>>) -> !fir.ref<f32>
+
+// array%array_complex_comp(1)%im
+func.func @component_array_ref_cplx_part(%arg0 : !fir.box<!fir.array<10x!fir.type<t3{array_comp:!fir.array<100x!fir.complex<8>>}>>>) {
+  %c1 = arith.constant 1 : index
+  %component_shape = fir.undefined !fir.shapeshift<1>
+  %result_shape = fir.undefined !fir.shape<1>
+  %0 = hlfir.designate %arg0 {"array_comp"}<%component_shape>(%c1) imag shape %result_shape : (!fir.box<!fir.array<10x!fir.type<t3{array_comp:!fir.array<100x!fir.complex<8>>}>>>, !fir.shapeshift<1>, index, !fir.shape<1>) -> !fir.box<!fir.array<10x!fir.real<8>>>
+  return
+}
+// CHECK-LABEL: func.func @component_array_ref_cplx_part(
+// CHECK-SAME:    %[[VAL_0:.*]]: !fir.box<!fir.array<10x!fir.type<t3{array_comp:!fir.array<100x!fir.complex<8>>}>>>) {
+// CHECK:  %[[VAL_1:.*]] = arith.constant 1 : index
+// CHECK:  %[[VAL_2:.*]] = fir.undefined !fir.shapeshift<1>
+// CHECK:  %[[VAL_3:.*]] = fir.undefined !fir.shape<1>
+// CHECK:  %[[VAL_4:.*]] = hlfir.designate %[[VAL_0]]{"array_comp"} <%[[VAL_2]]> (%[[VAL_1]]) imag shape %[[VAL_3]] : (!fir.box<!fir.array<10x!fir.type<t3{array_comp:!fir.array<100x!fir.complex<8>>}>>>, !fir.shapeshift<1>, index, !fir.shape<1>) -> !fir.box<!fir.array<10x!fir.real<8>>>
+
+// pdt_array(1)
+func.func @pdt(%arg0 : !fir.box<!fir.array<?x!fir.type<pdt(param:i32){field:i32}>>>) {
+  %c1 = arith.constant 1 : index
+  %0 = hlfir.designate %arg0(%c1) typeparams %c1 :  (!fir.box<!fir.array<?x!fir.type<pdt(param:i32){field:i32}>>>, index, index) -> !fir.box<!fir.type<pdt(param:i32){field:i32}>>
+  return
+}
+// CHECK-LABEL: func.func @pdt(
+// CHECK-SAME:    %[[VAL_0:.*]]: !fir.box<!fir.array<?x!fir.type<pdt(param:i32){field:i32}>>>) {
+// CHECK:  %[[VAL_1:.*]] = arith.constant 1 : index
+// CHECK:  %[[VAL_2:.*]] = hlfir.designate %[[VAL_0]] (%[[VAL_1]]) typeparams %[[VAL_1]] : (!fir.box<!fir.array<?x!fir.type<pdt(param:i32){field:i32}>>>, index, index) -> !fir.box<!fir.type<pdt(param:i32){field:i32}>>

diff  --git a/flang/test/HLFIR/invalid.fir b/flang/test/HLFIR/invalid.fir
index cf99e6bd75993..a4b901fc0d95a 100644
--- a/flang/test/HLFIR/invalid.fir
+++ b/flang/test/HLFIR/invalid.fir
@@ -70,3 +70,188 @@ func.func @bad_assign_array_2(%arg0: !fir.ref<!fir.array<10xi32>>, %arg1: !hlfir
   hlfir.assign %arg0 to %arg1 : !fir.ref<!fir.array<10xi32>>, !hlfir.expr<?xi32>
   return
 }
+
+// -----
+func.func @bad_designate_component(%arg0 : !fir.ref<i32>) {
+  // expected-error at +1 {{'hlfir.designate' op component must be provided only when the memref is a derived type}}
+  %0 = hlfir.designate %arg0 {"some_component"} :  (!fir.ref<i32>) -> !fir.ref<i32>
+  return
+}
+
+// -----
+func.func @bad_designate_component_2(%arg0 : !fir.ref<!fir.type<t{i:i32}>>) {
+  // expected-error at +1 {{'hlfir.designate' op component "bad_comp" is not a component of memref element type '!fir.type<t{i:i32}>'}}
+  %0 = hlfir.designate %arg0 {"bad_comp"} :  (!fir.ref<!fir.type<t{i:i32}>>) -> !fir.ref<i32>
+  return
+}
+
+// -----
+func.func @bad_designate_component_3(%arg0 : !fir.ref<!fir.array<20x!fir.type<t{i:!fir.array<100xi32>}>>>) {
+  // expected-error at +1 {{'hlfir.designate' op indices must be provided and must not contain triplets when both memref and component are arrays}}
+  %0 = hlfir.designate %arg0 {"i"} :  (!fir.ref<!fir.array<20x!fir.type<t{i:!fir.array<100xi32>}>>>) -> !fir.ref<i32>
+  return
+}
+
+// -----
+func.func @bad_designate_component_4(%arg0 : !fir.ref<!fir.array<20x!fir.type<t{i:!fir.array<100xi32>}>>>) {
+  %component_shape = fir.undefined !fir.shape<1>
+  %c1 = arith.constant 1 : index
+  // expected-error at +1 {{'hlfir.designate' op indices must be provided and must not contain triplets when both memref and component are arrays}}
+  %0 = hlfir.designate %arg0 {"i"}<%component_shape>(%c1:%c1:%c1):  (!fir.ref<!fir.array<20x!fir.type<t{i:!fir.array<100xi32>}>>>, !fir.shape<1>, index, index, index) -> !fir.ref<!fir.array<20xi32>>
+  return
+}
+
+// -----
+func.func @bad_designate_component_5(%arg0 : !fir.ref<!fir.array<20x!fir.type<t{i:!fir.array<100xi32>}>>>) {
+  %component_shape = fir.undefined !fir.shape<2>
+  %c1 = arith.constant 1 : index
+  // expected-error at +1 {{'hlfir.designate' op component_shape must be a fir.shape or fir.shapeshift with the rank of the component}}
+  %0 = hlfir.designate %arg0 {"i"}<%component_shape>(%c1):  (!fir.ref<!fir.array<20x!fir.type<t{i:!fir.array<100xi32>}>>>, !fir.shape<2>, index) -> !fir.ref<!fir.array<20xi32>>
+  return
+}
+
+// -----
+func.func @bad_designate_component_6(%arg0 : !fir.ref<!fir.array<20x!fir.type<t{i:!fir.array<100xi32>}>>>) {
+  %component_shape = fir.undefined !fir.shift<1>
+  %c1 = arith.constant 1 : index
+  // expected-error at +1 {{'hlfir.designate' op component_shape must be a fir.shape or fir.shapeshift with the rank of the component}}
+  %0 = hlfir.designate %arg0 {"i"}<%component_shape>(%c1):  (!fir.ref<!fir.array<20x!fir.type<t{i:!fir.array<100xi32>}>>>, !fir.shift<1>, index) -> !fir.ref<!fir.array<20xi32>>
+  return
+}
+
+// -----
+func.func @bad_designate_component_7(%arg0 : !fir.ref<!fir.array<20x!fir.type<t{i:!fir.array<100xi32>}>>>) {
+  %component_shape = fir.undefined !fir.shapeshift<2>
+  %c1 = arith.constant 1 : index
+  // expected-error at +1 {{'hlfir.designate' op component_shape must be a fir.shape or fir.shapeshift with the rank of the component}}
+  %0 = hlfir.designate %arg0 {"i"}<%component_shape>(%c1):  (!fir.ref<!fir.array<20x!fir.type<t{i:!fir.array<100xi32>}>>>, !fir.shapeshift<2>, index) -> !fir.ref<!fir.array<20xi32>>
+  return
+}
+
+// -----
+func.func @bad_designate_component_8(%arg0 : !fir.ref<!fir.type<t{i:!fir.array<100xi32>}>>) {
+  %c1 = arith.constant 1 : index
+  // expected-error at +1 {{'hlfir.designate' op component_shape must be provided when indexing a component}}
+  %0 = hlfir.designate %arg0 {"i"}(%c1):  (!fir.ref<!fir.type<t{i:!fir.array<100xi32>}>>, index) -> !fir.ref<i32>
+  return
+}
+
+// -----
+func.func @bad_designate_component_9(%arg0 : !fir.ref<!fir.array<20x!fir.type<t{i:i32}>>>) {
+  %c1 = arith.constant 1 : index
+  // expected-error at +1 {{'hlfir.designate' op indices must not be provided if component appears and is not an array component}}
+  %0 = hlfir.designate %arg0 {"i"}(%c1):  (!fir.ref<!fir.array<20x!fir.type<t{i:i32}>>>, index) -> !fir.ref<i32>
+  return
+}
+
+// -----
+func.func @bad_designate_component_10(%arg0 : !fir.ref<!fir.type<t{i:!fir.array<100xi32>}>>) {
+  %component_shape = fir.undefined !fir.shapeshift<1>
+  %c1 = arith.constant 1 : index
+  // expected-error at +1 {{'hlfir.designate' op indices number must match array component rank}}
+  %0 = hlfir.designate %arg0 {"i"}<%component_shape>(%c1, %c1):  (!fir.ref<!fir.type<t{i:!fir.array<100xi32>}>>, !fir.shapeshift<1>, index, index) -> !fir.ref<i32>
+  return
+}
+
+// -----
+func.func @bad_designate_substring_1(%arg0 : !fir.ref<!fir.char<1,20>>) {
+  %c1 = arith.constant 1 : index
+  // expected-error at +1 {{'hlfir.designate' op substring must contain 2 indices when provided}}
+  %0 = hlfir.designate %arg0 substr %c1, %c1, %c1:  (!fir.ref<!fir.char<1,20>>, index, index, index) -> !fir.boxchar<1>
+  return
+}
+
+// -----
+func.func @bad_designate_indices_1(%arg0 : !fir.ref<i32>) {
+  %c1 = arith.constant 1 : index
+  // expected-error at +1 {{'hlfir.designate' op indices number must match memref rank}}
+  %0 = hlfir.designate %arg0 (%c1, %c1):  (!fir.ref<i32>, index, index) -> !fir.ref<i32>
+  return
+}
+
+// -----
+func.func @bad_designate_indices_2(%arg0 : !fir.ref<!fir.array<10xi32>>) {
+  %c1 = arith.constant 1 : index
+  // expected-error at +1 {{'hlfir.designate' op indices number must match memref rank}}
+  %0 = hlfir.designate %arg0 (%c1, %c1):  (!fir.ref<!fir.array<10xi32>>, index, index) -> !fir.ref<i32>
+  return
+}
+
+// -----
+func.func @bad_designate_substring_2(%arg0 : !fir.ref<i32>) {
+  %c1 = arith.constant 1 : index
+  // expected-error at +1 {{'hlfir.designate' op memref or component must have character type if substring indices are provided}}
+  %0 = hlfir.designate %arg0 substr %c1, %c1:  (!fir.ref<i32>, index, index) -> !fir.boxchar<1>
+  return
+}
+
+// -----
+func.func @bad_designate_cmplx_part(%arg0 : !fir.ref<!fir.array<10xi32>>) {
+  %c1 = arith.constant 1 : index
+  // expected-error at +1 {{'hlfir.designate' op memref or component must have complex type if complex_part is provided}}
+  %0 = hlfir.designate %arg0 (%c1) imag:  (!fir.ref<!fir.array<10xi32>>, index) -> !fir.ref<i32>
+  return
+}
+
+// -----
+func.func @bad_designate_out_rank(%arg0 : !fir.ref<!fir.array<10xi32>>) {
+  %c1 = arith.constant 1 : index
+  // expected-error at +1 {{'hlfir.designate' op result type rank is not consistent with operands, expected rank 1}}
+  %0 = hlfir.designate %arg0 (%c1:%c1:%c1):  (!fir.ref<!fir.array<10xi32>>, index, index, index) -> !fir.ref<i32>
+  return
+}
+
+// -----
+func.func @bad_designate_out_type(%arg0 : !fir.ref<!fir.complex<4>>) {
+  // expected-error at +1 {{'hlfir.designate' op result element type is not consistent with operands, expected '!fir.real<4>'}}
+  %0 = hlfir.designate %arg0 imag:  (!fir.ref<!fir.complex<4>>) -> !fir.ref<!fir.complex<4>>
+  return
+}
+
+// -----
+func.func @bad_designate_out_type(%arg0 : !fir.ref<!fir.box<!fir.complex<4>>>) {
+  // expected-error at +1 {{'hlfir.designate' op result type must only be a box address type if it designates a component that is a fir.box or fir.class and if there are no indices, substrings, and complex part}}
+  %0 = hlfir.designate %arg0 imag:  (!fir.ref<!fir.box<!fir.complex<4>>>) -> !fir.ref<!fir.box<!fir.real<4>>>
+  return
+}
+
+// -----
+func.func @bad_designate_shape(%arg0 : !fir.ref<!fir.array<10xi32>>) {
+  %c1 = arith.constant 1 : index
+  // expected-error at +1 {{'hlfir.designate' op shape must be provided if and only if the result is an array that is not a box address}}
+  %0 = hlfir.designate %arg0 (%c1:%c1:%c1):  (!fir.ref<!fir.array<10xi32>>, index, index, index) -> !fir.box<!fir.array<?xi32>>
+  return
+}
+
+// -----
+func.func @bad_designate_shape_2(%arg0 : !fir.ref<!fir.array<10xi32>>) {
+  %c1 = arith.constant 1 : index
+  %shape = fir.undefined !fir.shape<1>
+  // expected-error at +1 {{'hlfir.designate' op shape must be provided if and only if the result is an array that is not a box address}}
+  %0 = hlfir.designate %arg0 (%c1) shape %shape:  (!fir.ref<!fir.array<10xi32>>, index, !fir.shape<1>) -> !fir.ref<i32>
+  return
+}
+
+// -----
+func.func @bad_designate_len_params(%arg0 : !fir.ref<!fir.char<1,10>>) {
+  %c1 = arith.constant 1 : index
+  // expected-error at +1 {{'hlfir.designate' op must be provided one length parameter when the result is a character}}
+  %0 = hlfir.designate %arg0 substr %c1, %c1:  (!fir.ref<!fir.char<1,10>>, index, index) -> !fir.boxchar<1>
+  return
+}
+
+// -----
+func.func @bad_designate_len_params_2(%arg0 : !fir.box<!fir.array<?x!fir.type<pdt(param:i32){field:i32}>>>) {
+  %c1 = arith.constant 1 : index
+  // expected-error at +1 {{'hlfir.designate' op must be provided the same number of length parameters as in the result derived type}}
+  %0 = hlfir.designate %arg0(%c1) typeparams %c1, %c1 :  (!fir.box<!fir.array<?x!fir.type<pdt(param:i32){field:i32}>>>, index, index, index) -> !fir.box<!fir.type<pdt(param:i32){field:i32}>>
+  return
+}
+
+// -----
+func.func @bad_designate_len_params_3(%arg0 : !fir.box<!fir.array<?xi32>>) {
+  %c1 = arith.constant 1 : index
+  // expected-error at +1 {{'hlfir.designate' op must not be provided length parameters if the result type does not have length parameters}}
+  %0 = hlfir.designate %arg0(%c1) typeparams %c1 :  (!fir.box<!fir.array<?xi32>>, index, index) -> !fir.ref<i32>
+  return
+}


        


More information about the flang-commits mailing list