-
Notifications
You must be signed in to change notification settings - Fork 77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better type hints for attribute definitions #103
Conversation
687dca1
to
134a400
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Looking forward to working with this, getting in more tooling support would help a lot in the development with xdsl. Plus, all the red lines in my project are annoying me, since I know that the types actually work out...
@math-fehr I committed a couple of changes with 42847e0 The main point of this commit are:
shape: ParameterDef[ArrayAttr[IntegerAttr]] instead of shape: ParameterDef[Annotated[ArrayAttr, ArrayOfConstraint(IntegerAttr)]] before. I updated the
A review of the commit from @math-fehr would be great (specifically the code I added to I will look over the rest and see if I have more comments, but I think I am fairly happy with this as a first step to add better typing. |
Thanks a lot! I'll add some more stuff tonight/tomorrow, but it already looks way better. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for doing a big pass over everything!
I'll try to make one change, to remove the attribute constraint from IRDL, and essentially allow users to encode special cases, rather than write them in IRDL.
src/xdsl/dialects/builtin.py
Outdated
value = IntAttr.build(value) | ||
if not isinstance(typ, IndexType): | ||
typ = IntegerType.build(typ) | ||
return IntegerAttr([value, typ]) | ||
|
||
|
||
@irdl_attr_definition | ||
class ArrayAttr(Data[List[Attribute]]): | ||
class ArrayAttr(Data[List[A]]): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, you are using the A
defined in ir.
Could we instead use a new typevar for each generic class? Otherwise, a change in ir
will have an effect here, and we probably won't think of updating this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type variables in Python are sufficiently weird.
The blank import of ir
imports A
as well that's why it is in scope.
Maybe we can have all type variables start with an underscore, so that they are not automatically imported everywhere?
src/xdsl/irdl.py
Outdated
return constraints[0] | ||
|
||
# Attribute class case | ||
# This is a coercion for an `BaseAttr`. | ||
if isclass(irdl) and issubclass(irdl, Attribute): | ||
return BaseAttr(irdl) | ||
|
||
# yapf: disable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a big fan of complexifying IRDL with this. This is because we would have the same issue with Dict
, or any generic data structure we could put in Data
. This would mean that any other case would have to be encoded in IRDL, which is not really extensible.
I feel that instead, we could add some fields and methods to ArrayAttr
, and make IRDL collect this information to produce these constraints. I'm gonna try a bit tomorrow to see how it look.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually thought about this as an alternative.
Reasons, why I went with this design:
- this fixes the type of the
data
field to be the once we want (e.g. when we writesomething.data[0].data
wheresomething: Data[List[IntAttr]]
then we get anint
back and the type checker knows this) - there is nothing more to be said, the constraint can be inferred automatically (as the implementation shows)
- I deliberately made the implementation in
IRDL
not specific toArrayAttr
but generic toData[List[A]]
, so any subclass ofData
with aList[A]
.
Given this type, thedata
field will beList[A]
and therefore the constraint to verify is that all elements of the list must satisfy the constraint ofA
.
All of this is just a continuation of whatData
does already.
We could also implement a constraint that verifies the equivalent for Dict[K, V]
checking that all keys satisfy the K
constraint and all associated values the V
constraint.
Furthermore, we could restrict what you are allowed to use as a type-argument with Data
, e.g. Attribute | List | Dict
but nothing more ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I completely agree with points 1
and 2
, and don't intend to change them!
The only thing I don't really like is that IRDL
now needs to be modified for any new data structure that users might use.
I'll try something now to show an example on how I thought doing this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wrote a pull request that should explain what I meant: #105
Feel free to merge it to this PR if you think this makes sense, otherwise we can discuss it on that PR!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a look at the PR.
You clearly succeeded at keeping 1
, but I don't think you managed to keep 2
, as you now have to implement the generic_constraint_coercion
function in ArrayAttr
.
I am ok with the change, as I can see your point of keeping IRDL
free of "special" cases, even though I think that you now open the door to allowing people to misuse the API more easily (as they can define a subclass of Data[List[A]]
but forget to implement generic_constraint_coercion
, or implement it wrongly).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I think I misunderstood point 2
! I don't think there is actually any way to go around it, in the sense that I don't think we can handle most cases in IRDL, since each new case will require a change in IRDL (For instance, I don't think two users will have the same ArrayData
using List
).
We are sure that users won't forget to implement the generic_constraint_coercion
, since IRDL won't accept any generic Data without implementing this (This is because IRDL
cannot translate generic types to attribute constraints). However, we can't prevent them to implementing them wrongly I believe.
I'll add on the other PR some features to document it better when user forget to implement generic_constraint_coercion
!
5a0d522
to
73a9476
Compare
I believe that the PR should soon be ready to merge? The last thing I'd like to add before is the support for this:
so we can write Now consider this problem. When we encounter a I think if we restrain IRDL like this, I should be able to write something to make it work! |
I added this support on the last PR, tell me if this works for you! |
Would be great if you could wait for #111 to be fixed, s.t. we can roll out a new version with that fix, which I can use before rewriting everything ;) |
@math-fehr This can be merged now (after a rebase I guess) |
0999d9a
to
3de15ba
Compare
That should be now good on my end, @michel-steuwer, do you want to add something before I merge it? |
This PR changes:
Data
, which now is a generic typeThis is a first step to make our project pyright-safe.
Though this requires us also to switch to Python 3.10, which I do not really like, but at some point we need to evolve anyway, and this will let us write pattern-matching code.
Example
Previously, we defined parameters with
This breaks a bit the type system of python.
So here is what I'm proposing:
ParameterDef[T]
is a type alias forAnnotated[T, ParameterDefAnnot]
, so essentially this will be understood asT
by the type checker.This means that we can now define attributes expecting a single attribute type, and this will be understood by the type checker.
Type with non-structural constraints
We can also add more constraints, that do not necessarily change the type of the parameter.
For instance, here is how we could define a parameter that is an even number:
This means that for the type system, this will still be translated to
IntAttr
, so everything is fine!And for IRDL, this will check first that the parameter is an
IntAttr
, then will call the constraint to check that it respects the constraints.Current drawbacks
The major drawback I see is for instance for
ArrayAttr
. We want to define anArrayAttr
ofIntegerType
usingArrayAttr[IntegerType]
, but this cannot be understood by IRDL. Thus, we have to write something likeAnnotated[ArrayAttr[IntegerType], ArrayOfConstraint(IntegerType)]
, which is not really nice to write.Maybe one idea would be to also replace
ArrayOfConstraint(IntegerType)
withArrayOfConstraint[IntegerType]
, so we could write something likeArrayOf = Annotated[T, ArrayOfConstraint[T]]
, making it more concise, by just writingArrayOf[IntegerType]
in the end.Tell me what you think!