Complex bitcast recursive tag union pointers when we need them to be opaque

Resolves a discussion on Zulip: https://roc.zulipchat.com/#narrow/stream/231635-compiler-development/topic/When.20recursive.20structs.20aren't.20recursive
This commit is contained in:
Ayaz Hafiz 2022-09-14 16:58:37 -05:00
parent 4b0342ef34
commit 8cd4281173
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
3 changed files with 66 additions and 1 deletions

View file

@ -2432,6 +2432,15 @@ pub fn store_roc_value<'a, 'ctx, 'env>(
.unwrap();
}
} else {
let destination_type = destination
.get_type()
.get_element_type()
.try_into()
.unwrap();
let value =
cast_if_necessary_for_opaque_recursive_pointers(env.builder, value, destination_type);
env.builder.build_store(destination, value);
}
}
@ -2950,6 +2959,29 @@ pub fn load_symbol_and_lambda_set<'a, 'ctx, 'b>(
}
}
/// Cast a value to another value of the same size, but only if their types are not equivalent.
/// This is needed to allow us to interoperate between recursive pointers in unions that are
/// opaque, and well-typed.
///
/// This will no longer be necessary and should be removed after we employ opaque pointers from
/// LLVM.
pub fn cast_if_necessary_for_opaque_recursive_pointers<'ctx>(
builder: &Builder<'ctx>,
from_value: BasicValueEnum<'ctx>,
to_type: BasicTypeEnum<'ctx>,
) -> BasicValueEnum<'ctx> {
if from_value.get_type() != to_type {
complex_bitcast(
builder,
from_value,
to_type,
"bitcast_for_opaque_recursive_pointer",
)
} else {
from_value
}
}
/// Cast a value to another value of the same (or smaller?) size
pub fn cast_basic_basic<'ctx>(
builder: &Builder<'ctx>,

View file

@ -18,7 +18,7 @@ use roc_module::symbol::Interns;
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds, STLayoutInterner, UnionLayout};
use super::build::{load_roc_value, FunctionSpec};
use super::build::{cast_if_necessary_for_opaque_recursive_pointers, load_roc_value, FunctionSpec};
use super::convert::{argument_type_from_layout, argument_type_from_union_layout};
pub struct PointerToRefcount<'ctx> {
@ -515,6 +515,12 @@ fn call_help<'a, 'ctx, 'env>(
call_mode: CallMode<'ctx>,
value: BasicValueEnum<'ctx>,
) -> inkwell::values::CallSiteValue<'ctx> {
let value = cast_if_necessary_for_opaque_recursive_pointers(
env.builder,
value,
function.get_params()[0].get_type(),
);
let call = match call_mode {
CallMode::Inc(inc_amount) => {
env.builder

View file

@ -1963,3 +1963,30 @@ fn tag_union_let_generalization() {
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn fit_recursive_union_in_struct_into_recursive_pointer() {
assert_evals_to!(
indoc!(
r#"
NonEmpty := [
First Str,
Next { item: Str, rest: NonEmpty },
]
nonEmpty =
a = "abcdefgh"
b = @NonEmpty (First "ijkl")
c = Next { item: a, rest: b }
@NonEmpty c
when nonEmpty is
@NonEmpty (Next r) -> r.item
_ -> "<bad>"
"#
),
RocStr::from("abcdefgh"),
RocStr
);
}