This is an archive of the discontinued LLVM Phabricator instance.

[mlir][python] Move results types to keyword-only arg.
Needs RevisionPublic

Authored by jpienaar on May 23 2022, 1:41 PM.

Details

Summary

Previously result types were specified at the front of the builder args. But
this meant that when the build time type inference was added the python API
changed and folks needed to update call sites. Instead move result types to
keyword args and make it optional in the case where the result type can be
inferred. This uniformly uses results for the arguments name rather than
attempt to expand or special case it (did consider still supporting result and
results and could be convinced that is better). This allows adding type
inference without breaking change and also brings the arguments to the fore and
more aligned with ODS def.

Diff Detail

Event Timeline

jpienaar created this revision.May 23 2022, 1:41 PM
Herald added a project: Restricted Project. · View Herald Transcript
jpienaar requested review of this revision.May 23 2022, 1:41 PM
ftynse requested changes to this revision.Jan 16 2023, 4:15 AM

There are several points I'd like to discuss regarding this design choice:

  1. This makes optionality confusing: some ops require result types to be provided and some others do not. It is not clear when the kwarg can be omitted (and we don't generate docs indicating that AFAIK). Furthermore, we use kwarg-only arguments for optional or default-valued attributes, using it for _mandatory_ results would make it confusing.
  2. This puts all result type into a flat list, instead of grouping them per "out" field of the op definition in ODS, which may result in less readable builder calls.
  3. The change was originally motivated by avoiding the breakage when result type inference was being introduced. It has landed a while ago, so this change will now create the breakage it sought to avoid.
  4. Nit: what happens in case of a name conflict, i.e., when there is an attribute called results?

This change looks great for a more narrow use case: overriding the inferred types. I am supportive of it in a narrower scope: provide an override_result_types (longer name to decrease the likelihood of name clash) kwarg only for the ops for which we can infer result types. It sounds non-breaking for any of the existing code and will enable more functionality.

This revision now requires changes to proceed.Jan 16 2023, 4:15 AM

The change was originally motivated by avoiding the breakage when result type inference was being introduced. It has landed a while ago, so this change will now create the breakage it sought to avoid.

This is a breaking change as is, but also adding result type inference to an op is such still today. So if you have an op and you refine it's modelling by adding type inference, it breaks python side.

It is the easiest staging to only enable setting (and avoiding inference) when set and have it be optional in that case. But we still have the failure there when folks change which seems unfortunate - so it's question of breaking and setting scene for avoiding breakages later or having system that encourages small breakages as folks model ops more (and I'd suspect type inference being the common case).

Furthermore, we use kwarg-only arguments for optional or default-valued attributes, using it for _mandatory_ results would make it confusing

We use it for location too which is a required field.

Fair point that it is not documented for which may be elided.

This puts all result type into a flat list, instead of grouping them per "out" field of the op definition in ODS, which may result in less readable builder calls

Funnily I felt the opposite about this, the result type names made it difficult to know what was what when defining the op (is this an operand, attribute or result, if I want to set the result let me go look up the op in ODS). Making the interface more uniform was the intention and I think matches the C++ API closer too.

The change was originally motivated by avoiding the breakage when result type inference was being introduced. It has landed a while ago, so this change will now create the breakage it sought to avoid.

This is a breaking change as is, but also adding result type inference to an op is such still today. So if you have an op and you refine it's modelling by adding type inference, it breaks python side.

It is the easiest staging to only enable setting (and avoiding inference) when set and have it be optional in that case. But we still have the failure there when folks change which seems unfortunate - so it's question of breaking and setting scene for avoiding breakages later or having system that encourages small breakages as folks model ops more (and I'd suspect type inference being the common case).

I'd generally place adding type inference into the "structural changes to the op" category, similarly to adding an output or an operand, which comes with an expectation that things will break. It may even be desirable that things break in this case as it will prompt the users to re-examine their usage in the light of op changes. (Arguably, it's not the case for type inference, where the worst case would be some duplicate type logic that keeps happening even though it could be subsumed by type inference).

Furthermore, we use kwarg-only arguments for optional or default-valued attributes, using it for _mandatory_ results would make it confusing

We use it for location too which is a required field.

And for the insertion point, both of which are typically provided through context managers. I haven't seen explicit loc= being used in the wild, up to the point that I suspect it may no longer work :)

This puts all result type into a flat list, instead of grouping them per "out" field of the op definition in ODS, which may result in less readable builder calls

Funnily I felt the opposite about this, the result type names made it difficult to know what was what when defining the op (is this an operand, attribute or result, if I want to set the result let me go look up the op in ODS). Making the interface more uniform was the intention and I think matches the C++ API closer too.

I agree that it is difficult to differentiate results from operands in a long list of ops. This is a slightly different problem from listing all results in the _single_ list. And that is already solved by Python allowing keywords even for positional arguments: one can write dialect.MyOp(first_result=type_1, second_result_variadic=[type_2, type_3], third_result=type_4, first_operand=value) now and would have to write dialect.MyOp(first_operand=value, results=[type_1, [type_2, type_3], type_4]) with this change.