Compare commits

...

2 Commits

Author SHA1 Message Date
Alex Waygood
444749767e what happens if i do this 2026-01-03 17:09:10 +00:00
Alex Waygood
67e8d24aaf [ty] Never add elements to a UnionBuilder before setting recursively_defined 2026-01-03 14:19:35 +00:00
12 changed files with 190 additions and 154 deletions

View File

@@ -14,8 +14,8 @@ use crate::semantic_index::{
use crate::semantic_index::{DeclarationWithConstraint, global_scope, use_def_map};
use crate::types::{
ApplyTypeMappingVisitor, DynamicType, KnownClass, MaterializationKind, MemberLookupPolicy,
Truthiness, Type, TypeAndQualifiers, TypeQualifiers, UnionBuilder, UnionType, binding_type,
declaration_type, todo_type,
Truthiness, Type, TypeAndQualifiers, TypeQualifiers, UnionBuilder, UnionSettings, UnionType,
binding_type, declaration_type, todo_type,
};
use crate::{Db, FxOrderSet, Program};
@@ -1305,7 +1305,7 @@ impl<'db> PublicTypeBuilder<'db> {
PublicTypeBuilder {
db,
queue: None,
builder: UnionBuilder::new(db),
builder: UnionBuilder::new(db, UnionSettings::default()),
}
}

View File

@@ -208,8 +208,8 @@ use crate::semantic_index::predicate::{
Predicates, ScopedPredicateId,
};
use crate::types::{
CallableTypes, IntersectionBuilder, Truthiness, Type, TypeContext, UnionBuilder, UnionType,
infer_expression_type, static_expression_truthiness,
CallableTypes, IntersectionBuilder, Truthiness, Type, TypeContext, UnionBuilder, UnionSettings,
UnionType, infer_expression_type, static_expression_truthiness,
};
/// A ternary formula that defines under what conditions a binding is visible. (A ternary formula
@@ -358,7 +358,7 @@ fn type_excluded_by_previous_patterns<'db>(
db: &'db dyn Db,
mut predicate: PatternPredicate<'db>,
) -> Type<'db> {
let mut builder = UnionBuilder::new(db);
let mut builder = UnionBuilder::new(db, UnionSettings::default());
while let Some(previous) = predicate.previous_predicate(db) {
predicate = *previous;

View File

@@ -23,7 +23,7 @@ use ty_module_resolver::{KnownModule, Module, ModuleName, resolve_module};
use type_ordering::union_or_intersection_elements_ordering;
pub(crate) use self::builder::{IntersectionBuilder, UnionBuilder};
pub(crate) use self::builder::{IntersectionBuilder, UnionBuilder, UnionSettings};
pub use self::cyclic::CycleDetector;
pub(crate) use self::cyclic::{PairVisitor, TypeTransformer};
pub(crate) use self::diagnostic::register_lints;
@@ -46,7 +46,6 @@ use crate::semantic_index::scope::ScopeId;
use crate::semantic_index::{imported_modules, place_table, semantic_index};
use crate::suppression::check_suppressions;
use crate::types::bound_super::BoundSuperType;
use crate::types::builder::RecursivelyDefined;
use crate::types::call::{Binding, Bindings, CallArguments, CallableBinding};
pub(crate) use crate::types::class_base::ClassBase;
use crate::types::constraints::{
@@ -7730,7 +7729,7 @@ impl<'db> Type<'db> {
},
Type::Union(union) => {
let mut builder = UnionBuilder::new(db);
let mut builder = UnionBuilder::new(db, union.settings(db));
let mut invalid_expressions = smallvec::SmallVec::default();
for element in union.elements(db) {
match element.in_type_expression(db, scope_id, typevar_binding_context) {
@@ -10783,7 +10782,7 @@ impl<'db> TypeVarConstraints<'db> {
db: &'db dyn Db,
mut transform_fn: impl FnMut(&Type<'db>) -> PlaceAndQualifiers<'db>,
) -> PlaceAndQualifiers<'db> {
let mut builder = UnionBuilder::new(db);
let mut builder = UnionBuilder::new(db, UnionSettings::default());
let mut qualifiers = TypeQualifiers::empty();
let mut all_unbound = true;
@@ -11008,7 +11007,7 @@ impl<'db> UnionTypeInstance<'db> {
scope_id: ScopeId<'db>,
typevar_binding_context: Option<Definition<'db>>,
) -> Type<'db> {
let mut builder = UnionBuilder::new(db);
let mut builder = UnionBuilder::new(db, UnionSettings::default());
for ty in &value_expr_types {
match ty.in_type_expression(db, scope_id, typevar_binding_context) {
Ok(ty) => builder.add_in_place(ty),
@@ -13964,7 +13963,7 @@ pub struct UnionType<'db> {
pub elements: Box<[Type<'db>]>,
/// Whether the value pointed to by this type is recursively defined.
/// If `Yes`, union literal widening is performed early.
recursively_defined: RecursivelyDefined,
settings: UnionSettings,
}
pub(crate) fn walk_union<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
@@ -13990,9 +13989,10 @@ impl<'db> UnionType<'db> {
{
elements
.into_iter()
.fold(UnionBuilder::new(db), |builder, element| {
builder.add(element.into())
})
.fold(
UnionBuilder::new(db, UnionSettings::default()),
|builder, element| builder.add(element.into()),
)
.build()
}
@@ -14005,7 +14005,7 @@ impl<'db> UnionType<'db> {
elements
.into_iter()
.fold(
UnionBuilder::new(db).unpack_aliases(false),
UnionBuilder::new(db, UnionSettings::default().set_unpack_aliases(false)),
|builder, element| builder.add(element.into()),
)
.build()
@@ -14019,7 +14019,7 @@ impl<'db> UnionType<'db> {
elements
.into_iter()
.fold(
UnionBuilder::new(db).cycle_recovery(true),
UnionBuilder::new(db, UnionSettings::default().set_cycle_recovery(true)),
|builder, element| builder.add(element.into()),
)
.build()
@@ -14035,7 +14035,7 @@ impl<'db> UnionType<'db> {
I: IntoIterator<Item = Option<T>>,
T: Into<Type<'db>>,
{
let mut builder = UnionBuilder::new(db);
let mut builder = UnionBuilder::new(db, UnionSettings::default());
for element in elements {
builder = builder.add(element?.into());
}
@@ -14052,10 +14052,7 @@ impl<'db> UnionType<'db> {
self.elements(db)
.iter()
.map(transform_fn)
.fold(UnionBuilder::new(db), |builder, element| {
builder.add(element)
})
.recursively_defined(self.recursively_defined(db))
.fold(UnionBuilder::new(db, self.settings(db)), UnionBuilder::add)
.build()
}
@@ -14069,10 +14066,9 @@ impl<'db> UnionType<'db> {
.iter()
.map(transform_fn)
.fold(
UnionBuilder::new(db).unpack_aliases(false),
UnionBuilder::new(db, self.settings(db).set_unpack_aliases(false)),
UnionBuilder::add,
)
.recursively_defined(self.recursively_defined(db))
.build()
}
@@ -14088,11 +14084,10 @@ impl<'db> UnionType<'db> {
db: &'db dyn Db,
transform_fn: impl FnMut(&Type<'db>) -> Option<Type<'db>>,
) -> Option<Type<'db>> {
let mut builder = UnionBuilder::new(db);
let mut builder = UnionBuilder::new(db, self.settings(db));
for element in self.elements(db).iter().map(transform_fn) {
builder = builder.add(element?);
}
builder = builder.recursively_defined(self.recursively_defined(db));
Some(builder.build())
}
@@ -14108,10 +14103,10 @@ impl<'db> UnionType<'db> {
self.elements(db)
.iter()
.filter(|ty| f(ty))
.fold(UnionBuilder::new(db), |builder, element| {
builder.add(*element)
})
.recursively_defined(self.recursively_defined(db))
.fold(
UnionBuilder::new(db, self.settings(db)),
|builder, element| builder.add(*element),
)
.build()
}
@@ -14120,7 +14115,7 @@ impl<'db> UnionType<'db> {
db: &'db dyn Db,
mut transform_fn: impl FnMut(&Type<'db>) -> Place<'db>,
) -> Place<'db> {
let mut builder = UnionBuilder::new(db);
let mut builder = UnionBuilder::new(db, self.settings(db));
let mut all_unbound = true;
let mut possibly_unbound = false;
@@ -14147,9 +14142,7 @@ impl<'db> UnionType<'db> {
Place::Undefined
} else {
Place::Defined(
builder
.recursively_defined(self.recursively_defined(db))
.build(),
builder.build(),
origin,
if possibly_unbound {
Definedness::PossiblyUndefined
@@ -14166,7 +14159,7 @@ impl<'db> UnionType<'db> {
db: &'db dyn Db,
mut transform_fn: impl FnMut(&Type<'db>) -> PlaceAndQualifiers<'db>,
) -> PlaceAndQualifiers<'db> {
let mut builder = UnionBuilder::new(db);
let mut builder = UnionBuilder::new(db, self.settings(db));
let mut qualifiers = TypeQualifiers::empty();
let mut all_unbound = true;
@@ -14198,9 +14191,7 @@ impl<'db> UnionType<'db> {
Place::Undefined
} else {
Place::Defined(
builder
.recursively_defined(self.recursively_defined(db))
.build(),
builder.build(),
origin,
if possibly_unbound {
Definedness::PossiblyUndefined
@@ -14231,12 +14222,15 @@ impl<'db> UnionType<'db> {
.iter()
.map(|ty| ty.normalized_impl(db, visitor))
.fold(
UnionBuilder::new(db)
.order_elements(true)
.unpack_aliases(true),
UnionBuilder::new(
db,
self.settings(db)
.set_cycle_recovery(false)
.set_unpack_aliases(true)
.set_order_elements(true),
),
UnionBuilder::add,
)
.recursively_defined(self.recursively_defined(db))
.build()
}
@@ -14246,11 +14240,13 @@ impl<'db> UnionType<'db> {
div: Type<'db>,
nested: bool,
) -> Option<Type<'db>> {
let mut builder = UnionBuilder::new(db)
.order_elements(false)
.unpack_aliases(false)
.cycle_recovery(true)
.recursively_defined(self.recursively_defined(db));
let mut builder = UnionBuilder::new(
db,
self.settings(db)
.set_order_elements(false)
.set_unpack_aliases(false)
.set_cycle_recovery(true),
);
let mut empty = true;
for ty in self.elements(db) {
if nested {
@@ -14265,7 +14261,7 @@ impl<'db> UnionType<'db> {
// `Divergent` in a union type does not mean true divergence, so we skip it if not nested.
// e.g. T | Divergent == T | (T | (T | (T | ...))) == T
if ty == &div {
builder = builder.recursively_defined(RecursivelyDefined::Yes);
builder.settings = builder.settings.set_recursively_defined(true);
continue;
}
builder = builder.add(

View File

@@ -10,7 +10,7 @@ use crate::{
types::{
ClassBase, ClassType, DynamicType, IntersectionBuilder, KnownClass, MemberLookupPolicy,
NominalInstanceType, NormalizedVisitor, SpecialFormType, SubclassOfInner, Type,
TypeVarBoundOrConstraints, TypeVarInstance, UnionBuilder,
TypeVarBoundOrConstraints, TypeVarInstance, UnionBuilder, UnionSettings,
context::InferContext,
diagnostic::{INVALID_SUPER_ARGUMENT, UNAVAILABLE_IMPLICIT_SUPER_ARGUMENTS},
todo_type, visitor,
@@ -346,9 +346,10 @@ impl<'db> BoundSuperType<'db> {
return Ok(union
.elements(db)
.iter()
.try_fold(UnionBuilder::new(db), |builder, element| {
delegate_to(*element).map(|ty| builder.add(ty))
})?
.try_fold(
UnionBuilder::new(db, union.settings(db)),
|builder, element| delegate_to(*element).map(|ty| builder.add(ty)),
)?
.build());
}
Type::Intersection(intersection) => {
@@ -427,8 +428,8 @@ impl<'db> BoundSuperType<'db> {
Type::TypedDict(td) => {
// In general it isn't sound to upcast a `TypedDict` to a `dict`,
// but here it seems like it's probably sound?
let mut key_builder = UnionBuilder::new(db);
let mut value_builder = UnionBuilder::new(db);
let mut key_builder = UnionBuilder::new(db, UnionSettings::default());
let mut value_builder = UnionBuilder::new(db, UnionSettings::default());
for (name, field) in td.items(db) {
key_builder = key_builder.add(Type::string_literal(db, name));
value_builder = value_builder.add(field.declared_ty);

View File

@@ -202,22 +202,86 @@ enum ReduceResult<'db> {
Type(Type<'db>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
pub enum RecursivelyDefined {
Yes,
No,
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)]
struct UnionSettingsInner: u8 {
const UNPACK_ALIASES = 1 << 0;
const RECURSIVELY_DEFINED = 1 << 1;
const ORDER_ELEMENTS = 1 << 2;
const CYCLE_RECOVERY = 1 << 3;
}
}
impl RecursivelyDefined {
const fn is_yes(self) -> bool {
matches!(self, RecursivelyDefined::Yes)
impl get_size2::GetSize for UnionSettingsInner {}
impl Default for UnionSettingsInner {
fn default() -> Self {
Self::UNPACK_ALIASES
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, salsa::Update, get_size2::GetSize)]
pub struct UnionSettings(UnionSettingsInner);
impl UnionSettings {
#[must_use]
pub(crate) fn set_recursively_defined(mut self, value: bool) -> Self {
self.0.set(UnionSettingsInner::RECURSIVELY_DEFINED, value);
self
}
const fn or(self, other: RecursivelyDefined) -> RecursivelyDefined {
match (self, other) {
(RecursivelyDefined::Yes, _) | (_, RecursivelyDefined::Yes) => RecursivelyDefined::Yes,
_ => RecursivelyDefined::No,
#[must_use]
pub(super) fn set_order_elements(mut self, value: bool) -> Self {
self.0.set(UnionSettingsInner::ORDER_ELEMENTS, value);
self
}
/// This is enabled when joining types in a `cycle_recovery` function.
/// Since a cycle cannot be created within a `cycle_recovery` function
/// execution of `is_redundant_with` is skipped.
#[must_use]
pub(crate) fn set_cycle_recovery(mut self, value: bool) -> Self {
if value {
self.0.insert(UnionSettingsInner::CYCLE_RECOVERY);
self.0.remove(UnionSettingsInner::UNPACK_ALIASES);
} else {
self.0.remove(UnionSettingsInner::CYCLE_RECOVERY);
}
self
}
#[must_use]
pub(crate) fn set_unpack_aliases(mut self, value: bool) -> Self {
self.0.set(UnionSettingsInner::UNPACK_ALIASES, value);
self
}
#[must_use]
pub(crate) fn should_unpack_aliases(self) -> bool {
self.0.contains(UnionSettingsInner::UNPACK_ALIASES)
}
#[must_use]
pub(crate) fn is_recursively_defined(self) -> bool {
self.0.contains(UnionSettingsInner::RECURSIVELY_DEFINED)
}
#[must_use]
fn should_order_elements(self) -> bool {
self.0.contains(UnionSettingsInner::ORDER_ELEMENTS)
}
#[must_use]
fn in_cycle_recovery(self) -> bool {
self.0.contains(UnionSettingsInner::CYCLE_RECOVERY)
}
#[must_use]
fn merge_recursively_defined(mut self, other: UnionSettings) -> Self {
if other.is_recursively_defined() {
self.0.insert(UnionSettingsInner::RECURSIVELY_DEFINED);
}
self
}
}
@@ -230,49 +294,18 @@ const MAX_NON_RECURSIVE_UNION_LITERALS: usize = 256;
pub(crate) struct UnionBuilder<'db> {
elements: Vec<UnionElement<'db>>,
db: &'db dyn Db,
unpack_aliases: bool,
order_elements: bool,
// This is enabled when joining types in a `cycle_recovery` function.
// Since a cycle cannot be created within a `cycle_recovery` function, execution of `is_redundant_with` is skipped.
cycle_recovery: bool,
recursively_defined: RecursivelyDefined,
pub(crate) settings: UnionSettings,
}
impl<'db> UnionBuilder<'db> {
pub(crate) fn new(db: &'db dyn Db) -> Self {
pub(crate) fn new(db: &'db dyn Db, settings: UnionSettings) -> Self {
Self {
db,
elements: vec![],
unpack_aliases: true,
order_elements: false,
cycle_recovery: false,
recursively_defined: RecursivelyDefined::No,
settings,
}
}
pub(crate) fn unpack_aliases(mut self, val: bool) -> Self {
self.unpack_aliases = val;
self
}
pub(crate) fn order_elements(mut self, val: bool) -> Self {
self.order_elements = val;
self
}
pub(crate) fn cycle_recovery(mut self, val: bool) -> Self {
self.cycle_recovery = val;
if self.cycle_recovery {
self.unpack_aliases = false;
}
self
}
pub(crate) fn recursively_defined(mut self, val: RecursivelyDefined) -> Self {
self.recursively_defined = val;
self
}
pub(crate) fn is_empty(&self) -> bool {
self.elements.is_empty()
}
@@ -316,9 +349,9 @@ impl<'db> UnionBuilder<'db> {
}
pub(crate) fn add_in_place_impl(&mut self, ty: Type<'db>, seen_aliases: &mut Vec<Type<'db>>) {
let cycle_recovery = self.cycle_recovery;
let should_widen = |literals, recursively_defined: RecursivelyDefined| {
if recursively_defined.is_yes() && cycle_recovery {
let cycle_recovery = self.settings.in_cycle_recovery();
let should_widen = |literals, settings: UnionSettings| {
if settings.is_recursively_defined() && cycle_recovery {
literals >= MAX_RECURSIVE_UNION_LITERALS
} else {
literals >= MAX_NON_RECURSIVE_UNION_LITERALS
@@ -335,24 +368,24 @@ impl<'db> UnionBuilder<'db> {
for element in new_elements {
self.add_in_place_impl(*element, seen_aliases);
}
self.recursively_defined = self
.recursively_defined
.or(union.recursively_defined(self.db));
if self.cycle_recovery && self.recursively_defined.is_yes() {
self.settings = self
.settings
.merge_recursively_defined(union.settings(self.db));
if self.settings.in_cycle_recovery() && self.settings.is_recursively_defined() {
let literals = self.elements.iter().fold(0, |acc, elem| match elem {
UnionElement::IntLiterals(literals) => acc + literals.len(),
UnionElement::StringLiterals(literals) => acc + literals.len(),
UnionElement::BytesLiterals(literals) => acc + literals.len(),
UnionElement::Type(_) => acc,
});
if should_widen(literals, self.recursively_defined) {
if should_widen(literals, self.settings) {
self.widen_literal_types(seen_aliases);
}
}
}
// Adding `Never` to a union is a no-op.
Type::Never => {}
Type::TypeAlias(alias) if self.unpack_aliases => {
Type::TypeAlias(alias) if self.settings.should_unpack_aliases() => {
if seen_aliases.contains(&ty) {
// Union contains itself recursively via a type alias. This is an error, just
// leave out the recursive alias. TODO surface this error.
@@ -371,7 +404,7 @@ impl<'db> UnionBuilder<'db> {
for (index, element) in self.elements.iter_mut().enumerate() {
match element {
UnionElement::StringLiterals(literals) => {
if should_widen(literals.len(), self.recursively_defined) {
if should_widen(literals.len(), self.settings) {
let replace_with = KnownClass::Str.to_instance(self.db);
self.add_in_place_impl(replace_with, seen_aliases);
return;
@@ -418,7 +451,7 @@ impl<'db> UnionBuilder<'db> {
for (index, element) in self.elements.iter_mut().enumerate() {
match element {
UnionElement::BytesLiterals(literals) => {
if should_widen(literals.len(), self.recursively_defined) {
if should_widen(literals.len(), self.settings) {
let replace_with = KnownClass::Bytes.to_instance(self.db);
self.add_in_place_impl(replace_with, seen_aliases);
return;
@@ -465,7 +498,7 @@ impl<'db> UnionBuilder<'db> {
for (index, element) in self.elements.iter_mut().enumerate() {
match element {
UnionElement::IntLiterals(literals) => {
if should_widen(literals.len(), self.recursively_defined) {
if should_widen(literals.len(), self.settings) {
let replace_with = KnownClass::Int.to_instance(self.db);
self.add_in_place_impl(replace_with, seen_aliases);
return;
@@ -556,7 +589,8 @@ impl<'db> UnionBuilder<'db> {
// If an alias gets here, it means we aren't unpacking aliases, and we also
// shouldn't try to simplify aliases out of the union, because that will require
// unpacking them.
let should_simplify_full = !matches!(ty, Type::TypeAlias(_)) && !self.cycle_recovery;
let should_simplify_full =
!matches!(ty, Type::TypeAlias(_)) && !self.settings.in_cycle_recovery();
let mut ty_negated: Option<Type> = None;
let mut to_remove = SmallVec::<[usize; 2]>::new();
@@ -655,7 +689,7 @@ impl<'db> UnionBuilder<'db> {
UnionElement::Type(ty) => types.push(ty),
}
}
if self.order_elements {
if self.settings.should_order_elements() {
types.sort_unstable_by(|l, r| union_or_intersection_elements_ordering(self.db, l, r));
}
match types.len() {
@@ -664,7 +698,7 @@ impl<'db> UnionBuilder<'db> {
_ => Some(Type::Union(UnionType::new(
self.db,
types.into_boxed_slice(),
self.recursively_defined,
self.settings,
))),
}
}
@@ -779,7 +813,7 @@ impl<'db> IntersectionBuilder<'db> {
enum_member_literals(db, instance.class_literal(db), None)
.expect("Calling `enum_member_literals` on an enum class")
.collect::<Box<[_]>>(),
RecursivelyDefined::No,
UnionSettings::default(),
)),
seen_aliases,
)
@@ -1345,7 +1379,7 @@ mod tests {
use crate::db::tests::setup_db;
use crate::place::known_module_symbol;
use crate::types::enums::enum_member_literals;
use crate::types::{KnownClass, Truthiness};
use crate::types::{KnownClass, Truthiness, UnionSettings};
use test_case::test_case;
use ty_module_resolver::KnownModule;
@@ -1353,8 +1387,7 @@ mod tests {
#[test]
fn build_union_no_elements() {
let db = setup_db();
let empty_union = UnionBuilder::new(&db).build();
let empty_union = UnionBuilder::new(&db, UnionSettings::default()).build();
assert_eq!(empty_union, Type::Never);
}

View File

@@ -46,8 +46,8 @@ use crate::types::{
CallableTypeKind, ClassLiteral, DATACLASS_FLAGS, DataclassFlags, DataclassParams,
FieldInstance, KnownBoundMethodType, KnownClass, KnownInstanceType, MemberLookupPolicy,
NominalInstanceType, PropertyInstanceType, SpecialFormType, TrackedConstraintSet,
TypeAliasType, TypeContext, TypeVarVariance, UnionBuilder, UnionType, WrapperDescriptorKind,
enums, list_members, todo_type,
TypeAliasType, TypeContext, TypeVarVariance, UnionBuilder, UnionSettings, UnionType,
WrapperDescriptorKind, enums, list_members, todo_type,
};
use crate::unpack::EvaluationMode;
use crate::{DisplaySettings, Program};
@@ -2011,9 +2011,10 @@ impl<'db> CallableBinding<'db> {
}
}
let mut union_argument_type_builders = std::iter::repeat_with(|| UnionBuilder::new(db))
.take(max_parameter_count)
.collect::<Vec<_>>();
let mut union_argument_type_builders =
std::iter::repeat_with(|| UnionBuilder::new(db, UnionSettings::default()))
.take(max_parameter_count)
.collect::<Vec<_>>();
// The following loop is trying to construct a tuple of argument types that correspond to
// the participating parameter indexes. Considering the following example:
@@ -2083,9 +2084,10 @@ impl<'db> CallableBinding<'db> {
continue;
}
let mut union_parameter_types = std::iter::repeat_with(|| UnionBuilder::new(db))
.take(max_parameter_count)
.collect::<Vec<_>>();
let mut union_parameter_types =
std::iter::repeat_with(|| UnionBuilder::new(db, UnionSettings::default()))
.take(max_parameter_count)
.collect::<Vec<_>>();
// The number of parameters that have been skipped because they don't participate in
// the filtering process. This is used to make sure the types are added to the

View File

@@ -40,8 +40,8 @@ use crate::types::{
DeprecatedInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor, IntersectionType,
IsDisjointVisitor, IsEquivalentVisitor, KnownInstanceType, ManualPEP695TypeAliasType,
MaterializationKind, NormalizedVisitor, PropertyInstanceType, TypeAliasType, TypeContext,
TypeMapping, TypeRelation, TypedDictParams, UnionBuilder, VarianceInferable, binding_type,
declaration_type, determine_upper_bound,
TypeMapping, TypeRelation, TypedDictParams, UnionBuilder, UnionSettings, VarianceInferable,
binding_type, declaration_type, determine_upper_bound,
};
use crate::{
Db, FxIndexMap, FxIndexSet, FxOrderSet, Program,
@@ -2478,7 +2478,7 @@ impl<'db> ClassLiteral<'db> {
// this attribute is determined by possible `value` parameter types with which
// the `__set__` method can be called. We build a union of all possible options
// to account for possible overloads.
let mut value_types = UnionBuilder::new(db);
let mut value_types = UnionBuilder::new(db, UnionSettings::default());
for binding in &dunder_set.bindings(db) {
for overload in binding {
if let Some(value_param) =
@@ -3359,7 +3359,7 @@ impl<'db> ClassLiteral<'db> {
return Place::Undefined.into();
}
let mut union = UnionBuilder::new(db);
let mut union = UnionBuilder::new(db, UnionSettings::default());
let mut union_qualifiers = TypeQualifiers::empty();
let mut is_definitely_bound = false;
@@ -3470,7 +3470,7 @@ impl<'db> ClassLiteral<'db> {
// any method, we build a union of `Unknown` with the inferred types of all bindings of
// that attribute. We include `Unknown` in that union to account for the fact that the
// attribute might be externally modified.
let mut union_of_inferred_types = UnionBuilder::new(db);
let mut union_of_inferred_types = UnionBuilder::new(db, UnionSettings::default());
let mut qualifiers = TypeQualifiers::IMPLICIT_INSTANCE_ATTRIBUTE;
let mut is_attribute_bound = false;

View File

@@ -83,7 +83,7 @@ use crate::types::{
ClassBase, ClassLiteral, ClassType, DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor,
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType,
NormalizedVisitor, SpecialFormType, SubclassOfInner, SubclassOfType, Truthiness, Type,
TypeContext, TypeMapping, TypeRelation, TypeVarBoundOrConstraints, UnionBuilder, binding_type,
TypeContext, TypeMapping, TypeRelation, TypeVarBoundOrConstraints, UnionType, binding_type,
definition_expression_type, infer_definition_types, walk_signature,
};
use crate::{Db, FxOrderSet};
@@ -1537,10 +1537,12 @@ impl KnownFunction {
match self {
KnownFunction::RevealType => {
let revealed_type = overload
.arguments_for_parameter(call_arguments, 0)
.fold(UnionBuilder::new(db), |builder, (_, ty)| builder.add(ty))
.build();
let revealed_type = UnionType::from_elements(
db,
overload
.arguments_for_parameter(call_arguments, 0)
.map(|(_, ty)| ty),
);
if let Some(builder) =
context.report_diagnostic(DiagnosticId::RevealedType, Severity::Info)
{

View File

@@ -113,8 +113,8 @@ use crate::types::{
SubclassOfType, TrackedConstraintSet, Truthiness, Type, TypeAliasType, TypeAndQualifiers,
TypeContext, TypeQualifiers, TypeVarBoundOrConstraints, TypeVarBoundOrConstraintsEvaluation,
TypeVarDefaultEvaluation, TypeVarIdentity, TypeVarInstance, TypeVarKind, TypeVarVariance,
TypedDictType, UnionBuilder, UnionType, UnionTypeInstance, binding_type, infer_scope_types,
todo_type,
TypedDictType, UnionBuilder, UnionSettings, UnionType, UnionTypeInstance, binding_type,
infer_scope_types, todo_type,
};
use crate::types::{CallableTypes, overrides};
use crate::types::{ClassBase, add_inferred_python_version_hint_to_diagnostic};
@@ -3167,7 +3167,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
// If it's an `except*` handler, this won't actually be the type of the bound symbol;
// it will actually be the type of the generic parameters to `BaseExceptionGroup` or `ExceptionGroup`.
let symbol_ty = if let Some(tuple_spec) = node_ty.tuple_instance_spec(self.db()) {
let mut builder = UnionBuilder::new(self.db());
let mut builder = UnionBuilder::new(self.db(), UnionSettings::default());
let mut invalid_elements = vec![];
for (index, element) in tuple_spec.all_elements().iter().enumerate() {
@@ -9097,7 +9097,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
ApplicableConstraints::ConstrainedBindings(bindings) => {
let reachability_constraints = bindings.reachability_constraints;
let predicates = bindings.predicates;
let mut union = UnionBuilder::new(db);
let mut union = UnionBuilder::new(db, UnionSettings::default());
for binding in bindings {
let static_reachability = reachability_constraints.evaluate(
db,
@@ -9374,7 +9374,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
// definition of this name visible to us (would be `LOAD_DEREF` at runtime.)
// Note that we skip the scope containing the use that we are resolving, since we
// already looked for the place there up above.
let mut nonlocal_union_builder = UnionBuilder::new(db);
let mut nonlocal_union_builder = UnionBuilder::new(db, UnionSettings::default());
let mut found_some_definition = false;
for (enclosing_scope_file_id, _) in self.index.ancestor_scopes(file_scope_id).skip(1) {
// If the current enclosing scope is global, no place lookup is performed here,
@@ -10994,7 +10994,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let comparison_result = match (left, right) {
(Type::Union(union), other) => {
let mut builder = UnionBuilder::new(self.db());
let mut builder = UnionBuilder::new(self.db(), union.settings(self.db()));
for element in union.elements(self.db()) {
builder =
builder.add(self.infer_binary_type_comparison(*element, op, other, range, visitor)?);
@@ -11002,7 +11002,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
Some(Ok(builder.build()))
}
(other, Type::Union(union)) => {
let mut builder = UnionBuilder::new(self.db());
let mut builder = UnionBuilder::new(self.db(), union.settings(self.db()));
for element in union.elements(self.db()) {
builder =
builder.add(self.infer_binary_type_comparison(other, op, *element, range, visitor)?);
@@ -11482,7 +11482,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let left_iter = left.iter_all_elements();
let right_iter = right.iter_all_elements();
let mut builder = UnionBuilder::new(self.db());
let mut builder = UnionBuilder::new(self.db(), UnionSettings::default());
for (l_ty, r_ty) in left_iter.zip(right_iter) {
let pairwise_eq_result = self

View File

@@ -17,7 +17,7 @@ use crate::types::{
BindingContext, CallableType, DynamicType, GenericContext, IntersectionBuilder, KnownClass,
KnownInstanceType, LintDiagnosticGuard, Parameter, Parameters, SpecialFormType, SubclassOfType,
Type, TypeAliasType, TypeContext, TypeGuardType, TypeIsType, TypeMapping, TypeVarKind,
UnionBuilder, UnionType, any_over_type, todo_type,
UnionBuilder, UnionSettings, UnionType, any_over_type, todo_type,
};
/// Type expressions
@@ -1673,7 +1673,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
}
ast::Expr::Tuple(tuple) if !tuple.parenthesized => {
let mut errors = vec![];
let mut builder = UnionBuilder::new(self.db());
let mut builder = UnionBuilder::new(self.db(), UnionSettings::default());
for elt in tuple {
match self.infer_literal_parameter_type(elt) {
Ok(ty) => {

View File

@@ -17,7 +17,7 @@ use crate::types::typed_dict::{
use crate::types::{
CallableType, ClassLiteral, ClassType, IntersectionBuilder, IntersectionType, KnownClass,
KnownInstanceType, SpecialFormType, SubclassOfInner, SubclassOfType, Truthiness, Type,
TypeContext, TypeVarBoundOrConstraints, UnionBuilder, infer_expression_types,
TypeContext, TypeVarBoundOrConstraints, UnionBuilder, UnionSettings, infer_expression_types,
};
use ruff_db::parsed::{ParsedModuleRef, parsed_module};
@@ -853,7 +853,7 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> {
.ok()?
.homogeneous_element_type(self.db);
let mut builder = UnionBuilder::new(self.db);
let mut builder = UnionBuilder::new(self.db, UnionSettings::default());
// Add the narrowed values from the RHS first, to keep literals before broader types.
builder = builder.add(rhs_values);
@@ -900,8 +900,8 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> {
} else if lhs_ty.is_union_with_single_valued(self.db) {
// Split LHS into single-valued portion and the rest. Exclude RHS values from the
// single-valued portion, keep the rest intact.
let mut single_builder = UnionBuilder::new(self.db);
let mut rest_builder = UnionBuilder::new(self.db);
let mut single_builder = UnionBuilder::new(self.db, UnionSettings::default());
let mut rest_builder = UnionBuilder::new(self.db, UnionSettings::default());
if let Some(lhs_union) = lhs_ty.as_union() {
for element in lhs_union.elements(self.db) {

View File

@@ -24,7 +24,7 @@ use smallvec::{SmallVec, smallvec_inline};
use crate::semantic_index::definition::Definition;
use crate::subscript::{Nth, OutOfBoundsError, PyIndex, PySlice, StepSizeZeroError};
use crate::types::builder::RecursivelyDefined;
use crate::types::builder::UnionSettings;
use crate::types::class::{ClassType, KnownClass};
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
use crate::types::generics::InferableTypeVars;
@@ -1650,7 +1650,7 @@ impl<'db> Tuple<Type<'db>> {
// those techniques ensure that union elements are deduplicated and unions are eagerly simplified
// into other types where necessary. Here, however, we know that there are no duplicates
// in this union, so it's probably more efficient to use `UnionType::new()` directly.
Type::Union(UnionType::new(db, elements, RecursivelyDefined::No))
Type::Union(UnionType::new(db, elements, UnionSettings::default()))
};
TupleSpec::heterogeneous([
@@ -1707,14 +1707,16 @@ pub(crate) struct TupleUnpacker<'db> {
impl<'db> TupleUnpacker<'db> {
pub(crate) fn new(db: &'db dyn Db, len: TupleLength) -> Self {
let new_builders = |len: usize| std::iter::repeat_with(|| UnionBuilder::new(db)).take(len);
let new_builders = |len: usize| {
std::iter::repeat_with(|| UnionBuilder::new(db, UnionSettings::default())).take(len)
};
let targets = match len {
TupleLength::Fixed(len) => {
Tuple::Fixed(FixedLengthTuple::from_elements(new_builders(len)))
}
TupleLength::Variable(prefix, suffix) => VariableLengthTuple::mixed(
new_builders(prefix),
UnionBuilder::new(db),
UnionBuilder::new(db, UnionSettings::default()),
new_builders(suffix),
),
};