[ty] Attribute access on top/bottom materializations (#20221)

## Summary

Part of astral-sh/ty#994. The goal of this PR was to add correct
behavior for attribute access on the top and bottom materializations.
This is necessary for the end goal of using the top materialization for
narrowing generics (`isinstance(x, list)`): we want methods like
`x.append` to work correctly in that case.

It turned out to be convenient to represent materialization as a
TypeMapping, so it can be stashed in the `type_mappings` list of a
function object. This also allowed me to remove most concrete
`materialize` methods, since they usually just delegate to the subparts
of the type, the same as other type mappings. That is why the net effect
of this PR is to remove a few hundred lines.

## Test Plan

I added a few more tests. Much of this PR is refactoring and covered by
existing tests.

## Followups

Assigning to attributes of top materializations is not yet covered. This
seems less important so I'd like to defer it.

I noticed that the `materialize` implementation of `Parameters` was
wrong; it did the same for the top and bottom materializations. This PR
makes the bottom materialization slightly more reasonable, but
implementing this correctly will require extending the struct.
This commit is contained in:
Jelle Zijlstra 2025-09-04 12:01:44 -07:00 committed by GitHub
parent 555b9f78d6
commit de63f408b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 263 additions and 399 deletions

View file

@ -10,8 +10,9 @@ use crate::semantic_index::{
};
use crate::semantic_index::{DeclarationWithConstraint, global_scope, use_def_map};
use crate::types::{
DynamicType, KnownClass, Truthiness, Type, TypeAndQualifiers, TypeQualifiers, UnionBuilder,
UnionType, binding_type, declaration_type, todo_type,
ApplyTypeMappingVisitor, DynamicType, KnownClass, MaterializationKind, Truthiness, Type,
TypeAndQualifiers, TypeQualifiers, UnionBuilder, UnionType, binding_type, declaration_type,
todo_type,
};
use crate::{Db, FxOrderSet, Program, resolve_module};
@ -614,6 +615,15 @@ impl<'db> PlaceAndQualifiers<'db> {
}
}
pub(crate) fn materialize(
self,
db: &'db dyn Db,
materialization_kind: MaterializationKind,
visitor: &ApplyTypeMappingVisitor<'db>,
) -> PlaceAndQualifiers<'db> {
self.map_type(|ty| ty.materialize(db, materialization_kind, visitor))
}
/// Transform place and qualifiers into a [`LookupResult`],
/// a [`Result`] type in which the `Ok` variant represents a definitely bound place
/// and the `Err` variant represents a place that is either definitely or possibly unbound.