So, to give some post-meeting thoughts, now that some of this has crystalized.
Both because it’ll be useful for generic parsing/printing and for API ergonomics, I do think it’ll be a good idea to have - and move to - something like
class PropRef {
mlir::TypeID kind;
void* data;
...
};
Also, because the Properties structs of operations themselves have TypeIDs, we should similarly change Operation::getRawProperties() from returning an OpaqueProperties (aka void*) to a PropRef.
Then, for general-case parsing, we’d have what the slides called UniquePropStorage and what I’m thinking of as OwningPropRef (which’d be turnable into a PropRef, etc etc.).
I then assume that Properties structs get {get/set}Raw[Foo]() that take a PropRef because why now.
We’ll probably also get std:optional<PropRef> OperationName::getProperty(StringRef name, PropRef properties); and LogicalResult OperationName::setProperty(StringRef name, PropRef properties, PropRef property);
… Or maybe those become elements on properties structs themselves somehow?? Details tbd.
… side note here is that all inherent attributes can trivially become PropRefs - you just stash the attribute pointer in the data member and record the type ID.
Then, for parsing/printing (and code reuse between operations) … we’d probably need a separate tablegen for property definitions/declarations, which would create something along the lines of
/// Const, etc. elided
class I32PropRegistration : PropRegistryEntry {
[static] TypeID getTypeId() { return "..." }; }
[static] StringRef getMnemonic() { return "i32" };
static ParseResult I32PropRegistration::parse(OpAsmParser&, int32_t&) { // existing parser code, called in assembly formats etc. }
// Similarly for optional parsing code and the printer
// With the mnemonic and leading < already consumed, parse
// a property of this kind into a newly-created `OwningPropRef`
I32PropRegistration::parse(OpAsmParser&, OwningPropRef* result);
I32PropRegistration::printGeneric(OpAsmParser&, PropRef);
...
};
These get enrolled into some sort of mnemonic => registration map so that the OpAsmParser::parseGenericProp(...) (and so on for printers) can do its thing.
In things I realized: One other nice thing about this scheme is that each operation’s properties struct can generate such an entry for its Properties struct, which can get registered along with the operation.
This means that if I have my_dialect.my_op like in my example, the generic form of its Properties struct would look something like
&my_dialect.my_op.props<attr = 1 : i64, a = 1, b = "x", c = 0>
which can get printed in generic assembly. (This also lets us define custom properties structs by analogy to custom attribute definitions, which is cool)
(prop-dict will probably want its own special handling since it can have elided fields / doesn’t need to be complete / etc. but that’s not inherently related)
I think - aside from I’m not volunteering to code all this up myself - something like what I just extrapolated out from the slides would (now that I’ve understood the motivation and some of the inner workings of the proposal) be a good improvement to our existing infrastructure, both in terms of easy FFI and in terms of making APIs less clunky.
And it’d let us get rid of prop <=> attribute serialization, which is probably something of a wart.
I do still think that stuff like tensor constants would work rather awkwardly as a property - though I suppose you could wrap a shared_ptr<> to your data + a type in order to still have relatively cheap constructors … though not necessarily a cheap ==. So perhaps the vision of almost all to all attribute usage being replaced by non-attribute properties has some legs - even if I do think that attributes do have valid usecases.