Structured, custom attributes and types (for non-C++ bindings)

In relation to the C and Python bindings, I’ve been thinking on what to do with custom attributes and types. Barring additional, custom C++ bindings code, both are effectively limited to their string representations (i.e. I can’t just write/tablegen some python like I do with operations in order to create or introspect custom attributes and types: I have to write additional native C/C++ and python binding glue to work with them).

This second class nature can just be our answer: if the string form isn’t enough for non-C++, then the choice is to implement bindings for it and arrange to get them into whatever binary you are linking against (which can be non-trivial, especially for out of tree projects).

I wonder whether we want to consider a middle-road. Many custom attributes and types can be expressed in terms of a set of named, primitive attributes, just like operations can. From this perspective, the fact that we open code them as special C++ constructs seems like something that could be simplified in some common cases (i.e. have a StructuredType or StructuredAttribute which are defined via a closed set of more fundamental attributes). If we had such a thing (and used it), then I think we could easily write bindings against these types that would be introspectable and (assuming some registration things get worked out) constructable generically. We could still have the ability to open-code them, but I’d feel better about relegating such truly-opaque types/attributes to second class with respect to language bindings.

I haven’t yet looked at the right mechanism to achieve this, but it seems like a combination of type/attribute interfaces, tablegen glue and (possibly) some registration work should be able to get us there. I likely won’t have time for a proof of concept for a few weeks (working on bindings for operations next), but I wanted to float the idea and get opinions. I’m still not convinced it is a good idea in this form, but I’m struggling to find another scalable way to non-C++ interop.

@ftynse for visibility/thoughts

Random thought, but would it be enough to have sugared builder methods for these types/attributes that type erases the builder to an array of PointerUnion<Type,Attribute>? This would be better than type erasing to strings :slight_smile: and could be opt-in implemented by manually written custom types, and potentially autogenerated by ODS someday.

That would be a start on the construction side. We would also need a mechanism to generically bind to such builder methods, allowing invocation on them by type/attribute name.

For introspection, we could also follow suit with an interface for querying this representation tuple. We’d probably also want a primitive to effectively do an isa<> by name.

All of this moves types and attributes closer to Operations, which seems reasonable. Barring mechanics, I think the salient design point here is whether such a generic attribute’s represenational state can/should be represented effectively by its name plus tuple(Type|Attribute) and we provide entry points to build and query generically based on that (and we can always have variants that eschew that interface, but they would be truly opaque).

+1, this seems useful.

I think you’re onto a good tack with the idea of using interfaces for this – even if the default way of defining attributes is as a composition of others, we don’t want to rule out users defining their own highly-efficient internal representations if needed.

Btw, I don’t think it is merely “some common cases”. There is very rarely any special representation involved. I think if we moved to a model where the “default” way of defining the structure of these attributes is via a TableGen DSL (already under discussion in Ease of defining new types for types) that expresses them as basic compositions, then we could generate all this easily in the very common case. I think a simple struct/list/“oneof” type of composition system would work really well (would probably want to do a more thorough survey of use cases than I just did in the last 5 minutes :wink: ).

So basically the design could be:

  • have an attribute/type interface “buildable as a composition of other attributes/types”.
  • (eventually) TableGen’eration of almost all attributes/types; TableGen automatically implements the above interface, and can be used to generate binding wrappers.
  • we could also have introspection to avoid the need to statically generate the binding wrappers for most cases
1 Like