From 8880a3ef99890abb4e28715ba5ef5ddee30601a3 Mon Sep 17 00:00:00 2001 From: Douglas Creager Date: Thu, 9 Jan 2025 15:16:39 -0500 Subject: [PATCH 01/10] Consolidate all gradual types into single Type variant --- crates/red_knot_python_semantic/src/types.rs | 162 ++++++++------- .../src/types/builder.rs | 50 ++--- .../src/types/call.rs | 10 +- .../src/types/call/bind.rs | 4 +- .../src/types/class_base.rs | 45 ++--- .../src/types/display.rs | 10 +- .../src/types/infer.rs | 189 +++++++++--------- .../red_knot_python_semantic/src/types/mro.rs | 2 +- .../src/types/subclass_of.rs | 11 +- .../src/types/unpacker.rs | 6 +- 10 files changed, 243 insertions(+), 246 deletions(-) diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index 0f5e8294f749a..fe9b5418437f7 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -475,10 +475,14 @@ impl std::fmt::Display for TodoType { #[cfg(debug_assertions)] macro_rules! todo_type { () => { - Type::Todo(crate::types::TodoType::FileAndLine(file!(), line!())) + $crate::types::Type::Any($crate::types::AnyType::Todo( + crate::types::TodoType::FileAndLine(file!(), line!()), + )) }; ($message:literal) => { - Type::Todo(crate::types::TodoType::Message($message)) + $crate::types::Type::Any($crate::types::AnyType::Todo( + crate::types::TodoType::Message($message), + )) }; } @@ -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), + Any(AnyType), /// 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 annotated_any() -> Self { + Self::Any(AnyType::Annotated) + } + + pub const fn unknown() -> Self { + Self::Any(AnyType::Unknown) + } + pub const fn is_unknown(&self) -> bool { - matches!(self, Type::Unknown) + matches!(self, Type::Any(AnyType::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::Any(AnyType::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::Any(_), _) | (_, Type::Any(_)) => { 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::Any(_), _) => true, + (_, Type::Any(_)) => true, // All types are assignable to `object`. // TODO this special case might be removable once the below cases are comprehensive @@ -1085,9 +1082,9 @@ 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::Any(AnyType::Annotated), Type::Any(AnyType::Annotated)) + | (Type::Any(AnyType::Unknown), Type::Any(AnyType::Unknown)) + | (Type::Any(AnyType::Todo(_)), Type::Any(AnyType::Todo(_))) ) } @@ -1099,9 +1096,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::Any(_), _) | (_, Type::Any(_)) => false, (Type::Union(union), other) | (other, Type::Union(union)) => union .elements(db) @@ -1181,7 +1176,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::Any(_) => false, ClassBase::Class(class_a) => !class_b.is_subclass_of(db, class_a), }, @@ -1377,7 +1372,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::Any(_) => false, Type::Never | Type::FunctionLiteral(..) | Type::ModuleLiteral(..) @@ -1440,10 +1435,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::Any(_) | Type::Never - | Type::Unknown - | Type::Todo(_) | Type::IntLiteral(..) | Type::StringLiteral(..) | Type::BytesLiteral(..) @@ -1553,10 +1546,8 @@ impl<'db> Type<'db> { None => false, }, - Type::Any + Type::Any(_) | Type::Never - | Type::Unknown - | Type::Todo(_) | Type::Union(..) | Type::Intersection(..) | Type::LiteralString @@ -1577,7 +1568,7 @@ impl<'db> Type<'db> { } match self { - Type::Any | Type::Unknown | Type::Todo(_) => self.into(), + Type::Any(_) => self.into(), Type::Never => todo_type!("attribute lookup on Never").into(), @@ -1702,7 +1693,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::Any(_) | Type::Never => Truthiness::Ambiguous, Type::FunctionLiteral(_) => Truthiness::AlwaysTrue, Type::ModuleLiteral(_) => Truthiness::AlwaysTrue, Type::ClassLiteral(ClassLiteralType { class }) => { @@ -1836,7 +1827,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 +1863,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 +1871,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 +1886,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 +1964,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::Any(_) => CallOutcome::callable(CallBinding::from_return_ty(self)), Type::Union(union) => CallOutcome::union( self, @@ -2083,16 +2072,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::Any(_) => *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::Any(any) => Type::Any(any), }, Type::Union(union) => union.map(db, |element| element.to_instance(db)), Type::Intersection(_) => todo_type!("Type::Intersection.to_instance()"), @@ -2110,7 +2095,7 @@ impl<'db> Type<'db> { | Type::Tuple(_) | Type::LiteralString | Type::AlwaysTruthy - | Type::AlwaysFalsy => Type::Unknown, + | Type::AlwaysFalsy => Type::unknown(), } } @@ -2177,7 +2162,7 @@ impl<'db> Type<'db> { }) } } - Type::Unknown => Ok(Type::Unknown), + Type::Any(_) => 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 +2170,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::annotated_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 +2244,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::Any(_) => *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::Any(any) => SubclassOfType::from(db, ClassBase::Any(*any)), // TODO intersections Type::Intersection(_) => SubclassOfType::from( db, @@ -2277,7 +2260,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 +2320,36 @@ impl<'db> From<&Type<'db>> for Symbol<'db> { } } +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +pub enum AnyType { + // An explicitly annotated `typing.Any` + Annotated, + // An unannotated value + 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 AnyType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AnyType::Annotated => f.write_str("Any"), + AnyType::Unknown => f.write_str("Unknown"), + // `[Type::Todo]`'s display should be explicit that is not a valid display of + // any other type + AnyType::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 +2492,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 +3102,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 +3662,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 +3728,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::Any(_) => return Type::from(superclass).member(db, name), ClassBase::Class(class) => { let member = class.own_class_member(db, name); if !member.is_unbound() { @@ -4059,9 +4069,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::annotated_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 8d5a559f865e6..3cb44ed35e878 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::Any(_) => { // 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. @@ -479,7 +479,7 @@ mod tests { fn build_union_no_simplify_unknown() { let db = setup_db(); let t0 = KnownClass::Str.to_instance(&db); - let t1 = Type::Unknown; + let t1 = Type::unknown(); let u0 = UnionType::from_elements(&db, [t0, t1]); let u1 = UnionType::from_elements(&db, [t1, t0]); @@ -491,7 +491,7 @@ mod tests { fn build_union_simplify_multiple_unknown() { let db = setup_db(); let t0 = KnownClass::Str.to_instance(&db); - let t1 = Type::Unknown; + let t1 = Type::unknown(); let u = UnionType::from_elements(&db, [t0, t1, t1]); @@ -504,7 +504,7 @@ mod tests { let str_ty = KnownClass::Str.to_instance(&db); let int_ty = KnownClass::Int.to_instance(&db); let object_ty = KnownClass::Object.to_instance(&db); - let unknown_ty = Type::Unknown; + let unknown_ty = Type::unknown(); let u0 = UnionType::from_elements(&db, [str_ty, unknown_ty, int_ty, object_ty]); @@ -525,7 +525,7 @@ mod tests { fn build_intersection() { let db = setup_db(); let t0 = Type::IntLiteral(0); - let ta = Type::Any; + let ta = Type::annotated_any(); let intersection = IntersectionBuilder::new(&db) .add_positive(ta) .add_negative(t0) @@ -548,7 +548,7 @@ mod tests { #[test] fn build_intersection_flatten_positive() { let db = setup_db(); - let ta = Type::Any; + let ta = Type::annotated_any(); let t1 = Type::IntLiteral(1); let t2 = Type::IntLiteral(2); let i0 = IntersectionBuilder::new(&db) @@ -568,7 +568,7 @@ mod tests { #[test] fn build_intersection_flatten_negative() { let db = setup_db(); - let ta = Type::Any; + let ta = Type::annotated_any(); let t1 = Type::IntLiteral(1); let t2 = KnownClass::Int.to_instance(&db); // i0 = Any & ~Literal[1] @@ -594,13 +594,13 @@ mod tests { let db = setup_db(); let ty = IntersectionBuilder::new(&db) - .add_negative(Type::Any) + .add_negative(Type::annotated_any()) .build(); - assert_eq!(ty, Type::Any); + assert_eq!(ty, Type::annotated_any()); let ty = IntersectionBuilder::new(&db) .add_positive(Type::Never) - .add_negative(Type::Any) + .add_negative(Type::annotated_any()) .build(); assert_eq!(ty, Type::Never); } @@ -610,32 +610,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() ); @@ -646,7 +646,7 @@ mod tests { let db = setup_db(); let t0 = Type::IntLiteral(0); let t1 = Type::IntLiteral(1); - let ta = Type::Any; + let ta = Type::annotated_any(); let u0 = UnionType::from_elements(&db, [t0, t1]); let union = IntersectionBuilder::new(&db) @@ -1049,8 +1049,8 @@ mod tests { assert_eq!(ty, Type::BooleanLiteral(!bool_value)); } - #[test_case(Type::Any)] - #[test_case(Type::Unknown)] + #[test_case(Type::annotated_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..c2ee2aa1bf998 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, AnyType, Class, ClassLiteralType, 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), + Any(AnyType), Class(Class<'db>), } impl<'db> ClassBase<'db> { + pub const fn annotated_any() -> Self { + Self::Any(AnyType::Annotated) + } + + pub const fn unknown() -> Self { + Self::Any(AnyType::Unknown) + } + pub const fn is_dynamic(self) -> bool { match self { - ClassBase::Any | ClassBase::Unknown | ClassBase::Todo(_) => true, + ClassBase::Any(_) => 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::Any(any) => any.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::Any(AnyType::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::Any(any) => Some(Self::Any(any)), 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::Any(AnyType::Unknown)), + KnownInstanceType::Any => Some(Self::Any(AnyType::Annotated)), // 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)) @@ -148,6 +150,7 @@ impl<'db> ClassBase<'db> { } pub(super) fn into_class(self) -> Option> { + #[allow(clippy::match_wildcard_for_single_variants)] match self { Self::Class(class) => Some(class), _ => None, @@ -160,13 +163,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::Any(_) => Either::Left([self, ClassBase::object(db)].into_iter()), ClassBase::Class(class) => Either::Right(class.iter_mro(db)), } } @@ -181,9 +178,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::Any(any) => Type::Any(any), 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..6f0370689ea96 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::Any(any) => any.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::Any(any) => write!(f, "type[{any}]"), }, 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..b450a30fdb2fe 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, AnyType, 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, }; 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::Any(_)) => 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::Any(AnyType::Annotated), _, _) + | (_, any @ Type::Any(AnyType::Annotated), _) => Some(any), + (unknown @ Type::Any(AnyType::Unknown), _, _) + | (_, unknown @ Type::Any(AnyType::Unknown), _) => Some(unknown), + (todo @ Type::Any(AnyType::Todo(_)), _, _) + | (_, todo @ Type::Any(AnyType::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::Any(AnyType::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::Any(AnyType::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::Any(AnyType::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::Any(AnyType::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::Any(AnyType::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..d69b8ca15ad6b 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::Any(_) => 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::annotated_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::Any(_), _) | (_, ClassBase::Any(_)) => 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")); From 9d49bd8d60e2c11f97273222ee54c9523d44a7ef Mon Sep 17 00:00:00 2001 From: Douglas Creager Date: Thu, 9 Jan 2025 15:29:38 -0500 Subject: [PATCH 02/10] Fix test cases --- crates/red_knot_python_semantic/src/types.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index fe9b5418437f7..64ed590608543 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -489,10 +489,10 @@ macro_rules! todo_type { #[cfg(not(debug_assertions))] macro_rules! todo_type { () => { - Type::Todo(crate::types::TodoType) + $crate::types::Type::Any($crate::types::AnyType::Todo(crate::types::TodoType)) }; ($message:literal) => { - Type::Todo(crate::types::TodoType) + $crate::types::Type::Any($crate::types::AnyType::Todo(crate::types::TodoType)) }; } From cb070cb5fe08aa4f6914c35987050aa1b8fd3106 Mon Sep 17 00:00:00 2001 From: Douglas Creager Date: Thu, 9 Jan 2025 16:26:40 -0500 Subject: [PATCH 03/10] =?UTF-8?q?Rename=20Any=20=E2=86=92=20Gradual?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/red_knot_python_semantic/src/types.rs | 75 ++++++++++--------- .../src/types/builder.rs | 2 +- .../src/types/class_base.rs | 24 +++--- .../src/types/display.rs | 4 +- .../src/types/infer.rs | 34 ++++----- .../src/types/subclass_of.rs | 4 +- 6 files changed, 75 insertions(+), 68 deletions(-) diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index 64ed590608543..5f440491f9ce5 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -475,12 +475,12 @@ impl std::fmt::Display for TodoType { #[cfg(debug_assertions)] macro_rules! todo_type { () => { - $crate::types::Type::Any($crate::types::AnyType::Todo( + $crate::types::Type::Gradual($crate::types::GradualType::Todo( crate::types::TodoType::FileAndLine(file!(), line!()), )) }; ($message:literal) => { - $crate::types::Type::Any($crate::types::AnyType::Todo( + $crate::types::Type::Gradual($crate::types::GradualType::Todo( crate::types::TodoType::Message($message), )) }; @@ -489,10 +489,10 @@ macro_rules! todo_type { #[cfg(not(debug_assertions))] macro_rules! todo_type { () => { - $crate::types::Type::Any($crate::types::AnyType::Todo(crate::types::TodoType)) + $crate::types::Type::Gradual($crate::types::GradualType::Todo(crate::types::TodoType)) }; ($message:literal) => { - $crate::types::Type::Any($crate::types::AnyType::Todo(crate::types::TodoType)) + $crate::types::Type::Gradual($crate::types::GradualType::Todo(crate::types::TodoType)) }; } @@ -502,7 +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(AnyType), + Gradual(GradualType), /// The empty set of values Never, /// A specific function object @@ -547,15 +547,15 @@ pub enum Type<'db> { impl<'db> Type<'db> { pub const fn annotated_any() -> Self { - Self::Any(AnyType::Annotated) + Self::Gradual(GradualType::Annotated) } pub const fn unknown() -> Self { - Self::Any(AnyType::Unknown) + Self::Gradual(GradualType::Unknown) } pub const fn is_unknown(&self) -> bool { - matches!(self, Type::Any(AnyType::Unknown)) + matches!(self, Type::Gradual(GradualType::Unknown)) } pub const fn is_never(&self) -> bool { @@ -563,7 +563,7 @@ impl<'db> Type<'db> { } pub const fn is_todo(&self) -> bool { - matches!(self, Type::Any(AnyType::Todo(_))) + matches!(self, Type::Gradual(GradualType::Todo(_))) } pub const fn class_literal(class: Class<'db>) -> Self { @@ -755,7 +755,7 @@ impl<'db> Type<'db> { match (self, target) { // We should have handled these immediately above. - (Type::Any(_), _) | (_, Type::Any(_)) => { + (Type::Gradual(_), _) | (_, Type::Gradual(_)) => { unreachable!("Non-fully-static types do not participate in subtyping!") } @@ -972,8 +972,8 @@ impl<'db> Type<'db> { (Type::Never, _) => true, // The dynamic type is assignable-to and assignable-from any type. - (Type::Any(_), _) => true, - (_, Type::Any(_)) => true, + (Type::Gradual(_), _) => true, + (_, Type::Gradual(_)) => true, // All types are assignable to `object`. // TODO this special case might be removable once the below cases are comprehensive @@ -1082,9 +1082,16 @@ impl<'db> Type<'db> { pub(crate) fn is_same_gradual_form(self, other: Type<'db>) -> bool { matches!( (self, other), - (Type::Any(AnyType::Annotated), Type::Any(AnyType::Annotated)) - | (Type::Any(AnyType::Unknown), Type::Any(AnyType::Unknown)) - | (Type::Any(AnyType::Todo(_)), Type::Any(AnyType::Todo(_))) + ( + Type::Gradual(GradualType::Annotated), + Type::Gradual(GradualType::Annotated) + ) | ( + Type::Gradual(GradualType::Unknown), + Type::Gradual(GradualType::Unknown) + ) | ( + Type::Gradual(GradualType::Todo(_)), + Type::Gradual(GradualType::Todo(_)) + ) ) } @@ -1096,7 +1103,7 @@ impl<'db> Type<'db> { match (self, other) { (Type::Never, _) | (_, Type::Never) => true, - (Type::Any(_), _) | (_, Type::Any(_)) => false, + (Type::Gradual(_), _) | (_, Type::Gradual(_)) => false, (Type::Union(union), other) | (other, Type::Union(union)) => union .elements(db) @@ -1176,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(_) => false, + ClassBase::Gradual(_) => false, ClassBase::Class(class_a) => !class_b.is_subclass_of(db, class_a), }, @@ -1372,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(_) => false, + Type::Gradual(_) => false, Type::Never | Type::FunctionLiteral(..) | Type::ModuleLiteral(..) @@ -1435,7 +1442,7 @@ 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::Gradual(_) | Type::Never | Type::IntLiteral(..) | Type::StringLiteral(..) @@ -1546,7 +1553,7 @@ impl<'db> Type<'db> { None => false, }, - Type::Any(_) + Type::Gradual(_) | Type::Never | Type::Union(..) | Type::Intersection(..) @@ -1568,7 +1575,7 @@ impl<'db> Type<'db> { } match self { - Type::Any(_) => self.into(), + Type::Gradual(_) => self.into(), Type::Never => todo_type!("attribute lookup on Never").into(), @@ -1693,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::Never => Truthiness::Ambiguous, + Type::Gradual(_) | Type::Never => Truthiness::Ambiguous, Type::FunctionLiteral(_) => Truthiness::AlwaysTrue, Type::ModuleLiteral(_) => Truthiness::AlwaysTrue, Type::ClassLiteral(ClassLiteralType { class }) => { @@ -1964,7 +1971,7 @@ impl<'db> Type<'db> { } // Dynamic types are callable, and the return type is the same dynamic type - Type::Any(_) => CallOutcome::callable(CallBinding::from_return_ty(self)), + Type::Gradual(_) => CallOutcome::callable(CallBinding::from_return_ty(self)), Type::Union(union) => CallOutcome::union( self, @@ -2072,12 +2079,12 @@ impl<'db> Type<'db> { #[must_use] pub fn to_instance(&self, db: &'db dyn Db) -> Type<'db> { match self { - Type::Any(_) => *self, + Type::Gradual(_) => *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(any) => Type::Any(any), + ClassBase::Gradual(gradual) => Type::Gradual(gradual), }, Type::Union(union) => union.map(db, |element| element.to_instance(db)), Type::Intersection(_) => todo_type!("Type::Intersection.to_instance()"), @@ -2162,7 +2169,7 @@ impl<'db> Type<'db> { }) } } - Type::Any(_) => Ok(*self), + Type::Gradual(_) => 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)), @@ -2244,7 +2251,7 @@ 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(_) => *self, + ClassBase::Gradual(_) => *self, ClassBase::Class(class) => SubclassOfType::from( db, ClassBase::try_from_ty(db, class.metaclass(db)).unwrap_or(ClassBase::unknown()), @@ -2252,7 +2259,7 @@ impl<'db> Type<'db> { }, Type::StringLiteral(_) | Type::LiteralString => KnownClass::Str.to_class_literal(db), - Type::Any(any) => SubclassOfType::from(db, ClassBase::Any(*any)), + Type::Gradual(gradual) => SubclassOfType::from(db, ClassBase::Gradual(*gradual)), // TODO intersections Type::Intersection(_) => SubclassOfType::from( db, @@ -2321,7 +2328,7 @@ impl<'db> From<&Type<'db>> for Symbol<'db> { } #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] -pub enum AnyType { +pub enum GradualType { // An explicitly annotated `typing.Any` Annotated, // An unannotated value @@ -2338,14 +2345,14 @@ pub enum AnyType { Todo(TodoType), } -impl std::fmt::Display for AnyType { +impl std::fmt::Display for GradualType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - AnyType::Annotated => f.write_str("Any"), - AnyType::Unknown => f.write_str("Unknown"), + GradualType::Annotated => f.write_str("Any"), + GradualType::Unknown => f.write_str("Unknown"), // `[Type::Todo]`'s display should be explicit that is not a valid display of // any other type - AnyType::Todo(todo) => write!(f, "@Todo{todo}"), + GradualType::Todo(todo) => write!(f, "@Todo{todo}"), } } } @@ -3728,7 +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(_) => return Type::from(superclass).member(db, name), + ClassBase::Gradual(_) => return Type::from(superclass).member(db, name), ClassBase::Class(class) => { let member = class.own_class_member(db, name); if !member.is_unbound() { diff --git a/crates/red_knot_python_semantic/src/types/builder.rs b/crates/red_knot_python_semantic/src/types/builder.rs index 3cb44ed35e878..dadafaa3753e6 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(_) => { + ty @ Type::Gradual(_) => { // 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. 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 c2ee2aa1bf998..b66d961d61623 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, AnyType, Class, ClassLiteralType, KnownClass, KnownInstanceType, Type, + todo_type, Class, ClassLiteralType, GradualType, KnownClass, KnownInstanceType, Type, }; use crate::Db; use itertools::Either; @@ -11,22 +11,22 @@ use itertools::Either; /// transformed into [`ClassBase::unknown`] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, salsa::Update)] pub enum ClassBase<'db> { - Any(AnyType), + Gradual(GradualType), Class(Class<'db>), } impl<'db> ClassBase<'db> { pub const fn annotated_any() -> Self { - Self::Any(AnyType::Annotated) + Self::Gradual(GradualType::Annotated) } pub const fn unknown() -> Self { - Self::Any(AnyType::Unknown) + Self::Gradual(GradualType::Unknown) } pub const fn is_dynamic(self) -> bool { match self { - ClassBase::Any(_) => true, + ClassBase::Gradual(_) => true, ClassBase::Class(_) => false, } } @@ -40,7 +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(any) => any.fmt(f), + ClassBase::Gradual(gradual) => gradual.fmt(f), ClassBase::Class(class) => write!(f, "", class.name(self.db)), } } @@ -54,7 +54,7 @@ impl<'db> ClassBase<'db> { KnownClass::Object .to_class_literal(db) .into_class_literal() - .map_or(Self::Any(AnyType::Unknown), |ClassLiteralType { class }| { + .map_or(Self::unknown(), |ClassLiteralType { class }| { Self::Class(class) }) } @@ -64,7 +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(any) => Some(Self::Any(any)), + Type::Gradual(gradual) => Some(Self::Gradual(gradual)), Type::ClassLiteral(ClassLiteralType { class }) => Some(Self::Class(class)), Type::Union(_) => None, // TODO -- forces consideration of multiple possible MROs? Type::Intersection(_) => None, // TODO -- probably incorrect? @@ -106,8 +106,8 @@ impl<'db> ClassBase<'db> { | KnownInstanceType::Not | KnownInstanceType::Intersection | KnownInstanceType::TypeOf => None, - KnownInstanceType::Unknown => Some(Self::Any(AnyType::Unknown)), - KnownInstanceType::Any => Some(Self::Any(AnyType::Annotated)), + KnownInstanceType::Unknown => Some(Self::unknown()), + KnownInstanceType::Any => Some(Self::annotated_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)) @@ -163,7 +163,7 @@ impl<'db> ClassBase<'db> { db: &'db dyn Db, ) -> Either>, impl Iterator>> { match self { - ClassBase::Any(_) => Either::Left([self, ClassBase::object(db)].into_iter()), + ClassBase::Gradual(_) => Either::Left([self, ClassBase::object(db)].into_iter()), ClassBase::Class(class) => Either::Right(class.iter_mro(db)), } } @@ -178,7 +178,7 @@ impl<'db> From> for ClassBase<'db> { impl<'db> From> for Type<'db> { fn from(value: ClassBase<'db>) -> Self { match value { - ClassBase::Any(any) => Type::Any(any), + ClassBase::Gradual(gradual) => Type::Gradual(gradual), 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 6f0370689ea96..6ae6e0b0afa08 100644 --- a/crates/red_knot_python_semantic/src/types/display.rs +++ b/crates/red_knot_python_semantic/src/types/display.rs @@ -65,7 +65,7 @@ struct DisplayRepresentation<'db> { impl Display for DisplayRepresentation<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self.ty { - Type::Any(any) => any.fmt(f), + Type::Gradual(gradual) => gradual.fmt(f), Type::Never => f.write_str("Never"), Type::Instance(InstanceType { class }) => { let representation = match class.known(self.db) { @@ -84,7 +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)), - ClassBase::Any(any) => write!(f, "type[{any}]"), + ClassBase::Gradual(gradual) => write!(f, "type[{gradual}]"), }, 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 b450a30fdb2fe..9c5a565b30ef7 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, AnyType, 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, FunctionType, + GradualType, 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}; @@ -3355,7 +3355,7 @@ impl<'db> TypeInferenceBuilder<'db> { let operand_type = self.infer_expression(operand); match (op, operand_type) { - (_, Type::Any(_)) => operand_type, + (_, Type::Gradual(_)) => operand_type, (_, Type::Never) => Type::Never, (ast::UnaryOp::UAdd, Type::IntLiteral(value)) => Type::IntLiteral(value), @@ -3479,12 +3479,12 @@ impl<'db> TypeInferenceBuilder<'db> { match (left_ty, right_ty, op) { // 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::Any(AnyType::Annotated), _, _) - | (_, any @ Type::Any(AnyType::Annotated), _) => Some(any), - (unknown @ Type::Any(AnyType::Unknown), _, _) - | (_, unknown @ Type::Any(AnyType::Unknown), _) => Some(unknown), - (todo @ Type::Any(AnyType::Todo(_)), _, _) - | (_, todo @ Type::Any(AnyType::Todo(_)), _) => Some(todo), + (any @ Type::Gradual(GradualType::Annotated), _, _) + | (_, any @ Type::Gradual(GradualType::Annotated), _) => Some(any), + (unknown @ Type::Gradual(GradualType::Unknown), _, _) + | (_, unknown @ Type::Gradual(GradualType::Unknown), _) => Some(unknown), + (todo @ Type::Gradual(GradualType::Todo(_)), _, _) + | (_, todo @ Type::Gradual(GradualType::Todo(_)), _) => Some(todo), (Type::Never, _, _) | (_, Type::Never, _) => Some(Type::Never), (Type::IntLiteral(n), Type::IntLiteral(m), ast::Operator::Add) => Some( @@ -4152,7 +4152,7 @@ impl<'db> TypeInferenceBuilder<'db> { ).expect("infer_binary_type_comparison should never return None for `CmpOp::Eq`"); match eq_result { - todo @ Type::Any(AnyType::Todo(_)) => return Ok(todo), + todo @ Type::Gradual(GradualType::Todo(_)) => return Ok(todo), ty => match ty.bool(self.db()) { Truthiness::AlwaysTrue => eq_count += 1, Truthiness::AlwaysFalse => not_eq_count += 1, @@ -4177,7 +4177,7 @@ impl<'db> TypeInferenceBuilder<'db> { ); Ok(match eq_result { - todo @ Type::Any(AnyType::Todo(_)) => todo, + todo @ Type::Gradual(GradualType::Todo(_)) => todo, ty => match ty.bool(self.db()) { Truthiness::AlwaysFalse => Type::BooleanLiteral(op.is_is_not()), _ => KnownClass::Bool.to_instance(self.db()), @@ -4259,7 +4259,7 @@ impl<'db> TypeInferenceBuilder<'db> { match pairwise_eq_result { // If propagation is required, return the result as is - todo @ Type::Any(AnyType::Todo(_)) => return Ok(todo), + todo @ Type::Gradual(GradualType::Todo(_)) => return Ok(todo), ty => match ty.bool(self.db()) { // - AlwaysTrue : Continue to the next pair for lexicographic comparison Truthiness::AlwaysTrue => continue, @@ -5077,7 +5077,7 @@ impl<'db> TypeInferenceBuilder<'db> { Type::KnownInstance(known_instance) => { self.infer_parameterized_known_instance_type_expression(subscript, known_instance) } - Type::Any(AnyType::Todo(_)) => { + Type::Gradual(GradualType::Todo(_)) => { self.infer_type_expression(slice); value_ty } @@ -5716,7 +5716,7 @@ fn perform_membership_test_comparison<'db>( compare_result_opt .map(|ty| { - if matches!(ty, Type::Any(AnyType::Todo(_))) { + if matches!(ty, Type::Gradual(GradualType::Todo(_))) { return ty; } 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 d69b8ca15ad6b..8167046daa322 100644 --- a/crates/red_knot_python_semantic/src/types/subclass_of.rs +++ b/crates/red_knot_python_semantic/src/types/subclass_of.rs @@ -22,7 +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(_) => Type::SubclassOf(Self { subclass_of }), + ClassBase::Gradual(_) => Type::SubclassOf(Self { subclass_of }), ClassBase::Class(class) => { if class.is_final(db) { Type::ClassLiteral(ClassLiteralType { class }) @@ -75,7 +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::Any(_)) => false, + (ClassBase::Gradual(_), _) | (_, ClassBase::Gradual(_)) => 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`. From c88b49794956feba33d1dd1b529ae399744762cc Mon Sep 17 00:00:00 2001 From: Douglas Creager Date: Thu, 9 Jan 2025 16:27:31 -0500 Subject: [PATCH 04/10] =?UTF-8?q?Rename=20annotated=5Fany=20=E2=86=92=20an?= =?UTF-8?q?y?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/red_knot_python_semantic/src/types.rs | 6 +++--- .../src/types/builder.rs | 16 ++++++++-------- .../src/types/class_base.rs | 4 ++-- .../src/types/subclass_of.rs | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index 5f440491f9ce5..fb95e0251886e 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -546,7 +546,7 @@ pub enum Type<'db> { } impl<'db> Type<'db> { - pub const fn annotated_any() -> Self { + pub const fn any() -> Self { Self::Gradual(GradualType::Annotated) } @@ -2177,7 +2177,7 @@ impl<'db> Type<'db> { Ok(Type::Never) } Type::KnownInstance(KnownInstanceType::LiteralString) => Ok(Type::LiteralString), - Type::KnownInstance(KnownInstanceType::Any) => Ok(Type::annotated_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], @@ -4078,7 +4078,7 @@ pub(crate) mod tests { Ty::Never => Type::Never, Ty::Unknown => Type::unknown(), Ty::None => Type::none(db), - Ty::Any => Type::annotated_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 dadafaa3753e6..9c17f7b1947ca 100644 --- a/crates/red_knot_python_semantic/src/types/builder.rs +++ b/crates/red_knot_python_semantic/src/types/builder.rs @@ -525,7 +525,7 @@ mod tests { fn build_intersection() { let db = setup_db(); let t0 = Type::IntLiteral(0); - let ta = Type::annotated_any(); + let ta = Type::any(); let intersection = IntersectionBuilder::new(&db) .add_positive(ta) .add_negative(t0) @@ -548,7 +548,7 @@ mod tests { #[test] fn build_intersection_flatten_positive() { let db = setup_db(); - let ta = Type::annotated_any(); + let ta = Type::any(); let t1 = Type::IntLiteral(1); let t2 = Type::IntLiteral(2); let i0 = IntersectionBuilder::new(&db) @@ -568,7 +568,7 @@ mod tests { #[test] fn build_intersection_flatten_negative() { let db = setup_db(); - let ta = Type::annotated_any(); + let ta = Type::any(); let t1 = Type::IntLiteral(1); let t2 = KnownClass::Int.to_instance(&db); // i0 = Any & ~Literal[1] @@ -594,13 +594,13 @@ mod tests { let db = setup_db(); let ty = IntersectionBuilder::new(&db) - .add_negative(Type::annotated_any()) + .add_negative(Type::any()) .build(); - assert_eq!(ty, Type::annotated_any()); + assert_eq!(ty, Type::any()); let ty = IntersectionBuilder::new(&db) .add_positive(Type::Never) - .add_negative(Type::annotated_any()) + .add_negative(Type::any()) .build(); assert_eq!(ty, Type::Never); } @@ -646,7 +646,7 @@ mod tests { let db = setup_db(); let t0 = Type::IntLiteral(0); let t1 = Type::IntLiteral(1); - let ta = Type::annotated_any(); + let ta = Type::any(); let u0 = UnionType::from_elements(&db, [t0, t1]); let union = IntersectionBuilder::new(&db) @@ -1049,7 +1049,7 @@ mod tests { assert_eq!(ty, Type::BooleanLiteral(!bool_value)); } - #[test_case(Type::annotated_any())] + #[test_case(Type::any())] #[test_case(Type::unknown())] #[test_case(todo_type!())] fn build_intersection_t_and_negative_t_does_not_simplify(ty: Type) { 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 b66d961d61623..4135dfacbee58 100644 --- a/crates/red_knot_python_semantic/src/types/class_base.rs +++ b/crates/red_knot_python_semantic/src/types/class_base.rs @@ -16,7 +16,7 @@ pub enum ClassBase<'db> { } impl<'db> ClassBase<'db> { - pub const fn annotated_any() -> Self { + pub const fn any() -> Self { Self::Gradual(GradualType::Annotated) } @@ -107,7 +107,7 @@ impl<'db> ClassBase<'db> { | KnownInstanceType::Intersection | KnownInstanceType::TypeOf => None, KnownInstanceType::Unknown => Some(Self::unknown()), - KnownInstanceType::Any => Some(Self::annotated_any()), + 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)) 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 8167046daa322..aae26fd91ce77 100644 --- a/crates/red_knot_python_semantic/src/types/subclass_of.rs +++ b/crates/red_knot_python_semantic/src/types/subclass_of.rs @@ -45,7 +45,7 @@ impl<'db> SubclassOfType<'db> { /// Return a [`Type`] instance representing the type `type[Any]`. pub(crate) const fn subclass_of_any() -> Type<'db> { Type::SubclassOf(SubclassOfType { - subclass_of: ClassBase::annotated_any(), + subclass_of: ClassBase::any(), }) } From 8d12d1ac106e7f6fe5dd24045d90326b05ac47ba Mon Sep 17 00:00:00 2001 From: Douglas Creager Date: Thu, 9 Jan 2025 16:33:46 -0500 Subject: [PATCH 05/10] =?UTF-8?q?GradualType::Annotated=20=E2=86=92=20Any?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/red_knot_python_semantic/src/types.rs | 10 +++++----- .../red_knot_python_semantic/src/types/class_base.rs | 2 +- crates/red_knot_python_semantic/src/types/infer.rs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index fb95e0251886e..edcbee6d14590 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -547,7 +547,7 @@ pub enum Type<'db> { impl<'db> Type<'db> { pub const fn any() -> Self { - Self::Gradual(GradualType::Annotated) + Self::Gradual(GradualType::Any) } pub const fn unknown() -> Self { @@ -1083,8 +1083,8 @@ impl<'db> Type<'db> { matches!( (self, other), ( - Type::Gradual(GradualType::Annotated), - Type::Gradual(GradualType::Annotated) + Type::Gradual(GradualType::Any), + Type::Gradual(GradualType::Any) ) | ( Type::Gradual(GradualType::Unknown), Type::Gradual(GradualType::Unknown) @@ -2330,7 +2330,7 @@ impl<'db> From<&Type<'db>> for Symbol<'db> { #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub enum GradualType { // An explicitly annotated `typing.Any` - Annotated, + Any, // An unannotated value Unknown, /// Temporary type for symbols that can't be inferred yet because of missing implementations. @@ -2348,7 +2348,7 @@ pub enum GradualType { impl std::fmt::Display for GradualType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - GradualType::Annotated => f.write_str("Any"), + GradualType::Any => f.write_str("Any"), GradualType::Unknown => f.write_str("Unknown"), // `[Type::Todo]`'s display should be explicit that is not a valid display of // any other type 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 4135dfacbee58..67c28f85d78c7 100644 --- a/crates/red_knot_python_semantic/src/types/class_base.rs +++ b/crates/red_knot_python_semantic/src/types/class_base.rs @@ -17,7 +17,7 @@ pub enum ClassBase<'db> { impl<'db> ClassBase<'db> { pub const fn any() -> Self { - Self::Gradual(GradualType::Annotated) + Self::Gradual(GradualType::Any) } pub const fn unknown() -> Self { diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index 9c5a565b30ef7..467a48894a158 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -3479,8 +3479,8 @@ impl<'db> TypeInferenceBuilder<'db> { match (left_ty, right_ty, op) { // 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::Gradual(GradualType::Annotated), _, _) - | (_, any @ Type::Gradual(GradualType::Annotated), _) => Some(any), + (any @ Type::Gradual(GradualType::Any), _, _) + | (_, any @ Type::Gradual(GradualType::Any), _) => Some(any), (unknown @ Type::Gradual(GradualType::Unknown), _, _) | (_, unknown @ Type::Gradual(GradualType::Unknown), _) => Some(unknown), (todo @ Type::Gradual(GradualType::Todo(_)), _, _) From bea6c3ff8ba2405653c540d4f063ed7220810c7a Mon Sep 17 00:00:00 2001 From: Douglas Creager Date: Thu, 9 Jan 2025 16:45:32 -0500 Subject: [PATCH 06/10] Update crates/red_knot_python_semantic/src/types.rs Co-authored-by: Alex Waygood --- crates/red_knot_python_semantic/src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index edcbee6d14590..3fe16298b4ba7 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -2331,7 +2331,7 @@ impl<'db> From<&Type<'db>> for Symbol<'db> { pub enum GradualType { // An explicitly annotated `typing.Any` Any, - // An unannotated value + // An unannotated value, or a gradual type resulting from an error Unknown, /// Temporary type for symbols that can't be inferred yet because of missing implementations. /// From f58091006b0a7f1db726115ea8da94052fec0558 Mon Sep 17 00:00:00 2001 From: Douglas Creager Date: Thu, 9 Jan 2025 16:53:02 -0500 Subject: [PATCH 07/10] Gradual -> Dynamic --- crates/red_knot_python_semantic/src/types.rs | 74 +++++++++---------- .../src/types/builder.rs | 2 +- .../src/types/class_base.rs | 18 ++--- .../src/types/display.rs | 4 +- .../src/types/infer.rs | 34 ++++----- .../src/types/subclass_of.rs | 4 +- 6 files changed, 68 insertions(+), 68 deletions(-) diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index 3fe16298b4ba7..c169b13b97ddc 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -475,12 +475,12 @@ impl std::fmt::Display for TodoType { #[cfg(debug_assertions)] macro_rules! todo_type { () => { - $crate::types::Type::Gradual($crate::types::GradualType::Todo( + $crate::types::Type::Dynamic($crate::types::DynamicType::Todo( crate::types::TodoType::FileAndLine(file!(), line!()), )) }; ($message:literal) => { - $crate::types::Type::Gradual($crate::types::GradualType::Todo( + $crate::types::Type::Dynamic($crate::types::DynamicType::Todo( crate::types::TodoType::Message($message), )) }; @@ -489,10 +489,10 @@ macro_rules! todo_type { #[cfg(not(debug_assertions))] macro_rules! todo_type { () => { - $crate::types::Type::Gradual($crate::types::GradualType::Todo(crate::types::TodoType)) + $crate::types::Type::Dynamic($crate::types::DynamicType::Todo(crate::types::TodoType)) }; ($message:literal) => { - $crate::types::Type::Gradual($crate::types::GradualType::Todo(crate::types::TodoType)) + $crate::types::Type::Dynamic($crate::types::DynamicType::Todo(crate::types::TodoType)) }; } @@ -502,7 +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 - Gradual(GradualType), + Dynamic(DynamicType), /// The empty set of values Never, /// A specific function object @@ -547,15 +547,15 @@ pub enum Type<'db> { impl<'db> Type<'db> { pub const fn any() -> Self { - Self::Gradual(GradualType::Any) + Self::Dynamic(DynamicType::Any) } pub const fn unknown() -> Self { - Self::Gradual(GradualType::Unknown) + Self::Dynamic(DynamicType::Unknown) } pub const fn is_unknown(&self) -> bool { - matches!(self, Type::Gradual(GradualType::Unknown)) + matches!(self, Type::Dynamic(DynamicType::Unknown)) } pub const fn is_never(&self) -> bool { @@ -563,7 +563,7 @@ impl<'db> Type<'db> { } pub const fn is_todo(&self) -> bool { - matches!(self, Type::Gradual(GradualType::Todo(_))) + matches!(self, Type::Dynamic(DynamicType::Todo(_))) } pub const fn class_literal(class: Class<'db>) -> Self { @@ -755,7 +755,7 @@ impl<'db> Type<'db> { match (self, target) { // We should have handled these immediately above. - (Type::Gradual(_), _) | (_, Type::Gradual(_)) => { + (Type::Dynamic(_), _) | (_, Type::Dynamic(_)) => { unreachable!("Non-fully-static types do not participate in subtyping!") } @@ -972,8 +972,8 @@ impl<'db> Type<'db> { (Type::Never, _) => true, // The dynamic type is assignable-to and assignable-from any type. - (Type::Gradual(_), _) => true, - (_, Type::Gradual(_)) => 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 @@ -1083,14 +1083,14 @@ impl<'db> Type<'db> { matches!( (self, other), ( - Type::Gradual(GradualType::Any), - Type::Gradual(GradualType::Any) + Type::Dynamic(DynamicType::Any), + Type::Dynamic(DynamicType::Any) ) | ( - Type::Gradual(GradualType::Unknown), - Type::Gradual(GradualType::Unknown) + Type::Dynamic(DynamicType::Unknown), + Type::Dynamic(DynamicType::Unknown) ) | ( - Type::Gradual(GradualType::Todo(_)), - Type::Gradual(GradualType::Todo(_)) + Type::Dynamic(DynamicType::Todo(_)), + Type::Dynamic(DynamicType::Todo(_)) ) ) } @@ -1103,7 +1103,7 @@ impl<'db> Type<'db> { match (self, other) { (Type::Never, _) | (_, Type::Never) => true, - (Type::Gradual(_), _) | (_, Type::Gradual(_)) => false, + (Type::Dynamic(_), _) | (_, Type::Dynamic(_)) => false, (Type::Union(union), other) | (other, Type::Union(union)) => union .elements(db) @@ -1183,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::Gradual(_) => false, + ClassBase::Dynamic(_) => false, ClassBase::Class(class_a) => !class_b.is_subclass_of(db, class_a), }, @@ -1379,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::Gradual(_) => false, + Type::Dynamic(_) => false, Type::Never | Type::FunctionLiteral(..) | Type::ModuleLiteral(..) @@ -1442,7 +1442,7 @@ 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::Gradual(_) + Type::Dynamic(_) | Type::Never | Type::IntLiteral(..) | Type::StringLiteral(..) @@ -1553,7 +1553,7 @@ impl<'db> Type<'db> { None => false, }, - Type::Gradual(_) + Type::Dynamic(_) | Type::Never | Type::Union(..) | Type::Intersection(..) @@ -1575,7 +1575,7 @@ impl<'db> Type<'db> { } match self { - Type::Gradual(_) => self.into(), + Type::Dynamic(_) => self.into(), Type::Never => todo_type!("attribute lookup on Never").into(), @@ -1700,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::Gradual(_) | Type::Never => Truthiness::Ambiguous, + Type::Dynamic(_) | Type::Never => Truthiness::Ambiguous, Type::FunctionLiteral(_) => Truthiness::AlwaysTrue, Type::ModuleLiteral(_) => Truthiness::AlwaysTrue, Type::ClassLiteral(ClassLiteralType { class }) => { @@ -1971,7 +1971,7 @@ impl<'db> Type<'db> { } // Dynamic types are callable, and the return type is the same dynamic type - Type::Gradual(_) => CallOutcome::callable(CallBinding::from_return_ty(self)), + Type::Dynamic(_) => CallOutcome::callable(CallBinding::from_return_ty(self)), Type::Union(union) => CallOutcome::union( self, @@ -2079,12 +2079,12 @@ impl<'db> Type<'db> { #[must_use] pub fn to_instance(&self, db: &'db dyn Db) -> Type<'db> { match self { - Type::Gradual(_) => *self, + 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::Gradual(gradual) => Type::Gradual(gradual), + ClassBase::Dynamic(gradual) => Type::Dynamic(gradual), }, Type::Union(union) => union.map(db, |element| element.to_instance(db)), Type::Intersection(_) => todo_type!("Type::Intersection.to_instance()"), @@ -2169,7 +2169,7 @@ impl<'db> Type<'db> { }) } } - Type::Gradual(_) => Ok(*self), + 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)), @@ -2251,7 +2251,7 @@ 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::Gradual(_) => *self, + ClassBase::Dynamic(_) => *self, ClassBase::Class(class) => SubclassOfType::from( db, ClassBase::try_from_ty(db, class.metaclass(db)).unwrap_or(ClassBase::unknown()), @@ -2259,7 +2259,7 @@ impl<'db> Type<'db> { }, Type::StringLiteral(_) | Type::LiteralString => KnownClass::Str.to_class_literal(db), - Type::Gradual(gradual) => SubclassOfType::from(db, ClassBase::Gradual(*gradual)), + Type::Dynamic(gradual) => SubclassOfType::from(db, ClassBase::Dynamic(*gradual)), // TODO intersections Type::Intersection(_) => SubclassOfType::from( db, @@ -2328,7 +2328,7 @@ impl<'db> From<&Type<'db>> for Symbol<'db> { } #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] -pub enum GradualType { +pub enum DynamicType { // An explicitly annotated `typing.Any` Any, // An unannotated value, or a gradual type resulting from an error @@ -2345,14 +2345,14 @@ pub enum GradualType { Todo(TodoType), } -impl std::fmt::Display for GradualType { +impl std::fmt::Display for DynamicType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - GradualType::Any => f.write_str("Any"), - GradualType::Unknown => f.write_str("Unknown"), + DynamicType::Any => f.write_str("Any"), + DynamicType::Unknown => f.write_str("Unknown"), // `[Type::Todo]`'s display should be explicit that is not a valid display of // any other type - GradualType::Todo(todo) => write!(f, "@Todo{todo}"), + DynamicType::Todo(todo) => write!(f, "@Todo{todo}"), } } } @@ -3735,7 +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::Gradual(_) => 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() { diff --git a/crates/red_knot_python_semantic/src/types/builder.rs b/crates/red_knot_python_semantic/src/types/builder.rs index ba7636a343da8..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::Gradual(_) => { + 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. 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 67c28f85d78c7..84dbf90fe5224 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, GradualType, KnownClass, KnownInstanceType, Type, + todo_type, Class, ClassLiteralType, DynamicType, KnownClass, KnownInstanceType, Type, }; use crate::Db; use itertools::Either; @@ -11,22 +11,22 @@ use itertools::Either; /// transformed into [`ClassBase::unknown`] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, salsa::Update)] pub enum ClassBase<'db> { - Gradual(GradualType), + Dynamic(DynamicType), Class(Class<'db>), } impl<'db> ClassBase<'db> { pub const fn any() -> Self { - Self::Gradual(GradualType::Any) + Self::Dynamic(DynamicType::Any) } pub const fn unknown() -> Self { - Self::Gradual(GradualType::Unknown) + Self::Dynamic(DynamicType::Unknown) } pub const fn is_dynamic(self) -> bool { match self { - ClassBase::Gradual(_) => true, + ClassBase::Dynamic(_) => true, ClassBase::Class(_) => false, } } @@ -40,7 +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::Gradual(gradual) => gradual.fmt(f), + ClassBase::Dynamic(gradual) => gradual.fmt(f), ClassBase::Class(class) => write!(f, "", class.name(self.db)), } } @@ -64,7 +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::Gradual(gradual) => Some(Self::Gradual(gradual)), + Type::Dynamic(gradual) => Some(Self::Dynamic(gradual)), Type::ClassLiteral(ClassLiteralType { class }) => Some(Self::Class(class)), Type::Union(_) => None, // TODO -- forces consideration of multiple possible MROs? Type::Intersection(_) => None, // TODO -- probably incorrect? @@ -163,7 +163,7 @@ impl<'db> ClassBase<'db> { db: &'db dyn Db, ) -> Either>, impl Iterator>> { match self { - ClassBase::Gradual(_) => Either::Left([self, ClassBase::object(db)].into_iter()), + ClassBase::Dynamic(_) => Either::Left([self, ClassBase::object(db)].into_iter()), ClassBase::Class(class) => Either::Right(class.iter_mro(db)), } } @@ -178,7 +178,7 @@ impl<'db> From> for ClassBase<'db> { impl<'db> From> for Type<'db> { fn from(value: ClassBase<'db>) -> Self { match value { - ClassBase::Gradual(gradual) => Type::Gradual(gradual), + ClassBase::Dynamic(gradual) => Type::Dynamic(gradual), 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 6ae6e0b0afa08..af644b07ee8e7 100644 --- a/crates/red_knot_python_semantic/src/types/display.rs +++ b/crates/red_knot_python_semantic/src/types/display.rs @@ -65,7 +65,7 @@ struct DisplayRepresentation<'db> { impl Display for DisplayRepresentation<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self.ty { - Type::Gradual(gradual) => gradual.fmt(f), + Type::Dynamic(gradual) => gradual.fmt(f), Type::Never => f.write_str("Never"), Type::Instance(InstanceType { class }) => { let representation = match class.known(self.db) { @@ -84,7 +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)), - ClassBase::Gradual(gradual) => write!(f, "type[{gradual}]"), + ClassBase::Dynamic(gradual) => write!(f, "type[{gradual}]"), }, 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 467a48894a158..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, - GradualType, 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}; @@ -3355,7 +3355,7 @@ impl<'db> TypeInferenceBuilder<'db> { let operand_type = self.infer_expression(operand); match (op, operand_type) { - (_, Type::Gradual(_)) => operand_type, + (_, Type::Dynamic(_)) => operand_type, (_, Type::Never) => Type::Never, (ast::UnaryOp::UAdd, Type::IntLiteral(value)) => Type::IntLiteral(value), @@ -3479,12 +3479,12 @@ impl<'db> TypeInferenceBuilder<'db> { match (left_ty, right_ty, op) { // 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::Gradual(GradualType::Any), _, _) - | (_, any @ Type::Gradual(GradualType::Any), _) => Some(any), - (unknown @ Type::Gradual(GradualType::Unknown), _, _) - | (_, unknown @ Type::Gradual(GradualType::Unknown), _) => Some(unknown), - (todo @ Type::Gradual(GradualType::Todo(_)), _, _) - | (_, todo @ Type::Gradual(GradualType::Todo(_)), _) => Some(todo), + (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::IntLiteral(n), Type::IntLiteral(m), ast::Operator::Add) => Some( @@ -4152,7 +4152,7 @@ impl<'db> TypeInferenceBuilder<'db> { ).expect("infer_binary_type_comparison should never return None for `CmpOp::Eq`"); match eq_result { - todo @ Type::Gradual(GradualType::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, @@ -4177,7 +4177,7 @@ impl<'db> TypeInferenceBuilder<'db> { ); Ok(match eq_result { - todo @ Type::Gradual(GradualType::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()), @@ -4259,7 +4259,7 @@ impl<'db> TypeInferenceBuilder<'db> { match pairwise_eq_result { // If propagation is required, return the result as is - todo @ Type::Gradual(GradualType::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, @@ -5077,7 +5077,7 @@ impl<'db> TypeInferenceBuilder<'db> { Type::KnownInstance(known_instance) => { self.infer_parameterized_known_instance_type_expression(subscript, known_instance) } - Type::Gradual(GradualType::Todo(_)) => { + Type::Dynamic(DynamicType::Todo(_)) => { self.infer_type_expression(slice); value_ty } @@ -5716,7 +5716,7 @@ fn perform_membership_test_comparison<'db>( compare_result_opt .map(|ty| { - if matches!(ty, Type::Gradual(GradualType::Todo(_))) { + if matches!(ty, Type::Dynamic(DynamicType::Todo(_))) { return ty; } 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 aae26fd91ce77..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,7 +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::Gradual(_) => Type::SubclassOf(Self { subclass_of }), + ClassBase::Dynamic(_) => Type::SubclassOf(Self { subclass_of }), ClassBase::Class(class) => { if class.is_final(db) { Type::ClassLiteral(ClassLiteralType { class }) @@ -75,7 +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::Gradual(_), _) | (_, ClassBase::Gradual(_)) => 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`. From 03c581105a1d2c3f621b933d41c6b95e28d11975 Mon Sep 17 00:00:00 2001 From: Douglas Creager Date: Thu, 9 Jan 2025 21:06:30 -0500 Subject: [PATCH 08/10] Apply suggestions from code review Co-authored-by: Carl Meyer Co-authored-by: Alex Waygood --- crates/red_knot_python_semantic/src/types.rs | 6 +++--- crates/red_knot_python_semantic/src/types/class_base.rs | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index c169b13b97ddc..b7a46bbb5a7d2 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -2084,7 +2084,7 @@ impl<'db> Type<'db> { 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::Dynamic(gradual) => Type::Dynamic(gradual), + 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()"), @@ -2331,7 +2331,7 @@ impl<'db> From<&Type<'db>> for Symbol<'db> { pub enum DynamicType { // An explicitly annotated `typing.Any` Any, - // An unannotated value, or a gradual type resulting from an error + // 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. /// @@ -2350,7 +2350,7 @@ impl std::fmt::Display for DynamicType { match self { DynamicType::Any => f.write_str("Any"), DynamicType::Unknown => f.write_str("Unknown"), - // `[Type::Todo]`'s display should be explicit that is not a valid display of + // `DynamicType::Todo`'s display should be explicit that is not a valid display of // any other type DynamicType::Todo(todo) => write!(f, "@Todo{todo}"), } 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 84dbf90fe5224..07774d4743a5b 100644 --- a/crates/red_knot_python_semantic/src/types/class_base.rs +++ b/crates/red_knot_python_semantic/src/types/class_base.rs @@ -150,10 +150,9 @@ impl<'db> ClassBase<'db> { } pub(super) fn into_class(self) -> Option> { - #[allow(clippy::match_wildcard_for_single_variants)] match self { Self::Class(class) => Some(class), - _ => None, + Self::Dynamic(_) => None, } } From e12f63b607507edadc28f96e999ff92cf4cbdf97 Mon Sep 17 00:00:00 2001 From: Douglas Creager Date: Thu, 9 Jan 2025 21:09:33 -0500 Subject: [PATCH 09/10] Consolidate on `$crate` --- crates/red_knot_python_semantic/src/types.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index b7a46bbb5a7d2..136a60e7471d7 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -476,12 +476,12 @@ impl std::fmt::Display for TodoType { macro_rules! todo_type { () => { $crate::types::Type::Dynamic($crate::types::DynamicType::Todo( - crate::types::TodoType::FileAndLine(file!(), line!()), + $crate::types::TodoType::FileAndLine(file!(), line!()), )) }; ($message:literal) => { $crate::types::Type::Dynamic($crate::types::DynamicType::Todo( - crate::types::TodoType::Message($message), + $crate::types::TodoType::Message($message), )) }; } From 1a1491f7c4daa8330e4c944b9c776d78816be3f0 Mon Sep 17 00:00:00 2001 From: Douglas Creager Date: Thu, 9 Jan 2025 21:13:03 -0500 Subject: [PATCH 10/10] =?UTF-8?q?Last=20few=20gradual=20=E2=86=92=20dynami?= =?UTF-8?q?c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/red_knot_python_semantic/src/types.rs | 2 +- crates/red_knot_python_semantic/src/types/class_base.rs | 6 +++--- crates/red_knot_python_semantic/src/types/display.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index 136a60e7471d7..5a2c8e895562d 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -2259,7 +2259,7 @@ impl<'db> Type<'db> { }, Type::StringLiteral(_) | Type::LiteralString => KnownClass::Str.to_class_literal(db), - Type::Dynamic(gradual) => SubclassOfType::from(db, ClassBase::Dynamic(*gradual)), + Type::Dynamic(dynamic) => SubclassOfType::from(db, ClassBase::Dynamic(*dynamic)), // TODO intersections Type::Intersection(_) => SubclassOfType::from( db, 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 07774d4743a5b..1b4a35791453d 100644 --- a/crates/red_knot_python_semantic/src/types/class_base.rs +++ b/crates/red_knot_python_semantic/src/types/class_base.rs @@ -40,7 +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::Dynamic(gradual) => gradual.fmt(f), + ClassBase::Dynamic(dynamic) => dynamic.fmt(f), ClassBase::Class(class) => write!(f, "", class.name(self.db)), } } @@ -64,7 +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::Dynamic(gradual) => Some(Self::Dynamic(gradual)), + 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? @@ -177,7 +177,7 @@ impl<'db> From> for ClassBase<'db> { impl<'db> From> for Type<'db> { fn from(value: ClassBase<'db>) -> Self { match value { - ClassBase::Dynamic(gradual) => Type::Dynamic(gradual), + 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 af644b07ee8e7..50c3ae1fff8cb 100644 --- a/crates/red_knot_python_semantic/src/types/display.rs +++ b/crates/red_knot_python_semantic/src/types/display.rs @@ -65,7 +65,7 @@ struct DisplayRepresentation<'db> { impl Display for DisplayRepresentation<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self.ty { - Type::Dynamic(gradual) => gradual.fmt(f), + Type::Dynamic(dynamic) => dynamic.fmt(f), Type::Never => f.write_str("Never"), Type::Instance(InstanceType { class }) => { let representation = match class.known(self.db) { @@ -84,7 +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)), - ClassBase::Dynamic(gradual) => write!(f, "type[{gradual}]"), + 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)),