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