Compare commits
2 Commits
alex/subsc
...
alex/union
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
444749767e | ||
|
|
67e8d24aaf |
@@ -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()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user