test fixes from codex

This commit is contained in:
Douglas Creager 2025-11-21 08:44:30 -05:00
parent 290868b71d
commit 2d301b63bb
2 changed files with 81 additions and 24 deletions

View File

@ -1756,6 +1756,24 @@ impl<'db> Type<'db> {
} }
match (self, target) { match (self, target) {
// Pretend that instances of `dataclasses.Field` are assignable to their default type.
// This allows field definitions like `name: str = field(default="")` in dataclasses
// to pass the assignability check of the inferred type to the declared type.
(Type::KnownInstance(KnownInstanceType::Field(field)), right)
if relation.is_assignability() =>
{
field.default_type(db).when_none_or(|default_type| {
default_type.has_relation_to_impl(
db,
right,
inferable,
relation,
relation_visitor,
disjointness_visitor,
)
})
}
// Two identical typevars must always solve to the same type, so they are always // Two identical typevars must always solve to the same type, so they are always
// subtypes of each other and assignable to each other. // subtypes of each other and assignable to each other.
// //
@ -1766,6 +1784,30 @@ impl<'db> Type<'db> {
{ {
ConstraintSet::from(true) ConstraintSet::from(true)
} }
(Type::TypeVar(lhs_bound_typevar), Type::TypeVar(rhs_bound_typevar))
if relation.is_assignability()
&& lhs_bound_typevar.typevar(db).identity(db)
== rhs_bound_typevar.typevar(db).identity(db) =>
{
ConstraintSet::from(true)
}
// Typevars with non-fully-static bounds or constraints do not participate in
// subtyping while they are in a non-inferable position.
(Type::TypeVar(bound_typevar), _)
if relation.is_subtyping()
&& !bound_typevar.is_inferable(db, inferable)
&& !bound_typevar.has_fully_static_bound_or_constraints(db) =>
{
ConstraintSet::from(false)
}
(_, Type::TypeVar(bound_typevar))
if relation.is_subtyping()
&& !bound_typevar.is_inferable(db, inferable)
&& !bound_typevar.has_fully_static_bound_or_constraints(db) =>
{
ConstraintSet::from(false)
}
// A typevar satisfies a relation when...it satisfies the relation. Yes that's a // A typevar satisfies a relation when...it satisfies the relation. Yes that's a
// tautology! We're moving the caller's subtyping/assignability requirement into a // tautology! We're moving the caller's subtyping/assignability requirement into a
@ -1825,24 +1867,6 @@ impl<'db> Type<'db> {
}) })
} }
// Pretend that instances of `dataclasses.Field` are assignable to their default type.
// This allows field definitions like `name: str = field(default="")` in dataclasses
// to pass the assignability check of the inferred type to the declared type.
(Type::KnownInstance(KnownInstanceType::Field(field)), right)
if relation.is_assignability() =>
{
field.default_type(db).when_none_or(|default_type| {
default_type.has_relation_to_impl(
db,
right,
inferable,
relation,
relation_visitor,
disjointness_visitor,
)
})
}
// Dynamic is only a subtype of `object` and only a supertype of `Never`; both were // Dynamic is only a subtype of `object` and only a supertype of `Never`; both were
// handled above. It's always assignable, though. // handled above. It's always assignable, though.
// //

View File

@ -142,6 +142,25 @@ impl<'db> BoundTypeVarInstance<'db> {
} }
} }
} }
/// Returns `true` if all bounds or constraints on this typevar are fully static.
///
/// A type is fully static if its top and bottom materializations are the same; dynamic types
/// (like `Any`) will have different materializations.
pub(crate) fn has_fully_static_bound_or_constraints(self, db: &'db dyn Db) -> bool {
match self.typevar(db).bound_or_constraints(db) {
None => true,
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {
bound.top_materialization(db) == bound.bottom_materialization(db)
}
Some(TypeVarBoundOrConstraints::Constraints(constraints)) => constraints
.elements(db)
.iter()
.all(|constraint| {
constraint.top_materialization(db) == constraint.bottom_materialization(db)
}),
}
}
} }
impl<'a, 'db> InferableTypeVars<'a, 'db> { impl<'a, 'db> InferableTypeVars<'a, 'db> {
@ -1521,6 +1540,20 @@ impl<'db> SpecializationBuilder<'db> {
let bound_typevars = let bound_typevars =
(formal.elements(self.db).iter()).filter_map(|ty| ty.as_typevar()); (formal.elements(self.db).iter()).filter_map(|ty| ty.as_typevar());
if let Ok(bound_typevar) = bound_typevars.exactly_one() { if let Ok(bound_typevar) = bound_typevars.exactly_one() {
// If the actual argument can be satisfied by a non-typevar element of the
// union, then prefer that element and avoid inferring a mapping for the
// typevar. This lets other occurrences of the typevar (if any) drive the
// specialization instead of opportunistically binding it here.
let matches_non_typevar = formal.elements(self.db).iter().any(|ty| {
!ty.has_typevar(self.db)
&& actual
.when_subtype_of(self.db, *ty, self.inferable)
.satisfied_by_all_typevars(self.db, self.inferable)
});
if matches_non_typevar {
return Ok(());
}
self.add_type_mapping(bound_typevar, actual, polarity, f); self.add_type_mapping(bound_typevar, actual, polarity, f);
} }
} }