Summary
A plain function assigned into a class body becomes a method — accessing it through an instance binds self and consumes the first positional parameter. Basilisk does not model this, so calling a zero-parameter function bound this way produces no arity error.
Found via the "type torture" puzzle on X: https://x.com/charliermarsh/status/2082885991686090885
Reproduced on a clean cargo build --release of main @ 5b756d3.
Minimal reproduction
def f() -> int:
return 0
class C:
g = f
C().g()
Basilisk: All checked. No issues found.
Expected: an error on C().g(). Functions are descriptors, so C().g is a bound method that passes the instance as the first positional argument, but f declares no parameters. At runtime this raises TypeError: f() takes 0 positional arguments but 1 was given.
Reported context
In the torture puzzle the same shape appears as class C:__call__:"C";f=f followed by C(C()()).f(), where the module-level f is an overload set with no parameters.
What should be checked
Class-body assignment of a function creates a method binding, which means:
- the declared parameter list is consumed left-to-right starting with the implicit receiver;
- a zero-parameter function is never callable through an instance;
- accessing it on the class (
C.g()) stays unbound and remains a legal zero-argument call;
staticmethod/classmethod wrappers change the binding and must be honoured;
- the bound method's type — parameters minus the receiver — is what the rest of inference should see.
Controls
def f() -> int: return 0
class C:
g = f
C.g() # legal — unbound access, zero args
C().g() # ERROR expected — bound, receives self
Reporting the mismatch requires knowing the callee's signature at the call site, which is the same annotation/nominal-resolution capability tracked by the return-type issue in this batch.
Summary
A plain function assigned into a class body becomes a method — accessing it through an instance binds
selfand consumes the first positional parameter. Basilisk does not model this, so calling a zero-parameter function bound this way produces no arity error.Found via the "type torture" puzzle on X: https://x.com/charliermarsh/status/2082885991686090885
Reproduced on a clean
cargo build --releaseofmain@ 5b756d3.Minimal reproduction
Basilisk:
All checked. No issues found.Expected: an error on
C().g(). Functions are descriptors, soC().gis a bound method that passes the instance as the first positional argument, butfdeclares no parameters. At runtime this raisesTypeError: f() takes 0 positional arguments but 1 was given.Reported context
In the torture puzzle the same shape appears as
class C:__call__:"C";f=ffollowed byC(C()()).f(), where the module-levelfis an overload set with no parameters.What should be checked
Class-body assignment of a function creates a method binding, which means:
C.g()) stays unbound and remains a legal zero-argument call;staticmethod/classmethodwrappers change the binding and must be honoured;Controls
Reporting the mismatch requires knowing the callee's signature at the call site, which is the same annotation/nominal-resolution capability tracked by the return-type issue in this batch.