```
Along with result name access, variables of `Op` type may implicitly convert to
-`Value` or `ValueRange`. These variables are converted to `Value` when they are
-known (via ODS) to only have one result, in all other cases they convert to
-`ValueRange`:
+`Value` or `ValueRange`. If these variables are registered (has ODS entry), they
+are converted to `Value` when they are known to only have one result, otherwise
+they will be converted to `ValueRange`:
```pdll
// `resultOp` may also convert implicitly to a Value for use in `inputOp`:
let inputOp = op<my_dialect.input_op>(op<my_dialect.result_op>);
```
+#### Unregistered Operations
+
+A variable of unregistered op is still available for numeric result indexing.
+Given that we don't have knowledge of its result groups, numeric indexing
+returns a Value corresponding to the individual result at the given index.
+
+```pdll
+// Use the index `0` to refer to the first result value of the unregistered op.
+let inputOp = op<my_dialect.input_op>(op<my_dialect.unregistered_op>.0);
+```
+
### Attribute Expression
An attribute expression represents a literal MLIR attribute. It allows for
assert(opType.getName() && "expected valid operation name");
const ods::Operation *odsOp = odsContext.lookupOperation(*opType.getName());
- assert(odsOp && "expected valid ODS operation information");
+
+ if (!odsOp) {
+ assert(llvm::isDigit(name[0]) && "unregistered op only allows numeric indexing");
+ unsigned resultIndex;
+ name.getAsInteger(/*Radix=*/10, resultIndex);
+ IntegerAttr index = builder.getI32IntegerAttr(resultIndex);
+ return builder.create<pdl::ResultOp>(loc, genType(expr->getType()),
+ parentExprs[0], index);
+ }
// Find the result with the member name or by index.
ArrayRef<ods::OperandOrResult> results = odsOp->getResults();
});
if (it != results.end())
return it->isVariadic() ? valueRangeTy : valueTy;
+ } else if (llvm::isDigit(name[0])) {
+ // Allow unchecked numeric indexing of the results of unregistered
+ // operations. It returns a single value.
+ return valueTy;
}
-
} else if (auto tupleType = parentType.dyn_cast<ast::TupleType>()) {
// Handle indexed results.
unsigned index = 0;
// -----
+// Handle result indexing on unregistered op.
+// CHECK: pdl.pattern @UnregisteredOpResultIndexing
+// CHECK: %[[BAR_OP:.*]] = operation "my_dialect.unregistered_bar"
+// CHECK: %[[BAR_RES:.*]] = result 0 of %[[BAR_OP]]
+// CHECK: operation "my_dialect.unregistered_foo"(%[[BAR_RES]] : !pdl.value)
+Pattern UnregisteredOpResultIndexing {
+ let bar : Op<my_dialect.unregistered_bar>;
+ let op = op<my_dialect.unregistered_foo>(bar.0);
+ erase op;
+}
+
+// -----
+
// Handle implicit "named" operation results access.
#include "include/ops.td"
// -----
+// CHECK: Module
+// CHECK: `-VariableDecl {{.*}} Name<op> Type<Op<my_dialect.unregistered_foo>>
+// CHECK: `-OperationExpr {{.*}} Type<Op<my_dialect.unregistered_foo>>
+// CHECK: `-OpNameDecl {{.*}} Name<my_dialect.unregistered_foo>
+// CHECK: `Operands`
+// CHECK: `-MemberAccessExpr {{.*}} Member<0> Type<Value>
+// CHECK: `-OperationExpr {{.*}} Type<Op<my_dialect.unregistered_bar>>
+// CHECK: `-OpNameDecl {{.*}} Name<my_dialect.unregistered_bar>
+// CHECK: `Operands`
+// CHECK: `-DeclRefExpr {{.*}} Type<ValueRange>
+// CHECK: `-VariableDecl {{.*}} Name<_> Type<ValueRange>
+// CHECK: `Constraints`
+// CHECK: `-ValueRangeConstraintDecl
+Pattern {
+ let op = op<my_dialect.unregistered_foo>(op<my_dialect.unregistered_bar>.0);
+ erase op;
+}
+
+// -----
+
//===----------------------------------------------------------------------===//
// OperationExpr
//===----------------------------------------------------------------------===//