Skip to content
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

dialects: (builtin) Fix documentation for parsing of bools, and add conversion to Python bool #3689

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

watermelonwolverine
Copy link
Contributor

Updated documentation in attribute_parser and base_parser regarding the integer values of bools.
Added to_bool to IntegerAttr
See issue #3684

…he integer values of booleans.

Added `to_bool` to IntegerAttr
(xdslproject#3684)
Copy link

codecov bot commented Jan 2, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 91.31%. Comparing base (d7f6068) to head (d05edd4).
Report is 13 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3689      +/-   ##
==========================================
+ Coverage   91.30%   91.31%   +0.01%     
==========================================
  Files         466      467       +1     
  Lines       58357    58493     +136     
  Branches     5624     5640      +16     
==========================================
+ Hits        53280    53411     +131     
- Misses       3628     3632       +4     
- Partials     1449     1450       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Comment on lines 708 to 711
def to_bool(self) -> bool:
# Do assertion, yes, no?
assert self.type == i1
return bool(self.value.data)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def to_bool(self) -> bool:
# Do assertion, yes, no?
assert self.type == i1
return bool(self.value.data)
def __bool__(self) -> bool:
"""Returns True if value is non-zero."""
return bool(self.value.data)

Comment on lines 174 to 176
with pytest.raises(AssertionError):
IntegerAttr(1, i8).to_bool()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I don't think we need to be this strict, I'm happy to follow C/Python's truthiness as being non-zero.

@superlopuh
Copy link
Member

Why not __bool__?

@watermelonwolverine
Copy link
Contributor Author

watermelonwolverine commented Jan 2, 2025

Huh, I didn't think about that. Could that mess with checks where one checks if an optional attribute/property is present?

E.g.:

if self.opt_bool_attr:
    # Do something when attribute present

I think a saw a few of those checks. 🤔

@superlopuh
Copy link
Member

This is an antipattern and should never be used, for precisely this reason :) I would like to move towards more functions using attributes for calculations instead of python integers, and this is a valid use for them, I would argue that we want this functionality for them also. I would recommend making the change, and then waiting for @math-fehr and @alexarice's reviews before merging.

@superlopuh superlopuh added core xDSL core (ir, textual format, ...) dialects Changes on the dialects and removed core xDSL core (ir, textual format, ...) labels Jan 2, 2025
@superlopuh superlopuh changed the title core: (Parsing) Fix for issue #3684 dialects: (builtin) Fix documentation for parsing of bools, and add conversion to Python bool Jan 2, 2025
@watermelonwolverine
Copy link
Contributor Author

This is an antipattern and should never be used, for precisely this reason :) I would like to move towards more functions using attributes for calculations instead of python integers, and this is a valid use for them, I would argue that we want this functionality for them also. I would recommend making the change, and then waiting for @math-fehr and @alexarice's reviews before merging.

A test failed because there was such an if. This could potentially break a lot of code of people who use xDSL.

@alexarice
Copy link
Collaborator

Whether or not it is an antipattern, I feel that we have used conversion to bool in place of "is not None" in a lot of places and I'm not sure there's a good way to find all the places.

@math-fehr
Copy link
Collaborator

I would personally not overload bool, and would probably name the function is_nonnull instead?

Comment on lines +297 to +300
def __bool__(self) -> bool:
"""Returns True if value is non-zero."""
return bool(self.data)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be good to add unit tests for this for good measure

@superlopuh
Copy link
Member

@alexarice I've been pretty consistent in my PR reviews to discourage this approach, and I feel that we're actually in a good place here, modulo the two uses of it in csl.

@math-fehr, why not? I don't see why we shouldn't be able to use integer attrs as idiomatic Python integers, whether it's IntAttr or IntegerAttr.

@alexarice
Copy link
Collaborator

My memory is that we've changed a few if X is not None to if X in PRs, but maybe I'm misremembering. I still feel that bugs could creep in but am happy for the change to go ahead if this is the direction we want to go in.

@superlopuh
Copy link
Member

If I had anything to do with it it would have been the other way around, which is hopefully why all the tests pass with this change.

@watermelonwolverine
Copy link
Contributor Author

One thing you might want to consider when going with __bool__ is that there are some non-obvious pitfalls, for example you can't use:

if bool_attr is True:
    # Do stuff

or

if bool_attr is False:
    # Do stuff

You could argue that this code is simply wrong but it's a code a beginner might write (or someone who likes to make things very explicit). Basically the programmers would have to know when they can use a IntegerAttr like a bool and in what situations they shouldn't. Now, whether you really want to cater to beginner programmers is another question.

As another side note: What about adding a function is_present that checks if an optional attribute, region, operand is present? This would make things very explicit and would even open the door to changing the underlying implementation of optionals at some later point. I.e. using something other than None. At the moment it would simple check if the given value is not None.

@superlopuh
Copy link
Member

Pyright should flag that as the types have no overlap, so I don't think it's a significant concern.

@superlopuh
Copy link
Member

I'm not sure that it would be a good idea to add that helper, it's not really shorter or easier to understand than is not None

Copy link
Collaborator

@alexarice alexarice left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some small comments. If we're happy that we won't be introducing bugs, I'm happy with using __bool__ (and don't think we should have a helper for is not None though I can only see references to that in the discussion and not the code)



def test_IntegerAttr___bool__():
assert not IntegerAttr.from_bool(False)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert not IntegerAttr.from_bool(False)
assert not BoolAttr.from_bool(False)

Super nit and probably doesn't really matter


def test_IntegerAttr___bool__():
assert not IntegerAttr.from_bool(False)
assert IntegerAttr.from_bool(True)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert IntegerAttr.from_bool(True)
assert BoolAttr.from_bool(True)

@@ -45,7 +45,7 @@ def parse_optional_integer(
"""
Parse an (possible negative) integer. The integer can either be
decimal or hexadecimal.
Optionally allow parsing of 'true' or 'false' into 1 and 0.
Optionally allow parsing of 'true' or 'false' into -1 and 0.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not agree with the implementation below

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does though

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah actually not here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, all of the functions with the changed documentation here return the 1/0 values, it's only when creating the attribute that the value is different. This should be reflected in documentation for the attribute, not the parser.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I did not put a comment on the doc change for the attribute

@@ -78,7 +78,7 @@ def parse_integer(
"""
Parse an (possible negative) integer. The integer can
either be decimal or hexadecimal.
Optionally allow parsing of 'true' or 'false' into 1 and 0.
Optionally allow parsing of 'true' or 'false' into -1 and 0.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

@@ -121,7 +121,7 @@ def parse_optional_number(
) -> int | float | None:
"""
Parse a (possibly negative) integer or float literal, if present.
Can optionally parse 'true' or 'false' into 1 and 0.
Can optionally parse 'true' or 'false' into -1 and 0.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

@@ -149,7 +149,7 @@ def parse_number(
) -> int | float:
"""
Parse a (possibly negative) integer or float literal.
Can optionally parse 'true' or 'false' into 1 and 0.
Can optionally parse 'true' or 'false' into -1 and 0.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dialects Changes on the dialects
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants