1use crate::TypeDatabase;
8use crate::compat::{AssignabilityOverrideProvider, CompatChecker, NoopOverrideProvider};
9use crate::db::QueryDatabase;
10use crate::inheritance::InheritanceGraph;
11use crate::operations::AssignabilityChecker;
12use crate::subtype::{AnyPropagationMode, NoopResolver, SubtypeChecker, TypeResolver};
13use crate::types::{RelationCacheKey, SymbolRef, TypeId};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum RelationKind {
18 Assignable,
20 AssignableBivariantCallbacks,
22 Subtype,
24 Overlap,
26 RedeclarationIdentical,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct RelationPolicy {
33 pub flags: u16,
35 pub strict_subtype_checking: bool,
37 pub strict_any_propagation: bool,
39 pub any_propagation_mode: AnyPropagationMode,
41}
42
43impl Default for RelationPolicy {
44 fn default() -> Self {
45 Self {
46 flags: RelationCacheKey::FLAG_STRICT_NULL_CHECKS,
47 strict_subtype_checking: false,
48 strict_any_propagation: false,
49 any_propagation_mode: AnyPropagationMode::All,
50 }
51 }
52}
53
54impl RelationPolicy {
55 pub const fn from_flags(flags: u16) -> Self {
56 use crate::RelationCacheKey;
57 let strict_any = (flags & RelationCacheKey::FLAG_STRICT_FUNCTION_TYPES) != 0;
58 Self {
59 flags,
60 strict_subtype_checking: false,
61 strict_any_propagation: strict_any,
62 any_propagation_mode: if strict_any {
63 AnyPropagationMode::TopLevelOnly
64 } else {
65 AnyPropagationMode::All
66 },
67 }
68 }
69
70 pub const fn with_strict_subtype_checking(mut self, strict: bool) -> Self {
71 self.strict_subtype_checking = strict;
72 self
73 }
74
75 pub const fn with_strict_any_propagation(mut self, strict: bool) -> Self {
76 self.strict_any_propagation = strict;
77 self
78 }
79
80 pub const fn with_any_propagation_mode(mut self, mode: AnyPropagationMode) -> Self {
81 self.any_propagation_mode = mode;
82 self
83 }
84}
85
86#[derive(Clone, Copy, Default)]
88pub struct RelationContext<'a> {
89 pub query_db: Option<&'a dyn QueryDatabase>,
90 pub inheritance_graph: Option<&'a InheritanceGraph>,
91 pub class_check: Option<&'a dyn Fn(SymbolRef) -> bool>,
92}
93
94#[derive(Debug, Clone, Copy, PartialEq, Eq)]
96pub struct RelationResult {
97 pub kind: RelationKind,
98 pub related: bool,
99 pub depth_exceeded: bool,
100}
101
102impl RelationResult {
103 #[inline]
104 pub const fn is_related(self) -> bool {
105 self.related
106 }
107}
108
109#[derive(Debug, Clone)]
111pub struct AssignabilityFailureAnalysis {
112 pub weak_union_violation: bool,
113 pub failure_reason: Option<crate::SubtypeFailureReason>,
114}
115
116pub fn analyze_assignability_failure_with_resolver<'a, R: TypeResolver, F>(
118 interner: &'a dyn TypeDatabase,
119 resolver: &'a R,
120 source: TypeId,
121 target: TypeId,
122 configure: F,
123) -> AssignabilityFailureAnalysis
124where
125 F: FnOnce(&mut CompatChecker<'a, R>),
126{
127 let mut checker = CompatChecker::with_resolver(interner, resolver);
128 configure(&mut checker);
129 AssignabilityFailureAnalysis {
130 weak_union_violation: checker.is_weak_union_violation(source, target),
131 failure_reason: checker.explain_failure(source, target),
132 }
133}
134
135pub fn query_relation(
137 interner: &dyn TypeDatabase,
138 source: TypeId,
139 target: TypeId,
140 kind: RelationKind,
141 policy: RelationPolicy,
142 context: RelationContext<'_>,
143) -> RelationResult {
144 let resolver = NoopResolver;
145 query_relation_with_resolver(interner, &resolver, source, target, kind, policy, context)
146}
147
148pub fn query_relation_with_resolver<'a, R: TypeResolver>(
150 interner: &'a dyn TypeDatabase,
151 resolver: &'a R,
152 source: TypeId,
153 target: TypeId,
154 kind: RelationKind,
155 policy: RelationPolicy,
156 context: RelationContext<'a>,
157) -> RelationResult {
158 let overrides = NoopOverrideProvider;
159 query_relation_with_overrides(RelationQueryInputs {
160 interner,
161 resolver,
162 source,
163 target,
164 kind,
165 policy,
166 context,
167 overrides: &overrides,
168 })
169}
170
171pub fn query_relation_with_overrides<
173 'a,
174 R: TypeResolver,
175 P: AssignabilityOverrideProvider + ?Sized,
176>(
177 RelationQueryInputs {
178 interner,
179 resolver,
180 source,
181 target,
182 kind,
183 policy,
184 context,
185 overrides,
186 }: RelationQueryInputs<'a, R, P>,
187) -> RelationResult {
188 let (related, depth_exceeded) = match kind {
189 RelationKind::Assignable => {
190 let mut checker = configured_compat_checker(interner, resolver, policy, context);
191 (
192 checker.is_assignable_with_overrides(source, target, overrides),
193 false,
194 )
195 }
196 RelationKind::AssignableBivariantCallbacks => {
197 let mut checker = configured_compat_checker(interner, resolver, policy, context);
198 let _ = overrides;
199 (
200 checker.is_assignable_to_bivariant_callback(source, target),
201 false,
202 )
203 }
204 RelationKind::Subtype => {
205 let mut checker = configured_subtype_checker(interner, resolver, policy, context);
206 let related = checker.is_subtype_of(source, target);
207 (related, checker.depth_exceeded())
208 }
209 RelationKind::Overlap => {
210 let checker = configured_subtype_checker(interner, resolver, policy, context);
211 (checker.are_types_overlapping(source, target), false)
212 }
213 RelationKind::RedeclarationIdentical => {
214 let mut checker = configured_compat_checker(interner, resolver, policy, context);
215 (
216 checker.are_types_identical_for_redeclaration(source, target),
217 false,
218 )
219 }
220 };
221
222 RelationResult {
223 kind,
224 related,
225 depth_exceeded,
226 }
227}
228
229pub struct RelationQueryInputs<'a, R: TypeResolver, P: AssignabilityOverrideProvider + ?Sized> {
231 pub interner: &'a dyn TypeDatabase,
232 pub resolver: &'a R,
233 pub source: TypeId,
234 pub target: TypeId,
235 pub kind: RelationKind,
236 pub policy: RelationPolicy,
237 pub context: RelationContext<'a>,
238 pub overrides: &'a P,
239}
240
241fn configured_compat_checker<'a, R: TypeResolver>(
242 interner: &'a dyn TypeDatabase,
243 resolver: &'a R,
244 policy: RelationPolicy,
245 context: RelationContext<'a>,
246) -> CompatChecker<'a, R> {
247 let mut checker = CompatChecker::with_resolver(interner, resolver);
248 checker.apply_flags(policy.flags);
249 checker.set_inheritance_graph(context.inheritance_graph);
250 checker.set_strict_subtype_checking(policy.strict_subtype_checking);
251 checker.set_strict_any_propagation(policy.strict_any_propagation);
252 if let Some(query_db) = context.query_db {
253 checker.set_query_db(query_db);
254 }
255 checker
256}
257
258fn configured_subtype_checker<'a, R: TypeResolver>(
259 interner: &'a dyn TypeDatabase,
260 resolver: &'a R,
261 policy: RelationPolicy,
262 context: RelationContext<'a>,
263) -> SubtypeChecker<'a, R> {
264 let mut checker = SubtypeChecker::with_resolver(interner, resolver)
265 .apply_flags(policy.flags)
266 .with_any_propagation_mode(policy.any_propagation_mode);
267 if let Some(query_db) = context.query_db {
268 checker = checker.with_query_db(query_db);
269 }
270 if let Some(inheritance_graph) = context.inheritance_graph {
271 checker = checker.with_inheritance_graph(inheritance_graph);
272 }
273 if let Some(class_check) = context.class_check {
274 checker = checker.with_class_check(class_check);
275 }
276 checker
277}
278
279#[cfg(test)]
280#[path = "../tests/relation_queries_tests.rs"]
281mod tests;