Nimblesite / Nimblesite/Basilisk

aliases_implicit: ParamSpec argument check is a shape guess ("probably wrong") that never identifies the ParamSpec position

Open
#409 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

spec-violation
Dominant language
Rust
Stars
54
Forks
3
PR merge metrics
No merged PRs in 30d

Description

Split out of #408. Confirmed live on main @ da74283 and on the bidirectionaltype-inference branch (that branch's only change to this file is routing is_assignable_to_bound through a SubtypingContext; this code is untouched).

The code

crates/basilisk-checker/src/rules/aliases_implicit.rs:693-698

// If all args are simple types (no `[...]` or `...`),
// the ParamSpec arg is probably wrong
let all_simple = args.iter().all(|arg| {
    let trimmed = arg.trim();
    !trimmed.contains('[') && trimmed != "..."
});
if all_simple && args.len() > 1 {
    // → error: "Invalid type argument for `ParamSpec` parameter in `{base}`"
}

This emits a hard error on a shape guess. Its own comment says "probably".

Why it looks correct

The alias positions come from collect_typevar_bounds, which records TypeVar names in order of first textual appearance in the RHS. For the conformance suite's

GoodTypeAlias9 = Callable[Concatenate[int, P], R]

that yields [P, R], so GoodTypeAlias9[int, int] gives arg_count == typevar_count == 2, both args "simple", len > 1 → fires. That is aliases_implicit.py:80, and it is the only case the branch was ever exercised against.

It never identifies the ParamSpec position

The check never asks which argument corresponds to the ParamSpec. It asks whether all arguments look bracket-free. Consequences:

False negative — invalid ParamSpec argument accepted:

def f(x: GoodTypeAlias9[int, [str]]): ...

Position 0 is P (a ParamSpec) and receives int — invalid, it must be a parameter list or .... Position 1 is R (a TypeVar) and receives [str] — also invalid, that is not a type. Because "[str]" contains [, all_simple is false and the entire branch is skipped. Two errors, zero diagnostics.

False negative — nested annotations are never reached:

annotation_base_name splits on the first [ and check_single_annotation is called only from the two top-level loops (:606, :627) — it never recurses into type arguments. So:

def f(x: list[GoodTypeAlias2[int]]): ...

resolves the base name to list, finds no alias, and returns. The inner misuse — the same error the suite checks at top level on line 76 — is invisible at any nesting depth.

Fix

Determine parameter kinds from the alias's declared type parameters, match each argument to its position, and validate a ParamSpec argument as a parameter list, Concatenate[...], ..., or another ParamSpec. Recurse into type arguments so nested annotations are checked. Until that exists, this diagnostic should not be emitted — a check whose comment says "probably" must not produce an error.

Needs off-suite regression tests: both cases above are invisible to the conformance suite.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in crates/basilisk-checker/src/rules/aliases_implicit.rs around lines 606, 627, and 693-698, then trace how collect_typevar_bounds records declared parameters. Add off-suite regression tests for invalid top-level and nested ParamSpec arguments, and verify that parameter kinds are matched by position, nested type arguments are checked, and the speculative diagnostic is not emitted.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, rust
Domain
compilers, testing
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.