Split out of #408. Confirmed live on main @ da74283 and on the bidirectionaltype-inference branch.
The code
crates/basilisk-checker/src/rules/aliases_implicit.rs:757
fn is_assignable_to_bound(
subtyping: &crate::subtyping::SubtypingContext,
arg: &str,
bound: &str,
) -> bool {
match bound {
"int" | "float" | "complex" => subtyping.is_subtype(arg, bound),
_ => true,
}
}
Every TypeVar bound that is not int, float, or complex returns true — accept. Bound checking on type-alias parameterization exists only for the numeric tower.
Why that is exactly the numeric tower
aliases_implicit.py declares exactly one bounded TypeVar:
TFloat = TypeVar("TFloat", bound=float) # line 10
GoodTypeAlias12 = list[TFloat] # line 41
...
p6: GoodTypeAlias12[str], # E: type argument doesn't match bound (line 81)
float is the only bound the suite exercises. The implemented set is int | float | complex — that one bound plus its two numeric-tower neighbours. Nothing else in the language is checked.
Failing case
from typing import TypeVar
T = TypeVar("T", bound=str)
Alias = list[T]
def f(x: Alias[int]) -> None: ... # should error: int does not satisfy bound str
bound is "str", so the _ => true arm accepts. No diagnostic. The same holds for every user-defined class bound, every protocol bound, and every constrained TypeVar (TypeVar("T", str, bytes) — constraints are not consulted here at all; AliasInfo::typevar_bounds only carries bound_type_name).
Note also that arg and bound are compared as strings (bound_type_name is a name, arg is annotation text), so even within the numeric tower this cannot see through an alias, a qualified name, or a subscripted generic.
Fix
Route all bounds — not a hardcoded three — through the real subtyping context, resolving both the argument and the bound to types rather than comparing names. Carry TypeVar constraints alongside bounds and check membership for constrained TypeVars. _ => true must not be the default answer for the rest of the type system.
Needs off-suite regression tests: a bound=str case cannot fail in the conformance suite, because the suite has no such case.
Split out of #408. Confirmed live on
main@da74283and on thebidirectionaltype-inferencebranch.The code
crates/basilisk-checker/src/rules/aliases_implicit.rs:757Every TypeVar bound that is not
int,float, orcomplexreturnstrue— accept. Bound checking on type-alias parameterization exists only for the numeric tower.Why that is exactly the numeric tower
aliases_implicit.pydeclares exactly one bounded TypeVar:floatis the only bound the suite exercises. The implemented set isint | float | complex— that one bound plus its two numeric-tower neighbours. Nothing else in the language is checked.Failing case
boundis"str", so the_ => truearm accepts. No diagnostic. The same holds for every user-defined class bound, every protocol bound, and every constrained TypeVar (TypeVar("T", str, bytes)— constraints are not consulted here at all;AliasInfo::typevar_boundsonly carriesbound_type_name).Note also that
argandboundare compared as strings (bound_type_nameis a name,argis annotation text), so even within the numeric tower this cannot see through an alias, a qualified name, or a subscripted generic.Fix
Route all bounds — not a hardcoded three — through the real subtyping context, resolving both the argument and the bound to types rather than comparing names. Carry TypeVar constraints alongside bounds and check membership for constrained TypeVars.
_ => truemust not be the default answer for the rest of the type system.Needs off-suite regression tests: a
bound=strcase cannot fail in the conformance suite, because the suite has no such case.