Allow direct packing, unpacking of erased types

This commit is contained in:
Ayaz Hafiz 2023-07-02 13:17:44 -05:00
parent 1d1db83cc7
commit cd64134b0a
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
6 changed files with 296 additions and 185 deletions

View file

@ -29,6 +29,7 @@ pub enum UseKind {
ExpectLookup, ExpectLookup,
ErasedMake(ErasedField), ErasedMake(ErasedField),
Erased, Erased,
FunctionPointer,
} }
pub enum ProblemKind<'a> { pub enum ProblemKind<'a> {
@ -119,6 +120,24 @@ pub enum ProblemKind<'a> {
num_needed: usize, num_needed: usize,
num_given: usize, num_given: usize,
}, },
ErasedMakeValueNotBoxed {
symbol: Symbol,
def_layout: InLayout<'a>,
def_line: usize,
},
ErasedMakeCalleeNotFunctionPointer {
symbol: Symbol,
def_layout: InLayout<'a>,
def_line: usize,
},
ErasedLoadValueNotBoxed {
symbol: Symbol,
target_layout: InLayout<'a>,
},
ErasedLoadCalleeNotFunctionPointer {
symbol: Symbol,
target_layout: InLayout<'a>,
},
} }
pub struct Problem<'a> { pub struct Problem<'a> {
@ -276,7 +295,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
match body { match body {
Stmt::Let(x, e, x_layout, rest) => { Stmt::Let(x, e, x_layout, rest) => {
if let Some(e_layout) = self.check_expr(e) { if let Some(e_layout) = self.check_expr(e, *x_layout) {
if self.not_equiv(e_layout, *x_layout) { if self.not_equiv(e_layout, *x_layout) {
self.problem(ProblemKind::SymbolDefMismatch { self.problem(ProblemKind::SymbolDefMismatch {
symbol: *x, symbol: *x,
@ -393,7 +412,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
} }
} }
fn check_expr(&mut self, e: &Expr<'a>) -> Option<InLayout<'a>> { fn check_expr(&mut self, e: &Expr<'a>, target_layout: InLayout<'a>) -> Option<InLayout<'a>> {
match e { match e {
Expr::Literal(_) => None, Expr::Literal(_) => None,
Expr::NullPointer => None, Expr::NullPointer => None,
@ -486,11 +505,10 @@ impl<'a, 'r> Ctx<'a, 'r> {
} }
} }
}), }),
&Expr::ErasedMake { value, callee } => { &Expr::ErasedMake { value, callee } => Some(self.check_erased_make(value, callee)),
self.check_erased_make(value, callee); &Expr::ErasedLoad { symbol, field } => {
Some(Layout::ERASED) Some(self.check_erased_load(symbol, field, target_layout))
} }
&Expr::ErasedLoad { symbol, field } => Some(self.check_erased_load(symbol, field)),
&Expr::FunctionPointer { lambda_name } => { &Expr::FunctionPointer { lambda_name } => {
let lambda_symbol = lambda_name.name(); let lambda_symbol = lambda_name.name();
if !self.procs.iter().any(|((name, proc), _)| { if !self.procs.iter().any(|((name, proc), _)| {
@ -500,7 +518,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
symbol: lambda_symbol, symbol: lambda_symbol,
}); });
} }
Some(Layout::OPAQUE_PTR) Some(target_layout)
} }
&Expr::Reset { &Expr::Reset {
symbol, symbol,
@ -691,7 +709,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
args: arg_layouts, args: arg_layouts,
ret: *ret_layout, ret: *ret_layout,
})); }));
self.check_sym_layout(*pointer, expected_layout, UseKind::SwitchCond); self.check_sym_layout(*pointer, expected_layout, UseKind::FunctionPointer);
for (arg, wanted_layout) in arguments.iter().zip(arg_layouts.iter()) { for (arg, wanted_layout) in arguments.iter().zip(arg_layouts.iter()) {
self.check_sym_layout(*arg, *wanted_layout, UseKind::CallArg); self.check_sym_layout(*arg, *wanted_layout, UseKind::CallArg);
} }
@ -756,28 +774,67 @@ impl<'a, 'r> Ctx<'a, 'r> {
} }
} }
fn check_erased_make(&mut self, value: Option<Symbol>, callee: Symbol) { fn check_erased_make(&mut self, value: Option<Symbol>, callee: Symbol) -> InLayout<'a> {
if let Some(value) = value { if let Some(value) = value {
self.check_sym_layout( self.with_sym_layout(value, |this, def_line, layout| {
value, let repr = this.interner.get_repr(layout);
Layout::OPAQUE_PTR, if !matches!(repr, LayoutRepr::Boxed(_)) {
UseKind::ErasedMake(ErasedField::Value), this.problem(ProblemKind::ErasedMakeValueNotBoxed {
); symbol: value,
def_layout: layout,
def_line,
});
}
Option::<()>::None
});
} }
self.check_sym_layout( self.with_sym_layout(callee, |this, def_line, layout| {
callee, let repr = this.interner.get_repr(layout);
Layout::OPAQUE_PTR, if !matches!(repr, LayoutRepr::FunctionPointer(_)) {
UseKind::ErasedMake(ErasedField::Callee), this.problem(ProblemKind::ErasedMakeCalleeNotFunctionPointer {
); symbol: callee,
def_layout: layout,
def_line,
});
}
Option::<()>::None
});
Layout::ERASED
} }
fn check_erased_load(&mut self, symbol: Symbol, field: ErasedField) -> InLayout<'a> { fn check_erased_load(
&mut self,
symbol: Symbol,
field: ErasedField,
target_layout: InLayout<'a>,
) -> InLayout<'a> {
self.check_sym_layout(symbol, Layout::ERASED, UseKind::Erased); self.check_sym_layout(symbol, Layout::ERASED, UseKind::Erased);
match field { match field {
ErasedField::Value => Layout::OPAQUE_PTR, ErasedField::Value => {
ErasedField::Callee => Layout::OPAQUE_PTR, let repr = self.interner.get_repr(target_layout);
if !matches!(repr, LayoutRepr::Boxed(_)) {
self.problem(ProblemKind::ErasedLoadValueNotBoxed {
symbol,
target_layout,
});
}
}
ErasedField::Callee => {
let repr = self.interner.get_repr(target_layout);
if !matches!(repr, LayoutRepr::FunctionPointer(_)) {
self.problem(ProblemKind::ErasedLoadCalleeNotFunctionPointer {
symbol,
target_layout,
});
}
}
} }
target_layout
} }
} }

View file

@ -415,6 +415,74 @@ where
f.as_string(num_given), f.as_string(num_given),
]) ])
} }
ProblemKind::ErasedMakeValueNotBoxed {
symbol,
def_layout,
def_line,
} => {
title = "ERASED VALUE IS NOT BOXED";
docs_before = vec![(
def_line,
f.concat([
f.reflow("The value "),
format_symbol(f, interns, symbol),
f.reflow(" defined here"),
]),
)];
f.concat([
f.reflow("must be boxed in order to be erased, but has layout "),
interner.to_doc_top(def_layout, f),
])
}
ProblemKind::ErasedMakeCalleeNotFunctionPointer {
symbol,
def_layout,
def_line,
} => {
title = "ERASED CALLEE IS NOT A FUNCTION POINTER";
docs_before = vec![(
def_line,
f.concat([
f.reflow("The value "),
format_symbol(f, interns, symbol),
f.reflow(" defined here"),
]),
)];
f.concat([
f.reflow(
"must be a function pointer in order to be an erasure callee, but has layout ",
),
interner.to_doc_top(def_layout, f),
])
}
ProblemKind::ErasedLoadValueNotBoxed {
symbol,
target_layout,
} => {
title = "ERASED VALUE IS NOT BOXED";
docs_before = vec![];
f.concat([
f.reflow("The erased value load "),
format_symbol(f, interns, symbol),
f.reflow(" has layout "),
interner.to_doc_top(target_layout, f),
f.reflow(", but should be boxed!"),
])
}
ProblemKind::ErasedLoadCalleeNotFunctionPointer {
symbol,
target_layout,
} => {
title = "ERASED CALLEE IS NOT A FUNCTION POINTER";
docs_before = vec![];
f.concat([
f.reflow("The erased callee load "),
format_symbol(f, interns, symbol),
f.reflow(" has layout "),
interner.to_doc_top(target_layout, f),
f.reflow(", but should be a function pointer!"),
])
}
}; };
(title, docs_before, doc) (title, docs_before, doc)
} }
@ -443,6 +511,7 @@ fn format_use_kind(use_kind: UseKind) -> &'static str {
ErasedField::Callee => "erased callee field", ErasedField::Callee => "erased callee field",
}, },
UseKind::Erased => "erasure", UseKind::Erased => "erasure",
UseKind::FunctionPointer => "function pointer",
} }
} }

