[mlir][OpFormatGen] Refactor `type_ref` into a more general `ref` directive

This allows for referencing nearly every component of an operation from within a custom directive.

It also fixes a bug with the current type_ref implementation, PR48478

Differential Revision: https://reviews.llvm.org/D96189
This commit is contained in:
River Riddle 2021-02-09 14:32:15 -08:00
parent b9c876bd7e
commit 6e3292b0b7
6 changed files with 511 additions and 399 deletions

View File

@ -613,6 +613,15 @@ The available directives are as follows:
- Represents all of the operands of an operation. - Represents all of the operands of an operation.
* `ref` ( input )
- Represents a reference to the a variable or directive, that must have
already been resolved, that may be used as a parameter to a `custom`
directive.
- Used to pass previously parsed entities to custom directives.
- The input may be any directive or variable, aside from `functional-type`
and `custom`.
* `regions` * `regions`
- Represents all of the regions of an operation. - Represents all of the regions of an operation.
@ -631,14 +640,6 @@ The available directives are as follows:
- `input` must be either an operand or result [variable](#variables), the - `input` must be either an operand or result [variable](#variables), the
`operands` directive, or the `results` directive. `operands` directive, or the `results` directive.
* `type_ref` ( input )
- Represents a reference to the type of the given input that must have
already been resolved.
- `input` must be either an operand or result [variable](#variables), the
`operands` directive, or the `results` directive.
- Used to pass previously parsed types to custom directives.
#### Literals #### Literals
A literal is either a keyword or punctuation surrounded by \`\`. A literal is either a keyword or punctuation surrounded by \`\`.
@ -716,6 +717,10 @@ declarative parameter to `parse` method argument is detailed below:
- Single: `OpAsmParser::OperandType &` - Single: `OpAsmParser::OperandType &`
- Optional: `Optional<OpAsmParser::OperandType> &` - Optional: `Optional<OpAsmParser::OperandType> &`
- Variadic: `SmallVectorImpl<OpAsmParser::OperandType> &` - Variadic: `SmallVectorImpl<OpAsmParser::OperandType> &`
* Ref Directives
- A reference directive is passed to the parser using the same mapping as
the input operand. For example, a single region would be passed as a
`Region &`.
* Region Variables * Region Variables
- Single: `Region &` - Single: `Region &`
- Variadic: `SmallVectorImpl<std::unique_ptr<Region>> &` - Variadic: `SmallVectorImpl<std::unique_ptr<Region>> &`
@ -726,10 +731,6 @@ declarative parameter to `parse` method argument is detailed below:
- Single: `Type &` - Single: `Type &`
- Optional: `Type &` - Optional: `Type &`
- Variadic: `SmallVectorImpl<Type> &` - Variadic: `SmallVectorImpl<Type> &`
* TypeRef Directives
- Single: `Type`
- Optional: `Type`
- Variadic: `const SmallVectorImpl<Type> &`
* `attr-dict` Directive: `NamedAttrList &` * `attr-dict` Directive: `NamedAttrList &`
When a variable is optional, the value should only be specified if the variable When a variable is optional, the value should only be specified if the variable
@ -748,6 +749,10 @@ declarative parameter to `print` method argument is detailed below:
- Single: `Value` - Single: `Value`
- Optional: `Value` - Optional: `Value`
- Variadic: `OperandRange` - Variadic: `OperandRange`
* Ref Directives
- A reference directive is passed to the printer using the same mapping as
the input operand. For example, a single region would be passed as a
`Region &`.
* Region Variables * Region Variables
- Single: `Region &` - Single: `Region &`
- Variadic: `MutableArrayRef<Region>` - Variadic: `MutableArrayRef<Region>`
@ -758,10 +763,6 @@ declarative parameter to `print` method argument is detailed below:
- Single: `Type` - Single: `Type`
- Optional: `Type` - Optional: `Type`
- Variadic: `TypeRange` - Variadic: `TypeRange`
* TypeRef Directives
- Single: `Type`
- Optional: `Type`
- Variadic: `TypeRange`
* `attr-dict` Directive: `DictionaryAttr` * `attr-dict` Directive: `DictionaryAttr`
When a variable is optional, the provided value may be null. When a variable is optional, the provided value may be null.

View File

@ -352,6 +352,14 @@ static ParseResult parseCustomDirectiveAttrDict(OpAsmParser &parser,
NamedAttrList &attrs) { NamedAttrList &attrs) {
return parser.parseOptionalAttrDict(attrs); return parser.parseOptionalAttrDict(attrs);
} }
static ParseResult parseCustomDirectiveOptionalOperandRef(
OpAsmParser &parser, Optional<OpAsmParser::OperandType> &optOperand) {
int64_t operandCount = 0;
if (parser.parseInteger(operandCount))
return failure();
bool expectedOptionalOperand = operandCount == 0;
return success(expectedOptionalOperand != optOperand.hasValue());
}
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Printing // Printing
@ -417,6 +425,13 @@ static void printCustomDirectiveAttrDict(OpAsmPrinter &printer, Operation *op,
DictionaryAttr attrs) { DictionaryAttr attrs) {
printer.printOptionalAttrDict(attrs.getValue()); printer.printOptionalAttrDict(attrs.getValue());
} }
static void printCustomDirectiveOptionalOperandRef(OpAsmPrinter &printer,
Operation *op,
Value optOperand) {
printer << (optOperand ? "1" : "0");
}
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Test IsolatedRegionOp - parse passthrough region arguments. // Test IsolatedRegionOp - parse passthrough region arguments.
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@ -1698,12 +1698,22 @@ def FormatCustomDirectiveResultsWithTypeRefs
type($result), type($optResult), type($varResults) type($result), type($optResult), type($varResults)
) )
custom<CustomDirectiveWithTypeRefs>( custom<CustomDirectiveWithTypeRefs>(
type_ref($result), type_ref($optResult), type_ref($varResults) ref(type($result)), ref(type($optResult)), ref(type($varResults))
) )
attr-dict attr-dict
}]; }];
} }
def FormatCustomDirectiveWithOptionalOperandRef
: TEST_Op<"format_custom_directive_with_optional_operand_ref"> {
let arguments = (ins Optional<I64>:$optOperand);
let assemblyFormat = [{
($optOperand^)? `:`
custom<CustomDirectiveOptionalOperandRef>(ref($optOperand))
attr-dict
}];
}
def FormatCustomDirectiveSuccessors def FormatCustomDirectiveSuccessors
: TEST_Op<"format_custom_directive_successors", [Terminator]> { : TEST_Op<"format_custom_directive_successors", [Terminator]> {
let successors = (successor AnySuccessor:$successor, let successors = (successor AnySuccessor:$successor,

View File

@ -7,8 +7,8 @@ include "mlir/IR/OpBase.td"
def TestDialect : Dialect { def TestDialect : Dialect {
let name = "test"; let name = "test";
} }
class TestFormat_Op<string name, string fmt, list<OpTrait> traits = []> class TestFormat_Op<string fmt, list<OpTrait> traits = []>
: Op<TestDialect, name, traits> { : Op<TestDialect, "format_op", traits> {
let assemblyFormat = fmt; let assemblyFormat = fmt;
} }
@ -20,25 +20,25 @@ class TestFormat_Op<string name, string fmt, list<OpTrait> traits = []>
// attr-dict // attr-dict
// CHECK: error: 'attr-dict' directive not found // CHECK: error: 'attr-dict' directive not found
def DirectiveAttrDictInvalidA : TestFormat_Op<"attrdict_invalid_a", [{ def DirectiveAttrDictInvalidA : TestFormat_Op<[{
}]>; }]>;
// CHECK: error: 'attr-dict' directive has already been seen // CHECK: error: 'attr-dict' directive has already been seen
def DirectiveAttrDictInvalidB : TestFormat_Op<"attrdict_invalid_b", [{ def DirectiveAttrDictInvalidB : TestFormat_Op<[{
attr-dict attr-dict attr-dict attr-dict
}]>; }]>;
// CHECK: error: 'attr-dict' directive has already been seen // CHECK: error: 'attr-dict' directive has already been seen
def DirectiveAttrDictInvalidC : TestFormat_Op<"attrdict_invalid_c", [{ def DirectiveAttrDictInvalidC : TestFormat_Op<[{
attr-dict attr-dict-with-keyword attr-dict attr-dict-with-keyword
}]>; }]>;
// CHECK: error: 'attr-dict' directive can only be used as a top-level directive // CHECK: error: 'attr-dict' directive can only be used as a top-level directive
def DirectiveAttrDictInvalidD : TestFormat_Op<"attrdict_invalid_d", [{ def DirectiveAttrDictInvalidD : TestFormat_Op<[{
type(attr-dict) type(attr-dict)
}]>; }]>;
// CHECK-NOT: error // CHECK-NOT: error
def DirectiveAttrDictValidA : TestFormat_Op<"attrdict_valid_a", [{ def DirectiveAttrDictValidA : TestFormat_Op<[{
attr-dict attr-dict
}]>; }]>;
def DirectiveAttrDictValidB : TestFormat_Op<"attrdict_valid_b", [{ def DirectiveAttrDictValidB : TestFormat_Op<[{
attr-dict-with-keyword attr-dict-with-keyword
}]>; }]>;
@ -46,42 +46,42 @@ def DirectiveAttrDictValidB : TestFormat_Op<"attrdict_valid_b", [{
// custom // custom
// CHECK: error: expected '<' before custom directive name // CHECK: error: expected '<' before custom directive name
def DirectiveCustomInvalidA : TestFormat_Op<"custom_invalid_a", [{ def DirectiveCustomInvalidA : TestFormat_Op<[{
custom( custom(
}]>; }]>;
// CHECK: error: expected custom directive name identifier // CHECK: error: expected custom directive name identifier
def DirectiveCustomInvalidB : TestFormat_Op<"custom_invalid_b", [{ def DirectiveCustomInvalidB : TestFormat_Op<[{
custom<> custom<>
}]>; }]>;
// CHECK: error: expected '>' after custom directive name // CHECK: error: expected '>' after custom directive name
def DirectiveCustomInvalidC : TestFormat_Op<"custom_invalid_c", [{ def DirectiveCustomInvalidC : TestFormat_Op<[{
custom<MyDirective( custom<MyDirective(
}]>; }]>;
// CHECK: error: expected '(' before custom directive parameters // CHECK: error: expected '(' before custom directive parameters
def DirectiveCustomInvalidD : TestFormat_Op<"custom_invalid_d", [{ def DirectiveCustomInvalidD : TestFormat_Op<[{
custom<MyDirective>) custom<MyDirective>)
}]>; }]>;
// CHECK: error: only variables and types may be used as parameters to a custom directive // CHECK: error: only variables and types may be used as parameters to a custom directive
def DirectiveCustomInvalidE : TestFormat_Op<"custom_invalid_e", [{ def DirectiveCustomInvalidE : TestFormat_Op<[{
custom<MyDirective>(operands) custom<MyDirective>(operands)
}]>; }]>;
// CHECK: error: expected ')' after custom directive parameters // CHECK: error: expected ')' after custom directive parameters
def DirectiveCustomInvalidF : TestFormat_Op<"custom_invalid_f", [{ def DirectiveCustomInvalidF : TestFormat_Op<[{
custom<MyDirective>($operand< custom<MyDirective>($operand<
}]>, Arguments<(ins I64:$operand)>; }]>, Arguments<(ins I64:$operand)>;
// CHECK: error: type directives within a custom directive may only refer to variables // CHECK: error: type directives within a custom directive may only refer to variables
def DirectiveCustomInvalidH : TestFormat_Op<"custom_invalid_h", [{ def DirectiveCustomInvalidH : TestFormat_Op<[{
custom<MyDirective>(type(operands)) custom<MyDirective>(type(operands))
}]>; }]>;
// CHECK-NOT: error // CHECK-NOT: error
def DirectiveCustomValidA : TestFormat_Op<"custom_valid_a", [{ def DirectiveCustomValidA : TestFormat_Op<[{
custom<MyDirective>($operand) attr-dict custom<MyDirective>($operand) attr-dict
}]>, Arguments<(ins Optional<I64>:$operand)>; }]>, Arguments<(ins Optional<I64>:$operand)>;
def DirectiveCustomValidB : TestFormat_Op<"custom_valid_b", [{ def DirectiveCustomValidB : TestFormat_Op<[{
custom<MyDirective>($operand, type($operand), type($result)) attr-dict custom<MyDirective>($operand, type($operand), type($result)) attr-dict
}]>, Arguments<(ins I64:$operand)>, Results<(outs I64:$result)>; }]>, Arguments<(ins I64:$operand)>, Results<(outs I64:$result)>;
def DirectiveCustomValidC : TestFormat_Op<"custom_valid_c", [{ def DirectiveCustomValidC : TestFormat_Op<[{
custom<MyDirective>($attr) attr-dict custom<MyDirective>($attr) attr-dict
}]>, Arguments<(ins I64Attr:$attr)>; }]>, Arguments<(ins I64Attr:$attr)>;
@ -89,31 +89,31 @@ def DirectiveCustomValidC : TestFormat_Op<"custom_valid_c", [{
// functional-type // functional-type
// CHECK: error: 'functional-type' is only valid as a top-level directive // CHECK: error: 'functional-type' is only valid as a top-level directive
def DirectiveFunctionalTypeInvalidA : TestFormat_Op<"functype_invalid_a", [{ def DirectiveFunctionalTypeInvalidA : TestFormat_Op<[{
functional-type(functional-type) functional-type(functional-type)
}]>; }]>;
// CHECK: error: expected '(' before argument list // CHECK: error: expected '(' before argument list
def DirectiveFunctionalTypeInvalidB : TestFormat_Op<"functype_invalid_b", [{ def DirectiveFunctionalTypeInvalidB : TestFormat_Op<[{
functional-type functional-type
}]>; }]>;
// CHECK: error: expected directive, literal, variable, or optional group // CHECK: error: expected directive, literal, variable, or optional group
def DirectiveFunctionalTypeInvalidC : TestFormat_Op<"functype_invalid_c", [{ def DirectiveFunctionalTypeInvalidC : TestFormat_Op<[{
functional-type( functional-type(
}]>; }]>;
// CHECK: error: expected ',' after inputs argument // CHECK: error: expected ',' after inputs argument
def DirectiveFunctionalTypeInvalidD : TestFormat_Op<"functype_invalid_d", [{ def DirectiveFunctionalTypeInvalidD : TestFormat_Op<[{
functional-type(operands functional-type(operands
}]>; }]>;
// CHECK: error: expected directive, literal, variable, or optional group // CHECK: error: expected directive, literal, variable, or optional group
def DirectiveFunctionalTypeInvalidE : TestFormat_Op<"functype_invalid_e", [{ def DirectiveFunctionalTypeInvalidE : TestFormat_Op<[{
functional-type(operands, functional-type(operands,
}]>; }]>;
// CHECK: error: expected ')' after argument list // CHECK: error: expected ')' after argument list
def DirectiveFunctionalTypeInvalidF : TestFormat_Op<"functype_invalid_f", [{ def DirectiveFunctionalTypeInvalidF : TestFormat_Op<[{
functional-type(operands, results functional-type(operands, results
}]>; }]>;
// CHECK-NOT: error // CHECK-NOT: error
def DirectiveFunctionalTypeValid : TestFormat_Op<"functype_invalid_a", [{ def DirectiveFunctionalTypeValid : TestFormat_Op<[{
functional-type(operands, results) attr-dict functional-type(operands, results) attr-dict
}]>; }]>;
@ -121,45 +121,128 @@ def DirectiveFunctionalTypeValid : TestFormat_Op<"functype_invalid_a", [{
// operands // operands
// CHECK: error: 'operands' directive creates overlap in format // CHECK: error: 'operands' directive creates overlap in format
def DirectiveOperandsInvalidA : TestFormat_Op<"operands_invalid_a", [{ def DirectiveOperandsInvalidA : TestFormat_Op<[{
operands operands operands operands
}]>; }]>;
// CHECK: error: 'operands' directive creates overlap in format // CHECK: error: 'operands' directive creates overlap in format
def DirectiveOperandsInvalidB : TestFormat_Op<"operands_invalid_b", [{ def DirectiveOperandsInvalidB : TestFormat_Op<[{
$operand operands $operand operands
}]>, Arguments<(ins I64:$operand)>; }]>, Arguments<(ins I64:$operand)>;
// CHECK-NOT: error: // CHECK-NOT: error:
def DirectiveOperandsValid : TestFormat_Op<"operands_valid", [{ def DirectiveOperandsValid : TestFormat_Op<[{
operands attr-dict operands attr-dict
}]>; }]>;
//===----------------------------------------------------------------------===//
// ref
// CHECK: error: 'ref' is only valid within a `custom` directive
def DirectiveRefInvalidA : TestFormat_Op<[{
ref(type($operand))
}]>, Arguments<(ins I64:$operand)>;
// CHECK: error: 'ref' of 'type($operand)' is not bound by a prior 'type' directive
def DirectiveRefInvalidB : TestFormat_Op<[{
custom<Foo>(ref(type($operand)))
}]>, Arguments<(ins I64:$operand)>;
// CHECK: error: 'ref' of 'type(operands)' is not bound by a prior 'type' directive
def DirectiveRefInvalidC : TestFormat_Op<[{
custom<Foo>(ref(type(operands)))
}]>;
// CHECK: error: 'ref' of 'type($result)' is not bound by a prior 'type' directive
def DirectiveRefInvalidD : TestFormat_Op<[{
custom<Foo>(ref(type($result)))
}]>, Results<(outs I64:$result)>;
// CHECK: error: 'ref' of 'type(results)' is not bound by a prior 'type' directive
def DirectiveRefInvalidE : TestFormat_Op<[{
custom<Foo>(ref(type(results)))
}]>;
// CHECK: error: 'ref' of 'successors' is not bound by a prior 'successors' directive
def DirectiveRefInvalidF : TestFormat_Op<[{
custom<Foo>(ref(successors))
}]>;
// CHECK: error: 'ref' of 'regions' is not bound by a prior 'regions' directive
def DirectiveRefInvalidG : TestFormat_Op<[{
custom<Foo>(ref(regions))
}]>;
// CHECK: error: expected '(' before argument list
def DirectiveRefInvalidH : TestFormat_Op<[{
custom<Foo>(ref)
}]>;
// CHECK: error: expected ')' after argument list
def DirectiveRefInvalidI : TestFormat_Op<[{
operands custom<Foo>(ref(operands(
}]>;
// CHECK: error: 'ref' of 'operands' is not bound by a prior 'operands' directive
def DirectiveRefInvalidJ : TestFormat_Op<[{
custom<Foo>(ref(operands))
}]>;
// CHECK: error: 'ref' of 'attr-dict' is not bound by a prior 'attr-dict' directive
def DirectiveRefInvalidK : TestFormat_Op<[{
custom<Foo>(ref(attr-dict))
}]>;
// CHECK: error: successor 'successor' must be bound before it is referenced
def DirectiveRefInvalidL : TestFormat_Op<[{
custom<Foo>(ref($successor))
}]> {
let successors = (successor AnySuccessor:$successor);
}
// CHECK: error: region 'region' must be bound before it is referenced
def DirectiveRefInvalidM : TestFormat_Op<[{
custom<Foo>(ref($region))
}]> {
let regions = (region AnyRegion:$region);
}
// CHECK: error: attribute 'attr' must be bound before it is referenced
def DirectiveRefInvalidN : TestFormat_Op<[{
custom<Foo>(ref($attr))
}]>, Arguments<(ins I64Attr:$attr)>;
// CHECK: error: operand 'operand' must be bound before it is referenced
def DirectiveRefInvalidO : TestFormat_Op<[{
custom<Foo>(ref($operand))
}]>, Arguments<(ins I64:$operand)>;
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// regions // regions
// CHECK: error: 'regions' directive creates overlap in format // CHECK: error: 'regions' directive creates overlap in format
def DirectiveRegionsInvalidA : TestFormat_Op<"regions_invalid_a", [{ def DirectiveRegionsInvalidA : TestFormat_Op<[{
regions regions attr-dict regions regions attr-dict
}]>; }]>;
// CHECK: error: 'regions' directive creates overlap in format // CHECK: error: 'regions' directive creates overlap in format
def DirectiveRegionsInvalidB : TestFormat_Op<"regions_invalid_b", [{ def DirectiveRegionsInvalidB : TestFormat_Op<[{
$region regions attr-dict $region regions attr-dict
}]> { }]> {
let regions = (region AnyRegion:$region); let regions = (region AnyRegion:$region);
} }
// CHECK: error: 'regions' is only valid as a top-level directive // CHECK: error: 'regions' is only valid as a top-level directive
def DirectiveRegionsInvalidC : TestFormat_Op<"regions_invalid_c", [{ def DirectiveRegionsInvalidC : TestFormat_Op<[{
type(regions) type(regions)
}]>; }]>;
// CHECK-NOT: error: // CHECK-NOT: error:
def DirectiveRegionsValid : TestFormat_Op<"regions_valid", [{ def DirectiveRegionsValid : TestFormat_Op<[{
regions attr-dict regions attr-dict
}]>; }]>;
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// results // results
// CHECK: error: 'results' directive can not be used as a top-level directive // CHECK: error: 'results' directive can can only be used as a child to a 'type' directive
def DirectiveResultsInvalidA : TestFormat_Op<"results_invalid_a", [{ def DirectiveResultsInvalidA : TestFormat_Op<[{
results results
}]>; }]>;
@ -167,7 +250,7 @@ def DirectiveResultsInvalidA : TestFormat_Op<"results_invalid_a", [{
// successors // successors
// CHECK: error: 'successors' is only valid as a top-level directive // CHECK: error: 'successors' is only valid as a top-level directive
def DirectiveSuccessorsInvalidA : TestFormat_Op<"successors_invalid_a", [{ def DirectiveSuccessorsInvalidA : TestFormat_Op<[{
type(successors) type(successors)
}]>; }]>;
@ -175,140 +258,78 @@ def DirectiveSuccessorsInvalidA : TestFormat_Op<"successors_invalid_a", [{
// type // type
// CHECK: error: expected '(' before argument list // CHECK: error: expected '(' before argument list
def DirectiveTypeInvalidA : TestFormat_Op<"type_invalid_a", [{ def DirectiveTypeInvalidA : TestFormat_Op<[{
type type
}]>; }]>;
// CHECK: error: expected directive, literal, variable, or optional group // CHECK: error: expected directive, literal, variable, or optional group
def DirectiveTypeInvalidB : TestFormat_Op<"type_invalid_b", [{ def DirectiveTypeInvalidB : TestFormat_Op<[{
type( type(
}]>; }]>;
// CHECK: error: expected ')' after argument list // CHECK: error: expected ')' after argument list
def DirectiveTypeInvalidC : TestFormat_Op<"type_invalid_c", [{ def DirectiveTypeInvalidC : TestFormat_Op<[{
type(operands type(operands
}]>; }]>;
// CHECK-NOT: error: // CHECK-NOT: error:
def DirectiveTypeValid : TestFormat_Op<"type_valid", [{ def DirectiveTypeValid : TestFormat_Op<[{
type(operands) attr-dict type(operands) attr-dict
}]>; }]>;
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// functional-type/type operands // functional-type/type operands
// CHECK: error: 'type' directive operand expects variable or directive operand // CHECK: error: literals may only be used in a top-level section of the format
def DirectiveTypeZOperandInvalidA : TestFormat_Op<"type_operand_invalid_a", [{ def DirectiveTypeZOperandInvalidA : TestFormat_Op<[{
type(`literal`) type(`literal`)
}]>; }]>;
// CHECK: error: 'operands' 'type' is already bound // CHECK: error: 'operands' 'type' is already bound
def DirectiveTypeZOperandInvalidB : TestFormat_Op<"type_operand_invalid_b", [{ def DirectiveTypeZOperandInvalidB : TestFormat_Op<[{
type(operands) type(operands) type(operands) type(operands)
}]>; }]>;
// CHECK: error: 'operands' 'type' is already bound // CHECK: error: 'operands' 'type' is already bound
def DirectiveTypeZOperandInvalidC : TestFormat_Op<"type_operand_invalid_c", [{ def DirectiveTypeZOperandInvalidC : TestFormat_Op<[{
type($operand) type(operands) type($operand) type(operands)
}]>, Arguments<(ins I64:$operand)>; }]>, Arguments<(ins I64:$operand)>;
// CHECK: error: 'type' of 'operand' is already bound // CHECK: error: 'type' of 'operand' is already bound
def DirectiveTypeZOperandInvalidD : TestFormat_Op<"type_operand_invalid_d", [{ def DirectiveTypeZOperandInvalidD : TestFormat_Op<[{
type(operands) type($operand) type(operands) type($operand)
}]>, Arguments<(ins I64:$operand)>; }]>, Arguments<(ins I64:$operand)>;
// CHECK: error: 'type' of 'operand' is already bound // CHECK: error: 'type' of 'operand' is already bound
def DirectiveTypeZOperandInvalidE : TestFormat_Op<"type_operand_invalid_e", [{ def DirectiveTypeZOperandInvalidE : TestFormat_Op<[{
type($operand) type($operand) type($operand) type($operand)
}]>, Arguments<(ins I64:$operand)>; }]>, Arguments<(ins I64:$operand)>;
// CHECK: error: 'results' 'type' is already bound // CHECK: error: 'results' 'type' is already bound
def DirectiveTypeZOperandInvalidF : TestFormat_Op<"type_operand_invalid_f", [{ def DirectiveTypeZOperandInvalidF : TestFormat_Op<[{
type(results) type(results) type(results) type(results)
}]>; }]>;
// CHECK: error: 'results' 'type' is already bound // CHECK: error: 'results' 'type' is already bound
def DirectiveTypeZOperandInvalidG : TestFormat_Op<"type_operand_invalid_g", [{ def DirectiveTypeZOperandInvalidG : TestFormat_Op<[{
type($result) type(results) type($result) type(results)
}]>, Results<(outs I64:$result)>; }]>, Results<(outs I64:$result)>;
// CHECK: error: 'type' of 'result' is already bound // CHECK: error: 'type' of 'result' is already bound
def DirectiveTypeZOperandInvalidH : TestFormat_Op<"type_operand_invalid_h", [{ def DirectiveTypeZOperandInvalidH : TestFormat_Op<[{
type(results) type($result) type(results) type($result)
}]>, Results<(outs I64:$result)>; }]>, Results<(outs I64:$result)>;
// CHECK: error: 'type' of 'result' is already bound // CHECK: error: 'type' of 'result' is already bound
def DirectiveTypeZOperandInvalidI : TestFormat_Op<"type_operand_invalid_i", [{ def DirectiveTypeZOperandInvalidI : TestFormat_Op<[{
type($result) type($result) type($result) type($result)
}]>, Results<(outs I64:$result)>; }]>, Results<(outs I64:$result)>;
//===----------------------------------------------------------------------===//
// type_ref
// CHECK: error: 'type_ref' of 'operand' is not bound by a prior 'type' directive
def DirectiveTypeZZTypeRefOperandInvalidC : TestFormat_Op<"type_ref_operand_invalid_c", [{
type_ref($operand) type(operands)
}]>, Arguments<(ins I64:$operand)>;
// CHECK: error: 'operands' 'type_ref' is not bound by a prior 'type' directive
def DirectiveTypeZZTypeRefOperandInvalidD : TestFormat_Op<"type_ref_operand_invalid_d", [{
type_ref(operands) type($operand)
}]>, Arguments<(ins I64:$operand)>;
// CHECK: error: 'type_ref' of 'operand' is not bound by a prior 'type' directive
def DirectiveTypeZZTypeRefOperandInvalidE : TestFormat_Op<"type_ref_operand_invalid_e", [{
type_ref($operand) type($operand)
}]>, Arguments<(ins I64:$operand)>;
// CHECK: error: 'type_ref' of 'result' is not bound by a prior 'type' directive
def DirectiveTypeZZTypeRefOperandInvalidG : TestFormat_Op<"type_ref_operand_invalid_g", [{
type_ref($result) type(results)
}]>, Results<(outs I64:$result)>;
// CHECK: error: 'results' 'type_ref' is not bound by a prior 'type' directive
def DirectiveTypeZZTypeRefOperandInvalidH : TestFormat_Op<"type_ref_operand_invalid_h", [{
type_ref(results) type($result)
}]>, Results<(outs I64:$result)>;
// CHECK: error: 'type_ref' of 'result' is not bound by a prior 'type' directive
def DirectiveTypeZZTypeRefOperandInvalidI : TestFormat_Op<"type_ref_operand_invalid_i", [{
type_ref($result) type($result)
}]>, Results<(outs I64:$result)>;
// CHECK-NOT: error
def DirectiveTypeZZTypeRefOperandB : TestFormat_Op<"type_ref_operand_valid_b", [{
type_ref(operands) attr-dict
}]>;
// CHECK-NOT: error
def DirectiveTypeZZTypeRefOperandD : TestFormat_Op<"type_ref_operand_valid_d", [{
type(operands) type_ref($operand) attr-dict
}]>, Arguments<(ins I64:$operand)>;
// CHECK-NOT: error
def DirectiveTypeZZTypeRefOperandE : TestFormat_Op<"type_ref_operand_valid_e", [{
type($operand) type_ref($operand) attr-dict
}]>, Arguments<(ins I64:$operand)>;
// CHECK-NOT: error
def DirectiveTypeZZTypeRefOperandF : TestFormat_Op<"type_ref_operand_valid_f", [{
type(results) type_ref(results) attr-dict
}]>;
// CHECK-NOT: error
def DirectiveTypeZZTypeRefOperandG : TestFormat_Op<"type_ref_operand_valid_g", [{
type($result) type_ref(results) attr-dict
}]>, Results<(outs I64:$result)>;
// CHECK-NOT: error
def DirectiveTypeZZTypeRefOperandH : TestFormat_Op<"type_ref_operand_valid_h", [{
type(results) type_ref($result) attr-dict
}]>, Results<(outs I64:$result)>;
// CHECK-NOT: error
def DirectiveTypeZZTypeRefOperandI : TestFormat_Op<"type_ref_operand_valid_i", [{
type($result) type_ref($result) attr-dict
}]>, Results<(outs I64:$result)>;
// CHECK-NOT: error:
def DirectiveTypeZZZOperandValid : TestFormat_Op<"type_operand_valid", [{
type(operands) type(results) attr-dict
}]>;
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Literals // Literals
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Test all of the valid literals. // Test all of the valid literals.
// CHECK: error: expected valid literal // CHECK: error: expected valid literal
def LiteralInvalidA : TestFormat_Op<"literal_invalid_a", [{ def LiteralInvalidA : TestFormat_Op<[{
`1` `1`
}]>; }]>;
// CHECK: error: unexpected end of file in literal // CHECK: error: unexpected end of file in literal
// CHECK: error: expected directive, literal, variable, or optional group // CHECK: error: expected directive, literal, variable, or optional group
def LiteralInvalidB : TestFormat_Op<"literal_invalid_b", [{ def LiteralInvalidB : TestFormat_Op<[{
` `
}]>; }]>;
// CHECK-NOT: error // CHECK-NOT: error
def LiteralValid : TestFormat_Op<"literal_valid", [{ def LiteralValid : TestFormat_Op<[{
`_` `:` `,` `=` `<` `>` `(` `)` `[` `]` `?` `+` `*` ` ` `` `->` `\n` `abc$._` `_` `:` `,` `=` `<` `>` `(` `)` `[` `]` `?` `+` `*` ` ` `` `->` `\n` `abc$._`
attr-dict attr-dict
}]>; }]>;
@ -318,60 +339,60 @@ def LiteralValid : TestFormat_Op<"literal_valid", [{
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// CHECK: error: optional groups can only be used as top-level elements // CHECK: error: optional groups can only be used as top-level elements
def OptionalInvalidA : TestFormat_Op<"optional_invalid_a", [{ def OptionalInvalidA : TestFormat_Op<[{
type(($attr^)?) attr-dict type(($attr^)?) attr-dict
}]>, Arguments<(ins OptionalAttr<I64Attr>:$attr)>; }]>, Arguments<(ins OptionalAttr<I64Attr>:$attr)>;
// CHECK: error: expected directive, literal, variable, or optional group // CHECK: error: expected directive, literal, variable, or optional group
def OptionalInvalidB : TestFormat_Op<"optional_invalid_b", [{ def OptionalInvalidB : TestFormat_Op<[{
() attr-dict () attr-dict
}]>, Arguments<(ins OptionalAttr<I64Attr>:$attr)>; }]>, Arguments<(ins OptionalAttr<I64Attr>:$attr)>;
// CHECK: error: optional group specified no anchor element // CHECK: error: optional group specified no anchor element
def OptionalInvalidC : TestFormat_Op<"optional_invalid_c", [{ def OptionalInvalidC : TestFormat_Op<[{
($attr)? attr-dict ($attr)? attr-dict
}]>, Arguments<(ins OptionalAttr<I64Attr>:$attr)>; }]>, Arguments<(ins OptionalAttr<I64Attr>:$attr)>;
// CHECK: error: first parsable element of an operand group must be an attribute, literal, operand, or region // CHECK: error: first parsable element of an operand group must be an attribute, literal, operand, or region
def OptionalInvalidD : TestFormat_Op<"optional_invalid_d", [{ def OptionalInvalidD : TestFormat_Op<[{
(type($operand) $operand^)? attr-dict (type($operand) $operand^)? attr-dict
}]>, Arguments<(ins Optional<I64>:$operand)>; }]>, Arguments<(ins Optional<I64>:$operand)>;
// CHECK: error: only literals, types, and variables can be used within an optional group // CHECK: error: only literals, types, and variables can be used within an optional group
def OptionalInvalidE : TestFormat_Op<"optional_invalid_e", [{ def OptionalInvalidE : TestFormat_Op<[{
(`,` $attr^ type(operands))? attr-dict (`,` $attr^ type(operands))? attr-dict
}]>, Arguments<(ins OptionalAttr<I64Attr>:$attr)>; }]>, Arguments<(ins OptionalAttr<I64Attr>:$attr)>;
// CHECK: error: only one element can be marked as the anchor of an optional group // CHECK: error: only one element can be marked as the anchor of an optional group
def OptionalInvalidF : TestFormat_Op<"optional_invalid_f", [{ def OptionalInvalidF : TestFormat_Op<[{
($attr^ $attr2^) attr-dict ($attr^ $attr2^) attr-dict
}]>, Arguments<(ins OptionalAttr<I64Attr>:$attr, OptionalAttr<I64Attr>:$attr2)>; }]>, Arguments<(ins OptionalAttr<I64Attr>:$attr, OptionalAttr<I64Attr>:$attr2)>;
// CHECK: error: only optional attributes can be used to anchor an optional group // CHECK: error: only optional attributes can be used to anchor an optional group
def OptionalInvalidG : TestFormat_Op<"optional_invalid_g", [{ def OptionalInvalidG : TestFormat_Op<[{
($attr^) attr-dict ($attr^) attr-dict
}]>, Arguments<(ins I64Attr:$attr)>; }]>, Arguments<(ins I64Attr:$attr)>;
// CHECK: error: only variable length operands can be used within an optional group // CHECK: error: only variable length operands can be used within an optional group
def OptionalInvalidH : TestFormat_Op<"optional_invalid_h", [{ def OptionalInvalidH : TestFormat_Op<[{
($arg^) attr-dict ($arg^) attr-dict
}]>, Arguments<(ins I64:$arg)>; }]>, Arguments<(ins I64:$arg)>;
// CHECK: error: only literals, types, and variables can be used within an optional group // CHECK: error: only literals, types, and variables can be used within an optional group
def OptionalInvalidI : TestFormat_Op<"optional_invalid_i", [{ def OptionalInvalidI : TestFormat_Op<[{
(functional-type($arg, results)^)? attr-dict (functional-type($arg, results)^)? attr-dict
}]>, Arguments<(ins Variadic<I64>:$arg)>; }]>, Arguments<(ins Variadic<I64>:$arg)>;
// CHECK: error: only literals, types, and variables can be used within an optional group // CHECK: error: only literals, types, and variables can be used within an optional group
def OptionalInvalidJ : TestFormat_Op<"optional_invalid_j", [{ def OptionalInvalidJ : TestFormat_Op<[{
(attr-dict) (attr-dict)
}]>; }]>;
// CHECK: error: expected '?' after optional group // CHECK: error: expected '?' after optional group
def OptionalInvalidK : TestFormat_Op<"optional_invalid_k", [{ def OptionalInvalidK : TestFormat_Op<[{
($arg^) ($arg^)
}]>, Arguments<(ins Variadic<I64>:$arg)>; }]>, Arguments<(ins Variadic<I64>:$arg)>;
// CHECK: error: only variables and types can be used to anchor an optional group // CHECK: error: only variables and types can be used to anchor an optional group
def OptionalInvalidL : TestFormat_Op<"optional_invalid_l", [{ def OptionalInvalidL : TestFormat_Op<[{
(custom<MyDirective>($arg)^)? (custom<MyDirective>($arg)^)?
}]>, Arguments<(ins I64:$arg)>; }]>, Arguments<(ins I64:$arg)>;
// CHECK: error: only variables and types can be used to anchor an optional group // CHECK: error: only variables and types can be used to anchor an optional group
def OptionalInvalidM : TestFormat_Op<"optional_invalid_m", [{ def OptionalInvalidM : TestFormat_Op<[{
(` `^)? (` `^)?
}]>, Arguments<(ins)>; }]>, Arguments<(ins)>;
// CHECK-NOT: error // CHECK-NOT: error
def OptionalValidA : TestFormat_Op<"optional_valid_a", [{ def OptionalValidA : TestFormat_Op<[{
(` ` `` $arg^)? (` ` `` $arg^)?
}]>; }]>;
@ -380,78 +401,78 @@ def OptionalValidA : TestFormat_Op<"optional_valid_a", [{
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// CHECK: error: expected variable to refer to an argument, region, result, or successor // CHECK: error: expected variable to refer to an argument, region, result, or successor
def VariableInvalidA : TestFormat_Op<"variable_invalid_a", [{ def VariableInvalidA : TestFormat_Op<[{
$unknown_arg attr-dict $unknown_arg attr-dict
}]>; }]>;
// CHECK: error: attribute 'attr' is already bound // CHECK: error: attribute 'attr' is already bound
def VariableInvalidB : TestFormat_Op<"variable_invalid_b", [{ def VariableInvalidB : TestFormat_Op<[{
$attr $attr attr-dict $attr $attr attr-dict
}]>, Arguments<(ins I64Attr:$attr)>; }]>, Arguments<(ins I64Attr:$attr)>;
// CHECK: error: operand 'operand' is already bound // CHECK: error: operand 'operand' is already bound
def VariableInvalidC : TestFormat_Op<"variable_invalid_c", [{ def VariableInvalidC : TestFormat_Op<[{
$operand $operand attr-dict $operand $operand attr-dict
}]>, Arguments<(ins I64:$operand)>; }]>, Arguments<(ins I64:$operand)>;
// CHECK: error: operand 'operand' is already bound // CHECK: error: operand 'operand' is already bound
def VariableInvalidD : TestFormat_Op<"variable_invalid_d", [{ def VariableInvalidD : TestFormat_Op<[{
operands $operand attr-dict operands $operand attr-dict
}]>, Arguments<(ins I64:$operand)>; }]>, Arguments<(ins I64:$operand)>;
// CHECK: error: results can not be used at the top level // CHECK: error: result variables can can only be used as a child to a 'type' directive
def VariableInvalidE : TestFormat_Op<"variable_invalid_e", [{ def VariableInvalidE : TestFormat_Op<[{
$result attr-dict $result attr-dict
}]>, Results<(outs I64:$result)>; }]>, Results<(outs I64:$result)>;
// CHECK: error: successor 'successor' is already bound // CHECK: error: successor 'successor' is already bound
def VariableInvalidF : TestFormat_Op<"variable_invalid_f", [{ def VariableInvalidF : TestFormat_Op<[{
$successor $successor attr-dict $successor $successor attr-dict
}]> { }]> {
let successors = (successor AnySuccessor:$successor); let successors = (successor AnySuccessor:$successor);
} }
// CHECK: error: successor 'successor' is already bound // CHECK: error: successor 'successor' is already bound
def VariableInvalidG : TestFormat_Op<"variable_invalid_g", [{ def VariableInvalidG : TestFormat_Op<[{
successors $successor attr-dict successors $successor attr-dict
}]> { }]> {
let successors = (successor AnySuccessor:$successor); let successors = (successor AnySuccessor:$successor);
} }
// CHECK: error: format ambiguity caused by `:` literal found after attribute `attr` which does not have a buildable type // CHECK: error: format ambiguity caused by `:` literal found after attribute `attr` which does not have a buildable type
def VariableInvalidH : TestFormat_Op<"variable_invalid_h", [{ def VariableInvalidH : TestFormat_Op<[{
$attr `:` attr-dict $attr `:` attr-dict
}]>, Arguments<(ins ElementsAttr:$attr)>; }]>, Arguments<(ins ElementsAttr:$attr)>;
// CHECK: error: format ambiguity caused by `:` literal found after attribute `attr` which does not have a buildable type // CHECK: error: format ambiguity caused by `:` literal found after attribute `attr` which does not have a buildable type
def VariableInvalidI : TestFormat_Op<"variable_invalid_i", [{ def VariableInvalidI : TestFormat_Op<[{
(`foo` $attr^)? `:` attr-dict (`foo` $attr^)? `:` attr-dict
}]>, Arguments<(ins OptionalAttr<ElementsAttr>:$attr)>; }]>, Arguments<(ins OptionalAttr<ElementsAttr>:$attr)>;
// CHECK: error: format ambiguity caused by `:` literal found after attribute `attr` which does not have a buildable type // CHECK: error: format ambiguity caused by `:` literal found after attribute `attr` which does not have a buildable type
def VariableInvalidJ : TestFormat_Op<"variable_invalid_j", [{ def VariableInvalidJ : TestFormat_Op<[{
$attr ` ` `:` attr-dict $attr ` ` `:` attr-dict
}]>, Arguments<(ins ElementsAttr:$attr)>; }]>, Arguments<(ins ElementsAttr:$attr)>;
// CHECK: error: region 'region' is already bound // CHECK: error: region 'region' is already bound
def VariableInvalidK : TestFormat_Op<"variable_invalid_k", [{ def VariableInvalidK : TestFormat_Op<[{
$region $region attr-dict $region $region attr-dict
}]> { }]> {
let regions = (region AnyRegion:$region); let regions = (region AnyRegion:$region);
} }
// CHECK: error: region 'region' is already bound // CHECK: error: region 'region' is already bound
def VariableInvalidL : TestFormat_Op<"variable_invalid_l", [{ def VariableInvalidL : TestFormat_Op<[{
regions $region attr-dict regions $region attr-dict
}]> { }]> {
let regions = (region AnyRegion:$region); let regions = (region AnyRegion:$region);
} }
// CHECK: error: regions can only be used at the top level // CHECK: error: regions can only be used at the top level
def VariableInvalidM : TestFormat_Op<"variable_invalid_m", [{ def VariableInvalidM : TestFormat_Op<[{
type($region) type($region)
}]> { }]> {
let regions = (region AnyRegion:$region); let regions = (region AnyRegion:$region);
} }
// CHECK: error: region #0, named 'region', not found // CHECK: error: region #0, named 'region', not found
def VariableInvalidN : TestFormat_Op<"variable_invalid_n", [{ def VariableInvalidN : TestFormat_Op<[{
attr-dict attr-dict
}]> { }]> {
let regions = (region AnyRegion:$region); let regions = (region AnyRegion:$region);
} }
// CHECK-NOT: error: // CHECK-NOT: error:
def VariableValidA : TestFormat_Op<"variable_valid_a", [{ def VariableValidA : TestFormat_Op<[{
$attr `:` attr-dict $attr `:` attr-dict
}]>, Arguments<(ins OptionalAttr<I1Attr>:$attr)>; }]>, Arguments<(ins OptionalAttr<I1Attr>:$attr)>;
def VariableValidB : TestFormat_Op<"variable_valid_b", [{ def VariableValidB : TestFormat_Op<[{
(`foo` $attr^)? `:` attr-dict (`foo` $attr^)? `:` attr-dict
}]>, Arguments<(ins OptionalAttr<I1Attr>:$attr)>; }]>, Arguments<(ins OptionalAttr<I1Attr>:$attr)>;
@ -461,75 +482,75 @@ def VariableValidB : TestFormat_Op<"variable_valid_b", [{
// CHECK: error: type of result #0, named 'result', is not buildable and a buildable type cannot be inferred // CHECK: error: type of result #0, named 'result', is not buildable and a buildable type cannot be inferred
// CHECK: note: suggest adding a type constraint to the operation or adding a 'type($result)' directive to the custom assembly format // CHECK: note: suggest adding a type constraint to the operation or adding a 'type($result)' directive to the custom assembly format
def ZCoverageInvalidA : TestFormat_Op<"variable_invalid_a", [{ def ZCoverageInvalidA : TestFormat_Op<[{
attr-dict attr-dict
}]>, Arguments<(ins AnyMemRef:$operand)>, Results<(outs AnyMemRef:$result)>; }]>, Arguments<(ins AnyMemRef:$operand)>, Results<(outs AnyMemRef:$result)>;
// CHECK: error: operand #0, named 'operand', not found // CHECK: error: operand #0, named 'operand', not found
// CHECK: note: suggest adding a '$operand' directive to the custom assembly format // CHECK: note: suggest adding a '$operand' directive to the custom assembly format
def ZCoverageInvalidB : TestFormat_Op<"variable_invalid_b", [{ def ZCoverageInvalidB : TestFormat_Op<[{
type($result) attr-dict type($result) attr-dict
}]>, Arguments<(ins AnyMemRef:$operand)>, Results<(outs AnyMemRef:$result)>; }]>, Arguments<(ins AnyMemRef:$operand)>, Results<(outs AnyMemRef:$result)>;
// CHECK: error: type of operand #0, named 'operand', is not buildable and a buildable type cannot be inferred // CHECK: error: type of operand #0, named 'operand', is not buildable and a buildable type cannot be inferred
// CHECK: note: suggest adding a type constraint to the operation or adding a 'type($operand)' directive to the custom assembly format // CHECK: note: suggest adding a type constraint to the operation or adding a 'type($operand)' directive to the custom assembly format
def ZCoverageInvalidC : TestFormat_Op<"variable_invalid_c", [{ def ZCoverageInvalidC : TestFormat_Op<[{
$operand type($result) attr-dict $operand type($result) attr-dict
}]>, Arguments<(ins AnyMemRef:$operand)>, Results<(outs AnyMemRef:$result)>; }]>, Arguments<(ins AnyMemRef:$operand)>, Results<(outs AnyMemRef:$result)>;
// CHECK: error: type of operand #0, named 'operand', is not buildable and a buildable type cannot be inferred // CHECK: error: type of operand #0, named 'operand', is not buildable and a buildable type cannot be inferred
// CHECK: note: suggest adding a type constraint to the operation or adding a 'type($operand)' directive to the custom assembly format // CHECK: note: suggest adding a type constraint to the operation or adding a 'type($operand)' directive to the custom assembly format
def ZCoverageInvalidD : TestFormat_Op<"variable_invalid_d", [{ def ZCoverageInvalidD : TestFormat_Op<[{
operands attr-dict operands attr-dict
}]>, Arguments<(ins Variadic<I64>:$operand)>; }]>, Arguments<(ins Variadic<I64>:$operand)>;
// CHECK: error: type of result #0, named 'result', is not buildable and a buildable type cannot be inferred // CHECK: error: type of result #0, named 'result', is not buildable and a buildable type cannot be inferred
// CHECK: note: suggest adding a type constraint to the operation or adding a 'type($result)' directive to the custom assembly format // CHECK: note: suggest adding a type constraint to the operation or adding a 'type($result)' directive to the custom assembly format
def ZCoverageInvalidE : TestFormat_Op<"variable_invalid_e", [{ def ZCoverageInvalidE : TestFormat_Op<[{
attr-dict attr-dict
}]>, Results<(outs Variadic<I64>:$result)>; }]>, Results<(outs Variadic<I64>:$result)>;
// CHECK: error: successor #0, named 'successor', not found // CHECK: error: successor #0, named 'successor', not found
// CHECK: note: suggest adding a '$successor' directive to the custom assembly format // CHECK: note: suggest adding a '$successor' directive to the custom assembly format
def ZCoverageInvalidF : TestFormat_Op<"variable_invalid_f", [{ def ZCoverageInvalidF : TestFormat_Op<[{
attr-dict attr-dict
}]> { }]> {
let successors = (successor AnySuccessor:$successor); let successors = (successor AnySuccessor:$successor);
} }
// CHECK: error: type of operand #0, named 'operand', is not buildable and a buildable type cannot be inferred // CHECK: error: type of operand #0, named 'operand', is not buildable and a buildable type cannot be inferred
// CHECK: note: suggest adding a type constraint to the operation or adding a 'type($operand)' directive to the custom assembly format // CHECK: note: suggest adding a type constraint to the operation or adding a 'type($operand)' directive to the custom assembly format
def ZCoverageInvalidG : TestFormat_Op<"variable_invalid_g", [{ def ZCoverageInvalidG : TestFormat_Op<[{
operands attr-dict operands attr-dict
}]>, Arguments<(ins Optional<I64>:$operand)>; }]>, Arguments<(ins Optional<I64>:$operand)>;
// CHECK: error: type of result #0, named 'result', is not buildable and a buildable type cannot be inferred // CHECK: error: type of result #0, named 'result', is not buildable and a buildable type cannot be inferred
// CHECK: note: suggest adding a type constraint to the operation or adding a 'type($result)' directive to the custom assembly format // CHECK: note: suggest adding a type constraint to the operation or adding a 'type($result)' directive to the custom assembly format
def ZCoverageInvalidH : TestFormat_Op<"variable_invalid_h", [{ def ZCoverageInvalidH : TestFormat_Op<[{
attr-dict attr-dict
}]>, Results<(outs Optional<I64>:$result)>; }]>, Results<(outs Optional<I64>:$result)>;
// CHECK-NOT: error // CHECK-NOT: error
def ZCoverageValidA : TestFormat_Op<"variable_valid_a", [{ def ZCoverageValidA : TestFormat_Op<[{
$operand type($operand) type($result) attr-dict $operand type($operand) type($result) attr-dict
}]>, Arguments<(ins AnyMemRef:$operand)>, Results<(outs AnyMemRef:$result)>; }]>, Arguments<(ins AnyMemRef:$operand)>, Results<(outs AnyMemRef:$result)>;
def ZCoverageValidB : TestFormat_Op<"variable_valid_b", [{ def ZCoverageValidB : TestFormat_Op<[{
$operand type(operands) type(results) attr-dict $operand type(operands) type(results) attr-dict
}]>, Arguments<(ins AnyMemRef:$operand)>, Results<(outs AnyMemRef:$result)>; }]>, Arguments<(ins AnyMemRef:$operand)>, Results<(outs AnyMemRef:$result)>;
def ZCoverageValidC : TestFormat_Op<"variable_valid_c", [{ def ZCoverageValidC : TestFormat_Op<[{
operands functional-type(operands, results) attr-dict operands functional-type(operands, results) attr-dict
}]>, Arguments<(ins AnyMemRef:$operand)>, Results<(outs AnyMemRef:$result)>; }]>, Arguments<(ins AnyMemRef:$operand)>, Results<(outs AnyMemRef:$result)>;
// Check that we can infer type equalities from certain traits. // Check that we can infer type equalities from certain traits.
def ZCoverageValidD : TestFormat_Op<"variable_valid_d", [{ def ZCoverageValidD : TestFormat_Op<[{
operands type($result) attr-dict operands type($result) attr-dict
}], [SameOperandsAndResultType]>, Arguments<(ins AnyMemRef:$operand)>, }], [SameOperandsAndResultType]>, Arguments<(ins AnyMemRef:$operand)>,
Results<(outs AnyMemRef:$result)>; Results<(outs AnyMemRef:$result)>;
def ZCoverageValidE : TestFormat_Op<"variable_valid_e", [{ def ZCoverageValidE : TestFormat_Op<[{
$operand type($operand) attr-dict $operand type($operand) attr-dict
}], [SameOperandsAndResultType]>, Arguments<(ins AnyMemRef:$operand)>, }], [SameOperandsAndResultType]>, Arguments<(ins AnyMemRef:$operand)>,
Results<(outs AnyMemRef:$result)>; Results<(outs AnyMemRef:$result)>;
def ZCoverageValidF : TestFormat_Op<"variable_valid_f", [{ def ZCoverageValidF : TestFormat_Op<[{
operands type($other) attr-dict operands type($other) attr-dict
}], [SameTypeOperands]>, Arguments<(ins AnyMemRef:$operand, AnyMemRef:$other)>; }], [SameTypeOperands]>, Arguments<(ins AnyMemRef:$operand, AnyMemRef:$other)>;
def ZCoverageValidG : TestFormat_Op<"variable_valid_g", [{ def ZCoverageValidG : TestFormat_Op<[{
operands type($other) attr-dict operands type($other) attr-dict
}], [AllTypesMatch<["operand", "other"]>]>, }], [AllTypesMatch<["operand", "other"]>]>,
Arguments<(ins AnyMemRef:$operand, AnyMemRef:$other)>; Arguments<(ins AnyMemRef:$operand, AnyMemRef:$other)>;
def ZCoverageValidH : TestFormat_Op<"variable_valid_h", [{ def ZCoverageValidH : TestFormat_Op<[{
operands type($result) attr-dict operands type($result) attr-dict
}], [AllTypesMatch<["operand", "result"]>]>, }], [AllTypesMatch<["operand", "result"]>]>,
Arguments<(ins AnyMemRef:$operand)>, Results<(outs AnyMemRef:$result)>; Arguments<(ins AnyMemRef:$operand)>, Results<(outs AnyMemRef:$result)>;

View File

@ -291,6 +291,12 @@ test.format_custom_directive_results_with_type_refs : i64, i64 -> (i64) type_ref
// CHECK: test.format_custom_directive_results_with_type_refs : i64 -> (i64) type_refs_capture : i64 -> (i64) // CHECK: test.format_custom_directive_results_with_type_refs : i64 -> (i64) type_refs_capture : i64 -> (i64)
test.format_custom_directive_results_with_type_refs : i64 -> (i64) type_refs_capture : i64 -> (i64) test.format_custom_directive_results_with_type_refs : i64 -> (i64) type_refs_capture : i64 -> (i64)
// CHECK: test.format_custom_directive_with_optional_operand_ref %[[I64]] : 1
test.format_custom_directive_with_optional_operand_ref %i64 : 1
// CHECK: test.format_custom_directive_with_optional_operand_ref : 0
test.format_custom_directive_with_optional_operand_ref : 0
func @foo() { func @foo() {
// CHECK: test.format_custom_directive_successors ^bb1, ^bb2 // CHECK: test.format_custom_directive_successors ^bb1, ^bb2
test.format_custom_directive_successors ^bb1, ^bb2 test.format_custom_directive_successors ^bb1, ^bb2

View File

@ -58,11 +58,11 @@ public:
CustomDirective, CustomDirective,
FunctionalTypeDirective, FunctionalTypeDirective,
OperandsDirective, OperandsDirective,
RefDirective,
RegionsDirective, RegionsDirective,
ResultsDirective, ResultsDirective,
SuccessorsDirective, SuccessorsDirective,
TypeDirective, TypeDirective,
TypeRefDirective,
/// This element is a literal. /// This element is a literal.
Literal, Literal,
@ -234,10 +234,10 @@ private:
std::unique_ptr<Element> inputs, results; std::unique_ptr<Element> inputs, results;
}; };
/// This class represents the `type` directive. /// This class represents the `ref` directive.
class TypeDirective : public DirectiveElement<Element::Kind::TypeDirective> { class RefDirective : public DirectiveElement<Element::Kind::RefDirective> {
public: public:
TypeDirective(std::unique_ptr<Element> arg) : operand(std::move(arg)) {} RefDirective(std::unique_ptr<Element> arg) : operand(std::move(arg)) {}
Element *getOperand() const { return operand.get(); } Element *getOperand() const { return operand.get(); }
private: private:
@ -245,11 +245,10 @@ private:
std::unique_ptr<Element> operand; std::unique_ptr<Element> operand;
}; };
/// This class represents the `type_ref` directive. /// This class represents the `type` directive.
class TypeRefDirective class TypeDirective : public DirectiveElement<Element::Kind::TypeDirective> {
: public DirectiveElement<Element::Kind::TypeRefDirective> {
public: public:
TypeRefDirective(std::unique_ptr<Element> arg) : operand(std::move(arg)) {} TypeDirective(std::unique_ptr<Element> arg) : operand(std::move(arg)) {}
Element *getOperand() const { return operand.get(); } Element *getOperand() const { return operand.get(); }
private: private:
@ -873,19 +872,6 @@ static void genElementParserStorage(Element *element, OpMethodBody &body) {
<< llvm::formatv( << llvm::formatv(
" ::llvm::ArrayRef<::mlir::Type> {0}Types({0}RawTypes);\n", " ::llvm::ArrayRef<::mlir::Type> {0}Types({0}RawTypes);\n",
name); name);
} else if (auto *dir = dyn_cast<TypeRefDirective>(element)) {
ArgumentLengthKind lengthKind;
StringRef name = getTypeListName(dir->getOperand(), lengthKind);
// Refer to the previously encountered TypeDirective for name.
// Take a `const ::mlir::SmallVector<::mlir::Type, 1> &` in the declaration
// to properly track the types that will be parsed and pushed later on.
if (lengthKind != ArgumentLengthKind::Single)
body << " const ::mlir::SmallVector<::mlir::Type, 1> &" << name
<< "TypesRef(" << name << "Types);\n";
else
body << llvm::formatv(
" ::llvm::ArrayRef<::mlir::Type> {0}RawTypesRef({0}RawTypes);\n",
name);
} else if (auto *dir = dyn_cast<FunctionalTypeDirective>(element)) { } else if (auto *dir = dyn_cast<FunctionalTypeDirective>(element)) {
ArgumentLengthKind ignored; ArgumentLengthKind ignored;
body << " ::llvm::ArrayRef<::mlir::Type> " body << " ::llvm::ArrayRef<::mlir::Type> "
@ -897,7 +883,6 @@ static void genElementParserStorage(Element *element, OpMethodBody &body) {
/// Generate the parser for a parameter to a custom directive. /// Generate the parser for a parameter to a custom directive.
static void genCustomParameterParser(Element &param, OpMethodBody &body) { static void genCustomParameterParser(Element &param, OpMethodBody &body) {
body << ", ";
if (auto *attr = dyn_cast<AttributeVariable>(&param)) { if (auto *attr = dyn_cast<AttributeVariable>(&param)) {
body << attr->getVar()->name << "Attr"; body << attr->getVar()->name << "Attr";
} else if (isa<AttrDictDirective>(&param)) { } else if (isa<AttrDictDirective>(&param)) {
@ -926,15 +911,9 @@ static void genCustomParameterParser(Element &param, OpMethodBody &body) {
else else
body << llvm::formatv("{0}Successor", name); body << llvm::formatv("{0}Successor", name);
} else if (auto *dir = dyn_cast<TypeRefDirective>(&param)) { } else if (auto *dir = dyn_cast<RefDirective>(&param)) {
ArgumentLengthKind lengthKind; genCustomParameterParser(*dir->getOperand(), body);
StringRef listName = getTypeListName(dir->getOperand(), lengthKind);
if (lengthKind == ArgumentLengthKind::Variadic)
body << llvm::formatv("{0}TypesRef", listName);
else if (lengthKind == ArgumentLengthKind::Optional)
body << llvm::formatv("{0}TypeRef", listName);
else
body << formatv("{0}RawTypesRef[0]", listName);
} else if (auto *dir = dyn_cast<TypeDirective>(&param)) { } else if (auto *dir = dyn_cast<TypeDirective>(&param)) {
ArgumentLengthKind lengthKind; ArgumentLengthKind lengthKind;
StringRef listName = getTypeListName(dir->getOperand(), lengthKind); StringRef listName = getTypeListName(dir->getOperand(), lengthKind);
@ -967,27 +946,39 @@ static void genCustomDirectiveParser(CustomDirective *dir, OpMethodBody &body) {
"{0}Operand;\n", "{0}Operand;\n",
operand->getVar()->name); operand->getVar()->name);
} }
} else if (auto *dir = dyn_cast<TypeRefDirective>(&param)) {
// Reference to an optional which may or may not have been set.
// Retrieve from vector if not empty.
ArgumentLengthKind lengthKind;
StringRef listName = getTypeListName(dir->getOperand(), lengthKind);
if (lengthKind == ArgumentLengthKind::Optional)
body << llvm::formatv(
" ::mlir::Type {0}TypeRef = {0}TypesRef.empty() "
"? Type() : {0}TypesRef[0];\n",
listName);
} else if (auto *dir = dyn_cast<TypeDirective>(&param)) { } else if (auto *dir = dyn_cast<TypeDirective>(&param)) {
ArgumentLengthKind lengthKind; ArgumentLengthKind lengthKind;
StringRef listName = getTypeListName(dir->getOperand(), lengthKind); StringRef listName = getTypeListName(dir->getOperand(), lengthKind);
if (lengthKind == ArgumentLengthKind::Optional) if (lengthKind == ArgumentLengthKind::Optional)
body << llvm::formatv(" ::mlir::Type {0}Type;\n", listName); body << llvm::formatv(" ::mlir::Type {0}Type;\n", listName);
} else if (auto *dir = dyn_cast<RefDirective>(&param)) {
Element *input = dir->getOperand();
if (auto *operand = dyn_cast<OperandVariable>(input)) {
if (!operand->getVar()->isOptional())
continue;
body << llvm::formatv(
" {0} {1}Operand = {1}Operands.empty() ? {0}() : "
"{1}Operands[0];\n",
"llvm::Optional<::mlir::OpAsmParser::OperandType>",
operand->getVar()->name);
} else if (auto *type = dyn_cast<TypeDirective>(input)) {
ArgumentLengthKind lengthKind;
StringRef listName = getTypeListName(type->getOperand(), lengthKind);
if (lengthKind == ArgumentLengthKind::Optional) {
body << llvm::formatv(" ::mlir::Type {0}Type = {0}Types.empty() ? "
"::mlir::Type() : {0}Types[0];\n",
listName);
}
}
} }
} }
body << " if (parse" << dir->getName() << "(parser"; body << " if (parse" << dir->getName() << "(parser";
for (Element &param : dir->getArguments()) for (Element &param : dir->getArguments()) {
body << ", ";
genCustomParameterParser(param, body); genCustomParameterParser(param, body);
}
body << "))\n" body << "))\n"
<< " return ::mlir::failure();\n"; << " return ::mlir::failure();\n";
@ -1008,9 +999,6 @@ static void genCustomDirectiveParser(CustomDirective *dir, OpMethodBody &body) {
body << llvm::formatv(" if ({0}Operand.hasValue())\n" body << llvm::formatv(" if ({0}Operand.hasValue())\n"
" {0}Operands.push_back(*{0}Operand);\n", " {0}Operands.push_back(*{0}Operand);\n",
var->name); var->name);
} else if (isa<TypeRefDirective>(&param)) {
// In the `type_ref` case, do not parse a new Type that needs to be added.
// Just do nothing here.
} else if (auto *dir = dyn_cast<TypeDirective>(&param)) { } else if (auto *dir = dyn_cast<TypeDirective>(&param)) {
ArgumentLengthKind lengthKind; ArgumentLengthKind lengthKind;
StringRef listName = getTypeListName(dir->getOperand(), lengthKind); StringRef listName = getTypeListName(dir->getOperand(), lengthKind);
@ -1238,15 +1226,6 @@ void OperationFormat::genElementParser(Element *element, OpMethodBody &body,
} else if (isa<SuccessorsDirective>(element)) { } else if (isa<SuccessorsDirective>(element)) {
body << llvm::formatv(successorListParserCode, "full"); body << llvm::formatv(successorListParserCode, "full");
} else if (auto *dir = dyn_cast<TypeRefDirective>(element)) {
ArgumentLengthKind lengthKind;
StringRef listName = getTypeListName(dir->getOperand(), lengthKind);
if (lengthKind == ArgumentLengthKind::Variadic)
body << llvm::formatv(variadicTypeParserCode, listName);
else if (lengthKind == ArgumentLengthKind::Optional)
body << llvm::formatv(optionalTypeParserCode, listName);
else
body << formatv(typeParserCode, listName);
} else if (auto *dir = dyn_cast<TypeDirective>(element)) { } else if (auto *dir = dyn_cast<TypeDirective>(element)) {
ArgumentLengthKind lengthKind; ArgumentLengthKind lengthKind;
StringRef listName = getTypeListName(dir->getOperand(), lengthKind); StringRef listName = getTypeListName(dir->getOperand(), lengthKind);
@ -1587,39 +1566,28 @@ static void genSpacePrinter(bool value, OpMethodBody &body,
shouldEmitSpace = false; shouldEmitSpace = false;
} }
/// Generate the printer for a custom directive. /// Generate the printer for a custom directive parameter.
static void genCustomDirectivePrinter(CustomDirective *customDir, static void genCustomDirectiveParameterPrinter(Element *element,
OpMethodBody &body) { OpMethodBody &body) {
body << " print" << customDir->getName() << "(p, *this"; if (auto *attr = dyn_cast<AttributeVariable>(element)) {
for (Element &param : customDir->getArguments()) {
body << ", ";
if (auto *attr = dyn_cast<AttributeVariable>(&param)) {
body << attr->getVar()->name << "Attr()"; body << attr->getVar()->name << "Attr()";
} else if (isa<AttrDictDirective>(&param)) { } else if (isa<AttrDictDirective>(element)) {
body << "getOperation()->getAttrDictionary()"; body << "getOperation()->getAttrDictionary()";
} else if (auto *operand = dyn_cast<OperandVariable>(&param)) { } else if (auto *operand = dyn_cast<OperandVariable>(element)) {
body << operand->getVar()->name << "()"; body << operand->getVar()->name << "()";
} else if (auto *region = dyn_cast<RegionVariable>(&param)) { } else if (auto *region = dyn_cast<RegionVariable>(element)) {
body << region->getVar()->name << "()"; body << region->getVar()->name << "()";
} else if (auto *successor = dyn_cast<SuccessorVariable>(&param)) { } else if (auto *successor = dyn_cast<SuccessorVariable>(element)) {
body << successor->getVar()->name << "()"; body << successor->getVar()->name << "()";
} else if (auto *dir = dyn_cast<TypeRefDirective>(&param)) { } else if (auto *dir = dyn_cast<RefDirective>(element)) {
auto *typeOperand = dir->getOperand(); genCustomDirectiveParameterPrinter(dir->getOperand(), body);
auto *operand = dyn_cast<OperandVariable>(typeOperand);
auto *var = operand ? operand->getVar() } else if (auto *dir = dyn_cast<TypeDirective>(element)) {
: cast<ResultVariable>(typeOperand)->getVar();
if (var->isVariadic())
body << var->name << "().getTypes()";
else if (var->isOptional())
body << llvm::formatv("({0}() ? {0}().getType() : Type())", var->name);
else
body << var->name << "().getType()";
} else if (auto *dir = dyn_cast<TypeDirective>(&param)) {
auto *typeOperand = dir->getOperand(); auto *typeOperand = dir->getOperand();
auto *operand = dyn_cast<OperandVariable>(typeOperand); auto *operand = dyn_cast<OperandVariable>(typeOperand);
auto *var = operand ? operand->getVar() auto *var = operand ? operand->getVar()
@ -1635,6 +1603,14 @@ static void genCustomDirectivePrinter(CustomDirective *customDir,
} }
} }
/// Generate the printer for a custom directive.
static void genCustomDirectivePrinter(CustomDirective *customDir,
OpMethodBody &body) {
body << " print" << customDir->getName() << "(p, *this";
for (Element &param : customDir->getArguments()) {
body << ", ";
genCustomDirectiveParameterPrinter(&param, body);
}
body << ");\n"; body << ");\n";
} }
@ -1886,9 +1862,6 @@ void OperationFormat::genElementPrinter(Element *element, OpMethodBody &body,
} else if (auto *dir = dyn_cast<TypeDirective>(element)) { } else if (auto *dir = dyn_cast<TypeDirective>(element)) {
body << " p << "; body << " p << ";
genTypeOperandPrinter(dir->getOperand(), body) << ";\n"; genTypeOperandPrinter(dir->getOperand(), body) << ";\n";
} else if (auto *dir = dyn_cast<TypeRefDirective>(element)) {
body << " p << ";
genTypeOperandPrinter(dir->getOperand(), body) << ";\n";
} else if (auto *dir = dyn_cast<FunctionalTypeDirective>(element)) { } else if (auto *dir = dyn_cast<FunctionalTypeDirective>(element)) {
body << " p.printFunctionalType("; body << " p.printFunctionalType(";
genTypeOperandPrinter(dir->getInputs(), body) << ", "; genTypeOperandPrinter(dir->getInputs(), body) << ", ";
@ -1951,11 +1924,11 @@ public:
kw_custom, kw_custom,
kw_functional_type, kw_functional_type,
kw_operands, kw_operands,
kw_ref,
kw_regions, kw_regions,
kw_results, kw_results,
kw_successors, kw_successors,
kw_type, kw_type,
kw_type_ref,
keyword_end, keyword_end,
// String valued tokens. // String valued tokens.
@ -2156,11 +2129,11 @@ Token FormatLexer::lexIdentifier(const char *tokStart) {
.Case("custom", Token::kw_custom) .Case("custom", Token::kw_custom)
.Case("functional-type", Token::kw_functional_type) .Case("functional-type", Token::kw_functional_type)
.Case("operands", Token::kw_operands) .Case("operands", Token::kw_operands)
.Case("ref", Token::kw_ref)
.Case("regions", Token::kw_regions) .Case("regions", Token::kw_regions)
.Case("results", Token::kw_results) .Case("results", Token::kw_results)
.Case("successors", Token::kw_successors) .Case("successors", Token::kw_successors)
.Case("type", Token::kw_type) .Case("type", Token::kw_type)
.Case("type_ref", Token::kw_type_ref)
.Default(Token::identifier); .Default(Token::identifier);
return Token(kind, str); return Token(kind, str);
} }
@ -2191,6 +2164,19 @@ public:
LogicalResult parse(); LogicalResult parse();
private: private:
/// The current context of the parser when parsing an element.
enum ParserContext {
/// The element is being parsed in a "top-level" context, i.e. at the top of
/// the format or in an optional group.
TopLevelContext,
/// The element is being parsed as a custom directive child.
CustomDirectiveContext,
/// The element is being parsed as a type directive child.
TypeDirectiveContext,
/// The element is being parsed as a reference directive child.
RefDirectiveContext
};
/// This struct represents a type resolution instance. It includes a specific /// This struct represents a type resolution instance. It includes a specific
/// type as well as an optional transformer to apply to that type in order to /// type as well as an optional transformer to apply to that type in order to
/// properly resolve the type of a variable. /// properly resolve the type of a variable.
@ -2249,14 +2235,15 @@ private:
/// Parse a specific element. /// Parse a specific element.
LogicalResult parseElement(std::unique_ptr<Element> &element, LogicalResult parseElement(std::unique_ptr<Element> &element,
bool isTopLevel); ParserContext context);
LogicalResult parseVariable(std::unique_ptr<Element> &element, LogicalResult parseVariable(std::unique_ptr<Element> &element,
bool isTopLevel); ParserContext context);
LogicalResult parseDirective(std::unique_ptr<Element> &element, LogicalResult parseDirective(std::unique_ptr<Element> &element,
bool isTopLevel); ParserContext context);
LogicalResult parseLiteral(std::unique_ptr<Element> &element); LogicalResult parseLiteral(std::unique_ptr<Element> &element,
ParserContext context);
LogicalResult parseOptional(std::unique_ptr<Element> &element, LogicalResult parseOptional(std::unique_ptr<Element> &element,
bool isTopLevel); ParserContext context);
LogicalResult parseOptionalChildElement( LogicalResult parseOptionalChildElement(
std::vector<std::unique_ptr<Element>> &childElements, std::vector<std::unique_ptr<Element>> &childElements,
Optional<unsigned> &anchorIdx); Optional<unsigned> &anchorIdx);
@ -2265,26 +2252,29 @@ private:
/// Parse the various different directives. /// Parse the various different directives.
LogicalResult parseAttrDictDirective(std::unique_ptr<Element> &element, LogicalResult parseAttrDictDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel, llvm::SMLoc loc, ParserContext context,
bool withKeyword); bool withKeyword);
LogicalResult parseCustomDirective(std::unique_ptr<Element> &element, LogicalResult parseCustomDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel); llvm::SMLoc loc, ParserContext context);
LogicalResult parseCustomDirectiveParameter( LogicalResult parseCustomDirectiveParameter(
std::vector<std::unique_ptr<Element>> &parameters); std::vector<std::unique_ptr<Element>> &parameters);
LogicalResult parseFunctionalTypeDirective(std::unique_ptr<Element> &element, LogicalResult parseFunctionalTypeDirective(std::unique_ptr<Element> &element,
Token tok, bool isTopLevel); Token tok, ParserContext context);
LogicalResult parseOperandsDirective(std::unique_ptr<Element> &element, LogicalResult parseOperandsDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel); llvm::SMLoc loc, ParserContext context);
LogicalResult parseReferenceDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, ParserContext context);
LogicalResult parseRegionsDirective(std::unique_ptr<Element> &element, LogicalResult parseRegionsDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel); llvm::SMLoc loc, ParserContext context);
LogicalResult parseResultsDirective(std::unique_ptr<Element> &element, LogicalResult parseResultsDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel); llvm::SMLoc loc, ParserContext context);
LogicalResult parseSuccessorsDirective(std::unique_ptr<Element> &element, LogicalResult parseSuccessorsDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel); llvm::SMLoc loc,
ParserContext context);
LogicalResult parseTypeDirective(std::unique_ptr<Element> &element, Token tok, LogicalResult parseTypeDirective(std::unique_ptr<Element> &element, Token tok,
bool isTopLevel, bool isTypeRef = false); ParserContext context);
LogicalResult parseTypeDirectiveOperand(std::unique_ptr<Element> &element, LogicalResult parseTypeDirectiveOperand(std::unique_ptr<Element> &element,
bool isTypeRef = false); bool isRefChild = false);
//===--------------------------------------------------------------------===// //===--------------------------------------------------------------------===//
// Lexer Utilities // Lexer Utilities
@ -2340,7 +2330,7 @@ LogicalResult FormatParser::parse() {
// Parse each of the format elements into the main format. // Parse each of the format elements into the main format.
while (curToken.getKind() != Token::eof) { while (curToken.getKind() != Token::eof) {
std::unique_ptr<Element> element; std::unique_ptr<Element> element;
if (failed(parseElement(element, /*isTopLevel=*/true))) if (failed(parseElement(element, TopLevelContext)))
return ::mlir::failure(); return ::mlir::failure();
fmt.elements.push_back(std::move(element)); fmt.elements.push_back(std::move(element));
} }
@ -2634,25 +2624,25 @@ ConstArgument FormatParser::findSeenArg(StringRef name) {
} }
LogicalResult FormatParser::parseElement(std::unique_ptr<Element> &element, LogicalResult FormatParser::parseElement(std::unique_ptr<Element> &element,
bool isTopLevel) { ParserContext context) {
// Directives. // Directives.
if (curToken.isKeyword()) if (curToken.isKeyword())
return parseDirective(element, isTopLevel); return parseDirective(element, context);
// Literals. // Literals.
if (curToken.getKind() == Token::literal) if (curToken.getKind() == Token::literal)
return parseLiteral(element); return parseLiteral(element, context);
// Optionals. // Optionals.
if (curToken.getKind() == Token::l_paren) if (curToken.getKind() == Token::l_paren)
return parseOptional(element, isTopLevel); return parseOptional(element, context);
// Variables. // Variables.
if (curToken.getKind() == Token::variable) if (curToken.getKind() == Token::variable)
return parseVariable(element, isTopLevel); return parseVariable(element, context);
return emitError(curToken.getLoc(), return emitError(curToken.getLoc(),
"expected directive, literal, variable, or optional group"); "expected directive, literal, variable, or optional group");
} }
LogicalResult FormatParser::parseVariable(std::unique_ptr<Element> &element, LogicalResult FormatParser::parseVariable(std::unique_ptr<Element> &element,
bool isTopLevel) { ParserContext context) {
Token varTok = curToken; Token varTok = curToken;
consumeToken(); consumeToken();
@ -2663,42 +2653,67 @@ LogicalResult FormatParser::parseVariable(std::unique_ptr<Element> &element,
// op. // op.
/// Attributes /// Attributes
if (const NamedAttribute *attr = findArg(op.getAttributes(), name)) { if (const NamedAttribute *attr = findArg(op.getAttributes(), name)) {
if (isTopLevel && !seenAttrs.insert(attr)) if (context == TypeDirectiveContext)
return emitError(
loc, "attributes cannot be used as children to a `type` directive");
if (context == RefDirectiveContext) {
if (!seenAttrs.count(attr))
return emitError(loc, "attribute '" + name +
"' must be bound before it is referenced");
} else if (!seenAttrs.insert(attr)) {
return emitError(loc, "attribute '" + name + "' is already bound"); return emitError(loc, "attribute '" + name + "' is already bound");
}
element = std::make_unique<AttributeVariable>(attr); element = std::make_unique<AttributeVariable>(attr);
return ::mlir::success(); return ::mlir::success();
} }
/// Operands /// Operands
if (const NamedTypeConstraint *operand = findArg(op.getOperands(), name)) { if (const NamedTypeConstraint *operand = findArg(op.getOperands(), name)) {
if (isTopLevel) { if (context == TopLevelContext || context == CustomDirectiveContext) {
if (fmt.allOperands || !seenOperands.insert(operand).second) if (fmt.allOperands || !seenOperands.insert(operand).second)
return emitError(loc, "operand '" + name + "' is already bound"); return emitError(loc, "operand '" + name + "' is already bound");
} else if (context == RefDirectiveContext && !seenOperands.count(operand)) {
return emitError(loc, "operand '" + name +
"' must be bound before it is referenced");
} }
element = std::make_unique<OperandVariable>(operand); element = std::make_unique<OperandVariable>(operand);
return ::mlir::success(); return ::mlir::success();
} }
/// Regions /// Regions
if (const NamedRegion *region = findArg(op.getRegions(), name)) { if (const NamedRegion *region = findArg(op.getRegions(), name)) {
if (!isTopLevel) if (context == TopLevelContext || context == CustomDirectiveContext) {
return emitError(loc, "regions can only be used at the top level");
if (hasAllRegions || !seenRegions.insert(region).second) if (hasAllRegions || !seenRegions.insert(region).second)
return emitError(loc, "region '" + name + "' is already bound"); return emitError(loc, "region '" + name + "' is already bound");
} else if (context == RefDirectiveContext && !seenRegions.count(region)) {
return emitError(loc, "region '" + name +
"' must be bound before it is referenced");
} else {
return emitError(loc, "regions can only be used at the top level");
}
element = std::make_unique<RegionVariable>(region); element = std::make_unique<RegionVariable>(region);
return ::mlir::success(); return ::mlir::success();
} }
/// Results. /// Results.
if (const auto *result = findArg(op.getResults(), name)) { if (const auto *result = findArg(op.getResults(), name)) {
if (isTopLevel) if (context != TypeDirectiveContext)
return emitError(loc, "results can not be used at the top level"); return emitError(loc, "result variables can can only be used as a child "
"to a 'type' directive");
element = std::make_unique<ResultVariable>(result); element = std::make_unique<ResultVariable>(result);
return ::mlir::success(); return ::mlir::success();
} }
/// Successors. /// Successors.
if (const auto *successor = findArg(op.getSuccessors(), name)) { if (const auto *successor = findArg(op.getSuccessors(), name)) {
if (!isTopLevel) if (context == TopLevelContext || context == CustomDirectiveContext) {
return emitError(loc, "successors can only be used at the top level");
if (hasAllSuccessors || !seenSuccessors.insert(successor).second) if (hasAllSuccessors || !seenSuccessors.insert(successor).second)
return emitError(loc, "successor '" + name + "' is already bound"); return emitError(loc, "successor '" + name + "' is already bound");
} else if (context == RefDirectiveContext &&
!seenSuccessors.count(successor)) {
return emitError(loc, "successor '" + name +
"' must be bound before it is referenced");
} else {
return emitError(loc, "successors can only be used at the top level");
}
element = std::make_unique<SuccessorVariable>(successor); element = std::make_unique<SuccessorVariable>(successor);
return ::mlir::success(); return ::mlir::success();
} }
@ -2707,41 +2722,47 @@ LogicalResult FormatParser::parseVariable(std::unique_ptr<Element> &element,
} }
LogicalResult FormatParser::parseDirective(std::unique_ptr<Element> &element, LogicalResult FormatParser::parseDirective(std::unique_ptr<Element> &element,
bool isTopLevel) { ParserContext context) {
Token dirTok = curToken; Token dirTok = curToken;
consumeToken(); consumeToken();
switch (dirTok.getKind()) { switch (dirTok.getKind()) {
case Token::kw_attr_dict: case Token::kw_attr_dict:
return parseAttrDictDirective(element, dirTok.getLoc(), isTopLevel, return parseAttrDictDirective(element, dirTok.getLoc(), context,
/*withKeyword=*/false); /*withKeyword=*/false);
case Token::kw_attr_dict_w_keyword: case Token::kw_attr_dict_w_keyword:
return parseAttrDictDirective(element, dirTok.getLoc(), isTopLevel, return parseAttrDictDirective(element, dirTok.getLoc(), context,
/*withKeyword=*/true); /*withKeyword=*/true);
case Token::kw_custom: case Token::kw_custom:
return parseCustomDirective(element, dirTok.getLoc(), isTopLevel); return parseCustomDirective(element, dirTok.getLoc(), context);
case Token::kw_functional_type: case Token::kw_functional_type:
return parseFunctionalTypeDirective(element, dirTok, isTopLevel); return parseFunctionalTypeDirective(element, dirTok, context);
case Token::kw_operands: case Token::kw_operands:
return parseOperandsDirective(element, dirTok.getLoc(), isTopLevel); return parseOperandsDirective(element, dirTok.getLoc(), context);
case Token::kw_regions: case Token::kw_regions:
return parseRegionsDirective(element, dirTok.getLoc(), isTopLevel); return parseRegionsDirective(element, dirTok.getLoc(), context);
case Token::kw_results: case Token::kw_results:
return parseResultsDirective(element, dirTok.getLoc(), isTopLevel); return parseResultsDirective(element, dirTok.getLoc(), context);
case Token::kw_successors: case Token::kw_successors:
return parseSuccessorsDirective(element, dirTok.getLoc(), isTopLevel); return parseSuccessorsDirective(element, dirTok.getLoc(), context);
case Token::kw_type_ref: case Token::kw_ref:
return parseTypeDirective(element, dirTok, isTopLevel, /*isTypeRef=*/true); return parseReferenceDirective(element, dirTok.getLoc(), context);
case Token::kw_type: case Token::kw_type:
return parseTypeDirective(element, dirTok, isTopLevel); return parseTypeDirective(element, dirTok, context);
default: default:
llvm_unreachable("unknown directive token"); llvm_unreachable("unknown directive token");
} }
} }
LogicalResult FormatParser::parseLiteral(std::unique_ptr<Element> &element) { LogicalResult FormatParser::parseLiteral(std::unique_ptr<Element> &element,
ParserContext context) {
Token literalTok = curToken; Token literalTok = curToken;
if (context != TopLevelContext) {
return emitError(
literalTok.getLoc(),
"literals may only be used in a top-level section of the format");
}
consumeToken(); consumeToken();
StringRef value = literalTok.getSpelling().drop_front().drop_back(); StringRef value = literalTok.getSpelling().drop_front().drop_back();
@ -2766,9 +2787,9 @@ LogicalResult FormatParser::parseLiteral(std::unique_ptr<Element> &element) {
} }
LogicalResult FormatParser::parseOptional(std::unique_ptr<Element> &element, LogicalResult FormatParser::parseOptional(std::unique_ptr<Element> &element,
bool isTopLevel) { ParserContext context) {
llvm::SMLoc curLoc = curToken.getLoc(); llvm::SMLoc curLoc = curToken.getLoc();
if (!isTopLevel) if (context != TopLevelContext)
return emitError(curLoc, "optional groups can only be used as top-level " return emitError(curLoc, "optional groups can only be used as top-level "
"elements"); "elements");
consumeToken(); consumeToken();
@ -2812,7 +2833,7 @@ LogicalResult FormatParser::parseOptionalChildElement(
Optional<unsigned> &anchorIdx) { Optional<unsigned> &anchorIdx) {
llvm::SMLoc childLoc = curToken.getLoc(); llvm::SMLoc childLoc = curToken.getLoc();
childElements.push_back({}); childElements.push_back({});
if (failed(parseElement(childElements.back(), /*isTopLevel=*/true))) if (failed(parseElement(childElements.back(), TopLevelContext)))
return ::mlir::failure(); return ::mlir::failure();
// Check to see if this element is the anchor of the optional group. // Check to see if this element is the anchor of the optional group.
@ -2843,7 +2864,7 @@ LogicalResult FormatParser::verifyOptionalChildElement(Element *element,
}) })
// Only optional-like(i.e. variadic) operands can be within an optional // Only optional-like(i.e. variadic) operands can be within an optional
// group. // group.
.Case<OperandVariable>([&](OperandVariable *ele) { .Case([&](OperandVariable *ele) {
if (!ele->getVar()->isVariableLength()) if (!ele->getVar()->isVariableLength())
return emitError(childLoc, "only variable length operands can be " return emitError(childLoc, "only variable length operands can be "
"used within an optional group"); "used within an optional group");
@ -2851,22 +2872,22 @@ LogicalResult FormatParser::verifyOptionalChildElement(Element *element,
}) })
// Only optional-like(i.e. variadic) results can be within an optional // Only optional-like(i.e. variadic) results can be within an optional
// group. // group.
.Case<ResultVariable>([&](ResultVariable *ele) { .Case([&](ResultVariable *ele) {
if (!ele->getVar()->isVariableLength()) if (!ele->getVar()->isVariableLength())
return emitError(childLoc, "only variable length results can be " return emitError(childLoc, "only variable length results can be "
"used within an optional group"); "used within an optional group");
return ::mlir::success(); return ::mlir::success();
}) })
.Case<RegionVariable>([&](RegionVariable *) { .Case([&](RegionVariable *) {
// TODO: When ODS has proper support for marking "optional" regions, add // TODO: When ODS has proper support for marking "optional" regions, add
// a check here. // a check here.
return ::mlir::success(); return ::mlir::success();
}) })
.Case<TypeDirective>([&](TypeDirective *ele) { .Case([&](TypeDirective *ele) {
return verifyOptionalChildElement(ele->getOperand(), childLoc, return verifyOptionalChildElement(ele->getOperand(), childLoc,
/*isAnchor=*/false); /*isAnchor=*/false);
}) })
.Case<FunctionalTypeDirective>([&](FunctionalTypeDirective *ele) { .Case([&](FunctionalTypeDirective *ele) {
if (failed(verifyOptionalChildElement(ele->getInputs(), childLoc, if (failed(verifyOptionalChildElement(ele->getInputs(), childLoc,
/*isAnchor=*/false))) /*isAnchor=*/false)))
return failure(); return failure();
@ -2876,8 +2897,7 @@ LogicalResult FormatParser::verifyOptionalChildElement(Element *element,
// Literals, whitespace, and custom directives may be used, but they can't // Literals, whitespace, and custom directives may be used, but they can't
// anchor the group. // anchor the group.
.Case<LiteralElement, WhitespaceElement, CustomDirective, .Case<LiteralElement, WhitespaceElement, CustomDirective,
FunctionalTypeDirective, OptionalElement, TypeRefDirective>( FunctionalTypeDirective, OptionalElement>([&](Element *) {
[&](Element *) {
if (isAnchor) if (isAnchor)
return emitError(childLoc, "only variables and types can be used " return emitError(childLoc, "only variables and types can be used "
"to anchor an optional group"); "to anchor an optional group");
@ -2891,23 +2911,34 @@ LogicalResult FormatParser::verifyOptionalChildElement(Element *element,
LogicalResult LogicalResult
FormatParser::parseAttrDictDirective(std::unique_ptr<Element> &element, FormatParser::parseAttrDictDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel, llvm::SMLoc loc, ParserContext context,
bool withKeyword) { bool withKeyword) {
if (!isTopLevel) if (context == TypeDirectiveContext)
return emitError(loc, "'attr-dict' directive can only be used as a " return emitError(loc, "'attr-dict' directive can only be used as a "
"top-level directive"); "top-level directive");
if (context == RefDirectiveContext) {
if (!hasAttrDict)
return emitError(loc, "'ref' of 'attr-dict' is not bound by a prior "
"'attr-dict' directive");
// Otherwise, this is a top-level context.
} else {
if (hasAttrDict) if (hasAttrDict)
return emitError(loc, "'attr-dict' directive has already been seen"); return emitError(loc, "'attr-dict' directive has already been seen");
hasAttrDict = true; hasAttrDict = true;
}
element = std::make_unique<AttrDictDirective>(withKeyword); element = std::make_unique<AttrDictDirective>(withKeyword);
return ::mlir::success(); return ::mlir::success();
} }
LogicalResult LogicalResult
FormatParser::parseCustomDirective(std::unique_ptr<Element> &element, FormatParser::parseCustomDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel) { llvm::SMLoc loc, ParserContext context) {
llvm::SMLoc curLoc = curToken.getLoc(); llvm::SMLoc curLoc = curToken.getLoc();
if (context != TopLevelContext)
return emitError(loc, "'custom' is only valid as a top-level directive");
// Parse the custom directive name. // Parse the custom directive name.
if (failed( if (failed(
@ -2940,13 +2971,6 @@ FormatParser::parseCustomDirective(std::unique_ptr<Element> &element,
// After parsing all of the elements, ensure that all type directives refer // After parsing all of the elements, ensure that all type directives refer
// only to variables. // only to variables.
for (auto &ele : elements) { for (auto &ele : elements) {
if (auto *typeEle = dyn_cast<TypeRefDirective>(ele.get())) {
if (!isa<OperandVariable, ResultVariable>(typeEle->getOperand())) {
return emitError(curLoc,
"type_ref directives within a custom directive "
"may only refer to variables");
}
}
if (auto *typeEle = dyn_cast<TypeDirective>(ele.get())) { if (auto *typeEle = dyn_cast<TypeDirective>(ele.get())) {
if (!isa<OperandVariable, ResultVariable>(typeEle->getOperand())) { if (!isa<OperandVariable, ResultVariable>(typeEle->getOperand())) {
return emitError(curLoc, "type directives within a custom directive " return emitError(curLoc, "type directives within a custom directive "
@ -2964,13 +2988,13 @@ LogicalResult FormatParser::parseCustomDirectiveParameter(
std::vector<std::unique_ptr<Element>> &parameters) { std::vector<std::unique_ptr<Element>> &parameters) {
llvm::SMLoc childLoc = curToken.getLoc(); llvm::SMLoc childLoc = curToken.getLoc();
parameters.push_back({}); parameters.push_back({});
if (failed(parseElement(parameters.back(), /*isTopLevel=*/true))) if (failed(parseElement(parameters.back(), CustomDirectiveContext)))
return ::mlir::failure(); return ::mlir::failure();
// Verify that the element can be placed within a custom directive. // Verify that the element can be placed within a custom directive.
if (!isa<TypeRefDirective, TypeDirective, AttrDictDirective, if (!isa<RefDirective, TypeDirective, AttrDictDirective, AttributeVariable,
AttributeVariable, OperandVariable, RegionVariable, OperandVariable, RegionVariable, SuccessorVariable>(
SuccessorVariable>(parameters.back().get())) { parameters.back().get())) {
return emitError(childLoc, "only variables and types may be used as " return emitError(childLoc, "only variables and types may be used as "
"parameters to a custom directive"); "parameters to a custom directive");
} }
@ -2979,9 +3003,9 @@ LogicalResult FormatParser::parseCustomDirectiveParameter(
LogicalResult LogicalResult
FormatParser::parseFunctionalTypeDirective(std::unique_ptr<Element> &element, FormatParser::parseFunctionalTypeDirective(std::unique_ptr<Element> &element,
Token tok, bool isTopLevel) { Token tok, ParserContext context) {
llvm::SMLoc loc = tok.getLoc(); llvm::SMLoc loc = tok.getLoc();
if (!isTopLevel) if (context != TopLevelContext)
return emitError( return emitError(
loc, "'functional-type' is only valid as a top-level directive"); loc, "'functional-type' is only valid as a top-level directive");
@ -3000,8 +3024,13 @@ FormatParser::parseFunctionalTypeDirective(std::unique_ptr<Element> &element,
LogicalResult LogicalResult
FormatParser::parseOperandsDirective(std::unique_ptr<Element> &element, FormatParser::parseOperandsDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel) { llvm::SMLoc loc, ParserContext context) {
if (isTopLevel) { if (context == RefDirectiveContext) {
if (!fmt.allOperands)
return emitError(loc, "'ref' of 'operands' is not bound by a prior "
"'operands' directive");
} else if (context == TopLevelContext || context == CustomDirectiveContext) {
if (fmt.allOperands || !seenOperands.empty()) if (fmt.allOperands || !seenOperands.empty())
return emitError(loc, "'operands' directive creates overlap in format"); return emitError(loc, "'operands' directive creates overlap in format");
fmt.allOperands = true; fmt.allOperands = true;
@ -3010,65 +3039,96 @@ FormatParser::parseOperandsDirective(std::unique_ptr<Element> &element,
return ::mlir::success(); return ::mlir::success();
} }
LogicalResult
FormatParser::parseReferenceDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, ParserContext context) {
if (context != CustomDirectiveContext)
return emitError(loc, "'ref' is only valid within a `custom` directive");
std::unique_ptr<Element> operand;
if (failed(parseToken(Token::l_paren, "expected '(' before argument list")) ||
failed(parseElement(operand, RefDirectiveContext)) ||
failed(parseToken(Token::r_paren, "expected ')' after argument list")))
return ::mlir::failure();
element = std::make_unique<RefDirective>(std::move(operand));
return ::mlir::success();
}
LogicalResult LogicalResult
FormatParser::parseRegionsDirective(std::unique_ptr<Element> &element, FormatParser::parseRegionsDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel) { llvm::SMLoc loc, ParserContext context) {
if (!isTopLevel) if (context == TypeDirectiveContext)
return emitError(loc, "'regions' is only valid as a top-level directive"); return emitError(loc, "'regions' is only valid as a top-level directive");
if (context == RefDirectiveContext) {
if (!hasAllRegions)
return emitError(loc, "'ref' of 'regions' is not bound by a prior "
"'regions' directive");
// Otherwise, this is a TopLevel directive.
} else {
if (hasAllRegions || !seenRegions.empty()) if (hasAllRegions || !seenRegions.empty())
return emitError(loc, "'regions' directive creates overlap in format"); return emitError(loc, "'regions' directive creates overlap in format");
hasAllRegions = true; hasAllRegions = true;
}
element = std::make_unique<RegionsDirective>(); element = std::make_unique<RegionsDirective>();
return ::mlir::success(); return ::mlir::success();
} }
LogicalResult LogicalResult
FormatParser::parseResultsDirective(std::unique_ptr<Element> &element, FormatParser::parseResultsDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel) { llvm::SMLoc loc, ParserContext context) {
if (isTopLevel) if (context != TypeDirectiveContext)
return emitError(loc, "'results' directive can not be used as a " return emitError(loc, "'results' directive can can only be used as a child "
"top-level directive"); "to a 'type' directive");
element = std::make_unique<ResultsDirective>(); element = std::make_unique<ResultsDirective>();
return ::mlir::success(); return ::mlir::success();
} }
LogicalResult LogicalResult
FormatParser::parseSuccessorsDirective(std::unique_ptr<Element> &element, FormatParser::parseSuccessorsDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel) { llvm::SMLoc loc, ParserContext context) {
if (!isTopLevel) if (context == TypeDirectiveContext)
return emitError(loc, return emitError(loc,
"'successors' is only valid as a top-level directive"); "'successors' is only valid as a top-level directive");
if (context == RefDirectiveContext) {
if (!hasAllSuccessors)
return emitError(loc, "'ref' of 'successors' is not bound by a prior "
"'successors' directive");
// Otherwise, this is a TopLevel directive.
} else {
if (hasAllSuccessors || !seenSuccessors.empty()) if (hasAllSuccessors || !seenSuccessors.empty())
return emitError(loc, "'successors' directive creates overlap in format"); return emitError(loc, "'successors' directive creates overlap in format");
hasAllSuccessors = true; hasAllSuccessors = true;
}
element = std::make_unique<SuccessorsDirective>(); element = std::make_unique<SuccessorsDirective>();
return ::mlir::success(); return ::mlir::success();
} }
LogicalResult LogicalResult
FormatParser::parseTypeDirective(std::unique_ptr<Element> &element, Token tok, FormatParser::parseTypeDirective(std::unique_ptr<Element> &element, Token tok,
bool isTopLevel, bool isTypeRef) { ParserContext context) {
llvm::SMLoc loc = tok.getLoc(); llvm::SMLoc loc = tok.getLoc();
if (!isTopLevel) if (context == TypeDirectiveContext)
return emitError(loc, "'type' is only valid as a top-level directive"); return emitError(loc, "'type' cannot be used as a child of another `type`");
bool isRefChild = context == RefDirectiveContext;
std::unique_ptr<Element> operand; std::unique_ptr<Element> operand;
if (failed(parseToken(Token::l_paren, "expected '(' before argument list")) || if (failed(parseToken(Token::l_paren, "expected '(' before argument list")) ||
failed(parseTypeDirectiveOperand(operand, isTypeRef)) || failed(parseTypeDirectiveOperand(operand, isRefChild)) ||
failed(parseToken(Token::r_paren, "expected ')' after argument list"))) failed(parseToken(Token::r_paren, "expected ')' after argument list")))
return ::mlir::failure(); return ::mlir::failure();
if (isTypeRef)
element = std::make_unique<TypeRefDirective>(std::move(operand));
else
element = std::make_unique<TypeDirective>(std::move(operand)); element = std::make_unique<TypeDirective>(std::move(operand));
return ::mlir::success(); return ::mlir::success();
} }
LogicalResult LogicalResult
FormatParser::parseTypeDirectiveOperand(std::unique_ptr<Element> &element, FormatParser::parseTypeDirectiveOperand(std::unique_ptr<Element> &element,
bool isTypeRef) { bool isRefChild) {
llvm::SMLoc loc = curToken.getLoc(); llvm::SMLoc loc = curToken.getLoc();
if (failed(parseElement(element, /*isTopLevel=*/false))) if (failed(parseElement(element, TypeDirectiveContext)))
return ::mlir::failure(); return ::mlir::failure();
if (isa<LiteralElement>(element.get())) if (isa<LiteralElement>(element.get()))
return emitError( return emitError(
@ -3076,36 +3136,35 @@ FormatParser::parseTypeDirectiveOperand(std::unique_ptr<Element> &element,
if (auto *var = dyn_cast<OperandVariable>(element.get())) { if (auto *var = dyn_cast<OperandVariable>(element.get())) {
unsigned opIdx = var->getVar() - op.operand_begin(); unsigned opIdx = var->getVar() - op.operand_begin();
if (!isTypeRef && (fmt.allOperandTypes || seenOperandTypes.test(opIdx))) if (!isRefChild && (fmt.allOperandTypes || seenOperandTypes.test(opIdx)))
return emitError(loc, "'type' of '" + var->getVar()->name + return emitError(loc, "'type' of '" + var->getVar()->name +
"' is already bound"); "' is already bound");
if (isTypeRef && !(fmt.allOperandTypes || seenOperandTypes.test(opIdx))) if (isRefChild && !(fmt.allOperandTypes || seenOperandTypes.test(opIdx)))
return emitError(loc, "'type_ref' of '" + var->getVar()->name + return emitError(loc, "'ref' of 'type($" + var->getVar()->name +
"' is not bound by a prior 'type' directive"); ")' is not bound by a prior 'type' directive");
seenOperandTypes.set(opIdx); seenOperandTypes.set(opIdx);
} else if (auto *var = dyn_cast<ResultVariable>(element.get())) { } else if (auto *var = dyn_cast<ResultVariable>(element.get())) {
unsigned resIdx = var->getVar() - op.result_begin(); unsigned resIdx = var->getVar() - op.result_begin();
if (!isTypeRef && (fmt.allResultTypes || seenResultTypes.test(resIdx))) if (!isRefChild && (fmt.allResultTypes || seenResultTypes.test(resIdx)))
return emitError(loc, "'type' of '" + var->getVar()->name + return emitError(loc, "'type' of '" + var->getVar()->name +
"' is already bound"); "' is already bound");
if (isTypeRef && !(fmt.allResultTypes || seenResultTypes.test(resIdx))) if (isRefChild && !(fmt.allResultTypes || seenResultTypes.test(resIdx)))
return emitError(loc, "'type_ref' of '" + var->getVar()->name + return emitError(loc, "'ref' of 'type($" + var->getVar()->name +
"' is not bound by a prior 'type' directive"); ")' is not bound by a prior 'type' directive");
seenResultTypes.set(resIdx); seenResultTypes.set(resIdx);
} else if (isa<OperandsDirective>(&*element)) { } else if (isa<OperandsDirective>(&*element)) {
if (!isTypeRef && (fmt.allOperandTypes || seenOperandTypes.any())) if (!isRefChild && (fmt.allOperandTypes || seenOperandTypes.any()))
return emitError(loc, "'operands' 'type' is already bound"); return emitError(loc, "'operands' 'type' is already bound");
if (isTypeRef && !(fmt.allOperandTypes || seenOperandTypes.all())) if (isRefChild && !fmt.allOperandTypes)
return emitError( return emitError(loc, "'ref' of 'type(operands)' is not bound by a prior "
loc, "'type' directive");
"'operands' 'type_ref' is not bound by a prior 'type' directive");
fmt.allOperandTypes = true; fmt.allOperandTypes = true;
} else if (isa<ResultsDirective>(&*element)) { } else if (isa<ResultsDirective>(&*element)) {
if (!isTypeRef && (fmt.allResultTypes || seenResultTypes.any())) if (!isRefChild && (fmt.allResultTypes || seenResultTypes.any()))
return emitError(loc, "'results' 'type' is already bound"); return emitError(loc, "'results' 'type' is already bound");
if (isTypeRef && !(fmt.allResultTypes || seenResultTypes.all())) if (isRefChild && !fmt.allResultTypes)
return emitError( return emitError(loc, "'ref' of 'type(results)' is not bound by a prior "
loc, "'results' 'type_ref' is not bound by a prior 'type' directive"); "'type' directive");
fmt.allResultTypes = true; fmt.allResultTypes = true;
} else { } else {
return emitError(loc, "invalid argument to 'type' directive"); return emitError(loc, "invalid argument to 'type' directive");