mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 12:18:19 +00:00
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:
parent
4b0342ef34
commit
8cd4281173
3 changed files with 66 additions and 1 deletions
|
@ -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>,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue