mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 21:39:07 +00:00
correctly add padding between data and tag id
This commit is contained in:
parent
fb4a56e916
commit
c0f49ce62c
3 changed files with 167 additions and 13 deletions
|
@ -326,6 +326,10 @@ impl<'ctx> RocUnion<'ctx> {
|
|||
Self::new(context, target_info, data_align, data_width, None)
|
||||
}
|
||||
|
||||
pub fn data_width(&self) -> u32 {
|
||||
self.data_width
|
||||
}
|
||||
|
||||
pub fn tag_alignment(&self) -> u32 {
|
||||
let tag_id_alignment = match self.tag_type {
|
||||
None => 0,
|
||||
|
|
|
@ -129,6 +129,15 @@ fn write_state<'a, 'ctx, 'env>(
|
|||
env.builder.build_store(offset_ptr, offset);
|
||||
}
|
||||
|
||||
fn offset_add<'ctx>(
|
||||
builder: &Builder<'ctx>,
|
||||
current: IntValue<'ctx>,
|
||||
extra: u32,
|
||||
) -> IntValue<'ctx> {
|
||||
let intval = current.get_type().const_int(extra as _, false);
|
||||
builder.build_int_add(current, intval, "offset_add")
|
||||
}
|
||||
|
||||
pub(crate) fn notify_parent_expect(env: &Env, shared_memory: &SharedMemoryPointer) {
|
||||
let func = env
|
||||
.module
|
||||
|
@ -564,6 +573,56 @@ fn load_tag_data<'a, 'ctx, 'env>(
|
|||
env.builder.new_build_load(tag_type, data_ptr, "load_data")
|
||||
}
|
||||
|
||||
fn clone_tag_payload_and_id<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
ptr: PointerValue<'ctx>,
|
||||
cursors: Cursors<'ctx>,
|
||||
roc_union: RocUnion<'ctx>,
|
||||
tag_id: usize,
|
||||
payload_in_layout: InLayout<'a>,
|
||||
opaque_payload_ptr: PointerValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
let payload_type = basic_type_from_layout(env, layout_interner, payload_in_layout);
|
||||
|
||||
let payload_ptr = env.builder.build_pointer_cast(
|
||||
opaque_payload_ptr,
|
||||
payload_type.ptr_type(AddressSpace::default()),
|
||||
"cast_payload_ptr",
|
||||
);
|
||||
|
||||
let payload = env
|
||||
.builder
|
||||
.new_build_load(payload_type, payload_ptr, "payload");
|
||||
|
||||
// NOTE: `answer` includes any extra_offset that the tag payload may have needed
|
||||
// (e.g. because it includes a list). That is what we want to return, but not what
|
||||
// we need to write the padding and offset of this tag
|
||||
let answer = build_clone(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
ptr,
|
||||
cursors,
|
||||
payload,
|
||||
payload_in_layout,
|
||||
);
|
||||
|
||||
// include padding between data and tag id
|
||||
let tag_id_internal_offset = roc_union.data_width();
|
||||
|
||||
let tag_id_offset = offset_add(env.builder, cursors.offset, tag_id_internal_offset);
|
||||
|
||||
// write the tag id
|
||||
let value = env.context.i8_type().const_int(tag_id as _, false);
|
||||
build_copy(env, ptr, tag_id_offset, value.into());
|
||||
|
||||
// NOTE: padding after tag id (is taken care of by the cursor)
|
||||
|
||||
answer
|
||||
}
|
||||
|
||||
fn build_clone_tag_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
|
@ -622,22 +681,38 @@ fn build_clone_tag_help<'a, 'ctx, 'env>(
|
|||
let block = env.context.append_basic_block(parent, "tag_id_modify");
|
||||
env.builder.position_at_end(block);
|
||||
|
||||
let layout = layout_interner.insert(Layout::struct_no_name_order(field_layouts));
|
||||
let layout = layout_interner.insert(Layout::struct_no_name_order(
|
||||
env.arena.alloc([layout, union_layout.tag_id_layout()]),
|
||||
));
|
||||
|
||||
let basic_type = basic_type_from_layout(env, layout_interner, layout);
|
||||
let data = load_tag_data(
|
||||
env,
|
||||
let roc_union = RocUnion::tagged_from_slices(
|
||||
layout_interner,
|
||||
union_layout,
|
||||
tag_value.into_pointer_value(),
|
||||
basic_type,
|
||||
env.context,
|
||||
tags,
|
||||
env.target_info,
|
||||
);
|
||||
|
||||
let answer =
|
||||
build_clone(env, layout_interner, layout_ids, ptr, cursors, data, layout);
|
||||
// load the tag payload (if any)
|
||||
let payload_layout = Layout::struct_no_name_order(field_layouts);
|
||||
let payload_in_layout = layout_interner.insert(payload_layout);
|
||||
|
||||
let opaque_payload_ptr = env
|
||||
.builder
|
||||
.new_build_struct_gep(
|
||||
roc_union.struct_type(),
|
||||
tag_value.into_pointer_value(),
|
||||
RocUnion::TAG_DATA_INDEX,
|
||||
"data_buffer",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let answer = clone_tag_payload_and_id(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
ptr,
|
||||
cursors,
|
||||
roc_union,
|
||||
tag_id,
|
||||
payload_in_layout,
|
||||
opaque_payload_ptr,
|
||||
);
|
||||
|
||||
env.builder.build_return(Some(&answer));
|
||||
|
||||
|
|
|
@ -1105,4 +1105,79 @@ mod test {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_payloads_of_different_size() {
|
||||
run_expect_test(
|
||||
indoc!(
|
||||
r#"
|
||||
interface Test exposes [] imports []
|
||||
|
||||
actual : [Leftover (List U8), TooShort]
|
||||
actual = Leftover [49, 93]
|
||||
|
||||
expect
|
||||
expected : [Leftover (List U8), TooShort]
|
||||
expected = TooShort
|
||||
|
||||
actual == expected
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
This expectation failed:
|
||||
|
||||
6│> expect
|
||||
7│> expected : [Leftover (List U8), TooShort]
|
||||
8│> expected = TooShort
|
||||
9│>
|
||||
10│> actual == expected
|
||||
|
||||
When it failed, these variables had these values:
|
||||
|
||||
expected : [
|
||||
Leftover (List U8),
|
||||
TooShort,
|
||||
]
|
||||
expected = TooShort
|
||||
"#
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extra_offset_in_tag_union() {
|
||||
run_expect_test(
|
||||
indoc!(
|
||||
r#"
|
||||
interface Test exposes [] imports []
|
||||
|
||||
actual : Result Str U64
|
||||
actual = Err 1
|
||||
|
||||
expect
|
||||
expected : Result Str U64
|
||||
expected = Ok "foobar"
|
||||
|
||||
actual == expected
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
This expectation failed:
|
||||
|
||||
6│> expect
|
||||
7│> expected : Result Str U64
|
||||
8│> expected = Ok "foobar"
|
||||
9│>
|
||||
10│> actual == expected
|
||||
|
||||
When it failed, these variables had these values:
|
||||
|
||||
expected : Result Str U64
|
||||
expected = Ok "foobar"
|
||||
"#
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue