diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index 0f5e8294f749a..5a2c8e895562d 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -475,20 +475,24 @@ impl std::fmt::Display for TodoType { #[cfg(debug_assertions)] macro_rules! todo_type { () => { - Type::Todo(crate::types::TodoType::FileAndLine(file!(), line!())) + $crate::types::Type::Dynamic($crate::types::DynamicType::Todo( + $crate::types::TodoType::FileAndLine(file!(), line!()), + )) }; ($message:literal) => { - Type::Todo(crate::types::TodoType::Message($message)) + $crate::types::Type::Dynamic($crate::types::DynamicType::Todo( + $crate::types::TodoType::Message($message), + )) }; } #[cfg(not(debug_assertions))] macro_rules! todo_type { () => { - Type::Todo(crate::types::TodoType) + $crate::types::Type::Dynamic($crate::types::DynamicType::Todo(crate::types::TodoType)) }; ($message:literal) => { - Type::Todo(crate::types::TodoType) + $crate::types::Type::Dynamic($crate::types::DynamicType::Todo(crate::types::TodoType)) }; } @@ -498,21 +502,7 @@ pub(crate) use todo_type; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, salsa::Update)] pub enum Type<'db> { /// The dynamic type: a statically unknown set of values - Any, - /// Unknown type (either no annotation, or some kind of type error). - /// Equivalent to Any, or possibly to object in strict mode - Unknown, - /// Temporary type for symbols that can't be inferred yet because of missing implementations. - /// Behaves equivalently to `Any`. - /// - /// This variant should eventually be removed once red-knot is spec-compliant. - /// - /// General rule: `Todo` should only propagate when the presence of the input `Todo` caused the - /// output to be unknown. An output should only be `Todo` if fixing all `Todo` inputs to be not - /// `Todo` would change the output type. - /// - /// This variant should be created with the `todo_type!` macro. - Todo(TodoType), + Dynamic(DynamicType), /// The empty set of values Never, /// A specific function object @@ -556,8 +546,16 @@ pub enum Type<'db> { } impl<'db> Type<'db> { + pub const fn any() -> Self { + Self::Dynamic(DynamicType::Any) + } + + pub const fn unknown() -> Self { + Self::Dynamic(DynamicType::Unknown) + } + pub const fn is_unknown(&self) -> bool { - matches!(self, Type::Unknown) + matches!(self, Type::Dynamic(DynamicType::Unknown)) } pub const fn is_never(&self) -> bool { @@ -565,7 +563,7 @@ impl<'db> Type<'db> { } pub const fn is_todo(&self) -> bool { - matches!(self, Type::Todo(_)) + matches!(self, Type::Dynamic(DynamicType::Todo(_))) } pub const fn class_literal(class: Class<'db>) -> Self { @@ -757,8 +755,7 @@ impl<'db> Type<'db> { match (self, target) { // We should have handled these immediately above. - (Type::Any | Type::Unknown | Type::Todo(_), _) - | (_, Type::Any | Type::Unknown | Type::Todo(_)) => { + (Type::Dynamic(_), _) | (_, Type::Dynamic(_)) => { unreachable!("Non-fully-static types do not participate in subtyping!") } @@ -975,8 +972,8 @@ impl<'db> Type<'db> { (Type::Never, _) => true, // The dynamic type is assignable-to and assignable-from any type. - (Type::Unknown | Type::Any | Type::Todo(_), _) => true, - (_, Type::Unknown | Type::Any | Type::Todo(_)) => true, + (Type::Dynamic(_), _) => true, + (_, Type::Dynamic(_)) => true, // All types are assignable to `object`. // TODO this special case might be removable once the below cases are comprehensive @@ -1085,9 +1082,16 @@ impl<'db> Type<'db> { pub(crate) fn is_same_gradual_form(self, other: Type<'db>) -> bool { matches!( (self, other), - (Type::Unknown, Type::Unknown) - | (Type::Any, Type::Any) - | (Type::Todo(_), Type::Todo(_)) + ( + Type::Dynamic(DynamicType::Any), + Type::Dynamic(DynamicType::Any) + ) | ( + Type::Dynamic(DynamicType::Unknown), + Type::Dynamic(DynamicType::Unknown) + ) | ( + Type::Dynamic(DynamicType::Todo(_)), + Type::Dynamic(DynamicType::Todo(_)) + ) ) } @@ -1099,9 +1103,7 @@ impl<'db> Type<'db> { match (self, other) { (Type::Never, _) | (_, Type::Never) => true, - (Type::Any, _) | (_, Type::Any) => false, - (Type::Unknown, _) | (_, Type::Unknown) => false, - (Type::Todo(_), _) | (_, Type::Todo(_)) => false, + (Type::Dynamic(_), _) | (_, Type::Dynamic(_)) => false, (Type::Union(union), other) | (other, Type::Union(union)) => union .elements(db) @@ -1181,7 +1183,7 @@ impl<'db> Type<'db> { Type::ClassLiteral(ClassLiteralType { class: class_b }), Type::SubclassOf(subclass_of_ty), ) => match subclass_of_ty.subclass_of() { - ClassBase::Any | ClassBase::Todo(_) | ClassBase::Unknown => false, + ClassBase::Dynamic(_) => false, ClassBase::Class(class_a) => !class_b.is_subclass_of(db, class_a), }, @@ -1377,7 +1379,7 @@ impl<'db> Type<'db> { /// Returns true if the type does not contain any gradual forms (as a sub-part). pub(crate) fn is_fully_static(self, db: &'db dyn Db) -> bool { match self { - Type::Any | Type::Unknown | Type::Todo(_) => false, + Type::Dynamic(_) => false, Type::Never | Type::FunctionLiteral(..) | Type::ModuleLiteral(..) @@ -1440,10 +1442,8 @@ impl<'db> Type<'db> { /// for more complicated types that are actually singletons. pub(crate) fn is_singleton(self, db: &'db dyn Db) -> bool { match self { - Type::Any + Type::Dynamic(_) | Type::Never - | Type::Unknown - | Type::Todo(_) | Type::IntLiteral(..) | Type::StringLiteral(..) | Type::BytesLiteral(..) @@ -1553,10 +1553,8 @@ impl<'db> Type<'db> { None => false, }, - Type::Any + Type::Dynamic(_) | Type::Never - | Type::Unknown - | Type::Todo(_) | Type::Union(..) | Type::Intersection(..) | Type::LiteralString @@ -1577,7 +1575,7 @@ impl<'db> Type<'db> { } match self { - Type::Any | Type::Unknown | Type::Todo(_) => self.into(), + Type::Dynamic(_) => self.into(), Type::Never => todo_type!("attribute lookup on Never").into(), @@ -1702,7 +1700,7 @@ impl<'db> Type<'db> { /// when `bool(x)` is called on an object `x`. pub(crate) fn bool(&self, db: &'db dyn Db) -> Truthiness { match self { - Type::Any | Type::Todo(_) | Type::Never | Type::Unknown => Truthiness::Ambiguous, + Type::Dynamic(_) | Type::Never => Truthiness::Ambiguous, Type::FunctionLiteral(_) => Truthiness::AlwaysTrue, Type::ModuleLiteral(_) => Truthiness::AlwaysTrue, Type::ClassLiteral(ClassLiteralType { class }) => { @@ -1836,7 +1834,7 @@ impl<'db> Type<'db> { let mut binding = bind_call(db, arguments, function_type.signature(db), Some(self)); match function_type.known(db) { Some(KnownFunction::RevealType) => { - let revealed_ty = binding.one_parameter_ty().unwrap_or(Type::Unknown); + let revealed_ty = binding.one_parameter_ty().unwrap_or(Type::unknown()); CallOutcome::revealed(binding, revealed_ty) } Some(KnownFunction::StaticAssert) => { @@ -1872,7 +1870,7 @@ impl<'db> Type<'db> { Some(KnownFunction::IsEquivalentTo) => { let (ty_a, ty_b) = binding .two_parameter_tys() - .unwrap_or((Type::Unknown, Type::Unknown)); + .unwrap_or((Type::unknown(), Type::unknown())); binding .set_return_ty(Type::BooleanLiteral(ty_a.is_equivalent_to(db, ty_b))); CallOutcome::callable(binding) @@ -1880,14 +1878,14 @@ impl<'db> Type<'db> { Some(KnownFunction::IsSubtypeOf) => { let (ty_a, ty_b) = binding .two_parameter_tys() - .unwrap_or((Type::Unknown, Type::Unknown)); + .unwrap_or((Type::unknown(), Type::unknown())); binding.set_return_ty(Type::BooleanLiteral(ty_a.is_subtype_of(db, ty_b))); CallOutcome::callable(binding) } Some(KnownFunction::IsAssignableTo) => { let (ty_a, ty_b) = binding .two_parameter_tys() - .unwrap_or((Type::Unknown, Type::Unknown)); + .unwrap_or((Type::unknown(), Type::unknown())); binding .set_return_ty(Type::BooleanLiteral(ty_a.is_assignable_to(db, ty_b))); CallOutcome::callable(binding) @@ -1895,23 +1893,23 @@ impl<'db> Type<'db> { Some(KnownFunction::IsDisjointFrom) => { let (ty_a, ty_b) = binding .two_parameter_tys() - .unwrap_or((Type::Unknown, Type::Unknown)); + .unwrap_or((Type::unknown(), Type::unknown())); binding .set_return_ty(Type::BooleanLiteral(ty_a.is_disjoint_from(db, ty_b))); CallOutcome::callable(binding) } Some(KnownFunction::IsFullyStatic) => { - let ty = binding.one_parameter_ty().unwrap_or(Type::Unknown); + let ty = binding.one_parameter_ty().unwrap_or(Type::unknown()); binding.set_return_ty(Type::BooleanLiteral(ty.is_fully_static(db))); CallOutcome::callable(binding) } Some(KnownFunction::IsSingleton) => { - let ty = binding.one_parameter_ty().unwrap_or(Type::Unknown); + let ty = binding.one_parameter_ty().unwrap_or(Type::unknown()); binding.set_return_ty(Type::BooleanLiteral(ty.is_singleton(db))); CallOutcome::callable(binding) } Some(KnownFunction::IsSingleValued) => { - let ty = binding.one_parameter_ty().unwrap_or(Type::Unknown); + let ty = binding.one_parameter_ty().unwrap_or(Type::unknown()); binding.set_return_ty(Type::BooleanLiteral(ty.is_single_valued(db))); CallOutcome::callable(binding) } @@ -1973,9 +1971,7 @@ impl<'db> Type<'db> { } // Dynamic types are callable, and the return type is the same dynamic type - Type::Any | Type::Todo(_) | Type::Unknown => { - CallOutcome::callable(CallBinding::from_return_ty(self)) - } + Type::Dynamic(_) => CallOutcome::callable(CallBinding::from_return_ty(self)), Type::Union(union) => CallOutcome::union( self, @@ -2083,16 +2079,12 @@ impl<'db> Type<'db> { #[must_use] pub fn to_instance(&self, db: &'db dyn Db) -> Type<'db> { match self { - Type::Any => Type::Any, - todo @ Type::Todo(_) => *todo, - Type::Unknown => Type::Unknown, + Type::Dynamic(_) => *self, Type::Never => Type::Never, Type::ClassLiteral(ClassLiteralType { class }) => Type::instance(*class), Type::SubclassOf(subclass_of_ty) => match subclass_of_ty.subclass_of() { ClassBase::Class(class) => Type::instance(class), - ClassBase::Any => Type::Any, - ClassBase::Unknown => Type::Unknown, - ClassBase::Todo(todo) => Type::Todo(todo), + ClassBase::Dynamic(dynamic) => Type::Dynamic(dynamic), }, Type::Union(union) => union.map(db, |element| element.to_instance(db)), Type::Intersection(_) => todo_type!("Type::Intersection.to_instance()"), @@ -2110,7 +2102,7 @@ impl<'db> Type<'db> { | Type::Tuple(_) | Type::LiteralString | Type::AlwaysTruthy - | Type::AlwaysFalsy => Type::Unknown, + | Type::AlwaysFalsy => Type::unknown(), } } @@ -2177,7 +2169,7 @@ impl<'db> Type<'db> { }) } } - Type::Unknown => Ok(Type::Unknown), + Type::Dynamic(_) => Ok(*self), // TODO map this to a new `Type::TypeVar` variant Type::KnownInstance(KnownInstanceType::TypeVar(_)) => Ok(*self), Type::KnownInstance(KnownInstanceType::TypeAliasType(alias)) => Ok(alias.value_ty(db)), @@ -2185,18 +2177,17 @@ impl<'db> Type<'db> { Ok(Type::Never) } Type::KnownInstance(KnownInstanceType::LiteralString) => Ok(Type::LiteralString), - Type::KnownInstance(KnownInstanceType::Any) => Ok(Type::Any), + Type::KnownInstance(KnownInstanceType::Any) => Ok(Type::any()), // TODO: Should emit a diagnostic Type::KnownInstance(KnownInstanceType::Annotated) => Err(InvalidTypeExpressionError { invalid_expressions: smallvec::smallvec![InvalidTypeExpression::BareAnnotated], - fallback_type: Type::Unknown, + fallback_type: Type::unknown(), }), Type::KnownInstance(KnownInstanceType::Literal) => Err(InvalidTypeExpressionError { invalid_expressions: smallvec::smallvec![InvalidTypeExpression::BareLiteral], - fallback_type: Type::Unknown, + fallback_type: Type::unknown(), }), - Type::KnownInstance(KnownInstanceType::Unknown) => Ok(Type::Unknown), - Type::Todo(_) => Ok(*self), + Type::KnownInstance(KnownInstanceType::Unknown) => Ok(Type::unknown()), _ => Ok(todo_type!( "Unsupported or invalid type in a type expression" )), @@ -2260,16 +2251,15 @@ impl<'db> Type<'db> { Type::Tuple(_) => KnownClass::Tuple.to_class_literal(db), Type::ClassLiteral(ClassLiteralType { class }) => class.metaclass(db), Type::SubclassOf(subclass_of_ty) => match subclass_of_ty.subclass_of() { - ClassBase::Any | ClassBase::Unknown | ClassBase::Todo(_) => *self, + ClassBase::Dynamic(_) => *self, ClassBase::Class(class) => SubclassOfType::from( db, - ClassBase::try_from_ty(db, class.metaclass(db)).unwrap_or(ClassBase::Unknown), + ClassBase::try_from_ty(db, class.metaclass(db)).unwrap_or(ClassBase::unknown()), ), }, Type::StringLiteral(_) | Type::LiteralString => KnownClass::Str.to_class_literal(db), - Type::Any => SubclassOfType::subclass_of_any(), - Type::Unknown => SubclassOfType::subclass_of_unknown(), + Type::Dynamic(dynamic) => SubclassOfType::from(db, ClassBase::Dynamic(*dynamic)), // TODO intersections Type::Intersection(_) => SubclassOfType::from( db, @@ -2277,7 +2267,6 @@ impl<'db> Type<'db> { .expect("Type::Todo should be a valid ClassBase"), ), Type::AlwaysTruthy | Type::AlwaysFalsy => KnownClass::Type.to_instance(db), - Type::Todo(todo) => SubclassOfType::from(db, ClassBase::Todo(*todo)), } } @@ -2338,6 +2327,36 @@ impl<'db> From<&Type<'db>> for Symbol<'db> { } } +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +pub enum DynamicType { + // An explicitly annotated `typing.Any` + Any, + // An unannotated value, or a dynamic type resulting from an error + Unknown, + /// Temporary type for symbols that can't be inferred yet because of missing implementations. + /// + /// This variant should eventually be removed once red-knot is spec-compliant. + /// + /// General rule: `Todo` should only propagate when the presence of the input `Todo` caused the + /// output to be unknown. An output should only be `Todo` if fixing all `Todo` inputs to be not + /// `Todo` would change the output type. + /// + /// This variant should be created with the `todo_type!` macro. + Todo(TodoType), +} + +impl std::fmt::Display for DynamicType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + DynamicType::Any => f.write_str("Any"), + DynamicType::Unknown => f.write_str("Unknown"), + // `DynamicType::Todo`'s display should be explicit that is not a valid display of + // any other type + DynamicType::Todo(todo) => write!(f, "@Todo{todo}"), + } + } +} + /// Error struct providing information on type(s) that were deemed to be invalid /// in a type expression context, and the type we should therefore fallback to /// for the problematic type expression. @@ -2480,7 +2499,7 @@ impl<'db> KnownClass { pub fn to_class_literal(self, db: &'db dyn Db) -> Type<'db> { known_module_symbol(db, self.canonical_module(db), self.as_str()) .ignore_possibly_unbound() - .unwrap_or(Type::Unknown) + .unwrap_or(Type::unknown()) } pub fn to_subclass_of(self, db: &'db dyn Db) -> Type<'db> { @@ -3090,7 +3109,7 @@ impl<'db> IterationOutcome<'db> { Self::Iterable { element_ty } => element_ty, Self::NotIterable { not_iterable_ty } => { report_not_iterable(context, iterable_node, not_iterable_ty); - Type::Unknown + Type::unknown() } Self::PossiblyUnboundDunderIter { iterable_ty, @@ -3650,7 +3669,7 @@ impl<'db> Class<'db> { kind: MetaclassErrorKind::PartlyNotCallable(called_ty), }) } else { - Ok(return_ty.unwrap_or(Type::Unknown)) + Ok(return_ty.unwrap_or(Type::unknown())) } } @@ -3716,9 +3735,7 @@ impl<'db> Class<'db> { match superclass { // TODO we may instead want to record the fact that we encountered dynamic, and intersect it with // the type found on the next "real" class. - ClassBase::Any | ClassBase::Unknown | ClassBase::Todo(_) => { - return Type::from(superclass).member(db, name) - } + ClassBase::Dynamic(_) => return Type::from(superclass).member(db, name), ClassBase::Class(class) => { let member = class.own_class_member(db, name); if !member.is_unbound() { @@ -4059,9 +4076,9 @@ pub(crate) mod tests { pub(crate) fn into_type(self, db: &TestDb) -> Type<'_> { match self { Ty::Never => Type::Never, - Ty::Unknown => Type::Unknown, + Ty::Unknown => Type::unknown(), Ty::None => Type::none(db), - Ty::Any => Type::Any, + Ty::Any => Type::any(), Ty::Todo => todo_type!("Ty::Todo"), Ty::IntLiteral(n) => Type::IntLiteral(n), Ty::StringLiteral(s) => Type::string_literal(db, s), diff --git a/crates/red_knot_python_semantic/src/types/builder.rs b/crates/red_knot_python_semantic/src/types/builder.rs index 33ac5e00257c5..a24b7450905d4 100644 --- a/crates/red_knot_python_semantic/src/types/builder.rs +++ b/crates/red_knot_python_semantic/src/types/builder.rs @@ -321,7 +321,7 @@ impl<'db> InnerIntersectionBuilder<'db> { self.add_positive(db, *neg); } } - ty @ (Type::Any | Type::Unknown | Type::Todo(_)) => { + ty @ Type::Dynamic(_) => { // Adding any of these types to the negative side of an intersection // is equivalent to adding it to the positive side. We do this to // simplify the representation. @@ -434,7 +434,7 @@ mod tests { fn build_intersection() { let db = setup_db(); let t0 = Type::IntLiteral(0); - let ta = Type::Any; + let ta = Type::any(); let intersection = IntersectionBuilder::new(&db) .add_positive(ta) .add_negative(t0) @@ -457,7 +457,7 @@ mod tests { #[test] fn build_intersection_flatten_positive() { let db = setup_db(); - let ta = Type::Any; + let ta = Type::any(); let t1 = Type::IntLiteral(1); let t2 = Type::IntLiteral(2); let i0 = IntersectionBuilder::new(&db) @@ -477,7 +477,7 @@ mod tests { #[test] fn build_intersection_flatten_negative() { let db = setup_db(); - let ta = Type::Any; + let ta = Type::any(); let t1 = Type::IntLiteral(1); let t2 = KnownClass::Int.to_instance(&db); // i0 = Any & ~Literal[1] @@ -503,13 +503,13 @@ mod tests { let db = setup_db(); let ty = IntersectionBuilder::new(&db) - .add_negative(Type::Any) + .add_negative(Type::any()) .build(); - assert_eq!(ty, Type::Any); + assert_eq!(ty, Type::any()); let ty = IntersectionBuilder::new(&db) .add_positive(Type::Never) - .add_negative(Type::Any) + .add_negative(Type::any()) .build(); assert_eq!(ty, Type::Never); } @@ -519,32 +519,32 @@ mod tests { let db = setup_db(); let ty = IntersectionBuilder::new(&db) - .add_positive(Type::Unknown) - .add_positive(Type::Unknown) + .add_positive(Type::unknown()) + .add_positive(Type::unknown()) .build(); - assert_eq!(ty, Type::Unknown); + assert_eq!(ty, Type::unknown()); let ty = IntersectionBuilder::new(&db) - .add_positive(Type::Unknown) - .add_negative(Type::Unknown) + .add_positive(Type::unknown()) + .add_negative(Type::unknown()) .build(); - assert_eq!(ty, Type::Unknown); + assert_eq!(ty, Type::unknown()); let ty = IntersectionBuilder::new(&db) - .add_negative(Type::Unknown) - .add_negative(Type::Unknown) + .add_negative(Type::unknown()) + .add_negative(Type::unknown()) .build(); - assert_eq!(ty, Type::Unknown); + assert_eq!(ty, Type::unknown()); let ty = IntersectionBuilder::new(&db) - .add_positive(Type::Unknown) + .add_positive(Type::unknown()) .add_positive(Type::IntLiteral(0)) - .add_negative(Type::Unknown) + .add_negative(Type::unknown()) .build(); assert_eq!( ty, IntersectionBuilder::new(&db) - .add_positive(Type::Unknown) + .add_positive(Type::unknown()) .add_positive(Type::IntLiteral(0)) .build() ); @@ -555,7 +555,7 @@ mod tests { let db = setup_db(); let t0 = Type::IntLiteral(0); let t1 = Type::IntLiteral(1); - let ta = Type::Any; + let ta = Type::any(); let u0 = UnionType::from_elements(&db, [t0, t1]); let union = IntersectionBuilder::new(&db) @@ -958,8 +958,8 @@ mod tests { assert_eq!(ty, Type::BooleanLiteral(!bool_value)); } - #[test_case(Type::Any)] - #[test_case(Type::Unknown)] + #[test_case(Type::any())] + #[test_case(Type::unknown())] #[test_case(todo_type!())] fn build_intersection_t_and_negative_t_does_not_simplify(ty: Type) { let db = setup_db(); diff --git a/crates/red_knot_python_semantic/src/types/call.rs b/crates/red_knot_python_semantic/src/types/call.rs index 951fdb7858442..bffaa3a81c772 100644 --- a/crates/red_knot_python_semantic/src/types/call.rs +++ b/crates/red_knot_python_semantic/src/types/call.rs @@ -97,7 +97,7 @@ impl<'db> CallOutcome<'db> { match (acc, ty) { (None, None) => None, (None, Some(ty)) => Some(UnionBuilder::new(db).add(ty)), - (Some(builder), ty) => Some(builder.add(ty.unwrap_or(Type::Unknown))), + (Some(builder), ty) => Some(builder.add(ty.unwrap_or(Type::unknown()))), } }) .map(UnionBuilder::build), @@ -206,7 +206,7 @@ impl<'db> CallOutcome<'db> { } Self::NotCallable { not_callable_ty } => Err(NotCallableError::Type { not_callable_ty: *not_callable_ty, - return_ty: Type::Unknown, + return_ty: Type::unknown(), }), Self::PossiblyUnboundDunderCall { called_ty, @@ -215,7 +215,7 @@ impl<'db> CallOutcome<'db> { callable_ty: *called_ty, return_ty: call_outcome .return_ty(context.db()) - .unwrap_or(Type::Unknown), + .unwrap_or(Type::unknown()), }), Self::Union { outcomes, @@ -228,7 +228,7 @@ impl<'db> CallOutcome<'db> { let return_ty = match outcome { Self::NotCallable { not_callable_ty } => { not_callable.push(*not_callable_ty); - Type::Unknown + Type::unknown() } Self::RevealType { binding, @@ -307,7 +307,7 @@ impl<'db> CallOutcome<'db> { } } - Ok(Type::Unknown) + Ok(Type::unknown()) } } } diff --git a/crates/red_knot_python_semantic/src/types/call/bind.rs b/crates/red_knot_python_semantic/src/types/call/bind.rs index 75aa633d1471c..0aec770197584 100644 --- a/crates/red_knot_python_semantic/src/types/call/bind.rs +++ b/crates/red_knot_python_semantic/src/types/call/bind.rs @@ -125,10 +125,10 @@ pub(crate) fn bind_call<'db>( CallBinding { callable_ty, - return_ty: signature.return_ty.unwrap_or(Type::Unknown), + return_ty: signature.return_ty.unwrap_or(Type::unknown()), parameter_tys: parameter_tys .into_iter() - .map(|opt_ty| opt_ty.unwrap_or(Type::Unknown)) + .map(|opt_ty| opt_ty.unwrap_or(Type::unknown())) .collect(), errors, } diff --git a/crates/red_knot_python_semantic/src/types/class_base.rs b/crates/red_knot_python_semantic/src/types/class_base.rs index 7a441d3f9e263..1b4a35791453d 100644 --- a/crates/red_knot_python_semantic/src/types/class_base.rs +++ b/crates/red_knot_python_semantic/src/types/class_base.rs @@ -1,5 +1,5 @@ use crate::types::{ - todo_type, Class, ClassLiteralType, KnownClass, KnownInstanceType, TodoType, Type, + todo_type, Class, ClassLiteralType, DynamicType, KnownClass, KnownInstanceType, Type, }; use crate::Db; use itertools::Either; @@ -8,19 +8,25 @@ use itertools::Either; /// /// This is much more limited than the [`Type`] enum: /// all types that would be invalid to have as a class base are -/// transformed into [`ClassBase::Unknown`] +/// transformed into [`ClassBase::unknown`] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, salsa::Update)] pub enum ClassBase<'db> { - Any, - Unknown, - Todo(TodoType), + Dynamic(DynamicType), Class(Class<'db>), } impl<'db> ClassBase<'db> { + pub const fn any() -> Self { + Self::Dynamic(DynamicType::Any) + } + + pub const fn unknown() -> Self { + Self::Dynamic(DynamicType::Unknown) + } + pub const fn is_dynamic(self) -> bool { match self { - ClassBase::Any | ClassBase::Unknown | ClassBase::Todo(_) => true, + ClassBase::Dynamic(_) => true, ClassBase::Class(_) => false, } } @@ -34,9 +40,7 @@ impl<'db> ClassBase<'db> { impl std::fmt::Display for Display<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.base { - ClassBase::Any => f.write_str("Any"), - ClassBase::Todo(todo) => todo.fmt(f), - ClassBase::Unknown => f.write_str("Unknown"), + ClassBase::Dynamic(dynamic) => dynamic.fmt(f), ClassBase::Class(class) => write!(f, "", class.name(self.db)), } } @@ -50,7 +54,7 @@ impl<'db> ClassBase<'db> { KnownClass::Object .to_class_literal(db) .into_class_literal() - .map_or(Self::Unknown, |ClassLiteralType { class }| { + .map_or(Self::unknown(), |ClassLiteralType { class }| { Self::Class(class) }) } @@ -60,9 +64,7 @@ impl<'db> ClassBase<'db> { /// Return `None` if `ty` is not an acceptable type for a class base. pub(super) fn try_from_ty(db: &'db dyn Db, ty: Type<'db>) -> Option { match ty { - Type::Any => Some(Self::Any), - Type::Unknown => Some(Self::Unknown), - Type::Todo(todo) => Some(Self::Todo(todo)), + Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)), Type::ClassLiteral(ClassLiteralType { class }) => Some(Self::Class(class)), Type::Union(_) => None, // TODO -- forces consideration of multiple possible MROs? Type::Intersection(_) => None, // TODO -- probably incorrect? @@ -104,8 +106,8 @@ impl<'db> ClassBase<'db> { | KnownInstanceType::Not | KnownInstanceType::Intersection | KnownInstanceType::TypeOf => None, - KnownInstanceType::Unknown => Some(Self::Unknown), - KnownInstanceType::Any => Some(Self::Any), + KnownInstanceType::Unknown => Some(Self::unknown()), + KnownInstanceType::Any => Some(Self::any()), // TODO: Classes inheriting from `typing.Type` et al. also have `Generic` in their MRO KnownInstanceType::Dict => { Self::try_from_ty(db, KnownClass::Dict.to_class_literal(db)) @@ -150,7 +152,7 @@ impl<'db> ClassBase<'db> { pub(super) fn into_class(self) -> Option> { match self { Self::Class(class) => Some(class), - _ => None, + Self::Dynamic(_) => None, } } @@ -160,13 +162,7 @@ impl<'db> ClassBase<'db> { db: &'db dyn Db, ) -> Either>, impl Iterator>> { match self { - ClassBase::Any => Either::Left([ClassBase::Any, ClassBase::object(db)].into_iter()), - ClassBase::Unknown => { - Either::Left([ClassBase::Unknown, ClassBase::object(db)].into_iter()) - } - ClassBase::Todo(todo) => { - Either::Left([ClassBase::Todo(todo), ClassBase::object(db)].into_iter()) - } + ClassBase::Dynamic(_) => Either::Left([self, ClassBase::object(db)].into_iter()), ClassBase::Class(class) => Either::Right(class.iter_mro(db)), } } @@ -181,9 +177,7 @@ impl<'db> From> for ClassBase<'db> { impl<'db> From> for Type<'db> { fn from(value: ClassBase<'db>) -> Self { match value { - ClassBase::Any => Type::Any, - ClassBase::Todo(todo) => Type::Todo(todo), - ClassBase::Unknown => Type::Unknown, + ClassBase::Dynamic(dynamic) => Type::Dynamic(dynamic), ClassBase::Class(class) => Type::class_literal(class), } } diff --git a/crates/red_knot_python_semantic/src/types/display.rs b/crates/red_knot_python_semantic/src/types/display.rs index b0f49c4441bfd..50c3ae1fff8cb 100644 --- a/crates/red_knot_python_semantic/src/types/display.rs +++ b/crates/red_knot_python_semantic/src/types/display.rs @@ -65,9 +65,8 @@ struct DisplayRepresentation<'db> { impl Display for DisplayRepresentation<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self.ty { - Type::Any => f.write_str("Any"), + Type::Dynamic(dynamic) => dynamic.fmt(f), Type::Never => f.write_str("Never"), - Type::Unknown => f.write_str("Unknown"), Type::Instance(InstanceType { class }) => { let representation = match class.known(self.db) { Some(KnownClass::NoneType) => "None", @@ -76,9 +75,6 @@ impl Display for DisplayRepresentation<'_> { }; f.write_str(representation) } - // `[Type::Todo]`'s display should be explicit that is not a valid display of - // any other type - Type::Todo(todo) => write!(f, "@Todo{todo}"), Type::ModuleLiteral(module) => { write!(f, "", module.module(self.db).name()) } @@ -88,9 +84,7 @@ impl Display for DisplayRepresentation<'_> { // Only show the bare class name here; ClassBase::display would render this as // type[] instead of type[Foo]. ClassBase::Class(class) => write!(f, "type[{}]", class.name(self.db)), - base @ (ClassBase::Any | ClassBase::Todo(_) | ClassBase::Unknown) => { - write!(f, "type[{}]", base.display(self.db)) - } + ClassBase::Dynamic(dynamic) => write!(f, "type[{dynamic}]"), }, Type::KnownInstance(known_instance) => f.write_str(known_instance.repr(self.db)), Type::FunctionLiteral(function) => f.write_str(function.name(self.db)), diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index 915eb79e30feb..fd263a883619d 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -62,11 +62,11 @@ use crate::types::mro::MroErrorKind; use crate::types::unpacker::{UnpackResult, Unpacker}; use crate::types::{ bindings_ty, builtins_symbol, declarations_ty, global_symbol, symbol, todo_type, - typing_extensions_symbol, Boundness, CallDunderResult, Class, ClassLiteralType, FunctionType, - InstanceType, IntersectionBuilder, IntersectionType, IterationOutcome, KnownClass, - KnownFunction, KnownInstanceType, MetaclassCandidate, MetaclassErrorKind, SliceLiteralType, - SubclassOfType, Symbol, Truthiness, TupleType, Type, TypeAliasType, TypeArrayDisplay, - TypeVarBoundOrConstraints, TypeVarInstance, UnionBuilder, UnionType, + typing_extensions_symbol, Boundness, CallDunderResult, Class, ClassLiteralType, DynamicType, + FunctionType, InstanceType, IntersectionBuilder, IntersectionType, IterationOutcome, + KnownClass, KnownFunction, KnownInstanceType, MetaclassCandidate, MetaclassErrorKind, + SliceLiteralType, SubclassOfType, Symbol, Truthiness, TupleType, Type, TypeAliasType, + TypeArrayDisplay, TypeVarBoundOrConstraints, TypeVarInstance, UnionBuilder, UnionType, }; use crate::unpack::Unpack; use crate::util::subscript::{PyIndex, PySlice}; @@ -101,7 +101,7 @@ pub(crate) fn infer_scope_types<'db>(db: &'db dyn Db, scope: ScopeId<'db>) -> Ty TypeInferenceBuilder::new(db, InferenceRegion::Scope(scope), index).finish() } -/// Cycle recovery for [`infer_definition_types()`]: for now, just [`Type::Unknown`] +/// Cycle recovery for [`infer_definition_types()`]: for now, just [`Type::unknown`] /// TODO fixpoint iteration fn infer_definition_types_cycle_recovery<'db>( db: &'db dyn Db, @@ -112,10 +112,10 @@ fn infer_definition_types_cycle_recovery<'db>( let mut inference = TypeInference::empty(input.scope(db)); let category = input.category(db); if category.is_declaration() { - inference.declarations.insert(input, Type::Unknown); + inference.declarations.insert(input, Type::unknown()); } if category.is_binding() { - inference.bindings.insert(input, Type::Unknown); + inference.bindings.insert(input, Type::unknown()); } // TODO we don't fill in expression types for the cycle-participant definitions, which can // later cause a panic when looking up an expression type. @@ -842,7 +842,7 @@ impl<'db> TypeInferenceBuilder<'db> { let declarations = use_def.declarations_at_binding(binding); let mut bound_ty = ty; let declared_ty = declarations_ty(self.db(), declarations) - .map(|s| s.ignore_possibly_unbound().unwrap_or(Type::Unknown)) + .map(|s| s.ignore_possibly_unbound().unwrap_or(Type::unknown())) .unwrap_or_else(|(ty, conflicting)| { // TODO point out the conflicting declarations in the diagnostic? let symbol_table = self.index.symbol_table(binding.file_scope(self.db())); @@ -886,7 +886,7 @@ impl<'db> TypeInferenceBuilder<'db> { inferred_ty.display(self.db()) ), ); - Type::Unknown + Type::unknown() }; self.types.declarations.insert(declaration, ty); } @@ -916,7 +916,7 @@ impl<'db> TypeInferenceBuilder<'db> { node: AnyNodeRef, definition: Definition<'db>, ) { - self.add_declaration_with_binding(node, definition, Type::Unknown, Type::Unknown); + self.add_declaration_with_binding(node, definition, Type::unknown(), Type::unknown()); } fn infer_module(&mut self, module: &ast::ModModule) { @@ -1218,9 +1218,9 @@ impl<'db> TypeInferenceBuilder<'db> { ); } else { let ty = if let Some(default_ty) = default_ty { - UnionType::from_elements(self.db(), [Type::Unknown, default_ty]) + UnionType::from_elements(self.db(), [Type::unknown(), default_ty]) } else { - Type::Unknown + Type::unknown() }; self.add_binding(parameter.into(), definition, ty); } @@ -1487,7 +1487,7 @@ impl<'db> TypeInferenceBuilder<'db> { /// Infers the type of a context expression (`with expr`) and returns the target's type /// - /// Returns [`Type::Unknown`] if the context expression doesn't implement the context manager protocol. + /// Returns [`Type::unknown`] if the context expression doesn't implement the context manager protocol. /// /// ## Terminology /// See [PEP343](https://peps.python.org/pep-0343/#standard-terminology). @@ -1518,7 +1518,7 @@ impl<'db> TypeInferenceBuilder<'db> { context_expression_ty.display(self.db()) ), ); - Type::Unknown + Type::unknown() } (Symbol::Unbound, _) => { self.context.report_lint( @@ -1529,7 +1529,7 @@ impl<'db> TypeInferenceBuilder<'db> { context_expression_ty.display(self.db()) ), ); - Type::Unknown + Type::unknown() } (Symbol::Type(enter_ty, enter_boundness), exit) => { if enter_boundness == Boundness::PossiblyUnbound { @@ -1622,7 +1622,7 @@ impl<'db> TypeInferenceBuilder<'db> { // If there is no handled exception, it's invalid syntax; // a diagnostic will have already been emitted - let node_ty = node.map_or(Type::Unknown, |ty| self.infer_expression(ty)); + let node_ty = node.map_or(Type::unknown(), |ty| self.infer_expression(ty)); // 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`. @@ -1637,7 +1637,7 @@ impl<'db> TypeInferenceBuilder<'db> { if let Some(node) = node { report_invalid_exception_caught(&self.context, node, element); } - Type::Unknown + Type::unknown() }, ); } @@ -1652,7 +1652,7 @@ impl<'db> TypeInferenceBuilder<'db> { if let Some(node) = node { report_invalid_exception_caught(&self.context, node, node_ty); } - Type::Unknown + Type::unknown() } }; @@ -1953,11 +1953,11 @@ impl<'db> TypeInferenceBuilder<'db> { } let name_ast_id = name.scoped_expression_id(self.db(), self.scope()); - unpacked.get(name_ast_id).unwrap_or(Type::Unknown) + unpacked.get(name_ast_id).unwrap_or(Type::unknown()) } TargetKind::Name => { if self.in_stub() && value.is_ellipsis_literal_expr() { - Type::Unknown + Type::unknown() } else { value_ty } @@ -2113,7 +2113,7 @@ impl<'db> TypeInferenceBuilder<'db> { right_ty.display(self.db()) ), ); - Type::Unknown + Type::unknown() }); UnionType::from_elements( @@ -2142,7 +2142,7 @@ impl<'db> TypeInferenceBuilder<'db> { right_ty.display(self.db()) ), ); - Type::Unknown + Type::unknown() }) } @@ -2219,7 +2219,7 @@ impl<'db> TypeInferenceBuilder<'db> { self.context.extend(unpacked); } let name_ast_id = name.scoped_expression_id(self.db(), self.scope()); - unpacked.get(name_ast_id).unwrap_or(Type::Unknown) + unpacked.get(name_ast_id).unwrap_or(Type::unknown()) } TargetKind::Name => iterable_ty .iterate(self.db()) @@ -2672,7 +2672,7 @@ impl<'db> TypeInferenceBuilder<'db> { ast::Number::Float(_) => KnownClass::Float.to_instance(self.db()), ast::Number::Complex { .. } => builtins_symbol(self.db(), "complex") .ignore_possibly_unbound() - .unwrap_or(Type::Unknown) + .unwrap_or(Type::unknown()) .to_instance(self.db()), } } @@ -2759,7 +2759,7 @@ impl<'db> TypeInferenceBuilder<'db> { ) -> Type<'db> { builtins_symbol(self.db(), "Ellipsis") .ignore_possibly_unbound() - .unwrap_or(Type::Unknown) + .unwrap_or(Type::unknown()) } fn infer_tuple_expression(&mut self, tuple: &ast::ExprTuple) -> Type<'db> { @@ -3016,7 +3016,7 @@ impl<'db> TypeInferenceBuilder<'db> { // For syntactically invalid targets, we still need to run type inference: self.infer_expression(&named.target); self.infer_expression(&named.value); - Type::Unknown + Type::unknown() } } @@ -3267,7 +3267,7 @@ impl<'db> TypeInferenceBuilder<'db> { } Symbol::Unbound => { report_unresolved_reference(&self.context, name); - Type::Unknown + Type::unknown() } Symbol::Type(_, Boundness::Bound) => unreachable!("Handled above"), }, @@ -3279,7 +3279,7 @@ impl<'db> TypeInferenceBuilder<'db> { match name.ctx { ExprContext::Load => self.infer_name_load(name), ExprContext::Store | ExprContext::Del => Type::Never, - ExprContext::Invalid => Type::Unknown, + ExprContext::Invalid => Type::unknown(), } } @@ -3319,7 +3319,7 @@ impl<'db> TypeInferenceBuilder<'db> { attr.id ), ); - Type::Unknown + Type::unknown() } } } @@ -3340,7 +3340,7 @@ impl<'db> TypeInferenceBuilder<'db> { } ExprContext::Invalid => { self.infer_expression(value); - Type::Unknown + Type::unknown() } } } @@ -3355,10 +3355,8 @@ impl<'db> TypeInferenceBuilder<'db> { let operand_type = self.infer_expression(operand); match (op, operand_type) { - (_, Type::Any) => Type::Any, - (_, Type::Todo(_)) => operand_type, + (_, Type::Dynamic(_)) => operand_type, (_, Type::Never) => Type::Never, - (_, Type::Unknown) => Type::Unknown, (ast::UnaryOp::UAdd, Type::IntLiteral(value)) => Type::IntLiteral(value), (ast::UnaryOp::USub, Type::IntLiteral(value)) => Type::IntLiteral(-value), @@ -3428,7 +3426,7 @@ impl<'db> TypeInferenceBuilder<'db> { ), ); - Type::Unknown + Type::unknown() } } } @@ -3468,7 +3466,7 @@ impl<'db> TypeInferenceBuilder<'db> { right_ty.display(self.db()) ), ); - Type::Unknown + Type::unknown() }) } @@ -3479,12 +3477,15 @@ impl<'db> TypeInferenceBuilder<'db> { op: ast::Operator, ) -> Option> { match (left_ty, right_ty, op) { - // When interacting with Todo, Any and Unknown should propagate (as if we fix this - // `Todo` in the future, the result would then become Any or Unknown, respectively.) - (Type::Any, _, _) | (_, Type::Any, _) => Some(Type::Any), - (todo @ Type::Todo(_), _, _) | (_, todo @ Type::Todo(_), _) => Some(todo), + // Non-todo Anys take precedence over Todos (as if we fix this `Todo` in the future, + // the result would then become Any or Unknown, respectively). + (any @ Type::Dynamic(DynamicType::Any), _, _) + | (_, any @ Type::Dynamic(DynamicType::Any), _) => Some(any), + (unknown @ Type::Dynamic(DynamicType::Unknown), _, _) + | (_, unknown @ Type::Dynamic(DynamicType::Unknown), _) => Some(unknown), + (todo @ Type::Dynamic(DynamicType::Todo(_)), _, _) + | (_, todo @ Type::Dynamic(DynamicType::Todo(_)), _) => Some(todo), (Type::Never, _, _) | (_, Type::Never, _) => Some(Type::Never), - (Type::Unknown, _, _) | (_, Type::Unknown, _) => Some(Type::Unknown), (Type::IntLiteral(n), Type::IntLiteral(m), ast::Operator::Add) => Some( n.checked_add(m) @@ -3827,7 +3828,7 @@ impl<'db> TypeInferenceBuilder<'db> { | ast::CmpOp::Is | ast::CmpOp::IsNot => KnownClass::Bool.to_instance(self.db()), // Other operators can return arbitrary types - _ => Type::Unknown, + _ => Type::unknown(), } }) }), @@ -4151,7 +4152,7 @@ impl<'db> TypeInferenceBuilder<'db> { ).expect("infer_binary_type_comparison should never return None for `CmpOp::Eq`"); match eq_result { - todo @ Type::Todo(_) => return Ok(todo), + todo @ Type::Dynamic(DynamicType::Todo(_)) => return Ok(todo), ty => match ty.bool(self.db()) { Truthiness::AlwaysTrue => eq_count += 1, Truthiness::AlwaysFalse => not_eq_count += 1, @@ -4176,7 +4177,7 @@ impl<'db> TypeInferenceBuilder<'db> { ); Ok(match eq_result { - todo @ Type::Todo(_) => todo, + todo @ Type::Dynamic(DynamicType::Todo(_)) => todo, ty => match ty.bool(self.db()) { Truthiness::AlwaysFalse => Type::BooleanLiteral(op.is_is_not()), _ => KnownClass::Bool.to_instance(self.db()), @@ -4258,7 +4259,7 @@ impl<'db> TypeInferenceBuilder<'db> { match pairwise_eq_result { // If propagation is required, return the result as is - todo @ Type::Todo(_) => return Ok(todo), + todo @ Type::Dynamic(DynamicType::Todo(_)) => return Ok(todo), ty => match ty.bool(self.db()) { // - AlwaysTrue : Continue to the next pair for lexicographic comparison Truthiness::AlwaysTrue => continue, @@ -4357,7 +4358,7 @@ impl<'db> TypeInferenceBuilder<'db> { elements.len(), int, ); - Type::Unknown + Type::unknown() }) } // Ex) Given `("a", 1, Null)[0:2]`, return `("a", 1)` @@ -4369,7 +4370,7 @@ impl<'db> TypeInferenceBuilder<'db> { TupleType::from_elements(self.db(), new_elements) } else { report_slice_step_size_zero(&self.context, value_node.into()); - Type::Unknown + Type::unknown() } } // Ex) Given `"value"[1]`, return `"a"` @@ -4390,7 +4391,7 @@ impl<'db> TypeInferenceBuilder<'db> { literal_value.chars().count(), int, ); - Type::Unknown + Type::unknown() }) } // Ex) Given `"value"[1:3]`, return `"al"` @@ -4404,7 +4405,7 @@ impl<'db> TypeInferenceBuilder<'db> { Type::string_literal(self.db(), &literal) } else { report_slice_step_size_zero(&self.context, value_node.into()); - Type::Unknown + Type::unknown() }; result } @@ -4426,7 +4427,7 @@ impl<'db> TypeInferenceBuilder<'db> { literal_value.len(), int, ); - Type::Unknown + Type::unknown() }) } // Ex) Given `b"value"[1:3]`, return `b"al"` @@ -4439,7 +4440,7 @@ impl<'db> TypeInferenceBuilder<'db> { Type::bytes_literal(self.db(), &new_bytes) } else { report_slice_step_size_zero(&self.context, value_node.into()); - Type::Unknown + Type::unknown() } } // Ex) Given `"value"[True]`, return `"a"` @@ -4555,7 +4556,7 @@ impl<'db> TypeInferenceBuilder<'db> { ); } - Type::Unknown + Type::unknown() } } } @@ -4670,7 +4671,7 @@ impl<'db> TypeInferenceBuilder<'db> { bytes.into(), format_args!("Type expressions cannot use bytes literal"), ); - Type::Unknown + Type::unknown() } ast::Expr::FString(fstring) => { @@ -4680,7 +4681,7 @@ impl<'db> TypeInferenceBuilder<'db> { format_args!("Type expressions cannot use f-strings"), ); self.infer_fstring_expression(fstring); - Type::Unknown + Type::unknown() } // All other annotation expressions are (possibly) valid type expressions, so handle @@ -4703,7 +4704,7 @@ impl<'db> TypeInferenceBuilder<'db> { DeferredExpressionState::InStringAnnotation, ) } - None => Type::Unknown, + None => Type::unknown(), } } } @@ -4751,7 +4752,7 @@ impl<'db> TypeInferenceBuilder<'db> { .infer_name_expression(name) .in_type_expression(self.db()) .unwrap_or_else(|error| error.into_fallback_type(&self.context, expression)), - ast::ExprContext::Invalid => Type::Unknown, + ast::ExprContext::Invalid => Type::unknown(), ast::ExprContext::Store | ast::ExprContext::Del => todo_type!(), }, @@ -4760,7 +4761,7 @@ impl<'db> TypeInferenceBuilder<'db> { .infer_attribute_expression(attribute_expression) .in_type_expression(self.db()) .unwrap_or_else(|error| error.into_fallback_type(&self.context, expression)), - ast::ExprContext::Invalid => Type::Unknown, + ast::ExprContext::Invalid => Type::unknown(), ast::ExprContext::Store | ast::ExprContext::Del => todo_type!(), }, @@ -4813,7 +4814,7 @@ impl<'db> TypeInferenceBuilder<'db> { // anything else is an invalid annotation: _ => { self.infer_binary_expression(binary); - Type::Unknown + Type::unknown() } } } @@ -4826,90 +4827,90 @@ impl<'db> TypeInferenceBuilder<'db> { // Avoid inferring the types of invalid type expressions that have been parsed from a // string annotation, as they are not present in the semantic index. - _ if self.deferred_state.in_string_annotation() => Type::Unknown, + _ if self.deferred_state.in_string_annotation() => Type::unknown(), // Forms which are invalid in the context of annotation expressions: we infer their // nested expressions as normal expressions, but the type of the top-level expression is - // always `Type::Unknown` in these cases. + // always `Type::unknown` in these cases. ast::Expr::BoolOp(bool_op) => { self.infer_boolean_expression(bool_op); - Type::Unknown + Type::unknown() } ast::Expr::Named(named) => { self.infer_named_expression(named); - Type::Unknown + Type::unknown() } ast::Expr::UnaryOp(unary) => { self.infer_unary_expression(unary); - Type::Unknown + Type::unknown() } ast::Expr::Lambda(lambda_expression) => { self.infer_lambda_expression(lambda_expression); - Type::Unknown + Type::unknown() } ast::Expr::If(if_expression) => { self.infer_if_expression(if_expression); - Type::Unknown + Type::unknown() } ast::Expr::Dict(dict) => { self.infer_dict_expression(dict); - Type::Unknown + Type::unknown() } ast::Expr::Set(set) => { self.infer_set_expression(set); - Type::Unknown + Type::unknown() } ast::Expr::ListComp(listcomp) => { self.infer_list_comprehension_expression(listcomp); - Type::Unknown + Type::unknown() } ast::Expr::SetComp(setcomp) => { self.infer_set_comprehension_expression(setcomp); - Type::Unknown + Type::unknown() } ast::Expr::DictComp(dictcomp) => { self.infer_dict_comprehension_expression(dictcomp); - Type::Unknown + Type::unknown() } ast::Expr::Generator(generator) => { self.infer_generator_expression(generator); - Type::Unknown + Type::unknown() } ast::Expr::Await(await_expression) => { self.infer_await_expression(await_expression); - Type::Unknown + Type::unknown() } ast::Expr::Yield(yield_expression) => { self.infer_yield_expression(yield_expression); - Type::Unknown + Type::unknown() } ast::Expr::YieldFrom(yield_from) => { self.infer_yield_from_expression(yield_from); - Type::Unknown + Type::unknown() } ast::Expr::Compare(compare) => { self.infer_compare_expression(compare); - Type::Unknown + Type::unknown() } ast::Expr::Call(call_expr) => { self.infer_call_expression(call_expr); - Type::Unknown + Type::unknown() } ast::Expr::FString(fstring) => { self.infer_fstring_expression(fstring); - Type::Unknown + Type::unknown() } ast::Expr::List(list) => { self.infer_list_expression(list); - Type::Unknown + Type::unknown() } ast::Expr::Tuple(tuple) => { self.infer_tuple_expression(tuple); - Type::Unknown + Type::unknown() } ast::Expr::Slice(slice) => { self.infer_slice_expression(slice); - Type::Unknown + Type::unknown() } ast::Expr::IpyEscapeCommand(_) => todo!("Implement Ipy escape command support"), } @@ -4925,7 +4926,7 @@ impl<'db> TypeInferenceBuilder<'db> { DeferredExpressionState::InStringAnnotation, ) } - None => Type::Unknown, + None => Type::unknown(), } } @@ -5023,7 +5024,7 @@ impl<'db> TypeInferenceBuilder<'db> { slice.into(), format_args!("type[...] must have exactly one type argument"), ); - Type::Unknown + Type::unknown() } ast::Expr::Subscript(ast::ExprSubscript { value, @@ -5076,7 +5077,7 @@ impl<'db> TypeInferenceBuilder<'db> { Type::KnownInstance(known_instance) => { self.infer_parameterized_known_instance_type_expression(subscript, known_instance) } - Type::Todo(_) => { + Type::Dynamic(DynamicType::Todo(_)) => { self.infer_type_expression(slice); value_ty } @@ -5125,7 +5126,7 @@ impl<'db> TypeInferenceBuilder<'db> { let [type_expr, metadata @ ..] = &arguments[..] else { self.infer_type_expression(arguments_slice); - return Type::Unknown; + return Type::unknown(); }; for element in metadata { @@ -5150,7 +5151,7 @@ impl<'db> TypeInferenceBuilder<'db> { ), ); } - Type::Unknown + Type::unknown() } } } @@ -5193,7 +5194,7 @@ impl<'db> TypeInferenceBuilder<'db> { known_instance.repr(self.db()) ), ); - Type::Unknown + Type::unknown() } _ => { let argument_type = self.infer_type_expression(arguments_slice); @@ -5222,7 +5223,7 @@ impl<'db> TypeInferenceBuilder<'db> { known_instance.repr(self.db()) ), ); - Type::Unknown + Type::unknown() } _ => { // NB: This calls `infer_expression` instead of `infer_type_expression`. @@ -5314,7 +5315,7 @@ impl<'db> TypeInferenceBuilder<'db> { known_instance.repr(self.db()) ), ); - Type::Unknown + Type::unknown() } KnownInstanceType::TypingSelf | KnownInstanceType::TypeAlias @@ -5327,7 +5328,7 @@ impl<'db> TypeInferenceBuilder<'db> { known_instance.repr(self.db()) ), ); - Type::Unknown + Type::unknown() } KnownInstanceType::LiteralString => { self.context.report_lint( @@ -5338,7 +5339,7 @@ impl<'db> TypeInferenceBuilder<'db> { known_instance.repr(self.db()) ), ); - Type::Unknown + Type::unknown() } KnownInstanceType::Type => self.infer_subclass_of_type_expression(arguments_slice), KnownInstanceType::Tuple => self.infer_tuple_type_expression(arguments_slice), @@ -5361,7 +5362,7 @@ impl<'db> TypeInferenceBuilder<'db> { self.store_expression_type(parameters, ty); ty } else { - self.store_expression_type(parameters, Type::Unknown); + self.store_expression_type(parameters, Type::unknown()); return Err(vec![parameters]); } @@ -5388,7 +5389,7 @@ impl<'db> TypeInferenceBuilder<'db> { union_type } else { - self.store_expression_type(parameters, Type::Unknown); + self.store_expression_type(parameters, Type::unknown()); return Err(errors); } @@ -5408,7 +5409,7 @@ impl<'db> TypeInferenceBuilder<'db> { value_ty .member(self.db(), &attr.id) .ignore_possibly_unbound() - .unwrap_or(Type::Unknown) + .unwrap_or(Type::unknown()) } // for negative and positive numbers ast::Expr::UnaryOp(ref u) @@ -5715,7 +5716,7 @@ fn perform_membership_test_comparison<'db>( compare_result_opt .map(|ty| { - if matches!(ty, Type::Todo(_)) { + if matches!(ty, Type::Dynamic(DynamicType::Todo(_))) { return ty; } diff --git a/crates/red_knot_python_semantic/src/types/mro.rs b/crates/red_knot_python_semantic/src/types/mro.rs index 2f447f1582d3d..a20c591656ccf 100644 --- a/crates/red_knot_python_semantic/src/types/mro.rs +++ b/crates/red_knot_python_semantic/src/types/mro.rs @@ -34,7 +34,7 @@ impl<'db> Mro<'db> { pub(super) fn from_error(db: &'db dyn Db, class: Class<'db>) -> Self { Self::from([ ClassBase::Class(class), - ClassBase::Unknown, + ClassBase::unknown(), ClassBase::object(db), ]) } diff --git a/crates/red_knot_python_semantic/src/types/subclass_of.rs b/crates/red_knot_python_semantic/src/types/subclass_of.rs index 75d82e2c3419e..fe5cc5cd98629 100644 --- a/crates/red_knot_python_semantic/src/types/subclass_of.rs +++ b/crates/red_knot_python_semantic/src/types/subclass_of.rs @@ -22,9 +22,7 @@ impl<'db> SubclassOfType<'db> { pub(crate) fn from(db: &'db dyn Db, subclass_of: impl Into>) -> Type<'db> { let subclass_of = subclass_of.into(); match subclass_of { - ClassBase::Any | ClassBase::Unknown | ClassBase::Todo(_) => { - Type::SubclassOf(Self { subclass_of }) - } + ClassBase::Dynamic(_) => Type::SubclassOf(Self { subclass_of }), ClassBase::Class(class) => { if class.is_final(db) { Type::ClassLiteral(ClassLiteralType { class }) @@ -40,14 +38,14 @@ impl<'db> SubclassOfType<'db> { /// Return a [`Type`] instance representing the type `type[Unknown]`. pub(crate) const fn subclass_of_unknown() -> Type<'db> { Type::SubclassOf(SubclassOfType { - subclass_of: ClassBase::Unknown, + subclass_of: ClassBase::unknown(), }) } /// Return a [`Type`] instance representing the type `type[Any]`. pub(crate) const fn subclass_of_any() -> Type<'db> { Type::SubclassOf(SubclassOfType { - subclass_of: ClassBase::Any, + subclass_of: ClassBase::any(), }) } @@ -77,8 +75,7 @@ impl<'db> SubclassOfType<'db> { pub(crate) fn is_subtype_of(self, db: &'db dyn Db, other: SubclassOfType<'db>) -> bool { match (self.subclass_of, other.subclass_of) { // Non-fully-static types do not participate in subtyping - (ClassBase::Any | ClassBase::Unknown | ClassBase::Todo(_), _) - | (_, ClassBase::Any | ClassBase::Unknown | ClassBase::Todo(_)) => false, + (ClassBase::Dynamic(_), _) | (_, ClassBase::Dynamic(_)) => false, // For example, `type[bool]` describes all possible runtime subclasses of the class `bool`, // and `type[int]` describes all possible runtime subclasses of the class `int`. diff --git a/crates/red_knot_python_semantic/src/types/unpacker.rs b/crates/red_knot_python_semantic/src/types/unpacker.rs index 9d32c02be98c6..ba61b8d9f1065 100644 --- a/crates/red_knot_python_semantic/src/types/unpacker.rs +++ b/crates/red_knot_python_semantic/src/types/unpacker.rs @@ -52,7 +52,7 @@ impl<'db> Unpacker<'db> { .node_ref(self.db()) .is_ellipsis_literal_expr() { - value_ty = Type::Unknown; + value_ty = Type::unknown(); } if value.is_iterable() { // If the value is an iterable, then the type that needs to be unpacked is the iterator @@ -164,7 +164,7 @@ impl<'db> Unpacker<'db> { for (index, element) in elts.iter().enumerate() { // SAFETY: `target_types` is initialized with the same length as `elts`. let element_ty = match target_types[index].as_slice() { - [] => Type::Unknown, + [] => Type::unknown(), types => UnionType::from_elements(self.db(), types), }; self.unpack_inner(element, element_ty); @@ -241,7 +241,7 @@ impl<'db> Unpacker<'db> { // Subtract 1 to insert the starred expression type at the correct // index. - element_types.resize(targets.len() - 1, Type::Unknown); + element_types.resize(targets.len() - 1, Type::unknown()); // TODO: This should be `list[Unknown]` element_types.insert(starred_index, todo_type!("starred unpacking"));