[ty] Materialize only substituted typevars during specialization (#23725)
## Summary
See https://github.com/astral-sh/ty/issues/2955#issuecomment-4000172171
for a comprehensive write-up of the underlying issue.
Here's a Claude-generated example:
```python
from typing import Any
class InvariantWithAny[T: int]:
a: T
b: Any
def _(x: object):
if isinstance(x, InvariantWithAny):
# x is narrowed to InvariantWithAny[Unknown], then materialized.
# T was substituted (T → Unknown → object), so `a` becomes `object`.
# But `b` was annotated as `Any` by the user — it has nothing to do with T.
reveal_type(x.a) # revealed: object ← correct either way
reveal_type(x.b) # revealed: Any ← fixed (was: object)
```
On main, the second pass materializes every gradual type, so `Any` on
`b` was turned into `object`. Now, only the types that come from
substituting `T` gets materialized, so the explicit `Any` on `b` is left
alone.
IIUC, this is a bit worse in the contravariant case (again, with
Claude's help):
```python
class Handler[T]:
def handle(self, event: T, context: Any) -> None: ...
def _(x: object):
if isinstance(x, Handler):
# handle(event: Never, context: Never) ← bug
# handle(event: Never, context: Any) ← fixed
x.handle(42, {"key": "value"})
```
Closes https://github.com/astral-sh/ty/issues/2955.