Skip to content

Don't resolve implicit __bool__ through __getattr__ - #4542

Open
devteamaegis wants to merge 1 commit into
facebook:mainfrom
devteamaegis:fix/getattr-dunder-bool
Open

Don't resolve implicit __bool__ through __getattr__#4542
devteamaegis wants to merge 1 commit into
facebook:mainfrom
devteamaegis:fix/getattr-dunder-bool

Conversation

@devteamaegis

Copy link
Copy Markdown

Summary

Fixes #4467.

Implicit dunder methods such as __bool__ are looked up on the type and bypass __getattr__ at runtime (CPython resolves them via _PyObject_LookupSpecial, which ignores both the instance dict and __getattr__/__getattribute__).

When evaluating truthiness in as_bool, Pyrefly looked up __bool__ with the __getattr__ fallback enabled. So a class whose __getattr__ returns a non-callable type — e.g. torch.nn.Module, whose __getattr__ is typed to return Tensor | Module — was treated as defining a non-callable __bool__, and using an instance in a boolean context was wrongly reported as not-callable:

class Tensor: ...
class M:
    def __getattr__(self, name: str) -> "Tensor | M": ...

def f(x: M | None) -> M:
    return x or M()
    # before: ERROR Expected `__bool__` to be a callable, got `M | Tensor` [not-callable]
    # after:  0 errors

The fix disables the __getattr__ fallback for the __bool__ lookup in as_bool (pyrefly/lib/alt/expr.rs). The separate check_dunder_bool_is_callable path already disables the fallback; this brings the truthiness-evaluation path in line. The subsequent __bool__ call is only reached when a real __bool__ is found directly, so classes that genuinely define __bool__ are unaffected.

Test Plan

  • Added test_getattr_does_not_provide_dunder_bool in pyrefly/lib/test/attributes.rs. It fails before the change (expected 0 errors, but got 2) and passes after.
  • cargo test -p pyrefly --lib: all type-checking tests pass (the only failures on my machine are two pre-existing lsp_interaction::diagnostic timeout tests that also fail on unmodified main, unrelated to this change).
  • cargo clippy -p pyrefly --lib and cargo fmt --check are clean for the changed files.

Implicit dunder methods such as `__bool__` are looked up on the type and
bypass `__getattr__` at runtime. When evaluating truthiness, Pyrefly
resolved `__bool__` with the `__getattr__` fallback enabled, so a class
whose `__getattr__` returns a non-callable type (e.g. `torch.nn.Module`,
whose `__getattr__` is typed to return `Tensor | Module`) was treated as
defining a non-callable `__bool__` and wrongly reported as `not-callable`
in boolean contexts.

Disable the `__getattr__` fallback when looking up `__bool__` in `as_bool`.

Closes facebook#4467
@meta-codesync

meta-codesync Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

This pull request has been imported. If you are a Meta employee, you can view this in D115946422. (Because this pull request was imported automatically, there will not be any future comments.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

not-callable false positive when __getattr__ returns a non-callable type

1 participant