Skip to content

Function assigned into a class body is not bound as a method: no self consumed, arity errors missed #382

Description

@MelbourneDeveloper

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    high-priorityHigh priority — schedule ahead of other feature work

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions