@@ -3,14 +3,14 @@ use tracing::{debug, instrument};
3
3
use crate :: {
4
4
ast:: {
5
5
common:: TypeName ,
6
- expressions:: { Expression , IfExpr , MatchCaseExpr , MatchExpr } ,
6
+ expressions:: { Expression , IfExpr , MatchCaseExpr , MatchExpr , ValueExpr } ,
7
7
statements:: { self , AssignStmt , ForStmt , LetStmt , LetStmtTarget , ReturnStmt , WhileStmt } ,
8
8
types:: TypeDescriptor ,
9
9
} ,
10
10
ir:: {
11
- BasicBlock , ConstData , ConstKind , ConstValue , Mutability , Operand , Place , PlaceElem ,
12
- Rvalue , Statement , StatementKind , SwitchTargets , Terminator , TerminatorKind , Type ,
13
- ValueTree ,
11
+ BasicBlock , ConstData , ConstKind , ConstValue , Local , LocalKind , Mutability , Operand , Place ,
12
+ PlaceElem , Rvalue , Statement , StatementKind , SwitchTargets , Terminator , TerminatorKind ,
13
+ Type , ValueTree ,
14
14
lowering:: {
15
15
Symbol ,
16
16
expressions:: { lower_expression, lower_value_expr} ,
@@ -333,24 +333,20 @@ fn lower_match(builder: &mut FnIrBuilder, info: &MatchExpr) -> Result<(), Loweri
333
333
let ( discriminator, discriminator_type_idx, _disc_span) =
334
334
lower_expression ( builder, & info. expr , None ) ?;
335
335
336
- let local = builder. add_temp_local ( builder . builder . ir . get_bool_ty ( ) ) ;
336
+ let local = builder. add_temp_local ( discriminator_type_idx ) ;
337
337
let place = Place {
338
338
local,
339
339
projection : vec ! [ ] ,
340
340
} ;
341
341
342
342
builder. statements . push ( Statement {
343
343
span : None ,
344
- kind : StatementKind :: Assign ( place. clone ( ) , discriminator) ,
344
+ kind : StatementKind :: Assign ( place. clone ( ) , discriminator. clone ( ) ) ,
345
345
} ) ;
346
346
347
- // keep idx to change terminator
348
- let current_block_idx = builder. body . basic_blocks . len ( ) ;
349
-
350
- let outer_scope_locals = builder. name_to_local . clone ( ) ;
351
-
352
- // flush current block
353
347
let statements = std:: mem:: take ( & mut builder. statements ) ;
348
+ // keep idx to change terminator
349
+ let cond_block_idx = builder. body . basic_blocks . len ( ) ;
354
350
builder. body . basic_blocks . push ( BasicBlock {
355
351
statements,
356
352
terminator : Box :: new ( Terminator {
@@ -365,16 +361,39 @@ fn lower_match(builder: &mut FnIrBuilder, info: &MatchExpr) -> Result<(), Loweri
365
361
366
362
let mut is_enum_match: Option < Option < TypeName > > = None ;
367
363
368
- for variant in & info. variants {
369
- let mut variant_scope_locals = outer_scope_locals. clone ( ) ;
364
+ let outer_scope_locals = builder. name_to_local . clone ( ) ;
365
+ let outer_scope_local_exists = builder. local_exists . clone ( ) ;
366
+
367
+ for variant in info. variants . iter ( ) {
368
+ debug ! ( "lowering variant" ) ;
369
+ // keep idx for switch targets
370
+ let current_block_idx = builder. body . basic_blocks . len ( ) ;
371
+ targets. push ( current_block_idx) ;
372
+
373
+ let statements = std:: mem:: take ( & mut builder. statements ) ;
374
+ builder. body . basic_blocks . push ( BasicBlock {
375
+ statements,
376
+ terminator : Box :: new ( Terminator {
377
+ span : None ,
378
+ kind : TerminatorKind :: Unreachable ,
379
+ } ) ,
380
+ } ) ;
370
381
371
382
match & variant. case {
372
383
MatchCaseExpr :: Value ( value_expr) => {
373
- let value = lower_value_expr ( builder, value_expr, None ) ?;
374
- target_conds. push ( value) ;
384
+ let idx = match value_expr {
385
+ ValueExpr :: ConstBool ( v, _) => ( * v) as u32 ,
386
+ ValueExpr :: ConstChar ( v, _) => ( * v) as u32 ,
387
+ ValueExpr :: ConstInt ( v, _) => ( * v) as u32 ,
388
+ ValueExpr :: ConstFloat ( _, _) => todo ! ( ) ,
389
+ ValueExpr :: ConstStr ( _, _) => todo ! ( ) ,
390
+ ValueExpr :: Path ( _) => todo ! ( "todo: bind variable to other?" ) ,
391
+ } ;
392
+ target_conds. push ( ValueTree :: Leaf ( ConstValue :: U32 ( idx) ) ) ;
375
393
is_enum_match = Some ( None ) ;
376
394
}
377
395
MatchCaseExpr :: Enum ( enum_match_expr) => {
396
+ debug ! ( "lowering enum variant {}" , enum_match_expr. name. name. name) ;
378
397
if let Some ( None ) = is_enum_match {
379
398
panic ! ( "enum on a non enum match" )
380
399
}
@@ -419,21 +438,83 @@ fn lower_match(builder: &mut FnIrBuilder, info: &MatchExpr) -> Result<(), Loweri
419
438
. get ( & enum_match_expr. variant . name )
420
439
. expect ( "variant not found" ) ;
421
440
422
- let variant_value = Rvalue :: Use ( Operand :: Const ( ConstData {
423
- ty : builder. builder . ir . get_u32_ty ( ) ,
424
- span : enum_match_expr. span ,
425
- data : ConstKind :: Value ( ValueTree :: Leaf ( ConstValue :: U32 ( variant_idx as u32 ) ) ) ,
426
- } ) ) ;
441
+ let variant_value = ValueTree :: Leaf ( ConstValue :: U32 ( variant_idx as u32 ) ) ;
427
442
428
- target_conds. push ( ( variant_value, builder. builder . ir . get_u32_ty ( ) ) ) ;
429
- }
430
- }
443
+ target_conds. push ( variant_value) ;
431
444
432
- // todo: add locals from destructure, on each enum match block add the value extraction on the start of block.
445
+ // todo: add locals from destructure, on each enum match block add the value extraction on the start of block.
433
446
434
- // keep idx for switch targets
435
- targets. push ( builder. body . basic_blocks . len ( ) ) ;
436
- let current_block_idx = builder. body . basic_blocks . len ( ) ;
447
+ let enum_place = match & discriminator {
448
+ Rvalue :: Use ( operand) => match operand {
449
+ Operand :: Place ( place) => {
450
+ let mut place = place. clone ( ) ; // this place should have the GetVariant projection, remove it.
451
+ let popped = place. projection . pop ( ) ;
452
+ assert_eq ! ( popped, Some ( PlaceElem :: GetVariant ) ) ;
453
+ place
454
+ }
455
+ Operand :: Const ( _) => todo ! ( ) ,
456
+ } ,
457
+ Rvalue :: LogicOp ( _, _) => todo ! ( ) ,
458
+ Rvalue :: BinaryOp ( _, _) => todo ! ( ) ,
459
+ Rvalue :: UnaryOp ( _, _) => todo ! ( ) ,
460
+ Rvalue :: Ref ( mutability, place) => todo ! ( ) ,
461
+ Rvalue :: Cast ( operand, index, _span) => todo ! ( ) ,
462
+ } ;
463
+
464
+ for field_value in & enum_match_expr. field_values {
465
+ debug ! ( "lowering variant field {}" , field_value. name) ;
466
+ let field_idx = * adt_body. variants [ variant_idx]
467
+ . field_names
468
+ . get ( & field_value. name )
469
+ . expect ( "field not found" ) ;
470
+ let ty_idx = adt_body. variants [ variant_idx] . fields [ field_idx] . ty ;
471
+
472
+ let field_local_idx = builder. body . locals . len ( ) ;
473
+ builder
474
+ . name_to_local
475
+ . insert ( field_value. name . clone ( ) , field_local_idx) ;
476
+ builder. local_exists . insert ( field_local_idx) ;
477
+
478
+ let field_local = Local :: new (
479
+ Some ( field_value. span ) ,
480
+ LocalKind :: Temp ,
481
+ ty_idx,
482
+ Some ( field_value. name . clone ( ) ) ,
483
+ false ,
484
+ ) ;
485
+
486
+ builder. body . locals . push ( field_local) ;
487
+ builder. local_exists . insert ( field_local_idx) ;
488
+
489
+ let field_place = Place {
490
+ local : field_local_idx,
491
+ projection : Vec :: new ( ) ,
492
+ } ;
493
+
494
+ // todo: maybe add a "mut ref" keyword to be able to modify the field in the match
495
+
496
+ let enum_field_place = {
497
+ let mut place = enum_place. clone ( ) ;
498
+ place. projection . push ( PlaceElem :: Variant ( variant_idx) ) ;
499
+ place. projection . push ( PlaceElem :: Field ( field_idx) ) ;
500
+ place
501
+ } ;
502
+
503
+ builder. statements . push ( Statement {
504
+ span : Some ( enum_match_expr. span ) ,
505
+ kind : StatementKind :: Assign (
506
+ field_place,
507
+ Rvalue :: Use ( Operand :: Place ( enum_field_place) ) ,
508
+ ) ,
509
+ } ) ;
510
+
511
+ builder. statements . push ( Statement {
512
+ span : Some ( enum_match_expr. span ) ,
513
+ kind : StatementKind :: StorageLive ( field_local_idx) ,
514
+ } ) ;
515
+ }
516
+ }
517
+ }
437
518
438
519
for stmt in & variant. block {
439
520
get_locals ( builder, stmt) ?;
@@ -456,9 +537,51 @@ fn lower_match(builder: &mut FnIrBuilder, info: &MatchExpr) -> Result<(), Loweri
456
537
targets_last_block. push ( last_then_block_idx) ;
457
538
458
539
builder. name_to_local = outer_scope_locals. clone ( ) ;
540
+ builder. local_exists = outer_scope_local_exists. clone ( ) ;
459
541
}
460
542
461
- todo ! ( )
543
+ // todo: add otherwise block
544
+
545
+ // otherwise block
546
+ // todo: maybe its not needed if user adds a catch all.
547
+ // todo: identify catch all, maybe path
548
+ let statements = std:: mem:: take ( & mut builder. statements ) ;
549
+ let otherwise_block_idx = builder. body . basic_blocks . len ( ) ;
550
+ builder. body . basic_blocks . push ( BasicBlock {
551
+ statements,
552
+ terminator : Box :: new ( Terminator {
553
+ span : None ,
554
+ kind : TerminatorKind :: Unreachable ,
555
+ } ) ,
556
+ } ) ;
557
+ targets. push ( otherwise_block_idx) ;
558
+
559
+ let targets = SwitchTargets {
560
+ values : target_conds,
561
+ targets,
562
+ } ;
563
+
564
+ let kind = TerminatorKind :: SwitchInt {
565
+ discriminator : Operand :: Place ( place) ,
566
+ targets,
567
+ } ;
568
+ builder. body . basic_blocks [ cond_block_idx] . terminator . kind = kind;
569
+ builder. name_to_local = outer_scope_locals;
570
+
571
+ let next_block_idx = builder. body . basic_blocks . len ( ) ;
572
+
573
+ for blockidx in & targets_last_block {
574
+ if matches ! (
575
+ builder. body. basic_blocks[ * blockidx] . terminator. kind,
576
+ TerminatorKind :: Unreachable
577
+ ) {
578
+ builder. body . basic_blocks [ * blockidx] . terminator . kind = TerminatorKind :: Goto {
579
+ target : next_block_idx,
580
+ } ;
581
+ }
582
+ }
583
+
584
+ Ok ( ( ) )
462
585
}
463
586
464
587
#[ instrument( level = "debug" , skip_all) ]
0 commit comments