1use dada_ir_ast::{diagnostic::Errors, span::Span};
4use dada_util::boxed_async_fn;
5
6use crate::{
7 check::{
8 env::Env,
9 inference::{Direction, InferVarKind},
10 live_places::LivePlaces,
11 red::RedTy,
12 report::{Because, OrElse},
13 subtype::perms::require_sub_perms,
14 to_red::ToRedTy,
15 },
16 ir::{
17 classes::SymAggregateStyle,
18 indices::{FromInfer, InferVarIndex},
19 types::{SymGenericKind, SymGenericTerm, SymPerm, SymTy, SymTyKind, SymTyName},
20 },
21};
22
23pub async fn require_assignable_type<'db>(
24 env: &mut Env<'db>,
25 live_after: LivePlaces,
26 value_ty: SymTy<'db>,
27 place_ty: SymTy<'db>,
28 or_else: &dyn OrElse<'db>,
29) -> Errors<()> {
30 let db = env.db();
31
32 match (value_ty.kind(db), place_ty.kind(db)) {
33 (SymTyKind::Never, _) => Ok(()),
34 _ => require_sub_terms(env, live_after, value_ty.into(), place_ty.into(), or_else).await,
35 }
36}
37
38pub async fn require_sub_terms<'db>(
39 env: &mut Env<'db>,
40 live_after: LivePlaces,
41 lower: SymGenericTerm<'db>,
42 upper: SymGenericTerm<'db>,
43 or_else: &dyn OrElse<'db>,
44) -> Errors<()> {
45 env.log("require_sub_terms", &[&lower, &upper]);
46 let red_term_lower = lower.to_red_ty(env);
47 let red_term_upper = upper.to_red_ty(env);
48 require_sub_red_terms(env, live_after, red_term_lower, red_term_upper, or_else).await
49}
50
51#[boxed_async_fn]
52pub async fn require_sub_red_terms<'db>(
53 env: &mut Env<'db>,
54 live_after: LivePlaces,
55 (lower_red_ty, lower_perm): (RedTy<'db>, SymPerm<'db>),
56 (upper_red_ty, upper_perm): (RedTy<'db>, SymPerm<'db>),
57 or_else: &dyn OrElse<'db>,
58) -> Errors<()> {
59 env.log(
60 "require_sub_red_terms",
61 &[&lower_perm, &lower_red_ty, &upper_perm, &upper_red_ty],
62 );
63 match (&lower_red_ty, &upper_red_ty) {
64 (&RedTy::Error(reported), _) | (_, &RedTy::Error(reported)) => Err(reported),
65
66 (&RedTy::Infer(lower_infer), &RedTy::Infer(upper_infer)) => {
67 require_infer_sub_infer(
68 env,
69 live_after,
70 lower_perm,
71 lower_infer,
72 upper_perm,
73 upper_infer,
74 or_else,
75 )
76 .await
77 }
78
79 (&RedTy::Infer(lower_infer), _) => {
80 require_infer_sub_ty(
81 env,
82 live_after,
83 lower_perm,
84 lower_infer,
85 upper_perm,
86 upper_red_ty,
87 or_else,
88 )
89 .await
90 }
91
92 (_, &RedTy::Infer(upper_infer)) => {
93 require_ty_sub_infer(
94 env,
95 live_after,
96 lower_perm,
97 lower_red_ty,
98 upper_perm,
99 upper_infer,
100 or_else,
101 )
102 .await
103 }
104
105 (
106 &RedTy::Named(name_lower, ref lower_generics),
107 &RedTy::Named(name_upper, ref upper_generics),
108 ) => {
109 if name_lower == name_upper {
110 let variances = env.variances(name_lower);
111 assert_eq!(lower_generics.len(), upper_generics.len());
112 env.require_for_all(
113 variances
114 .iter()
115 .zip(lower_generics.iter().zip(upper_generics)),
116 async |env, (&variance, (&lower_generic, &upper_generic))| {
117 let db = env.db();
118 let mut lower_generic = lower_generic;
119 let mut upper_generic = upper_generic;
120
121 if !variance.relative {
122 lower_generic = lower_perm.apply_to(db, lower_generic);
123 upper_generic = upper_perm.apply_to(db, upper_generic);
124 }
125
126 env.require_both(
127 async |env| {
128 if variance.at_least_covariant {
129 require_sub_terms(
130 env,
131 live_after,
132 lower_generic,
133 upper_generic,
134 or_else,
135 )
136 .await
137 } else {
138 Ok(())
139 }
140 },
141 async |env| {
142 if variance.at_least_contravariant {
143 require_sub_terms(
144 env,
145 live_after,
146 upper_generic,
147 lower_generic,
148 or_else,
149 )
150 .await
151 } else {
152 Ok(())
153 }
154 },
155 )
156 .await
157 },
158 )
159 .await?;
160
161 match name_lower.style(env.db()) {
162 SymAggregateStyle::Struct => {}
163 SymAggregateStyle::Class => {
164 require_sub_perms(env, live_after, lower_perm, upper_perm, or_else).await?;
165 }
166 }
167
168 Ok(())
169 } else {
170 Err(or_else.report(env, Because::NameMismatch(name_lower, name_upper)))
171 }
172 }
173 (&RedTy::Named(..), _) | (_, &RedTy::Named(..)) => {
174 Err(or_else.report(env, Because::JustSo))
175 }
176
177 (&RedTy::Never, &RedTy::Never) => {
178 require_sub_perms(env, live_after, lower_perm, upper_perm, or_else).await
179 }
180 (&RedTy::Never, _) | (_, &RedTy::Never) => Err(or_else.report(env, Because::JustSo)),
181
182 (&RedTy::Var(var_lower), &RedTy::Var(var_upper)) => {
183 if var_lower == var_upper {
184 require_sub_perms(env, live_after, lower_perm, upper_perm, or_else).await
185 } else {
186 Err(or_else.report(env, Because::UniversalMismatch(var_lower, var_upper)))
187 }
188 }
189 (&RedTy::Var(_), _) | (_, &RedTy::Var(_)) => Err(or_else.report(env, Because::JustSo)),
190
191 (&RedTy::Perm, &RedTy::Perm) => {
192 require_sub_perms(env, live_after, lower_perm, upper_perm, or_else).await
193 }
194 }
195}
196
197async fn require_infer_sub_infer<'db>(
203 env: &mut Env<'db>,
204 live_after: LivePlaces,
205 lower_perm: SymPerm<'db>,
206 lower_infer: InferVarIndex,
207 upper_perm: SymPerm<'db>,
208 upper_infer: InferVarIndex,
209 or_else: &dyn OrElse<'db>,
210) -> Errors<()> {
211 debug_assert_eq!(env.infer_var_kind(lower_infer), InferVarKind::Type);
212 debug_assert_eq!(env.infer_var_kind(upper_infer), InferVarKind::Type);
213
214 if lower_infer == upper_infer {
215 return Ok(());
216 }
217
218 if env.insert_sub_infer_var_pair(lower_infer, upper_infer) {
220 env.require_both(
221 async |env| {
222 env.for_each_bound(
223 Direction::FromBelow,
224 lower_infer,
225 async |env, lower_bound, _or_else| {
226 require_sub_red_terms(
227 env,
228 live_after,
229 (lower_bound.clone(), lower_perm),
230 (RedTy::Infer(upper_infer), upper_perm),
231 or_else,
232 )
233 .await
234 },
235 )
236 .await
237 },
238 async |env| {
239 env.for_each_bound(
240 Direction::FromAbove,
241 upper_infer,
242 async |env, upper_bound, _or_else| {
243 require_sub_red_terms(
244 env,
245 live_after,
246 (RedTy::Infer(lower_infer), lower_perm),
247 (upper_bound.clone(), upper_perm),
248 or_else,
249 )
250 .await
251 },
252 )
253 .await
254 },
255 )
256 .await
257 } else {
258 Ok(())
259 }
260}
261
262async fn require_ty_sub_infer<'db>(
264 env: &mut Env<'db>,
265 live_after: LivePlaces,
266 lower_perm: SymPerm<'db>,
267 lower_ty: RedTy<'db>,
268 upper_perm: SymPerm<'db>,
269 upper_infer: InferVarIndex,
270 or_else: &dyn OrElse<'db>,
271) -> Errors<()> {
272 debug_assert!(
273 !matches!(lower_ty, RedTy::Infer(_)),
274 "unexpected inference variable"
275 );
276
277 let generalized_ty =
280 require_infer_has_bound(env, Direction::FromBelow, &lower_ty, upper_infer, or_else).await?;
281
282 require_sub_red_terms(
284 env,
285 live_after,
286 (lower_ty, lower_perm),
287 (generalized_ty, upper_perm),
288 or_else,
289 )
290 .await
291}
292
293async fn require_infer_sub_ty<'db>(
296 env: &mut Env<'db>,
297 live_after: LivePlaces,
298 lower_perm: SymPerm<'db>,
299 lower_infer: InferVarIndex,
300 upper_perm: SymPerm<'db>,
301 upper_ty: RedTy<'db>,
302 or_else: &dyn OrElse<'db>,
303) -> Errors<()> {
304 debug_assert!(
305 !matches!(upper_ty, RedTy::Infer(_)),
306 "unexpected inference variable"
307 );
308
309 let generalized_ty =
312 require_infer_has_bound(env, Direction::FromAbove, &upper_ty, lower_infer, or_else).await?;
313
314 require_sub_red_terms(
315 env,
316 live_after,
317 (generalized_ty, lower_perm),
318 (upper_ty, upper_perm),
319 or_else,
320 )
321 .await
322}
323
324async fn require_infer_has_bound<'db>(
327 env: &mut Env<'db>,
328 direction: Direction,
329 bound: &RedTy<'db>,
330 infer: InferVarIndex,
331 or_else: &dyn OrElse<'db>,
332) -> Errors<RedTy<'db>> {
333 match env.red_bound(infer, direction).peek_ty() {
334 None => {
335 let span = env.infer_var_span(infer);
338 let generalized = generalize(env, bound, span)?;
339 env.red_bound(infer, direction)
340 .set_ty(generalized.clone(), or_else);
341 Ok(generalized)
342 }
343
344 Some((generalized, _generalized_or_else)) => {
345 Ok(generalized)
349 }
350 }
351}
352
353fn generalize<'db>(env: &mut Env<'db>, red_ty: &RedTy<'db>, span: Span<'db>) -> Errors<RedTy<'db>> {
356 let db = env.db();
357 let red_ty_generalized = match red_ty {
358 RedTy::Error(reported) => return Err(*reported),
359 RedTy::Never => RedTy::Never,
360 RedTy::Infer(_) => unreachable!("infer should not get here"),
361 RedTy::Var(sym_variable) => RedTy::Var(*sym_variable),
362 RedTy::Perm => RedTy::Perm,
363 RedTy::Named(sym_ty_name, generics) => {
364 let generics_generalized = generics
365 .iter()
366 .map(|generic| match *generic {
367 SymGenericTerm::Type(_) => {
368 let v = env.fresh_inference_var(SymGenericKind::Type, span);
369 SymTy::infer(db, v).into()
370 }
371 SymGenericTerm::Perm(_) => {
372 let v = env.fresh_inference_var(SymGenericKind::Perm, span);
373 SymPerm::infer(db, v).into()
374 }
375 SymGenericTerm::Place(p) => SymGenericTerm::Place(p),
376 SymGenericTerm::Error(reported) => SymGenericTerm::Error(reported),
377 })
378 .collect();
379 RedTy::Named(*sym_ty_name, generics_generalized)
380 }
381 };
382 Ok(red_ty_generalized)
383}
384
385pub async fn reconcile_ty_bounds<'db>(env: &mut Env<'db>, infer: InferVarIndex) -> Errors<()> {
388 assert_eq!(env.infer_var_kind(infer), InferVarKind::Type);
389
390 env.require_all()
391 .require(async |env| propagate_inverse_bound(env, infer, Direction::FromAbove).await)
392 .require(async |env| propagate_inverse_bound(env, infer, Direction::FromBelow).await)
393 .finish()
394 .await
395}
396
397async fn propagate_inverse_bound<'db>(
413 env: &mut Env<'db>,
414 infer: InferVarIndex,
415 direction: Direction,
416) -> Errors<()> {
417 let db = env.db();
418
419 let span = env.infer_var_span(infer);
420
421 let perm_infer = SymPerm::infer(db, env.perm_infer(infer));
433
434 env.for_each_bound(direction, infer, async |env, red_ty, or_else| {
438 let opposite_bound = match red_ty {
440 RedTy::Error(_) => None,
441
442 RedTy::Named(sym_ty_name, _) => match sym_ty_name {
443 SymTyName::Primitive(_) | SymTyName::Future | SymTyName::Tuple { .. } => {
444 Some(generalize(env, red_ty, span)?)
445 }
446 SymTyName::Aggregate(_sym_aggregate) => {
447 Some(generalize(env, red_ty, span)?)
450 }
451 },
452
453 RedTy::Never | RedTy::Var(..) => Some(red_ty.clone()),
454
455 RedTy::Infer(..) | RedTy::Perm => {
456 unreachable!("unexpected kind for red-ty bound: {red_ty:?}")
457 }
458 };
459
460 if let Some(opposite_bound) = opposite_bound {
462 match direction {
463 Direction::FromAbove => {
464 require_ty_sub_infer(
466 env,
467 LivePlaces::fixme(),
468 perm_infer,
470 opposite_bound,
471 perm_infer,
473 infer,
474 &or_else,
475 )
476 .await?;
477 }
478 Direction::FromBelow => {
479 require_infer_sub_ty(
481 env,
482 LivePlaces::fixme(),
483 perm_infer,
485 infer,
486 perm_infer,
488 opposite_bound,
489 &or_else,
490 )
491 .await?
492 }
493 }
494 }
495
496 Ok(())
497 })
498 .await
499}