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.peel();
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(_) | Type::GenericParam { .. } | Type::Unknown | Type::Never => {
137 }
140 Type::Null => {
141 }
144 Type::Undefined => {
145 let idx = ctx
146 .symbols
147 .intrinsic_type_indices()
148 .ok_or_else(|| {
149 crate::codegen::internal_failure("undefined intrinsic not registered")
150 })?
151 .undefined;
152 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
153 }
154 Type::Void => {}
155 Type::Error => {
156 return Err(crate::codegen::internal_failure(
157 "invalid type representation during cast lowering",
158 ));
159 }
160 Type::NumberEnum { .. } | Type::StringEnum { .. } => {}
161 Type::Union(_) => {
162 match ctx.symbols.value_type(ty)? {
166 ValType::F64 => emit_box(emitter, ctx, &Type::Number)?,
167 ValType::I32 => emit_box(emitter, ctx, &Type::Boolean)?,
168 ValType::Ref(_) => {}
169 _ => {
170 return Err(crate::codegen::internal_failure(
171 "invalid type representation during cast lowering",
172 ));
173 }
174 }
175 }
176 Type::InterfaceRef { .. } | Type::ClassRef { .. } => {}
178 Type::AliasRef { .. } => {}
181 Type::Alias { .. } | Type::Refined { .. } | Type::Readonly(_) => {
182 return Err(crate::codegen::internal_failure(
183 "invalid type representation during cast lowering",
184 ));
185 }
186 };
187 Ok(())
188}
189
190pub fn emit_coerce_to_slot(
196 emitter: &mut FunctionEmitter<'_>,
197 ctx: &CodegenCtx<'_>,
198 source_ty: &Type,
199 target_ty: &Type,
200) -> Result<(), crate::compiler_error::CompilerFailure> {
201 let source_val = ctx.symbols.value_type(source_ty)?;
202 let target_val = ctx.symbols.value_type(target_ty)?;
203 if source_val == target_val {
204 return Ok(());
205 }
206 if matches!(source_ty.peel(), Type::Never) {
209 emit_cast_to(emitter, ctx, target_ty)?;
210 return Ok(());
211 }
212 emit_coerce_to_wasm_slot(emitter, ctx, source_ty, target_val)?;
213
214 Ok(())
215}
216
217pub fn emit_coerce_to_wasm_slot(
226 emitter: &mut FunctionEmitter<'_>,
227 ctx: &CodegenCtx<'_>,
228 source_ty: &Type,
229 target_val: ValType,
230) -> Result<(), crate::compiler_error::CompilerFailure> {
231 let source_val = ctx.symbols.value_type(source_ty)?;
232 if source_val == target_val {
233 return Ok(());
234 }
235 if matches!(source_ty.peel(), Type::Never) {
240 emitter.instruction(Instruction::Unreachable);
241 return Ok(());
242 }
243 if crate::codegen::closure_coercions::emit_coercion(emitter, ctx, source_ty, target_val)? {
244 return Ok(());
245 }
246 if source_ty == &Type::Unknown && target_val == ctx.symbols.value_type(&Type::String)? {
247 let function = ctx
248 .symbols
249 .prelude_func_idx("__value_to_string")
250 .ok_or_else(|| {
251 crate::codegen::internal_failure("dynamic string conversion collected")
252 })?;
253 emitter.instruction(Instruction::Call(function));
254 return Ok(());
255 }
256 match (&source_val, &target_val) {
257 (ValType::Ref(_), ValType::F64) if matches!(source_ty, Type::Unknown) => {
258 let function = ctx
259 .symbols
260 .prelude_func_idx("__value_to_number")
261 .ok_or_else(|| {
262 crate::codegen::internal_failure("dynamic number conversion collected")
263 })?;
264 emitter.instruction(Instruction::Call(function));
265 }
266 (ValType::Ref(_), ValType::I32) if matches!(source_ty, Type::Unknown) => {
267 emit_condition_to_i32(emitter, ctx, source_ty)?;
268 }
269 (ValType::Ref(_), ValType::F64) => {
270 emit_cast_to(emitter, ctx, &Type::Number)?;
271 }
272 (ValType::F64 | ValType::I32, ValType::Ref(_)) => emit_box(emitter, ctx, source_ty)?,
274 (ValType::Ref(source), ValType::Ref(target))
276 if ctx.symbols.ref_fits_slot(*source, *target) => {}
277 (ValType::Ref(_), ValType::Ref(target)) => {
283 if target.nullable {
284 emitter.instruction(Instruction::RefCastNullable(target.heap_type));
285 } else {
286 emitter.instruction(Instruction::RefCastNonNull(target.heap_type));
287 }
288 }
289 _ => {
290 return Err(crate::codegen::internal_failure(
291 "value representation does not fit the recorded slot",
292 ));
293 }
294 };
295 Ok(())
296}
297
298pub fn emit_coerce_to_return_slot(
305 emitter: &mut FunctionEmitter<'_>,
306 ctx: &CodegenCtx<'_>,
307 source_ty: &Type,
308) -> Result<(), crate::compiler_error::CompilerFailure> {
309 match emitter.return_target().clone() {
310 ReturnTarget::Slot(slot) => emit_coerce_to_wasm_slot(emitter, ctx, source_ty, slot)?,
311 ReturnTarget::Declared(ret) => emit_coerce_to_slot(emitter, ctx, source_ty, &ret)?,
312 ReturnTarget::NoResult => {}
313 };
314 Ok(())
315}
316
317pub fn emit_condition_to_i32(
321 emitter: &mut FunctionEmitter<'_>,
322 ctx: &CodegenCtx<'_>,
323 cond_ty: &Type,
324) -> Result<(), crate::compiler_error::CompilerFailure> {
325 let val = ctx.symbols.value_type(cond_ty)?;
326 match val {
327 ValType::F64 => {
330 emitter.instruction(Instruction::F64Abs);
331 emitter.instruction(Instruction::F64Const(Ieee64::from(0.0)));
332 emitter.instruction(Instruction::F64Gt);
333 }
334 ValType::Ref(rt) => emit_ref_truthiness(emitter, ctx, cond_ty, rt.nullable)?,
335 _ => {}
337 };
338 Ok(())
339}
340
341fn emit_ref_truthiness(
347 emitter: &mut FunctionEmitter<'_>,
348 ctx: &CodegenCtx<'_>,
349 cond_ty: &Type,
350 nullable: bool,
351) -> Result<(), crate::compiler_error::CompilerFailure> {
352 use TruthinessClass::*;
353 let peeled = cond_ty.peel();
354 let members: Vec<&Type> = match peeled {
355 Type::Union(members) => members.iter().collect(),
356 single => vec![single],
357 };
358 let classes: Vec<TruthinessClass> = members
359 .iter()
360 .filter(|m| !matches!(m.peel(), Type::Null))
361 .map(|m| match m.peel() {
362 Type::Void => Dynamic,
363 Type::Undefined => AlwaysFalsy,
364 _ => truthiness_class(m),
365 })
366 .collect();
367
368 if classes.iter().all(|c| *c == AlwaysTruthy) {
369 if nullable {
370 emitter.instruction(Instruction::RefIsNull);
371 emitter.instruction(Instruction::I32Eqz);
372 } else {
373 emitter.instruction(Instruction::Drop);
374 emitter.instruction(Instruction::I32Const(1));
375 }
376 return Ok(());
377 }
378 if classes.iter().all(|c| *c == AlwaysFalsy) {
379 emitter.instruction(Instruction::Drop);
380 emitter.instruction(Instruction::I32Const(0));
381 return Ok(());
382 }
383
384 let needs = |class: TruthinessClass, literal: fn(&Type) -> bool| {
385 classes.contains(&class)
386 || classes.contains(&Dynamic)
387 || members.iter().any(|m| literal(m.peel()))
388 };
389 let test_string = needs(StringLike, |m| matches!(m, Type::StringLiteral(_)));
390 let test_number = needs(NumberLike, |m| matches!(m, Type::NumberLiteral(_)));
391 let test_boolean = needs(BooleanLike, |m| matches!(m, Type::BooleanLiteral(_)));
392 let test_bigint = needs(BigIntLike, |_| false);
393 let test_undefined = classes.contains(&Dynamic)
394 || members
395 .iter()
396 .any(|member| matches!(member.peel(), Type::Undefined));
397
398 let slot = ctx.symbols.value_type(cond_ty)?;
399 let tmp = emitter.add_anonymous_local(slot)?;
400 emitter.instruction(Instruction::LocalSet(tmp));
401
402 let mut open_blocks = 0u32;
405 if nullable {
406 emitter.instruction(Instruction::LocalGet(tmp));
407 emitter.instruction(Instruction::RefIsNull);
408 emitter.emit_if(BlockType::Result(ValType::I32));
409 emitter.instruction(Instruction::I32Const(0));
410 emitter.emit_else();
411 open_blocks += 1;
412 }
413 if test_undefined {
414 emitter.instruction(Instruction::LocalGet(tmp));
415 super::expr::emit_is_undefined(emitter, ctx)?;
416 emitter.emit_if(BlockType::Result(ValType::I32));
417 emitter.instruction(Instruction::I32Const(0));
418 emitter.emit_else();
419 open_blocks += 1;
420 }
421 if test_string {
422 let idx = ctx
423 .symbols
424 .string_type_idx()
425 .ok_or_else(|| crate::codegen::internal_failure("$string intrinsic type registered"))?;
426 emitter.instruction(Instruction::LocalGet(tmp));
427 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(idx)));
428 emitter.emit_if(BlockType::Result(ValType::I32));
429 emitter.instruction(Instruction::LocalGet(tmp));
430 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
431 emitter.instruction(Instruction::StructGet {
432 struct_type_index: idx,
433 field_index: 1,
434 });
435 emitter.instruction(Instruction::ArrayLen);
436 emitter.emit_else();
437 open_blocks += 1;
438 }
439 if test_number {
440 let idx = ctx.symbols.boxed_number_type_idx().ok_or_else(|| {
441 crate::codegen::internal_failure("$BoxedNumber intrinsic type registered")
442 })?;
443 emitter.instruction(Instruction::LocalGet(tmp));
444 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(idx)));
445 emitter.emit_if(BlockType::Result(ValType::I32));
446 emitter.instruction(Instruction::LocalGet(tmp));
447 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
448 emitter.instruction(Instruction::StructGet {
449 struct_type_index: idx,
450 field_index: 1,
451 });
452 emitter.instruction(Instruction::F64Abs);
453 emitter.instruction(Instruction::F64Const(0.0.into()));
454 emitter.instruction(Instruction::F64Gt);
455 emitter.emit_else();
456 open_blocks += 1;
457 }
458 if test_boolean {
459 let idx = ctx.symbols.boxed_boolean_type_idx().ok_or_else(|| {
460 crate::codegen::internal_failure("$BoxedBoolean intrinsic type registered")
461 })?;
462 emitter.instruction(Instruction::LocalGet(tmp));
463 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(idx)));
464 emitter.emit_if(BlockType::Result(ValType::I32));
465 emitter.instruction(Instruction::LocalGet(tmp));
466 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
467 emitter.instruction(Instruction::StructGet {
468 struct_type_index: idx,
469 field_index: 1,
470 });
471 emitter.emit_else();
472 open_blocks += 1;
473 }
474 if test_bigint {
475 let idx = ctx
476 .symbols
477 .bigint_type_idx()
478 .ok_or_else(|| crate::codegen::internal_failure("$bigint intrinsic type registered"))?;
479 emitter.instruction(Instruction::LocalGet(tmp));
480 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(idx)));
481 emitter.emit_if(BlockType::Result(ValType::I32));
482 emitter.instruction(Instruction::LocalGet(tmp));
483 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
484 emitter.instruction(Instruction::StructGet {
486 struct_type_index: idx,
487 field_index: 1,
488 });
489 emitter.emit_else();
490 open_blocks += 1;
491 }
492 emitter.instruction(Instruction::I32Const(1));
493 for _ in 0..open_blocks {
494 emitter.emit_end();
495 }
496 Ok(())
497}
498
499pub fn emit_narrowing_cast(
503 emitter: &mut FunctionEmitter<'_>,
504 ctx: &CodegenCtx<'_>,
505 cast_info: &crate::typechecker::infer::narrowing::CastInfo,
506) -> Result<(), crate::compiler_error::CompilerFailure> {
507 use crate::typechecker::infer::narrowing::CastKind;
508 if matches!(cast_info.to_ty.peel(), Type::Never) {
509 emitter.instruction(Instruction::Drop);
510 emitter.instruction(Instruction::Unreachable);
511 return Ok(());
512 }
513 let from_val = ctx.symbols.value_type(&cast_info.from_ty)?;
514 let to_val = ctx.symbols.value_type(&cast_info.to_ty)?;
515 if from_val == to_val {
521 return Ok(());
522 }
523 if let (ValType::Ref(s), ValType::Ref(t)) = (&from_val, &to_val)
529 && s.heap_type == t.heap_type
530 && s.nullable
531 && !t.nullable
532 {
533 emitter.instruction(Instruction::RefAsNonNull);
534 return Ok(());
535 }
536 match cast_info.cast_kind {
537 CastKind::NonNull | CastKind::Unbox => {
538 emit_cast_to(emitter, ctx, &cast_info.to_ty)?;
541 }
542 CastKind::RefSubtype => {
543 let ValType::Ref(target_ref) = to_val else {
546 return Err(crate::codegen::internal_failure(
547 "invalid type representation during cast lowering",
548 ));
549 };
550 let cast = if target_ref.nullable {
551 Instruction::RefCastNullable(target_ref.heap_type)
552 } else {
553 Instruction::RefCastNonNull(target_ref.heap_type)
554 };
555 emitter.instruction(cast);
556 }
557 };
558 Ok(())
559}
560
561pub fn emit_unerase(
572 emitter: &mut FunctionEmitter<'_>,
573 ctx: &CodegenCtx<'_>,
574 ty: &Type,
575) -> Result<(), crate::compiler_error::CompilerFailure> {
576 if ctx.symbols.slot_value_type(ty)? != ctx.symbols.value_type(ty)? {
577 emit_cast_to(emitter, ctx, ty)?;
578 };
579 Ok(())
580}
581
582pub fn emit_cast_to(
590 emitter: &mut FunctionEmitter<'_>,
591 ctx: &CodegenCtx<'_>,
592 ty: &Type,
593) -> Result<(), crate::compiler_error::CompilerFailure> {
594 let ty = ty.peel();
597 if !target_allows_null(ctx, ty)? {
600 emitter.instruction(Instruction::RefAsNonNull);
601 }
602 match ty {
603 Type::Number | Type::NumberLiteral(_) => {
604 let boxed_idx = ctx
605 .symbols
606 .boxed_number_type_idx()
607 .ok_or_else(|| crate::codegen::internal_failure("boxed_number type registered"))?;
608 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(boxed_idx)));
609 emitter.instruction(Instruction::StructGet {
610 struct_type_index: boxed_idx,
611 field_index: 1,
612 });
613 }
614 Type::Boolean | Type::BooleanLiteral(_) => {
615 let boxed_idx = ctx
616 .symbols
617 .boxed_boolean_type_idx()
618 .ok_or_else(|| crate::codegen::internal_failure("boxed_boolean type registered"))?;
619 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(boxed_idx)));
620 emitter.instruction(Instruction::StructGet {
621 struct_type_index: boxed_idx,
622 field_index: 1,
623 });
624 }
625 Type::String | Type::StringLiteral(_) => {
626 let idx = ctx
627 .symbols
628 .string_type_idx()
629 .ok_or_else(|| crate::codegen::internal_failure("string type registered"))?;
630 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
631 }
632 Type::BigInt | Type::BigIntLiteral(_) => {
633 let idx = ctx
634 .symbols
635 .bigint_type_idx()
636 .ok_or_else(|| crate::codegen::internal_failure("bigint type registered"))?;
637 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
638 }
639 Type::Object { .. } => {
640 let idx = ctx
647 .symbols
648 .object_shape_type_idx()
649 .ok_or_else(|| crate::codegen::internal_failure("$ObjectShape type registered"))?;
650 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
651 }
652 Type::Array(_) | Type::Tuple(_) => {
653 let idx = ctx
658 .symbols
659 .array_type_idx()
660 .ok_or_else(|| crate::codegen::internal_failure("array type registered"))?;
661 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
662 }
663 Type::Uint8Array => {
664 let idx = ctx
665 .symbols
666 .uint8_array_type_idx()
667 .ok_or_else(|| crate::codegen::internal_failure("uint8_array type registered"))?;
668 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
669 }
670 Type::TypeVar(_) | Type::GenericParam { .. } | Type::Unknown | Type::Never => {
671 }
674 Type::Function { .. } => {
675 crate::codegen::closure_coercions::emit_erased_cast(
676 emitter,
677 ctx,
678 crate::codegen::closures::classify(ty)?,
679 )?;
680 }
681 Type::Null => {
682 }
684 Type::Undefined => {
685 let idx = ctx
686 .symbols
687 .intrinsic_type_indices()
688 .ok_or_else(|| {
689 crate::codegen::internal_failure("undefined intrinsic not registered")
690 })?
691 .undefined;
692 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
693 }
694 Type::Void => {}
695 Type::Error => {
696 return Err(crate::codegen::internal_failure(
697 "invalid type representation during cast lowering",
698 ));
699 }
700 Type::Union(_) => {
701 match ctx.symbols.value_type(ty)? {
704 ValType::F64 => emit_cast_to(emitter, ctx, &Type::Number)?,
705 ValType::I32 => emit_cast_to(emitter, ctx, &Type::Boolean)?,
706 ValType::Ref(target_ref) => {
707 let cast = if target_ref.nullable {
708 Instruction::RefCastNullable(target_ref.heap_type)
709 } else {
710 Instruction::RefCastNonNull(target_ref.heap_type)
711 };
712 emitter.instruction(cast);
713 }
714 _ => {
715 return Err(crate::codegen::internal_failure(
716 "invalid type representation during cast lowering",
717 ));
718 }
719 }
720 }
721 Type::NumberEnum { .. } => {
722 let boxed_idx = ctx
727 .symbols
728 .boxed_number_type_idx()
729 .ok_or_else(|| crate::codegen::internal_failure("boxed_number type registered"))?;
730 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(boxed_idx)));
731 }
732 Type::StringEnum { .. } => {
733 let idx = ctx
734 .symbols
735 .string_type_idx()
736 .ok_or_else(|| crate::codegen::internal_failure("string type registered"))?;
737 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
738 }
739 Type::InterfaceRef { .. } => {
740 }
743 Type::ClassRef { mangled, .. } => {
744 let idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
750 crate::codegen::internal_failure("class struct type is not registered")
751 })?;
752 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(idx)));
753 }
754 Type::AliasRef { .. } => {
755 }
760 Type::Alias { .. } | Type::Refined { .. } | Type::Readonly(_) => {
761 return Err(crate::codegen::internal_failure(
762 "invalid type representation during cast lowering",
763 ));
764 }
765 };
766 Ok(())
767}
768
769pub fn target_allows_null(
782 ctx: &CodegenCtx<'_>,
783 ty: &Type,
784) -> Result<bool, crate::compiler_error::CompilerFailure> {
785 let ty = ty.peel();
786 Ok(matches!(
787 ty,
788 Type::Null
789 | Type::Void
790 | Type::Unknown
791 | Type::Never
792 | Type::TypeVar(_)
793 | Type::GenericParam { .. }
794 | Type::AliasRef { .. }
795 ) || (matches!(ty, Type::Union(_)) && is_nullable_ref(ctx.symbols.value_type(ty)?)))
796}