1use alloc::{boxed::Box, vec::Vec};
2
3use crate::{
4 frontend::RuntimeAssign,
5 ir::Switch,
6 prelude::{CubeEnum, ExpandTypeClone},
7};
8use crate::{
9 ir::{Branch, If, IfElse, Loop, Scope},
10 prelude::Assign,
11};
12
13use super::{Int, NativeExpand};
14
15pub trait Iterable: Sized {
18 type Item;
19
20 fn expand(self, scope: &Scope, body: impl FnMut(&Scope, Self::Item));
27 fn expand_unroll(self, scope: &Scope, body: impl FnMut(&Scope, Self::Item));
34 fn const_len(&self) -> Option<usize> {
36 None
37 }
38}
39
40pub fn for_expand<I: Iterable>(
41 scope: &Scope,
42 range: I,
43 unroll: bool,
44 body: impl FnMut(&Scope, I::Item),
45) {
46 if unroll || range.const_len() == Some(1) {
47 range.expand_unroll(scope, body);
48 } else {
49 range.expand(scope, body);
50 }
51}
52
53pub fn if_expand(scope: &Scope, condition: NativeExpand<bool>, block: impl FnOnce(&Scope)) {
54 let comptime_cond = condition.expand.as_const().map(|it| it.as_bool());
55 match comptime_cond {
56 Some(cond) => {
57 if cond {
58 block(scope);
59 }
60 }
61 None => {
62 let child = scope.child();
63
64 block(&child);
65
66 scope.register(Branch::If(Box::new(If {
67 cond: condition.expand,
68 scope: child,
69 })));
70 }
71 }
72}
73
74#[allow(clippy::large_enum_variant)]
75pub enum IfElseExpand {
76 ComptimeThen,
77 ComptimeElse,
78 Runtime {
79 runtime_cond: NativeExpand<bool>,
80 then_child: Scope,
81 },
82}
83
84impl IfElseExpand {
85 pub fn or_else(self, scope: &Scope, else_block: impl FnOnce(&Scope)) {
86 match self {
87 Self::Runtime {
88 runtime_cond,
89 then_child,
90 } => {
91 let else_child = scope.child();
92 else_block(&else_child);
93
94 scope.register(Branch::IfElse(Box::new(IfElse {
95 cond: runtime_cond.expand,
96 scope_if: then_child,
97 scope_else: else_child,
98 })));
99 }
100 Self::ComptimeElse => else_block(scope),
101 Self::ComptimeThen => (),
102 }
103 }
104}
105
106pub fn if_else_expand(
107 scope: &Scope,
108 condition: NativeExpand<bool>,
109 then_block: impl FnOnce(&Scope),
110) -> IfElseExpand {
111 let comptime_cond = condition.expand.as_const().map(|it| it.as_bool());
112 match comptime_cond {
113 Some(true) => {
114 then_block(scope);
115 IfElseExpand::ComptimeThen
116 }
117 Some(false) => IfElseExpand::ComptimeElse,
118 None => {
119 let then_child = scope.child();
120 then_block(&then_child);
121
122 IfElseExpand::Runtime {
123 runtime_cond: condition,
124 then_child,
125 }
126 }
127 }
128}
129
130#[allow(clippy::large_enum_variant)]
131pub enum IfElseExprExpand<C: Assign> {
132 ComptimeThen(C),
133 ComptimeElse,
134 Runtime {
135 runtime_cond: NativeExpand<bool>,
136 out: C,
137 then_child: Scope,
138 },
139}
140
141impl<C: Assign> IfElseExprExpand<C> {
142 pub fn or_else<R: RuntimeAssign<Expand = C>>(
143 self,
144 scope: &Scope,
145 else_block: impl FnOnce(&Scope) -> R,
146 ) -> C {
147 match self {
148 Self::Runtime {
149 runtime_cond,
150 mut out,
151 then_child,
152 } => {
153 let else_child = scope.child();
154 let ret = else_block(&else_child);
155 out.__expand_assign_method(&else_child, ret.into_expand(scope));
156
157 scope.register(Branch::IfElse(Box::new(IfElse {
158 cond: runtime_cond.expand,
159 scope_if: then_child,
160 scope_else: else_child,
161 })));
162 out
163 }
164 Self::ComptimeElse => else_block(scope).into_expand(scope),
165 Self::ComptimeThen(ret) => ret,
166 }
167 }
168}
169
170pub fn if_else_expr_expand<C: RuntimeAssign>(
171 scope: &Scope,
172 condition: NativeExpand<bool>,
173 then_block: impl FnOnce(&Scope) -> C,
174) -> IfElseExprExpand<C::Expand> {
175 let comptime_cond = condition.expand.as_const().map(|it| it.as_bool());
176 match comptime_cond {
177 Some(true) => {
178 let ret = then_block(scope);
179 IfElseExprExpand::ComptimeThen(ret.into_expand(scope))
180 }
181 Some(false) => IfElseExprExpand::ComptimeElse,
182 None => {
183 let then_child = scope.child();
184 let ret = then_block(&then_child);
185 let mut out = ret.init_mut(scope);
186 out.__expand_assign_method(&then_child, ret.into_expand(scope));
187
188 IfElseExprExpand::Runtime {
189 runtime_cond: condition,
190 out,
191 then_child,
192 }
193 }
194 }
195}
196
197pub struct SwitchExpand<I: Int> {
198 value: NativeExpand<I>,
199 default: Scope,
200 cases: Vec<(NativeExpand<I>, Scope)>,
201}
202
203impl<I: Int> SwitchExpand<I> {
204 pub fn case(mut self, scope: &Scope, value: impl Int, block: impl FnOnce(&Scope)) -> Self {
205 let value = I::from(value).unwrap();
206 let case_child = scope.child();
207 block(&case_child);
208 self.cases.push((value.into(), case_child));
209 self
210 }
211
212 pub fn finish(self, scope: &Scope) {
213 let value_var = self.value.expand;
214 scope.register(Branch::Switch(Box::new(Switch {
215 value: value_var,
216 scope_default: self.default,
217 cases: self
218 .cases
219 .into_iter()
220 .map(|it| (it.0.expand, it.1))
221 .collect(),
222 })));
223 }
224}
225
226pub fn switch_expand<I: Int>(
227 scope: &Scope,
228 value: NativeExpand<I>,
229 default_block: impl FnOnce(&Scope),
230) -> SwitchExpand<I> {
231 let default_child = scope.child();
232 default_block(&default_child);
233
234 SwitchExpand {
235 value,
236 default: default_child,
237 cases: Vec::new(),
238 }
239}
240
241pub struct SwitchExpandExpr<I: Int, C: Assign> {
242 value: NativeExpand<I>,
243 out: C,
244 default: Scope,
245 cases: Vec<(NativeExpand<I>, Scope)>,
246}
247
248impl<I: Int, C: Assign> SwitchExpandExpr<I, C> {
249 pub fn case<T: RuntimeAssign<Expand = C>>(
250 mut self,
251 scope: &Scope,
252 value: impl Int,
253 block: impl FnOnce(&Scope) -> T,
254 ) -> Self {
255 let value = I::from(value).unwrap();
256 let case_child = scope.child();
257 let ret = block(&case_child);
258 self.out
259 .__expand_assign_method(&case_child, ret.into_expand(scope));
260 self.cases.push((value.into(), case_child));
261 self
262 }
263
264 pub fn finish(self, scope: &Scope) -> C {
265 let value_var = self.value.expand;
266 scope.register(Branch::Switch(Box::new(Switch {
267 value: value_var,
268 scope_default: self.default,
269 cases: self
270 .cases
271 .into_iter()
272 .map(|it| (it.0.expand, it.1))
273 .collect(),
274 })));
275 self.out
276 }
277}
278
279pub fn switch_expand_expr<I: Int, C: RuntimeAssign>(
280 scope: &Scope,
281 value: NativeExpand<I>,
282 default_block: impl FnOnce(&Scope) -> C,
283) -> SwitchExpandExpr<I, C::Expand> {
284 let default_child = scope.child();
285 let default = default_block(&default_child);
286 let mut out = default.init_mut(scope);
287 out.__expand_assign_method(&default_child, default.into_expand(scope));
288
289 SwitchExpandExpr {
290 value,
291 out,
292 default: default_child,
293 cases: Vec::new(),
294 }
295}
296
297#[allow(clippy::large_enum_variant)]
298pub enum MatchExpand<T: CubeEnum> {
299 ComptimeVariant {
300 variant: i32,
301 runtime_value: T::RuntimeValue,
302 matched: bool,
303 },
304 RuntimeVariant {
305 variant: NativeExpand<i32>,
306 cases: Vec<(NativeExpand<i32>, Scope)>,
307 runtime_value: T::RuntimeValue,
308 default: Option<Scope>,
309 },
310}
311
312impl<T: CubeEnum> MatchExpand<T> {
313 pub fn case(
314 mut self,
315 scope: &Scope,
316 value: i32,
317 block: impl FnOnce(&Scope, T::RuntimeValue),
318 ) -> Self {
319 match &mut self {
320 Self::RuntimeVariant {
321 cases,
322 runtime_value,
323 ..
324 } => {
325 let case_child = scope.child();
326 block(&case_child, (*runtime_value).clone_unchecked());
327 cases.push((value.into(), case_child));
328 }
329 Self::ComptimeVariant {
330 variant,
331 runtime_value,
332 matched,
333 } => {
334 if value == *variant {
335 block(scope, (*runtime_value).clone_unchecked());
336 *matched = true;
337 }
338 }
339 }
340 self
341 }
342
343 pub fn default(mut self, scope: &Scope, block: impl FnOnce(&Scope, T::RuntimeValue)) -> Self {
344 match &mut self {
345 Self::RuntimeVariant {
346 runtime_value,
347 default,
348 ..
349 } => {
350 let case_child = scope.child();
351 block(&case_child, (*runtime_value).clone_unchecked());
352 *default = Some(case_child);
353 }
354 Self::ComptimeVariant {
355 runtime_value,
356 matched,
357 ..
358 } => {
359 if !*matched {
360 block(scope, (*runtime_value).clone_unchecked());
361 *matched = true;
362 }
363 }
364 }
365 self
366 }
367
368 pub fn finish(self, scope: &Scope) {
369 match self {
370 MatchExpand::ComptimeVariant { .. } => {}
371 MatchExpand::RuntimeVariant {
372 variant,
373 cases,
374 default,
375 ..
376 } => {
377 let variant_var = variant.expand;
378 let scope_default = default.unwrap_or_else(|| {
379 let scope_default = scope.child();
380 unreachable_unchecked::expand(&scope_default);
381 scope_default
382 });
383
384 scope.register(Branch::Switch(Box::new(Switch {
385 value: variant_var,
386 scope_default,
387 cases: cases.into_iter().map(|it| (it.0.expand, it.1)).collect(),
388 })));
389 }
390 }
391 }
392}
393
394pub fn match_expand<T: CubeEnum>(
395 scope: &Scope,
396 value: T,
397 discriminant0: i32,
398 arm0: impl FnOnce(&Scope, T::RuntimeValue),
399) -> MatchExpand<T> {
400 let discriminant = value.discriminant();
401 match discriminant.constant() {
402 Some(const_variant) if const_variant.as_i32() == discriminant0 => {
403 let runtime_value = value.runtime_value();
404 arm0(scope, runtime_value.clone_unchecked());
405 MatchExpand::ComptimeVariant {
406 variant: const_variant.as_i32(),
407 runtime_value,
408 matched: true,
409 }
410 }
411 Some(const_variant) => MatchExpand::ComptimeVariant {
412 variant: const_variant.as_i32(),
413 runtime_value: value.runtime_value(),
414 matched: false,
415 },
416 None => {
417 let runtime_value = value.runtime_value();
418 let case_child = scope.child();
419 arm0(&case_child, runtime_value.clone_unchecked());
420
421 MatchExpand::RuntimeVariant {
422 variant: discriminant,
423 cases: alloc::vec![(discriminant0.into(), case_child)],
424 runtime_value,
425 default: None,
426 }
427 }
428 }
429}
430
431#[allow(clippy::large_enum_variant)]
432pub enum MatchExpandExpr<T: CubeEnum, C: Assign> {
433 ComptimeVariant {
434 variant: i32,
435 runtime_value: T::RuntimeValue,
436 out: Option<C>,
437 matched: bool,
438 },
439 RuntimeVariant {
440 variant: NativeExpand<i32>,
441 out: C,
442 cases: Vec<(NativeExpand<i32>, Scope)>,
443 runtime_value: T::RuntimeValue,
444 default: Option<Scope>,
445 },
446}
447
448impl<T: CubeEnum, C: Assign> MatchExpandExpr<T, C> {
449 pub fn case<R: RuntimeAssign<Expand = C>>(
450 mut self,
451 scope: &Scope,
452 value: i32,
453 block: impl FnOnce(&Scope, T::RuntimeValue) -> R,
454 ) -> Self {
455 match &mut self {
456 Self::RuntimeVariant {
457 cases,
458 out,
459 runtime_value,
460 ..
461 } => {
462 let case_child = scope.child();
463 let ret_val = block(&case_child, (*runtime_value).clone_unchecked());
464 out.__expand_assign_method(&case_child, ret_val.into_expand(scope));
465 cases.push((value.into(), case_child));
466 }
467 Self::ComptimeVariant {
468 variant,
469 runtime_value,
470 out,
471 matched,
472 } => {
473 if value == *variant {
474 *out =
475 Some(block(scope, (*runtime_value).clone_unchecked()).into_expand(scope));
476 *matched = true;
477 }
478 }
479 }
480 self
481 }
482
483 pub fn default<R: RuntimeAssign<Expand = C>>(
484 mut self,
485 scope: &Scope,
486 block: impl FnOnce(&Scope, T::RuntimeValue) -> R,
487 ) -> Self {
488 match &mut self {
489 Self::RuntimeVariant {
490 runtime_value,
491 out,
492 default,
493 ..
494 } => {
495 let case_child = scope.child();
496 let ret_val = block(&case_child, (*runtime_value).clone_unchecked());
497 out.__expand_assign_method(&case_child, ret_val.into_expand(scope));
498 *default = Some(case_child);
499 }
500 Self::ComptimeVariant {
501 runtime_value,
502 out,
503 matched,
504 ..
505 } => {
506 if !*matched {
507 *out =
508 Some(block(scope, (*runtime_value).clone_unchecked()).into_expand(scope));
509 *matched = true;
510 }
511 }
512 }
513 self
514 }
515
516 pub fn finish(self, scope: &Scope) -> C {
517 match self {
518 MatchExpandExpr::ComptimeVariant { out, .. } => {
519 out.expect("At least one variant should be matched")
520 }
521 MatchExpandExpr::RuntimeVariant {
522 variant,
523 cases,
524 out,
525 default,
526 ..
527 } => {
528 let variant_var = variant.expand;
529 let scope_default = default.unwrap_or_else(|| {
530 let scope_default = scope.child();
531 unreachable_unchecked::expand(&scope_default);
532 scope_default
533 });
534 scope.register(Branch::Switch(Box::new(Switch {
535 value: variant_var,
536 scope_default,
537 cases: cases.into_iter().map(|it| (it.0.expand, it.1)).collect(),
538 })));
539 out
540 }
541 }
542 }
543}
544
545pub fn match_expand_expr<T: CubeEnum, C: RuntimeAssign>(
546 scope: &Scope,
547 value: T,
548 discriminant0: i32,
549 arm0: impl FnOnce(&Scope, T::RuntimeValue) -> C,
550) -> MatchExpandExpr<T, C::Expand> {
551 let discriminant = value.discriminant();
552 match discriminant.constant() {
553 Some(const_variant) if const_variant.as_i32() == discriminant0 => {
554 let runtime_value = value.runtime_value();
555 let out = arm0(scope, runtime_value.clone_unchecked());
556 MatchExpandExpr::ComptimeVariant {
557 variant: const_variant.as_i32(),
558 out: Some(out.into_expand(scope)),
559 runtime_value,
560 matched: true,
561 }
562 }
563 Some(const_variant) => MatchExpandExpr::ComptimeVariant {
564 variant: const_variant.as_i32(),
565 out: None,
566 runtime_value: value.runtime_value(),
567 matched: false,
568 },
569 None => {
570 let runtime_value = value.runtime_value();
571 let case_child = scope.child();
572 let ret_val = arm0(&case_child, runtime_value.clone_unchecked());
573
574 let mut out = ret_val.init_mut(scope);
575 out.__expand_assign_method(&case_child, ret_val.into_expand(scope));
576
577 MatchExpandExpr::RuntimeVariant {
578 variant: discriminant,
579 out,
580 cases: alloc::vec![(discriminant0.into(), case_child)],
581 runtime_value,
582 default: None,
583 }
584 }
585 }
586}
587
588pub fn break_expand(scope: &Scope) {
589 scope.register(Branch::Break);
590}
591
592pub fn return_expand(scope: &Scope) {
593 scope.register(Branch::Return);
594}
595
596pub mod unreachable_unchecked {
597 use super::*;
598
599 pub fn expand(scope: &Scope) {
600 scope.register(Branch::Unreachable);
601 }
602}
603
604pub fn loop_expand(scope: &Scope, mut block: impl FnMut(&Scope)) {
606 let inside_loop = scope.child();
607
608 block(&inside_loop);
609 scope.register(Branch::Loop(Box::new(Loop { scope: inside_loop })));
610}