compiler: Return initialized expression results

fb2692fb970121dc46128f75cf267858a6bcf2034b8f5d257d85fd736bef3b72
Alexis Sellier committed ago 1 parent 91c34233
lib/std/lang/lower.rad +42 -38
7475 7475
/// This is the main expression dispatch, all expression nodes go through here.
7476 7476
unsafe fn lowerExpr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
7477 7477
    if self.low.options.debug {
7478 7478
        set self.srcLoc.offset = node.span.offset;
7479 7479
    }
7480 -
    let mut val: il::Val = undefined;
7480 +
    let val = try lowerExprValue(self, node);
7481 +
    return try applyCoercion(self, node, val);
7482 +
}
7481 7483
7484 +
/// Construct an expression value before its resolver-requested coercion.
7485 +
unsafe fn lowerExprValue 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
7482 7486
    match node.value {
7483 7487
        case ast::NodeValue::Ident(_) => {
7484 7488
            // First try local variable lookup.
7485 7489
            // Otherwise fall back to global symbol lookup.
7486 7490
            if let v = lookupLocalVar(&self.vars, node) {
7487 -
                set val = try useVar(self, v);
7491 +
                let val = try useVar(self, v);
7488 7492
                if self.vars.items[*v].addressTaken {
7489 7493
                    let typ = try typeOf(self, node);
7490 7494
                    let ptr = emitValToReg(self, val);
7491 -
                    set val = emitRead(self, ptr, 0, typ);
7495 +
                    return emitRead(self, ptr, 0, typ);
7492 7496
                }
7497 +
                return val;
7493 7498
            } else {
7494 -
                set val = try lowerGlobalSymbol(self, node);
7499 +
                return try lowerGlobalSymbol(self, node);
7495 7500
            }
7496 7501
        }
7497 7502
        case ast::NodeValue::ScopeAccess(_) => {
7498 -
            set val = try lowerScopeAccess(self, node);
7503 +
            return try lowerScopeAccess(self, node);
7499 7504
        }
7500 7505
        case ast::NodeValue::Number(lit) => {
7501 -
            set val = il::Val::Imm(lit.magnitude as i64);
7506 +
            return il::Val::Imm(lit.magnitude as i64);
7502 7507
        }
7503 7508
        case ast::NodeValue::Bool(b) => {
7504 -
            set val = il::Val::Imm(1) if b else il::Val::Imm(0);
7509 +
            return il::Val::Imm(1) if b else il::Val::Imm(0);
7505 7510
        }
7506 7511
        case ast::NodeValue::Char(c) => {
7507 -
            set val = il::Val::Imm(c as i64);
7512 +
            return il::Val::Imm(c as i64);
7508 7513
        }
7509 7514
        case ast::NodeValue::Nil => {
7510 7515
            let typ = try typeOf(self, node);
7511 7516
            if let case resolver::Type::Optional(_) = typ {
7512 -
                set val = try buildNilOptional(self, typ);
7517 +
                return try buildNilOptional(self, typ);
7513 7518
            } else if let case resolver::Type::Nil = typ {
7514 7519
                // Standalone `nil` without a concrete optional type. We can't
7515 7520
                // generate a proper value representation.
7516 7521
                throw LowerError::MissingType(node);
7517 7522
            } else {
7518 7523
                throw LowerError::NilInNonOptional;
7519 7524
            }
7520 7525
        }
7521 7526
        case ast::NodeValue::RecordLit(lit) => {
7522 -
            set val = try lowerRecordLit(self, node, lit);
7527 +
            return try lowerRecordLit(self, node, lit);
7523 7528
        }
7524 7529
        case ast::NodeValue::AddressOf(addr) => {
7525 -
            set val = try lowerAddressOf(self, node, addr);
7530 +
            return try lowerAddressOf(self, node, addr);
7526 7531
        }
7527 7532
        case ast::NodeValue::Deref(target) => {
7528 -
            set val = try lowerDeref(self, node, target);
7533 +
            return try lowerDeref(self, node, target);
7529 7534
        }
7530 7535
        case ast::NodeValue::BinOp(binop) => {
7531 -
            set val = try lowerBinOp(self, node, binop);
7536 +
            return try lowerBinOp(self, node, binop);
7532 7537
        }
7533 7538
        case ast::NodeValue::UnOp(unop) => {
7534 -
            set val = try lowerUnOp(self, node, unop);
7539 +
            return try lowerUnOp(self, node, unop);
7535 7540
        }
7536 7541
        case ast::NodeValue::Subscript { container, index } => {
7537 -
            set val = try lowerSubscript(self, node, container, index);
7542 +
            return try lowerSubscript(self, node, container, index);
7538 7543
        }
7539 7544
        case ast::NodeValue::BuiltinCall { kind, args } => {
7540 -
            set val = try lowerBuiltinCall(self, node, kind, args);
7545 +
            return try lowerBuiltinCall(self, node, kind, args);
7541 7546
        }
7542 7547
        case ast::NodeValue::Call(call) => {
7543 -
            set val = try lowerCallOrCtor(self, node, call);
7548 +
            return try lowerCallOrCtor(self, node, call);
7544 7549
        }
7545 7550
        case ast::NodeValue::Try(t) => {
7546 -
            set val = try lowerTry(self, node, t);
7551 +
            return try lowerTry(self, node, t);
7547 7552
        }
7548 7553
        case ast::NodeValue::FieldAccess(access) => {
7549 7554
            // Check for compile-time constant (e.g., `arr.len` on fixed-size arrays).
7550 7555
            if let constVal = resolver::constValueEntry(self.low.resolver, node) {
7551 7556
                match constVal {
7552 7557
                    // TODO: Handle `u32` values that don't fit in an `i32`.
7553 7558
                    //       Perhaps just store the `ConstInt`.
7554 -
                    case resolver::ConstValue::Int(i) => set val = il::Val::Imm(constIntToI64(i)),
7555 -
                    else => set val = try lowerFieldAccess(self, access),
7559 +
                    case resolver::ConstValue::Int(i) => return il::Val::Imm(constIntToI64(i)),
7560 +
                    else => return try lowerFieldAccess(self, access),
7556 7561
                }
7557 7562
            } else {
7558 -
                set val = try lowerFieldAccess(self, access);
7563 +
                return try lowerFieldAccess(self, access);
7559 7564
            }
7560 7565
        }
7561 7566
        case ast::NodeValue::ArrayLit(elements) => {
7562 -
            set val = try lowerArrayLit(self, node, elements);
7567 +
            return try lowerArrayLit(self, node, elements);
7563 7568
        }
7564 7569
        case ast::NodeValue::ArrayRepeatLit(repeat) => {
7565 -
            set val = try lowerArrayRepeatLit(self, node, repeat);
7570 +
            return try lowerArrayRepeatLit(self, node, repeat);
7566 7571
        }
7567 7572
        case ast::NodeValue::RegionApply { value, .. } => {
7568 -
            set val = try lowerExpr(self, value);
7573 +
            return try lowerExpr(self, value);
7569 7574
        }
7570 7575
        case ast::NodeValue::As(cast) => {
7571 -
            set val = try lowerCast(self, node, cast);
7576 +
            return try lowerCast(self, node, cast);
7572 7577
        }
7573 7578
        case ast::NodeValue::CondExpr(cond) => {
7574 -
            set val = try lowerCondExpr(self, node, cond);
7579 +
            return try lowerCondExpr(self, node, cond);
7575 7580
        }
7576 7581
        case ast::NodeValue::String(s) => {
7577 -
            set val = try lowerStringLit(self, node, s);
7582 +
            return try lowerStringLit(self, node, s);
7578 7583
        }
7579 7584
        case ast::NodeValue::Undef => {
7580 7585
            let typ = try typeOf(self, node);
7581 7586
            if isAggregateType(typ) {
7582 7587
                // When `undefined` appears as a stand-alone expression,
7583 7588
                /// we need a stack slot for reads and writes.
7584 7589
                let slot = try emitReserve(self, typ);
7585 -
                set val = il::Val::Reg(slot);
7590 +
                return il::Val::Reg(slot);
7586 7591
            } else {
7587 -
                set val = il::Val::Undef;
7592 +
                return il::Val::Undef;
7588 7593
            }
7589 7594
        }
7590 7595
        case ast::NodeValue::Panic { .. } => {
7591 7596
            // Panic in expression context (e.g. match arm). Emit unreachable
7592 7597
            // and return a dummy value since control won't continue.
7593 7598
            emit(self, il::Instr::Unreachable);
7594 -
            set val = il::Val::Undef;
7599 +
            return il::Val::Undef;
7595 7600
        }
7596 7601
        case ast::NodeValue::Assert { .. } => {
7597 7602
            // Assert in expression context. Lower as statement, return `void`.
7598 7603
            try lowerNode(self, node);
7599 -
            set val = il::Val::Undef;
7604 +
            return il::Val::Undef;
7600 7605
        }
7601 7606
        case ast::NodeValue::Block(_) => {
7602 7607
            try lowerBlock(self, node);
7603 -
            set val = il::Val::Undef;
7608 +
            return il::Val::Undef;
7604 7609
        }
7605 7610
        case ast::NodeValue::ExprStmt(expr) => {
7606 7611
            let _ = expr;
7607 -
            set val = il::Val::Undef;
7612 +
            return il::Val::Undef;
7608 7613
        }
7609 7614
        // Lower these as statements.
7610 7615
        case ast::NodeValue::ConstDecl(decl) => {
7611 7616
            try registerLocalDataDeclName(self, node);
7612 7617
            try lowerDataDecl(self.low, node, decl.value, true);
7613 -
            set val = il::Val::Undef;
7618 +
            return il::Val::Undef;
7614 7619
        }
7615 7620
        case ast::NodeValue::StaticDecl(decl) => {
7616 7621
            try registerLocalDataDeclName(self, node);
7617 7622
            try lowerDataDecl(self.low, node, decl.value, false);
7618 -
            set val = il::Val::Undef;
7623 +
            return il::Val::Undef;
7619 7624
        }
7620 7625
        case ast::NodeValue::Throw { .. },
7621 7626
             ast::NodeValue::Return { .. },
7622 7627
             ast::NodeValue::Continue,
7623 7628
             ast::NodeValue::Break => {
7624 7629
            try lowerNode(self, node);
7625 -
            set val = il::Val::Undef;
7630 +
            return il::Val::Undef;
7626 7631
        }
7627 7632
        else => {
7628 -
            panic "lowerExpr: node is not an expression";
7633 +
            panic "lowerExprValue: node is not an expression";
7629 7634
        }
7630 7635
    }
7631 -
    return try applyCoercion(self, node, val);
7632 7636
}
7633 7637
7634 7638
/// Translate a Radiance type to an IL type.
7635 7639
///
7636 7640
/// The IL type system is much simpler than Radiance's, only primitive types
test/tests/expression.result.coercion.rad added +53 -0
1 +
//! returns: 0
2 +
3 +
/// Aggregate used for field and address expressions.
4 +
record Pair: Copy {
5 +
    /// Narrow field used in a widening cast.
6 +
    first: u8,
7 +
    /// Full-width field.
8 +
    second: u64,
9 +
}
10 +
11 +
/// Exercise scalar expression construction and result coercion.
12 +
fn scalar(input: u8, choose: bool) -> u64 {
13 +
    let widened = input as u64;
14 +
    let selected = input as u64 if choose else 7 as u64;
15 +
    let values: [u8; 3] = [input, 2, 3];
16 +
    let pointer = &values[1];
17 +
    return widened + selected + *pointer as u64 + values.len as u64;
18 +
}
19 +
20 +
/// Exercise aggregate, field, subscript, and optional expression results.
21 +
fn aggregate(input: u8) -> ?u64 {
22 +
    let pair = Pair { first: input, second: 0x100000000 };
23 +
    let values = [pair, Pair { first: 7, second: 9 }];
24 +
    let mut slot: u64 = values[0].second;
25 +
    let pointer: 'update = &mut slot in {
26 +
        set *pointer += pair.first as u64;
27 +
    }
28 +
    return slot;
29 +
}
30 +
31 +
/// Preserve character, boolean, conditional, and nil values.
32 +
fn optional(present: bool) -> ?u32 {
33 +
    if present {
34 +
        return 'A' as u32;
35 +
    }
36 +
    return nil;
37 +
}
38 +
39 +
/// Check expression values after the common coercion step.
40 +
@default fn main() -> u32 {
41 +
    for index in 0..16 {
42 +
        assert scalar(index as u8, true) == index as u64 * 2 + 5;
43 +
        assert scalar(index as u8, false) == index as u64 + 12;
44 +
        let value = aggregate(index as u8) else 0;
45 +
        assert value == 0x100000000 + index as u64;
46 +
    }
47 +
    let character = optional(true) else 0;
48 +
    assert character == 65;
49 +
    assert optional(false) == nil;
50 +
    assert not false;
51 +
    assert -(-3) == 3;
52 +
    return 0;
53 +
}