Revert "[ty] Optimize structural protocol comparisons" This reverts commit 6c47a3a6e61a47c49ade0e0ebc2c85061fe5b71e.
diff --git a/crates/ty_python_semantic/src/types/protocol_class.rs b/crates/ty_python_semantic/src/types/protocol_class.rs index abcc332..a3c518e 100644 --- a/crates/ty_python_semantic/src/types/protocol_class.rs +++ b/crates/ty_python_semantic/src/types/protocol_class.rs
@@ -1536,62 +1536,41 @@ db: &'db dyn Db, env: &ProgramEnvironment<'db>, ) -> ProtocolMemberCapabilities<'db> { - ProtocolMemberCapabilities { - instance: self.access(db, env, ProtocolMemberAccessMode::Instance), - class: self.access(db, env, ProtocolMemberAccessMode::Class), - } - } - - /// Derive only the requested access so class access does not bind an unused instance receiver. - fn access( - &self, - db: &'db dyn Db, - env: &ProgramEnvironment<'db>, - mode: ProtocolMemberAccessMode, - ) -> ProtocolMemberAccess<'db> { - match (self.kind, mode) { - ( - ProtocolMemberKind::Method(member, ProtocolMethodKind::Instance), - ProtocolMemberAccessMode::Instance, - ) => { - let member = - match member.ty() { - Type::Callable(callable) => member.with_ty(Type::Callable( - protocol_bind_self(db, env.program(db), callable, None), - )), - _ => member, - }; - ProtocolMemberAccess::new(Some(member), None) + match self.kind { + ProtocolMemberKind::Method(member, kind) => { + let instance_method = match (member.ty(), kind) { + (Type::Callable(callable), ProtocolMethodKind::Instance) => member.with_ty( + Type::Callable(protocol_bind_self(db, env.program(db), callable, None)), + ), + _ => member, + }; + ProtocolMemberCapabilities { + instance: ProtocolMemberAccess::new(Some(instance_method), None), + class: ProtocolMemberAccess::new(Some(member), None), + } } - (ProtocolMemberKind::Method(member, _), _) => { - ProtocolMemberAccess::new(Some(member), None) - } - (ProtocolMemberKind::Property { read, write }, ProtocolMemberAccessMode::Instance) => { - ProtocolMemberAccess::new(read, write) - } - (ProtocolMemberKind::Property { .. }, ProtocolMemberAccessMode::Class) => { - ProtocolMemberAccess::NONE - } - (ProtocolMemberKind::Attribute(member_ty), ProtocolMemberAccessMode::Instance) => { + ProtocolMemberKind::Property { read, write } => ProtocolMemberCapabilities { + instance: ProtocolMemberAccess::new(read, write), + class: ProtocolMemberAccess::NONE, + }, + ProtocolMemberKind::Attribute(member_ty) => { let is_class_var = self.qualifiers.contains(TypeQualifiers::CLASS_VAR); let is_final = self.qualifiers.contains(TypeQualifiers::FINAL); - ProtocolMemberAccess::new( - Some(member_ty), - (!is_class_var && !is_final) - .then_some(ProtocolMemberWrite::from_type(member_ty)), - ) - } - (ProtocolMemberKind::Attribute(member_ty), ProtocolMemberAccessMode::Class) - if self.qualifiers.contains(TypeQualifiers::CLASS_VAR) => - { - ProtocolMemberAccess::new( - Some(member_ty), - (!self.qualifiers.contains(TypeQualifiers::FINAL)) - .then_some(ProtocolMemberWrite::from_type(member_ty)), - ) - } - (ProtocolMemberKind::Attribute(_), ProtocolMemberAccessMode::Class) => { - ProtocolMemberAccess::NONE + ProtocolMemberCapabilities { + instance: ProtocolMemberAccess::new( + Some(member_ty), + (!is_class_var && !is_final) + .then_some(ProtocolMemberWrite::from_type(member_ty)), + ), + class: if is_class_var { + ProtocolMemberAccess::new( + Some(member_ty), + (!is_final).then_some(ProtocolMemberWrite::from_type(member_ty)), + ) + } else { + ProtocolMemberAccess::NONE + }, + } } } } @@ -2137,7 +2116,11 @@ env: &ProgramEnvironment<'db>, mode: ProtocolMemberAccessMode, ) -> ProtocolMemberAccess<'db> { - let access = self.data.access(db, env, mode); + let capabilities = self.data.capabilities(db, env); + let access = match mode { + ProtocolMemberAccessMode::Instance => capabilities.instance, + ProtocolMemberAccessMode::Class => capabilities.class, + }; self.materialization .map_or(access, |kind| access.materialize(db, env, kind)) } @@ -2464,7 +2447,6 @@ ty: Type<'db>, receiver_ty: Type<'db>, member: &ProtocolMember<'_, 'db>, - source_member: Option<&ProtocolMember<'_, 'db>>, access: ProtocolMemberAccessMode, ) -> Option<Type<'db>> { // A callback protocol describes call syntax. Use the candidate's callable type instead of an @@ -2478,7 +2460,11 @@ // Reuse declared protocol members to avoid the generic member lookup, descriptor dispatch, // and Salsa interning otherwise required on this common comparison path. - if let Some(source_member) = source_member { + if let Type::ProtocolInstance(source_protocol) = ty + && let Some(source_member) = source_protocol + .interface(db) + .member_by_name(db, member.name) + { let read = source_member.access(db, env, access).read?; if source_member.is_method() { @@ -2540,19 +2526,16 @@ /// /// Resolution is shared with real assignments, but this path evaluates the result using the /// active type relation and constraints instead of inferring an expression or emitting an - /// assignment diagnostic. A previously resolved protocol requirement avoids looking up the - /// same declared member again. + /// assignment diagnostic. fn check_property_write( &self, db: &'db dyn Db, ty: Type<'db>, member_name: &str, value_ty: Type<'db>, - protocol_requirement: Option<AttributeWriteRequirement<'db>>, ) -> ConstraintSet<'db, 'c> { let env = self.env; - let requirement = protocol_requirement - .unwrap_or_else(|| attribute_write_requirement(db, env, ty, member_name)); + let requirement = attribute_write_requirement(db, env, ty, member_name); let result = self.check_property_write_requirement(db, &requirement, member_name, value_ty); if let Some(context) = self.report_context() @@ -2880,13 +2863,12 @@ ty: Type<'db>, receiver_ty: Type<'db>, member: &ProtocolMember<'_, 'db>, - source_member: Option<&ProtocolMember<'_, 'db>>, required_ty: ProtocolMemberType<'db>, access: ProtocolMemberAccessMode, ) -> ConstraintSet<'db, 'c> { let env = self.env; let Some(attribute_type) = - protocol_member_read_type(db, env, ty, receiver_ty, member, source_member, access) + protocol_member_read_type(db, env, ty, receiver_ty, member, access) else { return self.never(); }; @@ -3013,7 +2995,6 @@ ty: Type<'db>, receiver_ty: Type<'db>, member: &ProtocolMember<'_, 'db>, - source_member: Option<&ProtocolMember<'_, 'db>>, required: ProtocolMemberAccess<'db>, access: ProtocolMemberAccessMode, ) -> ConstraintSet<'db, 'c> { @@ -3028,17 +3009,12 @@ return self.always(); } - if source_member.is_some_and(ProtocolMember::is_method) { - return self.always(); - } - if protocol_member_read_type( db, self.env, ty, receiver_ty, member, - source_member, ProtocolMemberAccessMode::Class, ) .is_none() @@ -3048,7 +3024,9 @@ // TODO special-casing `ClassVar`s shouldn't be necessary here, this should naturally // fall out of more generalised checks. - let qualifiers = if let Some(source_member) = source_member { + let qualifiers = if let Type::ProtocolInstance(protocol) = ty + && let Some(source_member) = protocol.interface(db).member_by_name(db, member.name) + { source_member.qualifiers() } else { receiver_ty.member(db, self.env, member.name).qualifiers @@ -3063,15 +3041,7 @@ let read_result = required.read.map_or_else( || self.always(), |required_ty| { - self.check_protocol_member_read( - db, - ty, - receiver_ty, - member, - source_member, - required_ty, - access, - ) + self.check_protocol_member_read(db, ty, receiver_ty, member, required_ty, access) }, ); @@ -3091,30 +3061,7 @@ write .bind_compatibility_type(db, env, fallback_ty) .when_some_and(db, self.constraints, |write_ty| { - let protocol_requirement = source_member.map(|source_member| { - let write = source_member.access(db, env, access).write; - let write = match access { - ProtocolMemberAccessMode::Instance => write.and_then(|write| { - write.bind_requirement(db, env, receiver_ty) - }), - ProtocolMemberAccessMode::Class => write - .and_then(|write| { - write.bind_compatibility_type(db, env, ty) - }) - .map(ProtocolMemberWriteRequirement::AssignableTo), - }; - AttributeWriteRequirement::ProtocolMember { - write, - qualifiers: source_member.qualifiers(), - } - }); - self.check_property_write( - db, - receiver_ty, - member.name, - write_ty, - protocol_requirement, - ) + self.check_property_write(db, receiver_ty, member.name, write_ty) }) }, ) @@ -3129,27 +3076,12 @@ member: &ProtocolMember<'_, 'db>, ) -> ConstraintSet<'db, 'c> { let env = self.env; - let source_protocol = ty.as_protocol_instance(); - let source_member = source_protocol - .and_then(|protocol| protocol.interface(db).member_by_name(db, member.name)); + let class_receiver_ty = ty + .as_protocol_instance() + .map(|protocol| protocol.to_meta_type(db, env)) + .unwrap_or_else(|| ty.to_meta_type(db, env)); let instance_access = member.implementation_access(db, env, ty, ProtocolMemberAccessMode::Instance); - let class_access = - member.implementation_access(db, env, ty, ProtocolMemberAccessMode::Class); - // A declared protocol method already guarantees class-side presence. Callback protocols - // instead use the candidate's callable type directly, so neither needs a class receiver. - let class_access_already_satisfied = member.is_instance_method() - && (member.name == "__call__" - || source_member - .as_ref() - .is_some_and(ProtocolMember::is_method)); - let class_receiver_ty = (class_access != ProtocolMemberAccess::NONE - && !class_access_already_satisfied) - .then(|| { - source_protocol - .map(|protocol| protocol.to_meta_type(db, env)) - .unwrap_or_else(|| ty.to_meta_type(db, env)) - }); if let Some(context) = self.report_context() { let instance_read_missing = instance_access.read.is_some() && protocol_member_read_type( @@ -3158,27 +3090,23 @@ ty, ty, member, - source_member.as_ref(), ProtocolMemberAccessMode::Instance, ) .is_none(); - let class_read_missing = || { - class_access.read.is_some() - && !(member.is_instance_method() && member.name == "__call__") - && class_receiver_ty.is_some_and(|class_receiver_ty| { - protocol_member_read_type( - db, - env, - ty, - class_receiver_ty, - member, - source_member.as_ref(), - ProtocolMemberAccessMode::Class, - ) - .is_none() - }) - }; - if instance_read_missing || class_read_missing() { + let class_access = + member.implementation_access(db, env, ty, ProtocolMemberAccessMode::Class); + let class_read_missing = class_access.read.is_some() + && !(member.is_instance_method() && member.name == "__call__") + && protocol_member_read_type( + db, + env, + ty, + class_receiver_ty, + member, + ProtocolMemberAccessMode::Class, + ) + .is_none(); + if instance_read_missing || class_read_missing { if instance_read_missing && is_class_object_type(ty) && member.is_instance_method() @@ -3194,30 +3122,27 @@ } } - let instance_result = self.type_satisfies_protocol_member_access( - db, - ty, - ty, - member, - source_member.as_ref(), - instance_access, - ProtocolMemberAccessMode::Instance, - ); - let result = if let Some(class_receiver_ty) = class_receiver_ty { - instance_result.and(db, self.constraints, || { + let result = self + .type_satisfies_protocol_member_access( + db, + ty, + ty, + member, + instance_access, + ProtocolMemberAccessMode::Instance, + ) + .and(db, self.constraints, || { + let class_access = + member.implementation_access(db, env, ty, ProtocolMemberAccessMode::Class); self.type_satisfies_protocol_member_access( db, ty, class_receiver_ty, member, - source_member.as_ref(), class_access, ProtocolMemberAccessMode::Class, ) - }) - } else { - instance_result - }; + }); if let Some(context) = self.report_context() && result.is_never_satisfied(db, env) { @@ -3251,9 +3176,6 @@ return self.always(); } - let source_member = instance_ty - .as_protocol_instance() - .and_then(|protocol| protocol.interface(db).member_by_name(db, member.name)); let result = if member.is_method() { required.read.map_or_else( || self.always(), @@ -3263,7 +3185,6 @@ instance_ty, meta_ty, &member, - source_member.as_ref(), required_ty, ProtocolMemberAccessMode::Class, ) @@ -3275,7 +3196,6 @@ instance_ty, meta_ty, &member, - source_member.as_ref(), required, ProtocolMemberAccessMode::Class, )