1use wasm_encoder::{BlockType, HeapType, Ieee64, Instruction, RefType, ValType};
6
7use crate::Type;
8use crate::codegen::CodegenCtx;
9use crate::codegen::classes::VTABLE_PARENT_SLOT;
10use crate::codegen::function_emitter::{FunctionEmitter, ReturnTarget};
11use crate::codegen::symbol_table::is_nullable_ref;
12use crate::typechecker::infer::narrowing::{TruthinessClass, truthiness_class};
13
14pub fn emit_nominal_instance_test(
23 emitter: &mut FunctionEmitter<'_>,
24 ctx: &CodegenCtx<'_>,
25 target_vtable_global_idx: u32,
26) -> Result<(), crate::compiler_error::CompilerFailure> {
27 let intr = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
28 crate::codegen::internal_failure("intrinsic types declared before body emission")
29 })?;
30 let ref_null_to = |idx: u32| {
31 ValType::Ref(RefType {
32 nullable: true,
33 heap_type: HeapType::Concrete(idx),
34 })
35 };
36 let obj_local = emitter.add_anonymous_local(ref_null_to(intr.object))?;
37 let raw_vt_local = emitter.add_anonymous_local(ref_null_to(intr.vtable))?;
38 let vt_local = emitter.add_anonymous_local(ref_null_to(intr.class_vtable))?;
39
40 emitter.instruction(Instruction::LocalSet(obj_local));
41 emitter.emit_block(BlockType::Result(ValType::I32)); emitter.emit_block(BlockType::Empty); emitter.instruction(Instruction::LocalGet(obj_local));
44 emitter.instruction(Instruction::BrOnNull(0));
45 emitter.instruction(Instruction::StructGet {
46 struct_type_index: intr.object,
47 field_index: 0,
48 });
49 emitter.instruction(Instruction::LocalTee(raw_vt_local));
50 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
51 intr.class_vtable,
52 )));
53 emitter.instruction(Instruction::I32Eqz);
54 emitter.instruction(Instruction::BrIf(0));
55 emitter.instruction(Instruction::LocalGet(raw_vt_local));
56 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
57 intr.class_vtable,
58 )));
59 emitter.instruction(Instruction::LocalSet(vt_local));
60 emitter.emit_loop(BlockType::Empty); emitter.instruction(Instruction::LocalGet(vt_local));
62 emitter.instruction(Instruction::GlobalGet(target_vtable_global_idx));
63 emitter.instruction(Instruction::RefEq);
64 emitter.emit_if(BlockType::Empty);
65 emitter.instruction(Instruction::I32Const(1));
66 emitter.instruction(Instruction::Br(3)); emitter.emit_end();
68 emitter.instruction(Instruction::LocalGet(vt_local));
69 emitter.instruction(Instruction::StructGet {
70 struct_type_index: intr.class_vtable,
71 field_index: VTABLE_PARENT_SLOT,
72 });
73 emitter.instruction(Instruction::BrOnNull(1)); emitter.instruction(Instruction::LocalSet(vt_local));
75 emitter.instruction(Instruction::Br(0)); emitter.emit_end(); emitter.emit_end(); emitter.instruction(Instruction::I32Const(0));
79 emitter.emit_end(); Ok(())
82}
83
84pub fn emit_box(
87 emitter: &mut FunctionEmitter<'_>,
88 ctx: &CodegenCtx<'_>,
89 ty: &Type,
90) -> Result<(), crate::compiler_error::CompilerFailure> {
91 let ty = ty.lowered();
92 match ty {
93 Type::Number | Type::NumberLiteral(_) => {
94 let boxed_idx = ctx
95 .symbols
96 .boxed_number_type_idx()
97 .ok_or_else(|| crate::codegen::internal_failure("boxed_number type registered"))?;
98 let vtable_global = ctx
99 .symbols
100 .prelude_global_idx("boxed_number_vtable")
101 .ok_or_else(|| crate::codegen::internal_failure("boxed_number_vtable imported"))?;
102 let scratch = emitter.add_anonymous_local(wasm_encoder::ValType::F64)?;
105 emitter.instruction(Instruction::LocalSet(scratch));
106 emitter.instruction(Instruction::GlobalGet(vtable_global));
107 emitter.instruction(Instruction::LocalGet(scratch));
108 emitter.instruction(Instruction::StructNew(boxed_idx));
109 }
110 Type::Boolean | Type::BooleanLiteral(_) => {
111 let boxed_idx = ctx
112 .symbols
113 .boxed_boolean_type_idx()
114 .ok_or_else(|| crate::codegen::internal_failure("boxed_boolean type registered"))?;
115 let vtable_global = ctx
116 .symbols
117 .prelude_global_idx("boxed_boolean_vtable")
118 .ok_or_else(|| crate::codegen::internal_failure("boxed_boolean_vtable imported"))?;
119 let scratch = emitter.add_anonymous_local(wasm_encoder::ValType::I32)?;
120 emitter.instruction(Instruction::LocalSet(scratch));
121 emitter.instruction(Instruction::GlobalGet(vtable_global));
122 emitter.instruction(Instruction::LocalGet(scratch));
123 emitter.instruction(Instruction::StructNew(boxed_idx));
124 }
125 Type::String
126 | Type::StringLiteral(_)
127 | Type::TemplateLiteral { .. }
128 | Type::BigInt
129 | Type::BigIntLiteral(_)
130 | Type::Object { .. }
131 | Type::Array(_)
132 | Type::Tuple(_)
133 | Type::Uint8Array
134 | Type::Function { .. } => {
135 }
137 Type::TypeVar(_)
138 | Type::GenericParam { .. }
139 | Type::KeyOf(_)
140 | Type::IndexedAccess { .. }
141 | Type::Unknown
142 | Type::NonPrimitive
143 | Type::Never => {
144 }
147 Type::Null => {
148 }
151 Type::Undefined => {
152 let idx = ctx
153 .symbols
154 .intrinsic_type_indices()
155 .ok_or_else(|| {
156 crate::codegen::internal_failure("undefined intrinsic not registered")
157 })?
158 .undefined;
159 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
160 }
161 Type::Void => {}
162 Type::Error => {
163 return Err(crate::codegen::internal_failure(
164 "invalid type representation during cast lowering",
165 ));
166 }
167 Type::NumberEnum { .. } | Type::StringEnum { .. } => {}
168 Type::Union(_) => {
169 match ctx.symbols.value_type(ty)? {
173 ValType::F64 => emit_box(emitter, ctx, &Type::Number)?,
174 ValType::I32 => emit_box(emitter, ctx, &Type::Boolean)?,
175 ValType::Ref(_) => {}
176 _ => {
177 return Err(crate::codegen::internal_failure(
178 "invalid type representation during cast lowering",
179 ));
180 }
181 }
182 }
183 Type::InterfaceRef { .. } | Type::ClassRef { .. } => {}
185 Type::AliasRef { .. } => {}
188 Type::Alias { .. } | Type::Refined { .. } | Type::Readonly(_) => {
189 return Err(crate::codegen::internal_failure(
190 "invalid type representation during cast lowering",
191 ));
192 }
193 };
194 Ok(())
195}
196
197pub fn emit_coerce_to_slot(
203 emitter: &mut FunctionEmitter<'_>,
204 ctx: &CodegenCtx<'_>,
205 source_ty: &Type,
206 target_ty: &Type,
207) -> Result<(), crate::compiler_error::CompilerFailure> {
208 let source_val = ctx.symbols.value_type(source_ty)?;
209 let target_val = ctx.symbols.value_type(target_ty)?;
210 if source_val == target_val {
211 return Ok(());
212 }
213 if matches!(source_ty.lowered(), Type::Never) {
216 emit_cast_to(emitter, ctx, target_ty)?;
217 return Ok(());
218 }
219 emit_coerce_to_wasm_slot(emitter, ctx, source_ty, target_val)?;
220
221 Ok(())
222}
223
224pub fn emit_coerce_to_wasm_slot(
233 emitter: &mut FunctionEmitter<'_>,
234 ctx: &CodegenCtx<'_>,
235 source_ty: &Type,
236 target_val: ValType,
237) -> Result<(), crate::compiler_error::CompilerFailure> {
238 let source_val = ctx.symbols.value_type(source_ty)?;
239 if source_val == target_val {
240 return Ok(());
241 }
242 if matches!(source_ty.lowered(), Type::Never) {
247 emitter.instruction(Instruction::Unreachable);
248 return Ok(());
249 }
250 if crate::codegen::closure_coercions::emit_coercion(emitter, ctx, source_ty, target_val)? {
251 return Ok(());
252 }
253 if source_ty == &Type::Unknown && target_val == ctx.symbols.value_type(&Type::String)? {
254 let function = ctx
255 .symbols
256 .prelude_func_idx("__value_to_string")
257 .ok_or_else(|| {
258 crate::codegen::internal_failure("dynamic string conversion collected")
259 })?;
260 emitter.instruction(Instruction::Call(function));
261 return Ok(());
262 }
263 match (&source_val, &target_val) {
264 (ValType::Ref(_), ValType::F64) if matches!(source_ty, Type::Unknown) => {
265 let function = ctx
266 .symbols
267 .prelude_func_idx("__value_to_number")
268 .ok_or_else(|| {
269 crate::codegen::internal_failure("dynamic number conversion collected")
270 })?;
271 emitter.instruction(Instruction::Call(function));
272 }
273 (ValType::Ref(_), ValType::I32) if matches!(source_ty, Type::Unknown) => {
274 emit_condition_to_i32(emitter, ctx, source_ty)?;
275 }
276 (ValType::Ref(_), ValType::F64) => {
277 emit_cast_to(emitter, ctx, &Type::Number)?;
278 }
279 (ValType::F64 | ValType::I32, ValType::Ref(_)) => emit_box(emitter, ctx, source_ty)?,
281 (ValType::Ref(source), ValType::Ref(target))
283 if ctx.symbols.ref_fits_slot(*source, *target) => {}
284 (ValType::Ref(_), ValType::Ref(target)) => {
290 if target.nullable {
291 emitter.instruction(Instruction::RefCastNullable(target.heap_type));
292 } else {
293 emitter.instruction(Instruction::RefCastNonNull(target.heap_type));
294 }
295 }
296 _ => {
297 return Err(crate::codegen::internal_failure(
298 "value representation does not fit the recorded slot",
299 ));
300 }
301 };
302 Ok(())
303}
304
305pub fn emit_coerce_to_return_slot(
312 emitter: &mut FunctionEmitter<'_>,
313 ctx: &CodegenCtx<'_>,
314 source_ty: &Type,
315) -> Result<(), crate::compiler_error::CompilerFailure> {
316 match emitter.return_target().clone() {
317 ReturnTarget::Slot(slot) => emit_coerce_to_wasm_slot(emitter, ctx, source_ty, slot)?,
318 ReturnTarget::Declared(ret) => emit_coerce_to_slot(emitter, ctx, source_ty, &ret)?,
319 ReturnTarget::NoResult => {}
320 };
321 Ok(())
322}
323
324pub fn emit_condition_to_i32(
328 emitter: &mut FunctionEmitter<'_>,
329 ctx: &CodegenCtx<'_>,
330 cond_ty: &Type,
331) -> Result<(), crate::compiler_error::CompilerFailure> {
332 let val = ctx.symbols.value_type(cond_ty)?;
333 match val {
334 ValType::F64 => {
337 emitter.instruction(Instruction::F64Abs);
338 emitter.instruction(Instruction::F64Const(Ieee64::from(0.0)));
339 emitter.instruction(Instruction::F64Gt);
340 }
341 ValType::Ref(rt) => emit_ref_truthiness(emitter, ctx, cond_ty, rt.nullable)?,
342 _ => {}
344 };
345 Ok(())
346}
347
348fn emit_ref_truthiness(
354 emitter: &mut FunctionEmitter<'_>,
355 ctx: &CodegenCtx<'_>,
356 cond_ty: &Type,
357 nullable: bool,
358) -> Result<(), crate::compiler_error::CompilerFailure> {
359 use TruthinessClass::*;
360 let lowered = cond_ty.lowered();
361 let members: Vec<&Type> = match lowered {
362 Type::Union(members) => members.iter().collect(),
363 single => vec![single],
364 };
365 let classes: Vec<TruthinessClass> = members
366 .iter()
367 .filter(|m| !matches!(m.lowered(), Type::Null))
368 .map(|m| match m.lowered() {
369 Type::Void => Dynamic,
370 Type::Undefined => AlwaysFalsy,
371 _ => truthiness_class(m),
372 })
373 .collect();
374
375 if classes.iter().all(|c| *c == AlwaysTruthy) {
376 if nullable {
377 emitter.instruction(Instruction::RefIsNull);
378 emitter.instruction(Instruction::I32Eqz);
379 } else {
380 emitter.instruction(Instruction::Drop);
381 emitter.instruction(Instruction::I32Const(1));
382 }
383 return Ok(());
384 }
385 if classes.iter().all(|c| *c == AlwaysFalsy) {
386 emitter.instruction(Instruction::Drop);
387 emitter.instruction(Instruction::I32Const(0));
388 return Ok(());
389 }
390
391 let needs = |class: TruthinessClass, literal: fn(&Type) -> bool| {
392 classes.contains(&class)
393 || classes.contains(&Dynamic)
394 || members.iter().any(|m| literal(m.lowered()))
395 };
396 let test_string = needs(StringLike, |m| {
397 matches!(m, Type::StringLiteral(_) | Type::TemplateLiteral { .. })
398 });
399 let test_number = needs(NumberLike, |m| matches!(m, Type::NumberLiteral(_)));
400 let test_boolean = needs(BooleanLike, |m| matches!(m, Type::BooleanLiteral(_)));
401 let test_bigint = needs(BigIntLike, |_| false);
402 let test_undefined = classes.contains(&Dynamic)
403 || members
404 .iter()
405 .any(|member| matches!(member.lowered(), Type::Undefined));
406
407 let slot = ctx.symbols.value_type(cond_ty)?;
408 let tmp = emitter.add_anonymous_local(slot)?;
409 emitter.instruction(Instruction::LocalSet(tmp));
410
411 let mut open_blocks = 0u32;
414 if nullable {
415 emitter.instruction(Instruction::LocalGet(tmp));
416 emitter.instruction(Instruction::RefIsNull);
417 emitter.emit_if(BlockType::Result(ValType::I32));
418 emitter.instruction(Instruction::I32Const(0));
419 emitter.emit_else();
420 open_blocks += 1;
421 }
422 if test_undefined {
423 emitter.instruction(Instruction::LocalGet(tmp));
424 super::expr::emit_is_undefined(emitter, ctx)?;
425 emitter.emit_if(BlockType::Result(ValType::I32));
426 emitter.instruction(Instruction::I32Const(0));
427 emitter.emit_else();
428 open_blocks += 1;
429 }
430 if test_string {
431 let idx = ctx
432 .symbols
433 .string_type_idx()
434 .ok_or_else(|| crate::codegen::internal_failure("$string intrinsic type registered"))?;
435 emitter.instruction(Instruction::LocalGet(tmp));
436 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(idx)));
437 emitter.emit_if(BlockType::Result(ValType::I32));
438 emitter.instruction(Instruction::LocalGet(tmp));
439 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
440 emitter.instruction(Instruction::StructGet {
441 struct_type_index: idx,
442 field_index: 1,
443 });
444 emitter.instruction(Instruction::ArrayLen);
445 emitter.emit_else();
446 open_blocks += 1;
447 }
448 if test_number {
449 let idx = ctx.symbols.boxed_number_type_idx().ok_or_else(|| {
450 crate::codegen::internal_failure("$BoxedNumber intrinsic type registered")
451 })?;
452 emitter.instruction(Instruction::LocalGet(tmp));
453 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(idx)));
454 emitter.emit_if(BlockType::Result(ValType::I32));
455 emitter.instruction(Instruction::LocalGet(tmp));
456 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
457 emitter.instruction(Instruction::StructGet {
458 struct_type_index: idx,
459 field_index: 1,
460 });
461 emitter.instruction(Instruction::F64Abs);
462 emitter.instruction(Instruction::F64Const(0.0.into()));
463 emitter.instruction(Instruction::F64Gt);
464 emitter.emit_else();
465 open_blocks += 1;
466 }
467 if test_boolean {
468 let idx = ctx.symbols.boxed_boolean_type_idx().ok_or_else(|| {
469 crate::codegen::internal_failure("$BoxedBoolean intrinsic type registered")
470 })?;
471 emitter.instruction(Instruction::LocalGet(tmp));
472 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(idx)));
473 emitter.emit_if(BlockType::Result(ValType::I32));
474 emitter.instruction(Instruction::LocalGet(tmp));
475 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
476 emitter.instruction(Instruction::StructGet {
477 struct_type_index: idx,
478 field_index: 1,
479 });
480 emitter.emit_else();
481 open_blocks += 1;
482 }
483 if test_bigint {
484 let idx = ctx
485 .symbols
486 .bigint_type_idx()
487 .ok_or_else(|| crate::codegen::internal_failure("$bigint intrinsic type registered"))?;
488 emitter.instruction(Instruction::LocalGet(tmp));
489 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(idx)));
490 emitter.emit_if(BlockType::Result(ValType::I32));
491 emitter.instruction(Instruction::LocalGet(tmp));
492 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
493 emitter.instruction(Instruction::StructGet {
495 struct_type_index: idx,
496 field_index: 1,
497 });
498 emitter.emit_else();
499 open_blocks += 1;
500 }
501 emitter.instruction(Instruction::I32Const(1));
502 for _ in 0..open_blocks {
503 emitter.emit_end();
504 }
505 Ok(())
506}
507
508pub fn emit_narrowing_cast(
512 emitter: &mut FunctionEmitter<'_>,
513 ctx: &CodegenCtx<'_>,
514 cast_info: &crate::typechecker::infer::narrowing::CastInfo,
515) -> Result<(), crate::compiler_error::CompilerFailure> {
516 use crate::typechecker::infer::narrowing::CastKind;
517 if matches!(cast_info.to_ty.lowered(), Type::Never) {
518 emitter.instruction(Instruction::Drop);
519 emitter.instruction(Instruction::Unreachable);
520 return Ok(());
521 }
522 let from_val = ctx.symbols.value_type(&cast_info.from_ty)?;
523 let to_val = ctx.symbols.value_type(&cast_info.to_ty)?;
524 if from_val == to_val {
530 return Ok(());
531 }
532 if let (ValType::Ref(s), ValType::Ref(t)) = (&from_val, &to_val)
538 && s.heap_type == t.heap_type
539 && s.nullable
540 && !t.nullable
541 {
542 emitter.instruction(Instruction::RefAsNonNull);
543 return Ok(());
544 }
545 match cast_info.cast_kind {
546 CastKind::NonNull | CastKind::Unbox => {
547 emit_cast_to(emitter, ctx, &cast_info.to_ty)?;
550 }
551 CastKind::RefSubtype => {
552 let ValType::Ref(target_ref) = to_val else {
555 return Err(crate::codegen::internal_failure(
556 "invalid type representation during cast lowering",
557 ));
558 };
559 let cast = if target_ref.nullable {
560 Instruction::RefCastNullable(target_ref.heap_type)
561 } else {
562 Instruction::RefCastNonNull(target_ref.heap_type)
563 };
564 emitter.instruction(cast);
565 }
566 };
567 Ok(())
568}
569
570pub fn emit_unerase(
581 emitter: &mut FunctionEmitter<'_>,
582 ctx: &CodegenCtx<'_>,
583 ty: &Type,
584) -> Result<(), crate::compiler_error::CompilerFailure> {
585 if ctx.symbols.slot_value_type(ty)? != ctx.symbols.value_type(ty)? {
586 emit_cast_to(emitter, ctx, ty)?;
587 };
588 Ok(())
589}
590
591pub fn emit_cast_to(
599 emitter: &mut FunctionEmitter<'_>,
600 ctx: &CodegenCtx<'_>,
601 ty: &Type,
602) -> Result<(), crate::compiler_error::CompilerFailure> {
603 let ty = ty.lowered();
606 if !target_allows_null(ctx, ty)? {
609 emitter.instruction(Instruction::RefAsNonNull);
610 }
611 match ty {
612 Type::Number | Type::NumberLiteral(_) => {
613 let boxed_idx = ctx
614 .symbols
615 .boxed_number_type_idx()
616 .ok_or_else(|| crate::codegen::internal_failure("boxed_number type registered"))?;
617 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(boxed_idx)));
618 emitter.instruction(Instruction::StructGet {
619 struct_type_index: boxed_idx,
620 field_index: 1,
621 });
622 }
623 Type::Boolean | Type::BooleanLiteral(_) => {
624 let boxed_idx = ctx
625 .symbols
626 .boxed_boolean_type_idx()
627 .ok_or_else(|| crate::codegen::internal_failure("boxed_boolean type registered"))?;
628 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(boxed_idx)));
629 emitter.instruction(Instruction::StructGet {
630 struct_type_index: boxed_idx,
631 field_index: 1,
632 });
633 }
634 Type::String | Type::StringLiteral(_) | Type::TemplateLiteral { .. } => {
635 let idx = ctx
636 .symbols
637 .string_type_idx()
638 .ok_or_else(|| crate::codegen::internal_failure("string type registered"))?;
639 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
640 }
641 Type::BigInt | Type::BigIntLiteral(_) => {
642 let idx = ctx
643 .symbols
644 .bigint_type_idx()
645 .ok_or_else(|| crate::codegen::internal_failure("bigint type registered"))?;
646 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
647 }
648 Type::Object { .. } => {
649 let idx = ctx
656 .symbols
657 .object_shape_type_idx()
658 .ok_or_else(|| crate::codegen::internal_failure("$ObjectShape type registered"))?;
659 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
660 }
661 Type::Array(_) | Type::Tuple(_) => {
662 let idx = ctx
667 .symbols
668 .array_type_idx()
669 .ok_or_else(|| crate::codegen::internal_failure("array type registered"))?;
670 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
671 }
672 Type::Uint8Array => {
673 let idx = ctx
674 .symbols
675 .uint8_array_type_idx()
676 .ok_or_else(|| crate::codegen::internal_failure("uint8_array type registered"))?;
677 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
678 }
679 Type::TypeVar(_)
680 | Type::GenericParam { .. }
681 | Type::KeyOf(_)
682 | Type::IndexedAccess { .. }
683 | Type::Unknown
684 | Type::NonPrimitive
685 | Type::Never => {
686 }
689 Type::Function { .. } => {
690 crate::codegen::closure_coercions::emit_erased_cast(
691 emitter,
692 ctx,
693 crate::codegen::closures::classify(ty)?,
694 )?;
695 }
696 Type::Null => {
697 }
699 Type::Undefined => {
700 let idx = ctx
701 .symbols
702 .intrinsic_type_indices()
703 .ok_or_else(|| {
704 crate::codegen::internal_failure("undefined intrinsic not registered")
705 })?
706 .undefined;
707 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
708 }
709 Type::Void => {}
710 Type::Error => {
711 return Err(crate::codegen::internal_failure(
712 "invalid type representation during cast lowering",
713 ));
714 }
715 Type::Union(_) => {
716 match ctx.symbols.value_type(ty)? {
719 ValType::F64 => emit_cast_to(emitter, ctx, &Type::Number)?,
720 ValType::I32 => emit_cast_to(emitter, ctx, &Type::Boolean)?,
721 ValType::Ref(target_ref) => {
722 let cast = if target_ref.nullable {
723 Instruction::RefCastNullable(target_ref.heap_type)
724 } else {
725 Instruction::RefCastNonNull(target_ref.heap_type)
726 };
727 emitter.instruction(cast);
728 }
729 _ => {
730 return Err(crate::codegen::internal_failure(
731 "invalid type representation during cast lowering",
732 ));
733 }
734 }
735 }
736 Type::NumberEnum { .. } => {
737 let boxed_idx = ctx
742 .symbols
743 .boxed_number_type_idx()
744 .ok_or_else(|| crate::codegen::internal_failure("boxed_number type registered"))?;
745 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(boxed_idx)));
746 }
747 Type::StringEnum { .. } => {
748 let idx = ctx
749 .symbols
750 .string_type_idx()
751 .ok_or_else(|| crate::codegen::internal_failure("string type registered"))?;
752 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
753 }
754 Type::InterfaceRef { .. } => {
755 }
758 Type::ClassRef { mangled, .. } => {
759 let idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
765 crate::codegen::internal_failure("class struct type is not registered")
766 })?;
767 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
768 }
769 Type::AliasRef { .. } => {
770 }
775 Type::Alias { .. } | Type::Refined { .. } | Type::Readonly(_) => {
776 return Err(crate::codegen::internal_failure(
777 "invalid type representation during cast lowering",
778 ));
779 }
780 };
781 Ok(())
782}
783
784pub fn target_allows_null(
797 ctx: &CodegenCtx<'_>,
798 ty: &Type,
799) -> Result<bool, crate::compiler_error::CompilerFailure> {
800 let ty = ty.lowered();
801 Ok(matches!(
802 ty,
803 Type::Null
804 | Type::Void
805 | Type::Unknown
806 | Type::Never
807 | Type::TypeVar(_)
808 | Type::GenericParam { .. }
809 | Type::KeyOf(_)
810 | Type::IndexedAccess { .. }
811 | Type::AliasRef { .. }
812 ) || (matches!(ty, Type::Union(_)) && is_nullable_ref(ctx.symbols.value_type(ty)?)))
813}