View file

@ -5268,12 +5268,18 @@ pub fn with_hole<'a>(
RawFunctionLayout::ZeroArgumentThunk(_) => { RawFunctionLayout::ZeroArgumentThunk(_) => {
unreachable!("a closure syntactically always must have at least one argument") unreachable!("a closure syntactically always must have at least one argument")
} }
RawFunctionLayout::ErasedFunction(_argument_layouts, _ret_layout) => { RawFunctionLayout::ErasedFunction(argument_layouts, ret_layout) => {
let captured_symbols = Vec::from_iter_in(captured_symbols, env.arena); let captured_symbols = Vec::from_iter_in(captured_symbols, env.arena);
let captured_symbols = captured_symbols.into_bump_slice(); let captured_symbols = captured_symbols.into_bump_slice();
let captured_symbols = CapturedSymbols::Captured(captured_symbols); let captured_symbols = CapturedSymbols::Captured(captured_symbols);
let resolved_erased_lambda = let resolved_erased_lambda = ResolvedErasedLambda::new(
ResolvedErasedLambda::new(env, layout_cache, name, captured_symbols); env,
layout_cache,
name,
captured_symbols,
argument_layouts,
ret_layout,
);
let inserted = procs.insert_anonymous( let inserted = procs.insert_anonymous(
env, env,
@ -8340,9 +8346,15 @@ fn specialize_symbol<'a>(
) )
} }
} }
RawFunctionLayout::ErasedFunction(..) => { RawFunctionLayout::ErasedFunction(argument_layouts, ret_layout) => {
let erased_lambda = let erased_lambda = erased::ResolvedErasedLambda::new(
erased::ResolvedErasedLambda::new(env, layout_cache, original, captured); env,
layout_cache,
original,
captured,
argument_layouts,
ret_layout,
);
let lambda_name = erased_lambda.lambda_name(); let lambda_name = erased_lambda.lambda_name();
let proc_layout = let proc_layout =

View file

@ -17,6 +17,7 @@ fn index_erased_function<'a>(
assign_to: Symbol, assign_to: Symbol,
erased_function: Symbol, erased_function: Symbol,
field: ErasedField, field: ErasedField,
layout: InLayout<'a>,
) -> impl FnOnce(Stmt<'a>) -> Stmt<'a> { ) -> impl FnOnce(Stmt<'a>) -> Stmt<'a> {
move |rest| { move |rest| {
Stmt::Let( Stmt::Let(
@ -25,29 +26,7 @@ fn index_erased_function<'a>(
symbol: erased_function, symbol: erased_function,
field, field,
}, },
Layout::OPAQUE_PTR, layout,
arena.alloc(rest),
)
}
}
fn cast_erased_callee<'a>(
arena: &'a Bump,
assign_to: Symbol,
erased_function: Symbol,
fn_ptr_layout: InLayout<'a>,
) -> impl FnOnce(Stmt<'a>) -> Stmt<'a> {
move |rest| {
Stmt::Let(
assign_to,
Expr::Call(Call {
call_type: CallType::LowLevel {
op: LowLevel::PtrCast,
update_mode: UpdateModeId::BACKEND_DUMMY,
},
arguments: arena.alloc([erased_function]),
}),
fn_ptr_layout,
arena.alloc(rest), arena.alloc(rest),
) )
} }
@ -83,13 +62,14 @@ fn is_null<'a>(
arena: &'a Bump, arena: &'a Bump,
assign_to: Symbol, assign_to: Symbol,
ptr_symbol: Symbol, ptr_symbol: Symbol,
layout: InLayout<'a>,
) -> impl FnOnce(Stmt<'a>) -> Stmt<'a> { ) -> impl FnOnce(Stmt<'a>) -> Stmt<'a> {
let null_symbol = env.unique_symbol(); let null_symbol = env.unique_symbol();
move |rest| { move |rest| {
Stmt::Let( Stmt::Let(
null_symbol, null_symbol,
Expr::NullPointer, Expr::NullPointer,
Layout::OPAQUE_PTR, layout,
arena.alloc(Stmt::Let( arena.alloc(Stmt::Let(
assign_to, assign_to,
Expr::Call(Call { Expr::Call(Call {
@ -106,6 +86,39 @@ fn is_null<'a>(
} }
} }
struct BuiltFunctionPointer<'a> {
function_pointer: InLayout<'a>,
reified_arguments: &'a [InLayout<'a>],
}
fn build_function_pointer<'a>(
arena: &'a Bump,
layout_cache: &mut LayoutCache<'a>,
argument_layouts: &'a [InLayout<'a>],
return_layout: InLayout<'a>,
pass_closure: bool,
) -> BuiltFunctionPointer<'a> {
let reified_arguments = if pass_closure {
let mut args = AVec::with_capacity_in(argument_layouts.len() + 1, arena);
args.extend(argument_layouts.iter().chain(&[Layout::ERASED]).copied());
args.into_bump_slice()
} else {
argument_layouts
};
let fn_ptr_layout = LayoutRepr::FunctionPointer(FunctionPointer {
args: reified_arguments,
ret: return_layout,
});
let function_pointer = layout_cache.put_in_direct_no_semantic(fn_ptr_layout);
BuiltFunctionPointer {
function_pointer,
reified_arguments,
}
}
/// Given /// Given
/// ///
/// ``` /// ```
@ -118,7 +131,7 @@ fn is_null<'a>(
/// f = compile(f) /// f = compile(f)
/// joinpoint join result: /// joinpoint join result:
/// <hole> /// <hole>
/// f_value: Ptr<[]> = ErasedLoad(f, .value) /// f_value: Box<[]> = ErasedLoad(f, .value)
/// f_callee: Ptr<[]> = ErasedLoad(f, .callee) /// f_callee: Ptr<[]> = ErasedLoad(f, .callee)
/// if (f_value != nullptr) { /// if (f_value != nullptr) {
/// f_callee = Cast(f_callee, (..params, Erased) -> ret); /// f_callee = Cast(f_callee, (..params, Erased) -> ret);
@ -151,55 +164,44 @@ pub fn call_erased_function<'a>(
// f_value = ErasedLoad(f, .value) // f_value = ErasedLoad(f, .value)
let f_value = env.unique_symbol(); let f_value = env.unique_symbol();
let let_f_value = index_erased_function(arena, f_value, f, ErasedField::Value); let let_f_value =
index_erased_function(arena, f_value, f, ErasedField::Value, Layout::OPAQUE_PTR);
// f_callee = ErasedLoad(f, .callee)
let f_callee = env.unique_symbol();
let let_f_callee = index_erased_function(arena, f_callee, f, ErasedField::Callee);
let mut build_closure_data_branch = |env: &mut Env, pass_closure| { let mut build_closure_data_branch = |env: &mut Env, pass_closure| {
// f_callee = Cast(f_callee, (..params) -> ret); // f_callee = Cast(f_callee, (..params) -> ret);
// result = f_callee ..args // result = f_callee ..args
// jump join result // jump join result
let (f_args, function_argument_symbols) = if pass_closure { let BuiltFunctionPointer {
// f_args = ...args, f function_pointer,
reified_arguments: f_args,
} = build_function_pointer(arena, layout_cache, f_args, f_ret, pass_closure);
// f_callee = ErasedLoad(f, .callee)
let f_callee = env.unique_symbol();
let let_f_callee =
index_erased_function(arena, f_callee, f, ErasedField::Callee, function_pointer);
let function_argument_symbols = if pass_closure {
// function_argument_symbols = ...args, f.value // function_argument_symbols = ...args, f.value
let f_args = { let mut args = AVec::with_capacity_in(function_argument_symbols.len() + 1, arena);
let mut args = AVec::with_capacity_in(f_args.len() + 1, arena); args.extend(function_argument_symbols.iter().chain(&[f]));
args.extend(f_args.iter().chain(&[Layout::ERASED]).copied()); args.into_bump_slice()
args.into_bump_slice()
};
let function_argument_symbols = {
let mut args = AVec::with_capacity_in(function_argument_symbols.len() + 1, arena);
args.extend(function_argument_symbols.iter().chain(&[f]));
args.into_bump_slice()
};
(f_args, function_argument_symbols)
} else { } else {
(f_args, function_argument_symbols) function_argument_symbols
}; };
let fn_ptr_layout =
layout_cache.put_in_direct_no_semantic(LayoutRepr::FunctionPointer(FunctionPointer {
args: f_args,
ret: f_ret,
}));
let f_callee_cast = env.unique_symbol();
let let_f_callee_cast = cast_erased_callee(arena, f_callee_cast, f_callee, fn_ptr_layout);
let result = env.unique_symbol(); let result = env.unique_symbol();
let let_result = call_callee( let let_result = call_callee(
arena, arena,
result, result,
f_ret, f_ret,
f_callee_cast, f_callee,
f_args, f_args,
function_argument_symbols, function_argument_symbols,
); );
let_f_callee_cast( let_f_callee(
// //
let_result( let_result(
// //
@ -209,7 +211,7 @@ pub fn call_erased_function<'a>(
}; };
let value_is_null = env.unique_symbol(); let value_is_null = env.unique_symbol();
let let_value_is_null = is_null(env, arena, value_is_null, f_value); let let_value_is_null = is_null(env, arena, value_is_null, f_value, Layout::OPAQUE_PTR);
let call_and_jump_on_value = let_value_is_null( let call_and_jump_on_value = let_value_is_null(
// //
@ -234,14 +236,10 @@ pub fn call_erased_function<'a>(
ownership: Ownership::Owned, ownership: Ownership::Owned,
}; };
let remainder = let remainder = let_f_value(
// f_value = ErasedLoad(f, .value) // f_value = ErasedLoad(f, .value)
let_f_value( // <rest>
// f_callee = ErasedLoad(f, .callee) call_and_jump_on_value,
let_f_callee(
//
call_and_jump_on_value,
),
); );
Stmt::Join { Stmt::Join {
@ -273,8 +271,7 @@ pub fn call_erased_function<'a>(
/// We generate /// We generate
/// ///
/// ``` /// ```
/// boxed_value = Expr::Box({s}) /// value = Expr::Box({s})
/// stack_value: Ptr<[]> = Cast(boxed_value, Ptr<[]>)
/// callee = Expr::FunctionPointer(f) /// callee = Expr::FunctionPointer(f)
/// f = Expr::ErasedMake({ value, callee }) /// f = Expr::ErasedMake({ value, callee })
/// ``` /// ```
@ -301,6 +298,8 @@ pub fn build_erased_function<'a>(
let ResolvedErasedLambda { let ResolvedErasedLambda {
captures, captures,
lambda_name, lambda_name,
arguments,
ret,
} = resolved_lambda; } = resolved_lambda;
let value = match captures { let value = match captures {
@ -319,11 +318,16 @@ pub fn build_erased_function<'a>(
hole, hole,
); );
let BuiltFunctionPointer {
function_pointer,
reified_arguments: _,
} = build_function_pointer(env.arena, layout_cache, arguments, ret, captures.is_some());
// callee = Expr::FunctionPointer(f) // callee = Expr::FunctionPointer(f)
let result = Stmt::Let( let result = Stmt::Let(
callee, callee,
Expr::FunctionPointer { lambda_name }, Expr::FunctionPointer { lambda_name },
Layout::OPAQUE_PTR, function_pointer,
env.arena.alloc(result), env.arena.alloc(result),
); );
@ -340,25 +344,11 @@ pub fn build_erased_function<'a>(
let stack_captures_layout = let stack_captures_layout =
layout_cache.put_in_direct_no_semantic(LayoutRepr::Struct(layouts)); layout_cache.put_in_direct_no_semantic(LayoutRepr::Struct(layouts));
let boxed_captures = env.unique_symbol();
let boxed_captures_layout = let boxed_captures_layout =
layout_cache.put_in_direct_no_semantic(LayoutRepr::Boxed(stack_captures_layout)); layout_cache.put_in_direct_no_semantic(LayoutRepr::Boxed(stack_captures_layout));
let result = Stmt::Let( let result = Stmt::Let(
value.unwrap(), value.unwrap(),
Expr::Call(Call {
call_type: CallType::LowLevel {
op: LowLevel::PtrCast,
update_mode: UpdateModeId::BACKEND_DUMMY,
},
arguments: env.arena.alloc([boxed_captures]),
}),
Layout::OPAQUE_PTR,
env.arena.alloc(result),
);
let result = Stmt::Let(
boxed_captures,
Expr::ExprBox { Expr::ExprBox {
symbol: stack_captures, symbol: stack_captures,
}, },
@ -386,6 +376,8 @@ struct ResolvedErasedCaptures<'a> {
pub struct ResolvedErasedLambda<'a> { pub struct ResolvedErasedLambda<'a> {
captures: Option<ResolvedErasedCaptures<'a>>, captures: Option<ResolvedErasedCaptures<'a>>,
lambda_name: LambdaName<'a>, lambda_name: LambdaName<'a>,
arguments: &'a [InLayout<'a>],
ret: InLayout<'a>,
} }
impl<'a> ResolvedErasedLambda<'a> { impl<'a> ResolvedErasedLambda<'a> {
@ -394,6 +386,8 @@ impl<'a> ResolvedErasedLambda<'a> {
layout_cache: &mut LayoutCache<'a>, layout_cache: &mut LayoutCache<'a>,
lambda_symbol: Symbol, lambda_symbol: Symbol,
captures: CapturedSymbols<'a>, captures: CapturedSymbols<'a>,
arguments: &'a [InLayout<'a>],
ret: InLayout<'a>,
) -> Self { ) -> Self {
let resolved_captures; let resolved_captures;
let lambda_name; let lambda_name;
@ -422,6 +416,8 @@ impl<'a> ResolvedErasedLambda<'a> {
Self { Self {
captures: resolved_captures, captures: resolved_captures,
lambda_name, lambda_name,
arguments,
ret,
} }
} }
@ -454,7 +450,6 @@ pub fn unpack_closure_data<'a>(
captures: &[(Symbol, Variable)], captures: &[(Symbol, Variable)],
mut hole: Stmt<'a>, mut hole: Stmt<'a>,
) -> Stmt<'a> { ) -> Stmt<'a> {
let loaded_captures = env.unique_symbol();
let heap_captures = env.unique_symbol(); let heap_captures = env.unique_symbol();
let stack_captures = env.unique_symbol(); let stack_captures = env.unique_symbol();
@ -494,24 +489,12 @@ pub fn unpack_closure_data<'a>(
env.arena.alloc(hole), env.arena.alloc(hole),
); );
hole = Stmt::Let(
heap_captures,
Expr::Call(Call {
call_type: CallType::LowLevel {
op: LowLevel::PtrCast,
update_mode: UpdateModeId::BACKEND_DUMMY,
},
arguments: env.arena.alloc([captures_symbol]),
}),
heap_captures_layout,
env.arena.alloc(hole),
);
let let_loaded_captures = index_erased_function( let let_loaded_captures = index_erased_function(
env.arena, env.arena,
loaded_captures, heap_captures,
captures_symbol, captures_symbol,
ErasedField::Value, ErasedField::Value,
heap_captures_layout,
); );
let_loaded_captures(hole) let_loaded_captures(hole)

View file

@ -722,9 +722,9 @@ impl<'a> FunctionPointer<'a> {
let ret = interner.to_doc(ret, alloc, seen_rec, parens); let ret = interner.to_doc(ret, alloc, seen_rec, parens);
alloc alloc
.text("FunPtr(") .text("FunPtr((")
.append(args) .append(args)
.append(alloc.text(" -> ")) .append(alloc.text(") -> "))
.append(ret) .append(ret)
.append(")") .append(")")
} }

View file

@ -18,92 +18,82 @@ procedure Bool.2 ():
ret Bool.23; ret Bool.23;
procedure Test.1 (Test.2): procedure Test.1 (Test.2):
let Test.38 : Int1 = CallByName Bool.2; let Test.34 : Int1 = CallByName Bool.2;
if Test.38 then if Test.34 then
dec Test.2; dec Test.2;
let Test.44 : {} = Struct {}; let Test.40 : {} = Struct {};
let Test.45 : Boxed({}) = Box Test.44; let Test.38 : Boxed({}) = Box Test.40;
let Test.42 : Boxed([]) = lowlevel PtrCast Test.45; let Test.39 : FunPtr(({}, ?Erased) -> Str) = FunctionPointer Test.3;
let Test.43 : Boxed([]) = FunctionPointer Test.3; let Test.35 : ?Erased = ErasedMake { value: Test.38, callee: Test.39 };
dec Test.43; ret Test.35;
let Test.39 : ?Erased = ErasedMake { value: Test.42, callee: Test.43 };
ret Test.39;
else else
let Test.36 : {Str} = Struct {Test.2}; let Test.33 : {Str} = Struct {Test.2};
let Test.37 : Boxed({Str}) = Box Test.36; let Test.31 : Boxed({Str}) = Box Test.33;
let Test.34 : Boxed([]) = lowlevel PtrCast Test.37; let Test.32 : FunPtr(({}, ?Erased) -> Str) = FunctionPointer Test.4;
let Test.35 : Boxed([]) = FunctionPointer Test.4; let Test.26 : ?Erased = ErasedMake { value: Test.31, callee: Test.32 };
dec Test.35; ret Test.26;
let Test.28 : ?Erased = ErasedMake { value: Test.34, callee: Test.35 };
ret Test.28;
procedure Test.3 (Test.40): procedure Test.3 (Test.36):
let Test.41 : Str = ""; let Test.37 : Str = "";
ret Test.41; ret Test.37;
procedure Test.4 (Test.29, #Attr.12): procedure Test.4 (Test.27, #Attr.12):
inc #Attr.12; let Test.29 : Boxed({Str}) = ErasedLoad #Attr.12 .Value;
let Test.31 : Boxed([]) = ErasedLoad #Attr.12 .Value; let Test.30 : {Str} = Unbox Test.29;
dec Test.31;
let Test.32 : Boxed({Str}) = lowlevel PtrCast #Attr.12;
let Test.33 : {Str} = Unbox Test.32;
joinpoint #Derived_gen.0: joinpoint #Derived_gen.0:
let Test.2 : Str = StructAtIndex 0 Test.33; let Test.2 : Str = StructAtIndex 0 Test.30;
ret Test.2; ret Test.2;
in in
let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.32; let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.29;
if #Derived_gen.1 then if #Derived_gen.1 then
free Test.32; free Test.29;
jump #Derived_gen.0; jump #Derived_gen.0;
else else
inc Test.33; inc Test.30;
decref Test.32; decref Test.29;
jump #Derived_gen.0; jump #Derived_gen.0;
procedure Test.0 (): procedure Test.0 ():
let Test.6 : {} = Struct {}; let Test.6 : {} = Struct {};
let Test.17 : Str = ""; let Test.16 : Str = "";
let Test.46 : Boxed([]) = FunctionPointer Test.1; let Test.41 : FunPtr((Str) -> ?Erased) = FunctionPointer Test.1;
dec Test.46; let Test.17 : ?Erased = ErasedMake { value: <null>, callee: Test.41 };
let Test.18 : ?Erased = ErasedMake { value: <null>, callee: Test.46 }; joinpoint Test.18 Test.7:
joinpoint Test.19 Test.7:
joinpoint Test.8 Test.5: joinpoint Test.8 Test.5:
ret Test.5; ret Test.5;
in in
let Test.9 : Boxed([]) = ErasedLoad Test.7 .Value; let Test.9 : Boxed([]) = ErasedLoad Test.7 .Value;
let Test.10 : Boxed([]) = ErasedLoad Test.7 .Callee; let Test.11 : Boxed([]) = NullPointer;
let Test.12 : Boxed([]) = NullPointer; let Test.10 : Int1 = lowlevel Eq Test.9 Test.11;
let Test.11 : Int1 = lowlevel Eq Test.9 Test.12; dec Test.11;
dec Test.12;
dec Test.9; dec Test.9;
switch Test.11: switch Test.10:
case 0: case 0:
let Test.13 : FunPtr({} -> Str) = lowlevel PtrCast Test.10; let Test.12 : FunPtr(({}) -> Str) = ErasedLoad Test.7 .Callee;
let Test.14 : Str = CallByPtr Test.13 Test.6; let Test.13 : Str = CallByPtr Test.12 Test.6;
jump Test.8 Test.14; jump Test.8 Test.13;
default: default:
inc Test.7; inc Test.7;
let Test.15 : FunPtr({}, ?Erased -> Str) = lowlevel PtrCast Test.10; let Test.14 : FunPtr(({}, ?Erased) -> Str) = ErasedLoad Test.7 .Callee;
let Test.16 : Str = CallByPtr Test.15 Test.6 Test.7; let Test.15 : Str = CallByPtr Test.14 Test.6 Test.7;
jump Test.8 Test.16; jump Test.8 Test.15;
in in
let Test.20 : Boxed([]) = ErasedLoad Test.18 .Value; let Test.19 : Boxed([]) = ErasedLoad Test.17 .Value;
let Test.21 : Boxed([]) = ErasedLoad Test.18 .Callee; let Test.21 : Boxed([]) = NullPointer;
let Test.23 : Boxed([]) = NullPointer; let Test.20 : Int1 = lowlevel Eq Test.19 Test.21;
let Test.22 : Int1 = lowlevel Eq Test.20 Test.23; dec Test.21;
dec Test.20; dec Test.19;
dec Test.23; switch Test.20:
switch Test.22:
case 0: case 0:
let Test.24 : FunPtr(Str -> ?Erased) = lowlevel PtrCast Test.21; let Test.22 : FunPtr((Str) -> ?Erased) = ErasedLoad Test.17 .Callee;
let Test.25 : ?Erased = CallByPtr Test.24 Test.17; let Test.23 : ?Erased = CallByPtr Test.22 Test.16;
jump Test.19 Test.25; jump Test.18 Test.23;
default: default:
inc Test.18; inc Test.17;
let Test.26 : FunPtr(Str, ?Erased -> ?Erased) = lowlevel PtrCast Test.21; let Test.24 : FunPtr((Str, ?Erased) -> ?Erased) = ErasedLoad Test.17 .Callee;
let Test.27 : ?Erased = CallByPtr Test.26 Test.17 Test.18; let Test.25 : ?Erased = CallByPtr Test.24 Test.16 Test.17;
jump Test.19 Test.27; jump Test.18 Test.25;