Skip to content

Aliased @overload decorator (o = overload) silently disables the entire overload rule family #380

Description

@MelbourneDeveloper

Summary

When @overload is referenced through a name other than the imported one — e.g. o = overload — the entire overload rule family stops firing. overloads_definitions (missing implementation, a hard spec error) and overloads_consistency (duplicate/unreachable overload) both go silent.

Found via the "type torture" puzzle on X: https://x.com/charliermarsh/status/2082885991686090885

Reported from the web playground; reproduced identically on the CLI. Reproduced on a clean cargo build --release of main @ 5b756d3.

Reported reproduction

from typing import*;o=overload
type R[T]=set[R[T|R[T]]]
@o
def f()->R[R]:0
@o
def f():0
class C:__call__:"C";f=f
C(C()()).f()

Basilisk reports exactly one diagnostic (generics_syntax_scoping on line 2 — see the separate issue about that rule) and misses the overload errors entirely.

Minimal cases

# A — direct decorator, no implementation: CAUGHT
from typing import overload
@overload
def f(x: int) -> int: ...
@overload
def f(x: str) -> str: ...
# error[overloads_definitions]: Function `f` has 2 `@overload` signatures but no implementation

# B — same file, decorator aliased: SILENT
from typing import overload
o = overload
@o
def f(x: int) -> int: ...
@o
def f(x: str) -> str: ...
# (no diagnostics)

# C — star import, direct decorator: CAUGHT  (so `import *` is not the trigger)
from typing import *
@overload
def f(x: int) -> int: ...
@overload
def f(x: str) -> str: ...
# error[overloads_definitions]: ...

The same aliasing also hides overloads_consistency. With the decorator written directly, the torture puzzle's two identical def f() signatures are caught:

from typing import *
@overload
def f()->int:0
@overload
def f():0
class C:
    __call__: "C"
    g = f
C(C()()).g()
# error[overloads_definitions]: Function `f` has 2 `@overload` signatures but no implementation
# error[overloads_consistency]: `@overload` variant of `f` has the same parameter signature as a previous overload

Swap @overload for @o and both disappear. So a single unrecognised decorator name suppresses two independent spec errors.

Expected

o = overload binds the name o to typing.overload itself; @o is @overload by identity. A function decorated with it is an overload, and outside a stub an overload set without an implementation is an error the spec requires.

Decorator recognition should resolve the decorator expression through the module's binding table to its originating symbol, rather than matching the decorator's spelled name. The same gap will apply to any other special form referenced through a local alias (from typing import overload as ov is worth checking as part of the fix, along with typing.overload / t.overload attribute spellings).

Severity note

This is a silent-under-report, not a false positive, so it does not threaten the conformance gate — the upstream suite always spells @overload directly. It is still a wrong answer on valid Python, and the failure mode is the dangerous kind: an entire rule family disappears with no signal.

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