compiler: Return initialized expression results
fb2692fb970121dc46128f75cf267858a6bcf2034b8f5d257d85fd736bef3b72
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 | + | } |