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::BigInt
128 | Type::BigIntLiteral(_)
129 | Type::Object { .. }
130 | Type::Array(_)
131 | Type::Tuple(_)
132 | Type::Uint8Array
133 | Type::Function { .. } => {
134 }
136 Type::TypeVar(_)
137 | Type::GenericParam { .. }
138 | Type::Unknown
139 | Type::NonPrimitive
140 | Type::Never => {
141 }
144 Type::Null => {
145 }
148 Type::Undefined => {
149 let idx = ctx
150 .symbols
151 .intrinsic_type_indices()
152 .ok_or_else(|| {
153 crate::codegen::internal_failure("undefined intrinsic not registered")
154 })?
155 .undefined;
156 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
157 }
158 Type::Void => {}
159 Type::Error => {
160 return Err(crate::codegen::internal_failure(
161 "invalid type representation during cast lowering",
162 ));
163 }
164 Type::NumberEnum { .. } | Type::StringEnum { .. } => {}
165 Type::Union(_) => {
166 match ctx.symbols.value_type(ty)? {
170 ValType::F64 => emit_box(emitter, ctx, &Type::Number)?,
171 ValType::I32 => emit_box(emitter, ctx, &Type::Boolean)?,
172 ValType::Ref(_) => {}
173 _ => {
174 return Err(crate::codegen::internal_failure(
175 "invalid type representation during cast lowering",
176 ));
177 }
178 }
179 }
180 Type::InterfaceRef { .. } | Type::ClassRef { .. } => {}
182 Type::AliasRef { .. } => {}
185 Type::Alias { .. } | Type::Refined { .. } | Type::Readonly(_) => {
186 return Err(crate::codegen::internal_failure(
187 "invalid type representation during cast lowering",
188 ));
189 }
190 };
191 Ok(())
192}
193
194pub fn emit_coerce_to_slot(
200 emitter: &mut FunctionEmitter<'_>,
201 ctx: &CodegenCtx<'_>,
202 source_ty: &Type,
203 target_ty: &Type,
204) -> Result<(), crate::compiler_error::CompilerFailure> {
205 let source_val = ctx.symbols.value_type(source_ty)?;
206 let target_val = ctx.symbols.value_type(target_ty)?;
207 if source_val == target_val {
208 return Ok(());
209 }
210 if matches!(source_ty.lowered(), Type::Never) {
213 emit_cast_to(emitter, ctx, target_ty)?;
214 return Ok(());
215 }
216 emit_coerce_to_wasm_slot(emitter, ctx, source_ty, target_val)?;
217
218 Ok(())
219}
220
221pub fn emit_coerce_to_wasm_slot(
230 emitter: &mut FunctionEmitter<'_>,
231 ctx: &CodegenCtx<'_>,
232 source_ty: &Type,
233 target_val: ValType,
234) -> Result<(), crate::compiler_error::CompilerFailure> {
235 let source_val = ctx.symbols.value_type(source_ty)?;
236 if source_val == target_val {
237 return Ok(());
238 }
239 if matches!(source_ty.lowered(), Type::Never) {
244 emitter.instruction(Instruction::Unreachable);
245 return Ok(());
246 }
247 if crate::codegen::closure_coercions::emit_coercion(emitter, ctx, source_ty, target_val)? {
248 return Ok(());
249 }
250 if source_ty == &Type::Unknown && target_val == ctx.symbols.value_type(&Type::String)? {
251 let function = ctx
252 .symbols
253 .prelude_func_idx("__value_to_string")
254 .ok_or_else(|| {
255 crate::codegen::internal_failure("dynamic string conversion collected")
256 })?;
257 emitter.instruction(Instruction::Call(function));
258 return Ok(());
259 }
260 match (&source_val, &target_val) {
261 (ValType::Ref(_), ValType::F64) if matches!(source_ty, Type::Unknown) => {
262 let function = ctx
263 .symbols
264 .prelude_func_idx("__value_to_number")
265 .ok_or_else(|| {
266 crate::codegen::internal_failure("dynamic number conversion collected")
267 })?;
268 emitter.instruction(Instruction::Call(function));
269 }
270 (ValType::Ref(_), ValType::I32) if matches!(source_ty, Type::Unknown) => {
271 emit_condition_to_i32(emitter, ctx, source_ty)?;
272 }
273 (ValType::Ref(_), ValType::F64) => {
274 emit_cast_to(emitter, ctx, &Type::Number)?;
275 }
276 (ValType::F64 | ValType::I32, ValType::Ref(_)) => emit_box(emitter, ctx, source_ty)?,
278 (ValType::Ref(source), ValType::Ref(target))
280 if ctx.symbols.ref_fits_slot(*source, *target) => {}
281 (ValType::Ref(_), ValType::Ref(target)) => {
287 if target.nullable {
288 emitter.instruction(Instruction::RefCastNullable(target.heap_type));
289 } else {
290 emitter.instruction(Instruction::RefCastNonNull(target.heap_type));
291 }
292 }
293 _ => {
294 return Err(crate::codegen::internal_failure(
295 "value representation does not fit the recorded slot",
296 ));
297 }
298 };
299 Ok(())
300}
301
302pub fn emit_coerce_to_return_slot(
309 emitter: &mut FunctionEmitter<'_>,
310 ctx: &CodegenCtx<'_>,
311 source_ty: &Type,
312) -> Result<(), crate::compiler_error::CompilerFailure> {
313 match emitter.return_target().clone() {
314 ReturnTarget::Slot(slot) => emit_coerce_to_wasm_slot(emitter, ctx, source_ty, slot)?,
315 ReturnTarget::Declared(ret) => emit_coerce_to_slot(emitter, ctx, source_ty, &ret)?,
316 ReturnTarget::NoResult => {}
317 };
318 Ok(())
319}
320
321pub fn emit_condition_to_i32(
325 emitter: &mut FunctionEmitter<'_>,
326 ctx: &CodegenCtx<'_>,
327 cond_ty: &Type,
328) -> Result<(), crate::compiler_error::CompilerFailure> {
329 let val = ctx.symbols.value_type(cond_ty)?;
330 match val {
331 ValType::F64 => {
334 emitter.instruction(Instruction::F64Abs);
335 emitter.instruction(Instruction::F64Const(Ieee64::from(0.0)));
336 emitter.instruction(Instruction::F64Gt);
337 }
338 ValType::Ref(rt) => emit_ref_truthiness(emitter, ctx, cond_ty, rt.nullable)?,
339 _ => {}
341 };
342 Ok(())
343}
344
345fn emit_ref_truthiness(
351 emitter: &mut FunctionEmitter<'_>,
352 ctx: &CodegenCtx<'_>,
353 cond_ty: &Type,
354 nullable: bool,
355) -> Result<(), crate::compiler_error::CompilerFailure> {
356 use TruthinessClass::*;
357 let lowered = cond_ty.lowered();
358 let members: Vec<&Type> = match lowered {
359 Type::Union(members) => members.iter().collect(),
360 single => vec![single],
361 };
362 let classes: Vec<TruthinessClass> = members
363 .iter()
364 .filter(|m| !matches!(m.lowered(), Type::Null))
365 .map(|m| match m.lowered() {
366 Type::Void => Dynamic,
367 Type::Undefined => AlwaysFalsy,
368 _ => truthiness_class(m),
369 })
370 .collect();
371
372 if classes.iter().all(|c| *c == AlwaysTruthy) {
373 if nullable {
374 emitter.instruction(Instruction::RefIsNull);
375 emitter.instruction(Instruction::I32Eqz);
376 } else {
377 emitter.instruction(Instruction::Drop);
378 emitter.instruction(Instruction::I32Const(1));
379 }
380 return Ok(());
381 }
382 if classes.iter().all(|c| *c == AlwaysFalsy) {
383 emitter.instruction(Instruction::Drop);
384 emitter.instruction(Instruction::I32Const(0));
385 return Ok(());
386 }
387
388 let needs = |class: TruthinessClass, literal: fn(&Type) -> bool| {
389 classes.contains(&class)
390 || classes.contains(&Dynamic)
391 || members.iter().any(|m| literal(m.lowered()))
392 };
393 let test_string = needs(StringLike, |m| matches!(m, Type::StringLiteral(_)));
394 let test_number = needs(NumberLike, |m| matches!(m, Type::NumberLiteral(_)));
395 let test_boolean = needs(BooleanLike, |m| matches!(m, Type::BooleanLiteral(_)));
396 let test_bigint = needs(BigIntLike, |_| false);
397 let test_undefined = classes.contains(&Dynamic)
398 || members
399 .iter()
400 .any(|member| matches!(member.lowered(), Type::Undefined));
401
402 let slot = ctx.symbols.value_type(cond_ty)?;
403 let tmp = emitter.add_anonymous_local(slot)?;
404 emitter.instruction(Instruction::LocalSet(tmp));
405
406 let mut open_blocks = 0u32;
409 if nullable {
410 emitter.instruction(Instruction::LocalGet(tmp));
411 emitter.instruction(Instruction::RefIsNull);
412 emitter.emit_if(BlockType::Result(ValType::I32));
413 emitter.instruction(Instruction::I32Const(0));
414 emitter.emit_else();
415 open_blocks += 1;
416 }
417 if test_undefined {
418 emitter.instruction(Instruction::LocalGet(tmp));
419 super::expr::emit_is_undefined(emitter, ctx)?;
420 emitter.emit_if(BlockType::Result(ValType::I32));
421 emitter.instruction(Instruction::I32Const(0));
422 emitter.emit_else();
423 open_blocks += 1;
424 }
425 if test_string {
426 let idx = ctx
427 .symbols
428 .string_type_idx()
429 .ok_or_else(|| crate::codegen::internal_failure("$string intrinsic type registered"))?;
430 emitter.instruction(Instruction::LocalGet(tmp));
431 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(idx)));
432 emitter.emit_if(BlockType::Result(ValType::I32));
433 emitter.instruction(Instruction::LocalGet(tmp));
434 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
435 emitter.instruction(Instruction::StructGet {
436 struct_type_index: idx,
437 field_index: 1,
438 });
439 emitter.instruction(Instruction::ArrayLen);
440 emitter.emit_else();
441 open_blocks += 1;
442 }
443 if test_number {
444 let idx = ctx.symbols.boxed_number_type_idx().ok_or_else(|| {
445 crate::codegen::internal_failure("$BoxedNumber intrinsic type registered")
446 })?;
447 emitter.instruction(Instruction::LocalGet(tmp));
448 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(idx)));
449 emitter.emit_if(BlockType::Result(ValType::I32));
450 emitter.instruction(Instruction::LocalGet(tmp));
451 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
452 emitter.instruction(Instruction::StructGet {
453 struct_type_index: idx,
454 field_index: 1,
455 });
456 emitter.instruction(Instruction::F64Abs);
457 emitter.instruction(Instruction::F64Const(0.0.into()));
458 emitter.instruction(Instruction::F64Gt);
459 emitter.emit_else();
460 open_blocks += 1;
461 }
462 if test_boolean {
463 let idx = ctx.symbols.boxed_boolean_type_idx().ok_or_else(|| {
464 crate::codegen::internal_failure("$BoxedBoolean intrinsic type registered")
465 })?;
466 emitter.instruction(Instruction::LocalGet(tmp));
467 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(idx)));
468 emitter.emit_if(BlockType::Result(ValType::I32));
469 emitter.instruction(Instruction::LocalGet(tmp));
470 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
471 emitter.instruction(Instruction::StructGet {
472 struct_type_index: idx,
473 field_index: 1,
474 });
475 emitter.emit_else();
476 open_blocks += 1;
477 }
478 if test_bigint {
479 let idx = ctx
480 .symbols
481 .bigint_type_idx()
482 .ok_or_else(|| crate::codegen::internal_failure("$bigint intrinsic type registered"))?;
483 emitter.instruction(Instruction::LocalGet(tmp));
484 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(idx)));
485 emitter.emit_if(BlockType::Result(ValType::I32));
486 emitter.instruction(Instruction::LocalGet(tmp));
487 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
488 emitter.instruction(Instruction::StructGet {
490 struct_type_index: idx,
491 field_index: 1,
492 });
493 emitter.emit_else();
494 open_blocks += 1;
495 }
496 emitter.instruction(Instruction::I32Const(1));
497 for _ in 0..open_blocks {
498 emitter.emit_end();
499 }
500 Ok(())
501}
502
503pub fn emit_narrowing_cast(
507 emitter: &mut FunctionEmitter<'_>,
508 ctx: &CodegenCtx<'_>,
509 cast_info: &crate::typechecker::infer::narrowing::CastInfo,
510) -> Result<(), crate::compiler_error::CompilerFailure> {
511 use crate::typechecker::infer::narrowing::CastKind;
512 if matches!(cast_info.to_ty.lowered(), Type::Never) {
513 emitter.instruction(Instruction::Drop);
514 emitter.instruction(Instruction::Unreachable);
515 return Ok(());
516 }
517 let from_val = ctx.symbols.value_type(&cast_info.from_ty)?;
518 let to_val = ctx.symbols.value_type(&cast_info.to_ty)?;
519 if from_val == to_val {
525 return Ok(());
526 }
527 if let (ValType::Ref(s), ValType::Ref(t)) = (&from_val, &to_val)
533 && s.heap_type == t.heap_type
534 && s.nullable
535 && !t.nullable
536 {
537 emitter.instruction(Instruction::RefAsNonNull);
538 return Ok(());
539 }
540 match cast_info.cast_kind {
541 CastKind::NonNull | CastKind::Unbox => {
542 emit_cast_to(emitter, ctx, &cast_info.to_ty)?;
545 }
546 CastKind::RefSubtype => {
547 let ValType::Ref(target_ref) = to_val else {
550 return Err(crate::codegen::internal_failure(
551 "invalid type representation during cast lowering",
552 ));
553 };
554 let cast = if target_ref.nullable {
555 Instruction::RefCastNullable(target_ref.heap_type)
556 } else {
557 Instruction::RefCastNonNull(target_ref.heap_type)
558 };
559 emitter.instruction(cast);
560 }
561 };
562 Ok(())
563}
564
565pub fn emit_unerase(
576 emitter: &mut FunctionEmitter<'_>,
577 ctx: &CodegenCtx<'_>,
578 ty: &Type,
579) -> Result<(), crate::compiler_error::CompilerFailure> {
580 if ctx.symbols.slot_value_type(ty)? != ctx.symbols.value_type(ty)? {
581 emit_cast_to(emitter, ctx, ty)?;
582 };
583 Ok(())
584}
585
586pub fn emit_cast_to(
594 emitter: &mut FunctionEmitter<'_>,
595 ctx: &CodegenCtx<'_>,
596 ty: &Type,
597) -> Result<(), crate::compiler_error::CompilerFailure> {
598 let ty = ty.lowered();
601 if !target_allows_null(ctx, ty)? {
604 emitter.instruction(Instruction::RefAsNonNull);
605 }
606 match ty {
607 Type::Number | Type::NumberLiteral(_) => {
608 let boxed_idx = ctx
609 .symbols
610 .boxed_number_type_idx()
611 .ok_or_else(|| crate::codegen::internal_failure("boxed_number type registered"))?;
612 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(boxed_idx)));
613 emitter.instruction(Instruction::StructGet {
614 struct_type_index: boxed_idx,
615 field_index: 1,
616 });
617 }
618 Type::Boolean | Type::BooleanLiteral(_) => {
619 let boxed_idx = ctx
620 .symbols
621 .boxed_boolean_type_idx()
622 .ok_or_else(|| crate::codegen::internal_failure("boxed_boolean type registered"))?;
623 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(boxed_idx)));
624 emitter.instruction(Instruction::StructGet {
625 struct_type_index: boxed_idx,
626 field_index: 1,
627 });
628 }
629 Type::String | Type::StringLiteral(_) => {
630 let idx = ctx
631 .symbols
632 .string_type_idx()
633 .ok_or_else(|| crate::codegen::internal_failure("string type registered"))?;
634 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
635 }
636 Type::BigInt | Type::BigIntLiteral(_) => {
637 let idx = ctx
638 .symbols
639 .bigint_type_idx()
640 .ok_or_else(|| crate::codegen::internal_failure("bigint type registered"))?;
641 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
642 }
643 Type::Object { .. } => {
644 let idx = ctx
651 .symbols
652 .object_shape_type_idx()
653 .ok_or_else(|| crate::codegen::internal_failure("$ObjectShape type registered"))?;
654 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
655 }
656 Type::Array(_) | Type::Tuple(_) => {
657 let idx = ctx
662 .symbols
663 .array_type_idx()
664 .ok_or_else(|| crate::codegen::internal_failure("array type registered"))?;
665 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
666 }
667 Type::Uint8Array => {
668 let idx = ctx
669 .symbols
670 .uint8_array_type_idx()
671 .ok_or_else(|| crate::codegen::internal_failure("uint8_array type registered"))?;
672 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
673 }
674 Type::TypeVar(_)
675 | Type::GenericParam { .. }
676 | Type::Unknown
677 | Type::NonPrimitive
678 | Type::Never => {
679 }
682 Type::Function { .. } => {
683 crate::codegen::closure_coercions::emit_erased_cast(
684 emitter,
685 ctx,
686 crate::codegen::closures::classify(ty)?,
687 )?;
688 }
689 Type::Null => {
690 }
692 Type::Undefined => {
693 let idx = ctx
694 .symbols
695 .intrinsic_type_indices()
696 .ok_or_else(|| {
697 crate::codegen::internal_failure("undefined intrinsic not registered")
698 })?
699 .undefined;
700 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
701 }
702 Type::Void => {}
703 Type::Error => {
704 return Err(crate::codegen::internal_failure(
705 "invalid type representation during cast lowering",
706 ));
707 }
708 Type::Union(_) => {
709 match ctx.symbols.value_type(ty)? {
712 ValType::F64 => emit_cast_to(emitter, ctx, &Type::Number)?,
713 ValType::I32 => emit_cast_to(emitter, ctx, &Type::Boolean)?,
714 ValType::Ref(target_ref) => {
715 let cast = if target_ref.nullable {
716 Instruction::RefCastNullable(target_ref.heap_type)
717 } else {
718 Instruction::RefCastNonNull(target_ref.heap_type)
719 };
720 emitter.instruction(cast);
721 }
722 _ => {
723 return Err(crate::codegen::internal_failure(
724 "invalid type representation during cast lowering",
725 ));
726 }
727 }
728 }
729 Type::NumberEnum { .. } => {
730 let boxed_idx = ctx
735 .symbols
736 .boxed_number_type_idx()
737 .ok_or_else(|| crate::codegen::internal_failure("boxed_number type registered"))?;
738 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(boxed_idx)));
739 }
740 Type::StringEnum { .. } => {
741 let idx = ctx
742 .symbols
743 .string_type_idx()
744 .ok_or_else(|| crate::codegen::internal_failure("string type registered"))?;
745 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
746 }
747 Type::InterfaceRef { .. } => {
748 }
751 Type::ClassRef { mangled, .. } => {
752 let idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
758 crate::codegen::internal_failure("class struct type is not registered")
759 })?;
760 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
761 }
762 Type::AliasRef { .. } => {
763 }
768 Type::Alias { .. } | Type::Refined { .. } | Type::Readonly(_) => {
769 return Err(crate::codegen::internal_failure(
770 "invalid type representation during cast lowering",
771 ));
772 }
773 };
774 Ok(())
775}
776
777pub fn target_allows_null(
790 ctx: &CodegenCtx<'_>,
791 ty: &Type,
792) -> Result<bool, crate::compiler_error::CompilerFailure> {
793 let ty = ty.lowered();
794 Ok(matches!(
795 ty,
796 Type::Null
797 | Type::Void
798 | Type::Unknown
799 | Type::Never
800 | Type::TypeVar(_)
801 | Type::GenericParam { .. }
802 | Type::AliasRef { .. }
803 ) || (matches!(ty, Type::Union(_)) && is_nullable_ref(ctx.symbols.value_type(ty)?)))
804}