Describe the bug
Attribute access, method calls, binary operators and subscripts on a value typed as explicit Any are inferred as Unknown (implicit Any). Pyright and mypy keep the result as explicit Any.
This is an issue when migrating from pyright, in our code base it causes hundreds of additional "useless" warnings against semi-typed operations in numpy and similar - once a value is explicit Any (like the result on NDArray[Any] operation), every expression built on it becomes unknown rather than staying Any as they do in pyright and mypy.
from typing import Any, reveal_type
def f(a: Any) -> None:
reveal_type(a.foo) # attribute access
reveal_type(a.foo()) # method call
reveal_type(a + 1) # binary operator
reveal_type(a[0]) # subscript
$ pyrefly check t.py
INFO t.py:4:16-23: revealed type: Unknown [reveal-type]
INFO t.py:5:16-25: revealed type: Unknown [reveal-type]
INFO t.py:6:16-23: revealed type: Unknown [reveal-type]
INFO t.py:7:16-22: revealed type: Unknown [reveal-type]
$ pyright t.py
t.py:4:17 - information: Type of "a.foo" is "Any"
t.py:5:17 - information: Type of "a.foo()" is "Any"
t.py:6:17 - information: Type of "a + 1" is "Any"
t.py:7:17 - information: Type of "a[0]" is "Any"
$ mypy t.py
t.py:4: note: Revealed type is "Any"
t.py:5: note: Revealed type is "Any"
t.py:6: note: Revealed type is "Any"
t.py:7: note: Revealed type is "Any"
Reproduces on pyrefly 1.2.0 and 1.3.0-dev.4. Compared against basedpyright 1.39.9 and mypy (latest).
AI-written detail:
The unknown-* error kinds are documented as implicit-Any-only. The error-kinds reference describes unknown-argument-type as "a call argument whose type is an implicit Any (unknown)" and says it "mirrors pyright's reportUnknownArgumentType", and the pyright diagnostics mapping rates reportUnknownVariableType → unknown-variable-type and reportUnknownArgumentType → unknown-argument-type as "Direct" fidelity.
This also matches the design described in #3661 (2026-07-28), where pyrefly's Anys are grouped into three categories, (1) implicitly inferred for un-annotated values, (2) explicitly annotated, (3) generated to stop error cascades, and the usage-site unknown-* errors were shipped for category 1 only, with reportAny named as the intended usage-site check for category 2.
Because of the inference above, an operation on a category-2 Any yields a category-1 Any, so the unknown-* kinds fire on explicit Any one step removed from its source. Pyright's rules deliberately exempt that case (basedpyright has reportAny / pyrefly has explicit-any for that policy):
from typing import Any
def f(a: Any) -> None:
b = a.foo # unknown-variable-type: The type of `b` is unknown; it is inferred as an implicit `Any`
print(a + 1) # unknown-argument-type: The type of this argument is unknown
On a real codebase this makes the checks unusable with numpy and torch. torch.nn.Module.__call__ is typed Callable[..., Any], so every q = self.proj(x) is explicit Any and the following q.reshape(...).transpose(1, 2) is flagged. numpy ufuncs return NDArray[Any], and np.isfinite(x) & np.isfinite(y) & np.isfinite(z) is flagged from the second & on. On a ~146k-line monorepo, unknown-variable-type / unknown-argument-type report 231 / 402 diagnostics where pyright's reportUnknownVariableType / reportUnknownArgumentType report 49 / 26 on the same tree. The difference is almost entirely this pattern.
Expected
An operation whose only source of Any is an explicit Any should yield explicit Any, and the unknown-* kinds should not fire on it.
Related
Sandbox Link
https://pyrefly.org/sandbox/?project=v2.pZRLCsIwFEW38iCDtgiNTrsK59ZBUmMNlCTkI7h7Xz6CrbUgTtOTR5N7c35V5kI2qa816-LHZdhvaM3aq9ZNXCXAvLeSBy8gn-sLXTfIk_KaUHrTtALCDg5lLMeE7AO0yTpbgU_7c2IRdoG7wUrjP1MkcMwLUC4E_YXxjwHHYqxtrwgKUsDNe-M6Sl_btR2pUPSiB0dnOyjEwrI7k8m983H4v9nSvfqnPlVuT7VVnip2B4nNPjwB
(Only applicable for extension issues) IDE Information
No response
Describe the bug
Attribute access, method calls, binary operators and subscripts on a value typed as explicit
Anyare inferred asUnknown(implicitAny). Pyright and mypy keep the result as explicitAny.This is an issue when migrating from pyright, in our code base it causes hundreds of additional "useless" warnings against semi-typed operations in numpy and similar - once a value is explicit Any (like the result on
NDArray[Any]operation), every expression built on it becomesunknownrather than stayingAnyas they do in pyright and mypy.Reproduces on pyrefly 1.2.0 and 1.3.0-dev.4. Compared against basedpyright 1.39.9 and mypy (latest).
AI-written detail:
The
unknown-*error kinds are documented as implicit-Any-only. The error-kinds reference describesunknown-argument-typeas "a call argument whose type is an implicitAny(unknown)" and says it "mirrors pyright'sreportUnknownArgumentType", and the pyright diagnostics mapping ratesreportUnknownVariableType→unknown-variable-typeandreportUnknownArgumentType→unknown-argument-typeas "Direct" fidelity.This also matches the design described in #3661 (2026-07-28), where pyrefly's
Anys are grouped into three categories, (1) implicitly inferred for un-annotated values, (2) explicitly annotated, (3) generated to stop error cascades, and the usage-siteunknown-*errors were shipped for category 1 only, withreportAnynamed as the intended usage-site check for category 2.Because of the inference above, an operation on a category-2
Anyyields a category-1Any, so theunknown-*kinds fire on explicitAnyone step removed from its source. Pyright's rules deliberately exempt that case (basedpyright hasreportAny/ pyrefly hasexplicit-anyfor that policy):On a real codebase this makes the checks unusable with numpy and torch.
torch.nn.Module.__call__is typedCallable[..., Any], so everyq = self.proj(x)is explicitAnyand the followingq.reshape(...).transpose(1, 2)is flagged. numpy ufuncs returnNDArray[Any], andnp.isfinite(x) & np.isfinite(y) & np.isfinite(z)is flagged from the second&on. On a ~146k-line monorepo,unknown-variable-type/unknown-argument-typereport 231 / 402 diagnostics where pyright'sreportUnknownVariableType/reportUnknownArgumentTypereport 49 / 26 on the same tree. The difference is almost entirely this pattern.Expected
An operation whose only source of
Anyis an explicitAnyshould yield explicitAny, and theunknown-*kinds should not fire on it.Related
str.joinresult incorrectly inferred asUnknownforlist[Any]#4845:str.joinonlist[Any]inferred asUnknown. Same underlying issue, reported as a 1.3.0-dev4 regression on one method; the cases above already fail on 1.2.0.reportUnknownArgumentType,reportUnknownMemberType&reportUnknownVariableTypeare not covered byimplicit-any#3661:implicit-anydoes not cover pyright'sreportUnknown*family. Contains the three-category design note quoted above.TypeVar(bound=tuple[int, ...])resolves toUnknownwhen object fallback overload is present #3977: overload resolution with anAny-containing argument yieldsUnknown.Any/Unknowninference after conditional reassignment #4846: explicit/implicitAnymixing changed in 1.3.0-dev3.Sandbox Link
https://pyrefly.org/sandbox/?project=v2.pZRLCsIwFEW38iCDtgiNTrsK59ZBUmMNlCTkI7h7Xz6CrbUgTtOTR5N7c35V5kI2qa816-LHZdhvaM3aq9ZNXCXAvLeSBy8gn-sLXTfIk_KaUHrTtALCDg5lLMeE7AO0yTpbgU_7c2IRdoG7wUrjP1MkcMwLUC4E_YXxjwHHYqxtrwgKUsDNe-M6Sl_btR2pUPSiB0dnOyjEwrI7k8m983H4v9nSvfqnPlVuT7VVnip2B4nNPjwB
(Only applicable for extension issues) IDE Information
No response