Skip to content

Dedup tparam-default substitution between alt and solver layers - #4406

Open
nitishagar wants to merge 1 commit into
facebook:mainfrom
nitishagar:nitishagar/4298-dedup-tparam-default
Open

Dedup tparam-default substitution between alt and solver layers#4406
nitishagar wants to merge 1 commit into
facebook:mainfrom
nitishagar:nitishagar/4298-dedup-tparam-default

Conversation

@nitishagar

@nitishagar nitishagar commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

The logic for substituting references to earlier type parameters inside a tparam's default was duplicated in two places:

  • get_tparam_default in pyrefly/lib/alt/class/targs.rs (used during call/specialization)
  • an inlined copy in finish_class_targs in pyrefly/lib/solver/solver.rs (used during class-level finalization), flagged by a // TODO: deal with code duplication in get_tparam_default marker.

The two copies differed only in their lookup source and their fallback value for out-of-scope references — differences that are intentional, since one path runs where an out-of-scope reference is an already-reported error (returns Any(Implicit)) and the other runs during finalization with no such error (returns the param's gradual type).

This PR extracts a shared substitute_tparam_defaults helper into pyrefly_types::quantified (next to the existing as_gradual_type_helper, which already uses the same TypeVar/TypeVarTuple/ParamSpec/Quantified match idiom). The helper takes the default by value, a &dyn Fn(&Name) -> Option<Type> lookup closure, and a caller-chosen fallback Type, so each call site keeps its own lookup source and fallback semantics. Both callers are rewritten to delegate to it, and the // TODO marker is removed.

Behavior

This is a behavior-preserving refactor. Each call site passes the same lookup and fallback it used inline, so the produced types are identical for every input:

  • get_tparam_default passes name_to_idx -> checked_targs lookup with self.heap.mk_any_implicit() fallback (unchanged).
  • finish_class_targs passes seen_params -> new_targs-then-targs lookup with param.as_gradual_type() fallback (unchanged).

The two fallbacks are deliberately not collapsed — they intentionally differ (error path vs finalization path).

Test plan

  • cargo build -p pyrefly_types — clean
  • cargo build -p pyrefly — clean
  • cargo test -p pyrefly --lib7462 passed, 0 failed, 3 ignored (the full typechecker regression suite; zero behavior diff)
  • python3 test.py --no-test --no-tensor-shapes --no-conformance --no-jsonschema — clean for the touched files

Fixes #4298


AI-generated: this PR was prepared by an AI agent (ZCode) following a reviewed implementation plan. The code has been read, built, and tested as described above.

The logic for substituting references to earlier type parameters inside a
tparam's default was duplicated in two places: `get_tparam_default` in the
alt/class layer (used during call/specialization) and an inlined copy in
`finish_class_targs` in the solver (used during class-level finalization).
The two copies differed only in their lookup source and their fallback value
for out-of-scope references — differences that are intentional, since one
path runs where an out-of-scope reference is an already-reported error and
the other runs during finalization with no such error.

Extract a shared `substitute_tparam_defaults` helper into
`pyrefly_types::quantified` (next to the existing `as_gradual_type_helper`,
which already uses the same TypeVar/TypeVarTuple/ParamSpec/Quantified match
idiom). The helper takes the default by value, a `&dyn Fn(&Name) -> Option<Type>`
lookup closure, and a caller-chosen fallback `Type`, so each call site keeps
its own lookup source and fallback semantics. Both callers are rewritten to
delegate to it; the `// TODO: deal with code duplication in get_tparam_default`
marker is removed.

This is behavior-preserving: each call site passes the same lookup and
fallback it used inline, so the produced types are identical for every input.
The full `cargo test -p pyrefly --lib` suite (7462 tests) passes unchanged.

Fixes facebook#4298
@meta-cla meta-cla Bot added the cla signed label Aug 2, 2026
@github-actions github-actions Bot added the size/s label Aug 2, 2026
@meta-codesync

meta-codesync Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

This pull request has been imported. If you are a Meta employee, you can view this in D114523923. (Because this pull request was imported automatically, there will not be any future comments.)

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TODO: deal with code duplication in get_tparam_default

1 participant