mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 07:14:46 +00:00
Merge remote-tracking branch 'origin/trunk' into nullable-tags
This commit is contained in:
commit
8cd744342b
54 changed files with 1208 additions and 724 deletions
|
@ -2,14 +2,15 @@ use std::fs::File;
|
|||
use std::io::prelude::Read;
|
||||
use std::vec::Vec;
|
||||
|
||||
const PATH: &str = env!(
|
||||
"BUILTINS_BC",
|
||||
"Env var BUILTINS_BC not found. Is there a problem with the build script?"
|
||||
);
|
||||
|
||||
pub fn get_bytes() -> Vec<u8> {
|
||||
// In the build script for the builtins module, we compile the builtins bitcode and set
|
||||
// BUILTINS_BC to the path to the compiled output.
|
||||
let path: &'static str = env!(
|
||||
"BUILTINS_BC",
|
||||
"Env var BUILTINS_BC not found. Is there a problem with the build script?"
|
||||
);
|
||||
let mut builtins_bitcode = File::open(path).expect("Unable to find builtins bitcode source");
|
||||
let mut builtins_bitcode = File::open(PATH).expect("Unable to find builtins bitcode source");
|
||||
let mut buffer = Vec::new();
|
||||
builtins_bitcode
|
||||
.read_to_end(&mut buffer)
|
||||
|
|
|
@ -291,6 +291,15 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
),
|
||||
);
|
||||
|
||||
// bitwiseXor : Int a, Int a -> Int a
|
||||
add_type(
|
||||
Symbol::NUM_BITWISE_XOR,
|
||||
top_level_function(
|
||||
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
|
||||
Box::new(int_type(flex(TVAR1))),
|
||||
),
|
||||
);
|
||||
|
||||
// rem : Int a, Int a -> Result (Int a) [ DivByZero ]*
|
||||
add_type(
|
||||
Symbol::NUM_REM,
|
||||
|
|
|
@ -294,6 +294,15 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
)
|
||||
});
|
||||
|
||||
// bitwiseAnd : Attr * Int, Attr * Int -> Attr * Int
|
||||
add_type(Symbol::NUM_BITWISE_XOR, {
|
||||
let_tvars! { star1, star2, star3, int };
|
||||
unique_function(
|
||||
vec![int_type(star1, int), int_type(star2, int)],
|
||||
int_type(star3, int),
|
||||
)
|
||||
});
|
||||
|
||||
// divFloat : Float, Float -> Float
|
||||
add_type(Symbol::NUM_DIV_FLOAT, {
|
||||
let_tvars! { star1, star2, star3, star4, star5};
|
||||
|
|
|
@ -117,7 +117,8 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
|||
NUM_ASIN => num_asin,
|
||||
NUM_MAX_INT => num_max_int,
|
||||
NUM_MIN_INT => num_min_int,
|
||||
NUM_BITWISE_AND => num_bitwise_and
|
||||
NUM_BITWISE_AND => num_bitwise_and,
|
||||
NUM_BITWISE_XOR => num_bitwise_xor
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1153,6 +1154,11 @@ fn num_bitwise_and(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
num_binop(symbol, var_store, LowLevel::NumBitwiseAnd)
|
||||
}
|
||||
|
||||
/// Num.bitwiseXor : Int, Int -> Int
|
||||
fn num_bitwise_xor(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
num_binop(symbol, var_store, LowLevel::NumBitwiseXor)
|
||||
}
|
||||
|
||||
/// List.isEmpty : List * -> Bool
|
||||
fn list_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let list_var = var_store.fresh();
|
||||
|
|
|
@ -3599,7 +3599,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
|
||||
build_num_binop(env, parent, lhs_arg, lhs_layout, rhs_arg, rhs_layout, op)
|
||||
}
|
||||
NumBitwiseAnd => {
|
||||
NumBitwiseAnd | NumBitwiseXor => {
|
||||
debug_assert_eq!(args.len(), 2);
|
||||
|
||||
let (lhs_arg, lhs_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||
|
@ -3953,6 +3953,7 @@ fn build_int_binop<'a, 'ctx, 'env>(
|
|||
NumDivUnchecked => bd.build_int_signed_div(lhs, rhs, "div_int").into(),
|
||||
NumPowInt => call_bitcode_fn(env, &[lhs.into(), rhs.into()], &bitcode::NUM_POW_INT),
|
||||
NumBitwiseAnd => bd.build_and(lhs, rhs, "int_bitwise_and").into(),
|
||||
NumBitwiseXor => bd.build_xor(lhs, rhs, "int_bitwise_xor").into(),
|
||||
_ => {
|
||||
unreachable!("Unrecognized int binary operation: {:?}", op);
|
||||
}
|
||||
|
|
|
@ -757,6 +757,14 @@ mod gen_num {
|
|||
assert_evals_to!("Num.bitwiseAnd 200 0", 0, i64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitwise_xor() {
|
||||
assert_evals_to!("Num.bitwiseXor 20 20", 0, i64);
|
||||
assert_evals_to!("Num.bitwiseXor 15 14", 1, i64);
|
||||
assert_evals_to!("Num.bitwiseXor 7 15", 8, i64);
|
||||
assert_evals_to!("Num.bitwiseXor 200 0", 200, i64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lt_i64() {
|
||||
assert_evals_to!("1 < 2", true, bool);
|
||||
|
|
|
@ -1901,6 +1901,26 @@ mod gen_primitives {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn case_or_pattern() {
|
||||
// the `0` branch body should only be generated once in the future
|
||||
// it is currently duplicated
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : [ Red, Green, Blue ]
|
||||
x = Red
|
||||
|
||||
when x is
|
||||
Red | Green -> 0
|
||||
Blue -> 1
|
||||
"#
|
||||
),
|
||||
0,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn rosetree_basic() {
|
||||
|
@ -1932,4 +1952,29 @@ mod gen_primitives {
|
|||
bool
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn case_jump() {
|
||||
// the decision tree will generate a jump to the `1` branch here
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
x : ConsList I64
|
||||
x = Nil
|
||||
|
||||
main =
|
||||
when Pair x x is
|
||||
Pair Nil _ -> 1
|
||||
Pair _ Nil -> 2
|
||||
Pair (Cons a _) (Cons b _) -> a + b + 3
|
||||
"#
|
||||
),
|
||||
1,
|
||||
i64
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ fn promote_expr_to_module(src: &str) -> String {
|
|||
pub fn helper<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
src: &str,
|
||||
stdlib: roc_builtins::std::StdLib,
|
||||
stdlib: &'a roc_builtins::std::StdLib,
|
||||
leak: bool,
|
||||
context: &'a inkwell::context::Context,
|
||||
) -> (&'static str, String, Library) {
|
||||
|
@ -295,40 +295,6 @@ pub fn helper<'a>(
|
|||
(main_fn_name, delayed_errors.join("\n"), lib)
|
||||
}
|
||||
|
||||
// TODO this is almost all code duplication with assert_llvm_evals_to
|
||||
// the only difference is that this calls uniq_expr instead of can_expr.
|
||||
// Should extract the common logic into test helpers.
|
||||
#[macro_export]
|
||||
macro_rules! assert_opt_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => {
|
||||
use bumpalo::Bump;
|
||||
use inkwell::context::Context;
|
||||
use roc_gen::run_jit_function;
|
||||
|
||||
let arena = Bump::new();
|
||||
|
||||
let context = Context::create();
|
||||
|
||||
// don't use uniqueness types any more
|
||||
// let stdlib = roc_builtins::unique::uniq_stdlib();
|
||||
let stdlib = roc_builtins::std::standard_stdlib();
|
||||
|
||||
let (main_fn_name, errors, lib) =
|
||||
$crate::helpers::eval::helper(&arena, $src, stdlib, $leak, &context);
|
||||
|
||||
let transform = |success| {
|
||||
let expected = $expected;
|
||||
let given = $transform(success);
|
||||
assert_eq!(&given, &expected);
|
||||
};
|
||||
run_jit_function!(lib, main_fn_name, $ty, transform, errors)
|
||||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
assert_opt_evals_to!($src, $expected, $ty, $transform, true)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_llvm_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => {
|
||||
|
@ -337,9 +303,10 @@ macro_rules! assert_llvm_evals_to {
|
|||
use roc_gen::run_jit_function;
|
||||
|
||||
let arena = Bump::new();
|
||||
|
||||
let context = Context::create();
|
||||
let stdlib = roc_builtins::std::standard_stdlib();
|
||||
|
||||
// NOTE the stdlib must be in the arena; just taking a reference will segfault
|
||||
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
|
||||
|
||||
let (main_fn_name, errors, lib) =
|
||||
$crate::helpers::eval::helper(&arena, $src, stdlib, $leak, &context);
|
||||
|
@ -377,7 +344,8 @@ macro_rules! assert_evals_to {
|
|||
assert_llvm_evals_to!($src, $expected, $ty, $transform, $leak);
|
||||
}
|
||||
{
|
||||
assert_opt_evals_to!($src, $expected, $ty, $transform, $leak);
|
||||
// NOTE at the moment, the optimized tests do the same thing
|
||||
// assert_opt_evals_to!($src, $expected, $ty, $transform, $leak);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ pub fn helper<'a>(
|
|||
arena,
|
||||
filename,
|
||||
&module_src,
|
||||
stdlib,
|
||||
&stdlib,
|
||||
src_dir,
|
||||
exposed_types,
|
||||
8,
|
||||
|
|
|
@ -699,7 +699,7 @@ struct State<'a> {
|
|||
pub root_id: ModuleId,
|
||||
pub platform_id: Option<ModuleId>,
|
||||
pub goal_phase: Phase,
|
||||
pub stdlib: StdLib,
|
||||
pub stdlib: &'a StdLib,
|
||||
pub exposed_types: SubsByModule,
|
||||
pub output_path: Option<&'a str>,
|
||||
pub platform_path: Option<To<'a>>,
|
||||
|
@ -944,7 +944,7 @@ fn enqueue_task<'a>(
|
|||
pub fn load_and_typecheck(
|
||||
arena: &Bump,
|
||||
filename: PathBuf,
|
||||
stdlib: StdLib,
|
||||
stdlib: &StdLib,
|
||||
src_dir: &Path,
|
||||
exposed_types: SubsByModule,
|
||||
ptr_bytes: u32,
|
||||
|
@ -970,7 +970,7 @@ pub fn load_and_typecheck(
|
|||
pub fn load_and_monomorphize<'a>(
|
||||
arena: &'a Bump,
|
||||
filename: PathBuf,
|
||||
stdlib: StdLib,
|
||||
stdlib: &'a StdLib,
|
||||
src_dir: &Path,
|
||||
exposed_types: SubsByModule,
|
||||
ptr_bytes: u32,
|
||||
|
@ -997,7 +997,7 @@ pub fn load_and_monomorphize_from_str<'a>(
|
|||
arena: &'a Bump,
|
||||
filename: PathBuf,
|
||||
src: &'a str,
|
||||
stdlib: StdLib,
|
||||
stdlib: &'a StdLib,
|
||||
src_dir: &Path,
|
||||
exposed_types: SubsByModule,
|
||||
ptr_bytes: u32,
|
||||
|
@ -1146,7 +1146,7 @@ fn load<'a>(
|
|||
arena: &'a Bump,
|
||||
//filename: PathBuf,
|
||||
load_start: LoadStart<'a>,
|
||||
stdlib: StdLib,
|
||||
stdlib: &'a StdLib,
|
||||
src_dir: &Path,
|
||||
exposed_types: SubsByModule,
|
||||
goal_phase: Phase,
|
||||
|
|
|
@ -82,7 +82,7 @@ mod test_load {
|
|||
roc_load::file::load_and_typecheck(
|
||||
arena,
|
||||
full_file_path,
|
||||
stdlib,
|
||||
&stdlib,
|
||||
dir.path(),
|
||||
exposed_types,
|
||||
8,
|
||||
|
@ -124,7 +124,7 @@ mod test_load {
|
|||
let loaded = roc_load::file::load_and_typecheck(
|
||||
&arena,
|
||||
filename,
|
||||
roc_builtins::std::standard_stdlib(),
|
||||
&roc_builtins::std::standard_stdlib(),
|
||||
src_dir.as_path(),
|
||||
subs_by_module,
|
||||
8,
|
||||
|
@ -287,7 +287,7 @@ mod test_load {
|
|||
let loaded = roc_load::file::load_and_typecheck(
|
||||
&arena,
|
||||
filename,
|
||||
roc_builtins::std::standard_stdlib(),
|
||||
&roc_builtins::std::standard_stdlib(),
|
||||
src_dir.as_path(),
|
||||
subs_by_module,
|
||||
8,
|
||||
|
|
|
@ -59,6 +59,7 @@ pub enum LowLevel {
|
|||
NumAcos,
|
||||
NumAsin,
|
||||
NumBitwiseAnd,
|
||||
NumBitwiseXor,
|
||||
Eq,
|
||||
NotEq,
|
||||
And,
|
||||
|
|
|
@ -823,15 +823,16 @@ define_builtins! {
|
|||
79 NUM_AT_BINARY32: "@Binary32"
|
||||
80 NUM_BINARY32: "Binary32" imported
|
||||
81 NUM_BITWISE_AND: "bitwiseAnd"
|
||||
82 NUM_SUB_WRAP: "subWrap"
|
||||
83 NUM_SUB_CHECKED: "subChecked"
|
||||
84 NUM_MUL_WRAP: "mulWrap"
|
||||
85 NUM_MUL_CHECKED: "mulChecked"
|
||||
86 NUM_INT: "Int" imported
|
||||
87 NUM_FLOAT: "Float" imported
|
||||
88 NUM_AT_NATURAL: "@Natural"
|
||||
89 NUM_NATURAL: "Natural" imported
|
||||
90 NUM_NAT: "Nat" imported
|
||||
82 NUM_BITWISE_XOR: "bitwiseXor"
|
||||
83 NUM_SUB_WRAP: "subWrap"
|
||||
84 NUM_SUB_CHECKED: "subChecked"
|
||||
85 NUM_MUL_WRAP: "mulWrap"
|
||||
86 NUM_MUL_CHECKED: "mulChecked"
|
||||
87 NUM_INT: "Int" imported
|
||||
88 NUM_FLOAT: "Float" imported
|
||||
89 NUM_AT_NATURAL: "@Natural"
|
||||
90 NUM_NATURAL: "Natural" imported
|
||||
91 NUM_NAT: "Nat" imported
|
||||
}
|
||||
2 BOOL: "Bool" => {
|
||||
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
||||
|
|
|
@ -578,9 +578,8 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
|||
|
||||
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap
|
||||
| NumSubChecked | NumMul | NumMulWrap | NumMulChecked | NumGt | NumGte | NumLt | NumLte
|
||||
| NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd => {
|
||||
arena.alloc_slice_copy(&[irrelevant, irrelevant])
|
||||
}
|
||||
| NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd
|
||||
| NumBitwiseXor => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
|
||||
|
||||
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumCeiling | NumFloor
|
||||
| NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin => {
|
||||
|
|
|
@ -905,7 +905,10 @@ pub fn optimize_when<'a>(
|
|||
|
||||
let decision_tree = compile(patterns);
|
||||
let decider = tree_to_decider(decision_tree);
|
||||
let target_counts = count_targets(&decider);
|
||||
|
||||
// for each target (branch body), count in how many ways it can be reached
|
||||
let mut target_counts = bumpalo::vec![in env.arena; 0; indexed_branches.len()];
|
||||
count_targets(&mut target_counts, &decider);
|
||||
|
||||
let mut choices = MutMap::default();
|
||||
let mut jumps = Vec::new();
|
||||
|
@ -913,8 +916,9 @@ pub fn optimize_when<'a>(
|
|||
for (index, branch) in indexed_branches.into_iter() {
|
||||
let ((branch_index, choice), opt_jump) = create_choices(&target_counts, index, branch);
|
||||
|
||||
if let Some(jump) = opt_jump {
|
||||
jumps.push(jump);
|
||||
if let Some((index, body)) = opt_jump {
|
||||
let id = JoinPointId(env.unique_symbol());
|
||||
jumps.push((index, id, body));
|
||||
}
|
||||
|
||||
choices.insert(branch_index, choice);
|
||||
|
@ -922,7 +926,7 @@ pub fn optimize_when<'a>(
|
|||
|
||||
let choice_decider = insert_choices(&choices, decider);
|
||||
|
||||
decide_to_branching(
|
||||
let mut stmt = decide_to_branching(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
|
@ -931,7 +935,18 @@ pub fn optimize_when<'a>(
|
|||
ret_layout,
|
||||
choice_decider,
|
||||
&jumps,
|
||||
)
|
||||
);
|
||||
|
||||
for (_, id, body) in jumps.into_iter() {
|
||||
stmt = Stmt::Join {
|
||||
id,
|
||||
parameters: &[],
|
||||
continuation: env.arena.alloc(body),
|
||||
remainder: env.arena.alloc(stmt),
|
||||
};
|
||||
}
|
||||
|
||||
stmt
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -1383,7 +1398,7 @@ fn decide_to_branching<'a>(
|
|||
cond_layout: Layout<'a>,
|
||||
ret_layout: Layout<'a>,
|
||||
decider: Decider<'a, Choice<'a>>,
|
||||
jumps: &Vec<(u64, Stmt<'a>)>,
|
||||
jumps: &Vec<(u64, JoinPointId, Stmt<'a>)>,
|
||||
) -> Stmt<'a> {
|
||||
use Choice::*;
|
||||
use Decider::*;
|
||||
|
@ -1392,12 +1407,11 @@ fn decide_to_branching<'a>(
|
|||
|
||||
match decider {
|
||||
Leaf(Jump(label)) => {
|
||||
// we currently inline the jumps: does fewer jumps but produces a larger artifact
|
||||
let (_, expr) = jumps
|
||||
.iter()
|
||||
.find(|(l, _)| l == &label)
|
||||
let index = jumps
|
||||
.binary_search_by_key(&label, |ref r| r.0)
|
||||
.expect("jump not in list of jumps");
|
||||
expr.clone()
|
||||
|
||||
Stmt::Jump(jumps[index].1, &[])
|
||||
}
|
||||
Leaf(Inline(expr)) => expr,
|
||||
Chain {
|
||||
|
@ -1674,39 +1688,32 @@ fn to_chain<'a>(
|
|||
/// If a target appears exactly once in a Decider, the corresponding expression
|
||||
/// can be inlined. Whether things are inlined or jumps is called a "choice".
|
||||
|
||||
fn count_targets(decision_tree: &Decider<u64>) -> MutMap<u64, u64> {
|
||||
let mut result = MutMap::default();
|
||||
count_targets_help(decision_tree, &mut result);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn count_targets_help(decision_tree: &Decider<u64>, targets: &mut MutMap<u64, u64>) {
|
||||
fn count_targets(targets: &mut bumpalo::collections::Vec<u64>, initial: &Decider<u64>) {
|
||||
use Decider::*;
|
||||
match decision_tree {
|
||||
Leaf(target) => match targets.get_mut(target) {
|
||||
None => {
|
||||
targets.insert(*target, 1);
|
||||
|
||||
let mut stack = vec![initial];
|
||||
|
||||
while let Some(decision_tree) = stack.pop() {
|
||||
match decision_tree {
|
||||
Leaf(target) => {
|
||||
targets[*target as usize] += 1;
|
||||
}
|
||||
Some(current) => {
|
||||
*current += 1;
|
||||
|
||||
Chain {
|
||||
success, failure, ..
|
||||
} => {
|
||||
stack.push(success);
|
||||
stack.push(failure);
|
||||
}
|
||||
},
|
||||
|
||||
Chain {
|
||||
success, failure, ..
|
||||
} => {
|
||||
count_targets_help(success, targets);
|
||||
count_targets_help(failure, targets);
|
||||
}
|
||||
FanOut {
|
||||
tests, fallback, ..
|
||||
} => {
|
||||
stack.push(fallback);
|
||||
|
||||
FanOut {
|
||||
tests, fallback, ..
|
||||
} => {
|
||||
count_targets_help(fallback, targets);
|
||||
|
||||
for (_, decider) in tests {
|
||||
count_targets_help(decider, targets);
|
||||
for (_, decider) in tests {
|
||||
stack.push(decider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1714,11 +1721,11 @@ fn count_targets_help(decision_tree: &Decider<u64>, targets: &mut MutMap<u64, u6
|
|||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn create_choices<'a>(
|
||||
target_counts: &MutMap<u64, u64>,
|
||||
target_counts: &bumpalo::collections::Vec<'a, u64>,
|
||||
target: u64,
|
||||
branch: Stmt<'a>,
|
||||
) -> ((u64, Choice<'a>), Option<(u64, Stmt<'a>)>) {
|
||||
match target_counts.get(&target) {
|
||||
match target_counts.get(target as usize) {
|
||||
None => unreachable!(
|
||||
"this should never happen: {:?} not in {:?}",
|
||||
target, target_counts
|
||||
|
|
|
@ -58,7 +58,7 @@ mod test_mono {
|
|||
arena,
|
||||
filename,
|
||||
&module_src,
|
||||
stdlib,
|
||||
&stdlib,
|
||||
src_dir,
|
||||
exposed_types,
|
||||
8,
|
||||
|
|
|
@ -59,7 +59,7 @@ mod solve_expr {
|
|||
let result = roc_load::file::load_and_typecheck(
|
||||
arena,
|
||||
full_file_path,
|
||||
stdlib,
|
||||
&stdlib,
|
||||
dir.path(),
|
||||
exposed_types,
|
||||
8,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue