llvm/mlir/test/Dialect/IRDL/testd.mlir

// RUN: mlir-opt %s --irdl-file=%S/testd.irdl.mlir -split-input-file -verify-diagnostics | FileCheck %s

//===----------------------------------------------------------------------===//
// Type or attribute constraint
//===----------------------------------------------------------------------===//

func.func @typeFitsType() {
  // CHECK: "testd.any"() : () -> !testd.parametric<i32>
  "testd.any"() : () -> !testd.parametric<i32>
  return
}

// -----

func.func @attrDoesntFitType() {
  "testd.any"() : () -> !testd.parametric<"foo">
  return
}

// -----

func.func @attrFitsAttr() {
  // CHECK: "testd.any"() : () -> !testd.attr_in_type_out<"foo">
  "testd.any"() : () -> !testd.attr_in_type_out<"foo">
  return
}

// -----

func.func @typeFitsAttr() {
  // CHECK: "testd.any"() : () -> !testd.attr_in_type_out<i32>
  "testd.any"() : () -> !testd.attr_in_type_out<i32>
  return
}

// -----

//===----------------------------------------------------------------------===//
// Equality constraint
//===----------------------------------------------------------------------===//

func.func @succeededEqConstraint() {
  // CHECK: "testd.eq"() : () -> i32
  "testd.eq"() : () -> i32
  return
}

// -----

func.func @failedEqConstraint() {
  // expected-error@+1 {{expected 'i32' but got 'i64'}}
  "testd.eq"() : () -> i64
  return
}

// -----

//===----------------------------------------------------------------------===//
// AnyOf constraint
//===----------------------------------------------------------------------===//

func.func @succeededAnyOfConstraint() {
  // CHECK: "testd.anyof"() : () -> i32
  "testd.anyof"() : () -> i32
  // CHECK: "testd.anyof"() : () -> i64
  "testd.anyof"() : () -> i64
  return
}

// -----

func.func @failedAnyOfConstraint() {
  // expected-error@+1 {{'i1' does not satisfy the constraint}}
  "testd.anyof"() : () -> i1
  return
}

// -----

//===----------------------------------------------------------------------===//
// AllOf constraint
//===----------------------------------------------------------------------===//

func.func @succeededAllOfConstraint() {
  // CHECK: "testd.all_of"() : () -> i64
  "testd.all_of"() : () -> i64
  return
}

// -----

func.func @failedAllOfConstraint1() {
  // expected-error@+1 {{'i1' does not satisfy the constraint}}
  "testd.all_of"() : () -> i1
  return
}

// -----

func.func @failedAllOfConstraint2() {
  // expected-error@+1 {{expected 'i64' but got 'i32'}}
  "testd.all_of"() : () -> i32
  return
}

// -----

//===----------------------------------------------------------------------===//
// Any constraint
//===----------------------------------------------------------------------===//

func.func @succeededAnyConstraint() {
  // CHECK: "testd.any"() : () -> i32
  "testd.any"() : () -> i32
  // CHECK: "testd.any"() : () -> i64
  "testd.any"() : () -> i64
  return
}

// -----

//===----------------------------------------------------------------------===//
// Base constraints
//===----------------------------------------------------------------------===//

func.func @succeededDynBaseConstraint() {
  // CHECK: "testd.dyn_type_base"() : () -> !testd.parametric<i32>
  "testd.dyn_type_base"() : () -> !testd.parametric<i32>
  // CHECK: "testd.dyn_type_base"() : () -> !testd.parametric<i64>
  "testd.dyn_type_base"() : () -> !testd.parametric<i64>
  // CHECK: "testd.dyn_type_base"() : () -> !testd.parametric<!testd.parametric<i64>>
  "testd.dyn_type_base"() : () -> !testd.parametric<!testd.parametric<i64>>
  // CHECK: "testd.dyn_attr_base"() {attr1 = #testd.parametric_attr<i32>} : () -> ()
  "testd.dyn_attr_base"() {attr1 = #testd.parametric_attr<i32>} : () -> ()
  // CHECK: "testd.dyn_attr_base"() {attr1 = #testd.parametric_attr<i64>} : () -> ()
  "testd.dyn_attr_base"() {attr1 = #testd.parametric_attr<i64>} : () -> ()
  return
}

// -----

func.func @failedDynTypeBaseConstraint() {
  // expected-error@+1 {{expected base type 'testd.parametric' but got 'builtin.integer'}}
  "testd.dyn_type_base"() : () -> i32
  return
}

// -----

func.func @failedDynAttrBaseConstraintNotType() {
  // expected-error@+1 {{expected base attribute 'testd.parametric_attr' but got 'builtin.type'}}
  "testd.dyn_attr_base"() {attr1 = i32}: () -> ()
  return
}

// -----


func.func @succeededNamedBaseConstraint() {
  // CHECK: "testd.named_type_base"() : () -> i32
  "testd.named_type_base"() : () -> i32
  // CHECK: "testd.named_type_base"() : () -> i64
  "testd.named_type_base"() : () -> i64
  // CHECK: "testd.named_attr_base"() {attr1 = 0 : i32} : () -> ()
  "testd.named_attr_base"() {attr1 = 0 : i32} : () -> ()
  // CHECK: "testd.named_attr_base"() {attr1 = 0 : i64} : () -> ()
  "testd.named_attr_base"() {attr1 = 0 : i64} : () -> ()
  return
}

// -----

func.func @failedNamedTypeBaseConstraint() {
  // expected-error@+1 {{expected base type 'builtin.integer' but got 'builtin.vector'}}
  "testd.named_type_base"() : () -> vector<i32>
  return
}

// -----

func.func @failedDynAttrBaseConstraintNotType() {
  // expected-error@+1 {{expected base attribute 'builtin.integer' but got 'builtin.type'}}
  "testd.named_attr_base"() {attr1 = i32}: () -> ()
  return
}

// -----

//===----------------------------------------------------------------------===//
// Dynamic parameters constraint
//===----------------------------------------------------------------------===//

func.func @succeededDynParamsConstraint() {
  // CHECK: "testd.dynparams"() : () -> !testd.parametric<i32>
  "testd.dynparams"() : () -> !testd.parametric<i32>
  // CHECK: "testd.dynparams"() : () -> !testd.parametric<i64>
  "testd.dynparams"() : () -> !testd.parametric<i64>
  return
}

// -----

func.func @failedDynParamsConstraintBase() {
  // expected-error@+1 {{expected base type 'testd.parametric' but got 'i32'}}
  "testd.dynparams"() : () -> i32
  return
}

// -----

func.func @failedDynParamsConstraintParam() {
  // expected-error@+1 {{'i1' does not satisfy the constraint}}
  "testd.dynparams"() : () -> !testd.parametric<i1>
  return
}

// -----

//===----------------------------------------------------------------------===//
// Constraint variables
//===----------------------------------------------------------------------===//

func.func @succeededConstraintVars() {
  // CHECK: "testd.constraint_vars"() : () -> (i32, i32)
  "testd.constraint_vars"() : () -> (i32, i32)
  return
}

// -----

func.func @succeededConstraintVars2() {
  // CHECK: "testd.constraint_vars"() : () -> (i64, i64)
  "testd.constraint_vars"() : () -> (i64, i64)
  return
}

// -----

func.func @failedConstraintVars() {
  // expected-error@+1 {{expected 'i64' but got 'i32'}}
  "testd.constraint_vars"() : () -> (i64, i32)
  return
}

// -----

//===----------------------------------------------------------------------===//
// Constraint attributes
//===----------------------------------------------------------------------===//

func.func @succeededAttrs() {
  // CHECK: "testd.attrs"() {attr1 = i32, attr2 = i64} : () -> ()
  "testd.attrs"() {attr1 = i32, attr2 = i64} : () -> ()
  return
}

// -----

func.func @failedAttrsMissingAttr() {
  // expected-error@+1 {{attribute "attr2" is expected but not provided}}
  "testd.attrs"() {attr1 = i32} : () -> ()
  return
}

// -----

func.func @failedAttrsConstraint() {
  // expected-error@+1 {{expected 'i32' but got 'i64'}}
  "testd.attrs"() {attr1 = i64, attr2 = i64} : () -> ()
  return
}

// -----

func.func @failedAttrsConstraint2() {
  // expected-error@+1 {{expected 'i64' but got 'i32'}}
  "testd.attrs"() {attr1 = i32, attr2 = i32} : () -> ()
  return
}

// -----

//===----------------------------------------------------------------------===//
// Regions
//===----------------------------------------------------------------------===//

// CHECK: func.func @succeededRegions
func.func @succeededRegions() {
  "testd.regions"() (
  {
    ^bb1:
      llvm.unreachable
  },
  {
    ^bb1(%arg0: i32, %arg1: i64):
      llvm.unreachable
  },
  {
    ^bb1:
      cf.br ^bb3
    ^bb2:
      cf.br ^bb3
    ^bb3:
      llvm.unreachable
  },
  {
    ^bb1:
      llvm.unreachable
  }) : () -> ()

  return
}
// -----

// CHECK: func.func @succeededRegionWithNoConstraints
func.func @succeededRegionWithNoConstraints() {
  "testd.regions"() (
  {
    ^bb1(%arg0: i32, %arg1: i64, %arg2 : f64):
      llvm.unreachable
    ^bb2(%arg3: i32, %arg4: i64, %arg5 : f64):
      llvm.unreachable
    ^bb3(%arg6: i32, %arg7: i64, %arg8 : f64):
      llvm.unreachable
    ^bb4(%arg9: i32, %arg10: i64, %arg11 : f64):
      llvm.unreachable
    ^bb5(%arg12: i32, %arg13: i64, %arg14 : f64):
      llvm.unreachable
  },
  {
    ^bb1(%arg0: i32, %arg1: i64):
      llvm.unreachable
  },
  {
    ^bb1:
      cf.br ^bb3
    ^bb2:
      cf.br ^bb3
    ^bb3:
      llvm.unreachable
  },
  {
    ^bb1:
      llvm.unreachable
  }) : () -> ()

  return
}

// -----

func.func @failedRegionArgsLessThanNeeded() {
  // expected-note@+1 {{see the operation}}
  "testd.regions"() (
  {
    ^bb1:
      llvm.unreachable
  },
  {
    // expected-error@+1 {{expected region 1 to have 2 arguments but got 1}}
    ^bb1(%arg0: i32):
      llvm.unreachable
  },
  {
    ^bb1:
      cf.br ^bb3
    ^bb2:
      cf.br ^bb3
    ^bb3:
      llvm.unreachable
  },
  {
    ^bb1:
      llvm.unreachable
  }) : () -> ()

  return
}

// -----

func.func @failedRegionArgsMoreThanNeeded() {
  // expected-note@+1 {{see the operation}}
  "testd.regions"() (
  {
    ^bb1:
      llvm.unreachable
  },
  {
    // expected-error@+1 {{expected region 1 to have 2 arguments but got 3}}
    ^bb1(%arg0: i32, %arg1: i64, %arg2 : f64):
      llvm.unreachable
  },
  {
    ^bb1:
      cf.br ^bb3
    ^bb2:
      cf.br ^bb3
    ^bb3:
      llvm.unreachable
  },
  {
    ^bb1:
      llvm.unreachable
  }) : () -> ()

  return
}

// -----

func.func @failedRegionArgsEmptyButRequired() {
  // expected-error@+1 {{expected region 1 to have 2 arguments but got 0}}
  "testd.regions"() (
  {
    ^bb1:
      llvm.unreachable
  },
  {
    ^bb1():
      llvm.unreachable
  },
  {
    ^bb1:
      cf.br ^bb3
    ^bb2:
      cf.br ^bb3
    ^bb3:
      llvm.unreachable
  },
  {
    ^bb1:
      llvm.unreachable
  }) : () -> ()

  return
}

// -----

func.func @faIledRegionArgsConstraint() {
  // expected-note@+1 {{see the operation}}
  "testd.regions"() (
  {
    ^bb1:
      llvm.unreachable
  },
  {
    // expected-error@+1 {{expected 'i64' but got 'f64'}}
    ^bb1(%arg0: i32, %arg1: f64):
      llvm.unreachable
  },
  {
    ^bb1:
      cf.br ^bb3
    ^bb2:
      cf.br ^bb3
    ^bb3:
      llvm.unreachable
  },
  {
    ^bb1:
      llvm.unreachable
  }) : () -> ()

  return
}

// -----

func.func @failedRegionBlocksCountLessThanNeeded() {
  // expected-error@+1 {{expected region 2 to have 3 block(s) but got 2}}
  "testd.regions"() (
  {
    ^bb1:
      llvm.unreachable
  },
  {
    ^bb1(%arg0: i32, %arg1: i64):
      llvm.unreachable
  },
  {
    ^bb1:
      cf.br ^bb3
    ^bb3:
      llvm.unreachable
  },
  {
    ^bb1:
      llvm.unreachable
  }) : () -> ()

  return
}

// -----

func.func @failedRegionBlocksCountMoreThanNeeded() {
  // expected-error@+1 {{expected region 2 to have 3 block(s) but got 4}}
  "testd.regions"() (
  {
    ^bb1:
      llvm.unreachable
  },
  {
    ^bb1(%arg0: i32, %arg1: i64):
      llvm.unreachable
  },
  {
    ^bb1:
      cf.br ^bb3
    ^bb2:
      cf.br ^bb3
    ^bb4:
      cf.br ^bb3
    ^bb3:
      llvm.unreachable
  },
  {
    ^bb1:
      llvm.unreachable
  }) : () -> ()

  return
}

// -----

func.func @failedRegionWithEmptyArgs() {
  // expected-note@+1 {{see the operation}}
  "testd.regions"() (
  {
    ^bb1:
      llvm.unreachable
  },
  {
    ^bb1(%arg0: i32, %arg1: i64):
      llvm.unreachable
  },
  {
    ^bb1:
      cf.br ^bb3
    ^bb2:
      cf.br ^bb3
    ^bb3:
      llvm.unreachable
  },
  {
    // expected-error@+1 {{expected region 3 to have 0 arguments but got 2}}
    ^bb1(%arg0: i32, %arg1: i64):
      llvm.unreachable
  }) : () -> ()

  return
}

// -----

func.func @failedRegionWithLessBlocksThanNeeded() {
  // expected-error@+1 {{'testd.regions' op unexpected number of regions: expected 4 but got 3}}
  "testd.regions"() (
  {
    ^bb1:
      llvm.unreachable
  },
  {
    ^bb1:
      cf.br ^bb3
    ^bb2:
      cf.br ^bb3
    ^bb3:
      llvm.unreachable
  },
  {
    ^bb1:
      llvm.unreachable
  }) : () -> ()

  return
}

// -----

func.func @failedRegionWithMoreBlocksThanNeeded() {
  // expected-error@+1 {{'testd.regions' op unexpected number of regions: expected 4 but got 5}}
  "testd.regions"() (
  {
    ^bb1:
      llvm.unreachable
  },
  {
    ^bb1(%arg0: i32, %arg1: i64):
      llvm.unreachable
  },
  {
    ^bb1:
      cf.br ^bb3
    ^bb2:
      cf.br ^bb3
    ^bb3:
      llvm.unreachable
  },
  {
    ^bb1(%arg0: i32, %arg1: i64):
      llvm.unreachable
  },
  {
    ^bb1(%arg0: i32, %arg1: i64):
      llvm.unreachable
  }) : () -> ()

  return
}

// -----

func.func @successReuseConstraintBetweenRegionAndOperand() {
  %0 = arith.constant 42 : i32
  "testd.region_and_operand"(%0) ({
    ^bb(%1: i32):
      llvm.unreachable
  }) : (i32) -> ()

  return
}

// -----

func.func @failedReuseConstraintBetweenRegionAndOperand() {
  %0 = arith.constant 42 : i32
  // expected-note@+1 {{see the operation}}
  "testd.region_and_operand"(%0) ({
    // expected-error@+1 {{expected 'i32' but got 'i64'}}
    ^bb(%1: i64):
      llvm.unreachable
  }) : (i32) -> ()

  return
}