Merge pull request #5246 from roc-lang/glue-entry-points

Glue entry points
This commit is contained in:
Richard Feldman 2023-04-02 20:39:19 -04:00 committed by GitHub
commit 120d7e64c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 406 additions and 191 deletions

View File

@ -101,7 +101,7 @@ RocTagUnion : [
{
name : Str,
tagName : Str,
payload: RocSingleTagPayload,
payload : RocSingleTagPayload,
},
## A recursive tag union with only two variants, where one is empty.
## Optimizations: Use null for the empty variant AND don't store a tag ID for the other variant.
@ -117,19 +117,20 @@ RocTagUnion : [
]
RocStructFields : [
HasNoClosure (List { name: Str, id: TypeId }),
HasClosure (List { name: Str, id: TypeId, accessors: { getter: Str } }),
HasNoClosure (List { name : Str, id : TypeId }),
HasClosure (List { name : Str, id : TypeId, accessors : { getter : Str } }),
]
RocSingleTagPayload: [
HasClosure (List { name: Str, id: TypeId }),
HasNoClosure (List { id: TypeId }),
RocSingleTagPayload : [
HasClosure (List { name : Str, id : TypeId }),
HasNoClosure (List { id : TypeId }),
]
RocFn : {
functionName: Str,
externName: Str,
args: List TypeId,
lambdaSet: TypeId,
ret: TypeId,
functionName : Str,
externName : Str,
args : List TypeId,
lambdaSet : TypeId,
ret : TypeId,
isToplevel : Bool,
}

View File

@ -1,5 +1,5 @@
interface Types
exposes [Types, shape, size, alignment, target, walkShapes]
exposes [Types, shape, size, alignment, target, walkShapes, entryPoints]
imports [Shape.{ Shape }, TypeId.{ TypeId }, Target.{ Target }, InternalTypeId]
# TODO: switch AssocList uses to Dict once roc_std is updated.
@ -19,12 +19,18 @@ Types := {
## This is important for declaration order in C; we need to output a
## type declaration earlier in the file than where it gets referenced by another type.
deps : List Tuple2,
## Names and types of the entry points of the program (e.g. mainForHost)
entrypoints : List Tuple1,
target : Target,
}
target : Types -> Target
target = \@Types types -> types.target
entryPoints : Types -> List Tuple1
entryPoints = \@Types { entrypoints } -> entrypoints
walkShapes : Types, state, (state, Shape, TypeId -> state) -> state
walkShapes = \@Types { types: shapes }, originalState, update ->
List.walk shapes { index: 0, state: originalState } \{ index, state }, elem ->

View File

@ -1,6 +1,6 @@
app "rust-glue"
packages { pf: "../platform/main.roc" }
imports [pf.Types.{ Types }, pf.File.{ File }, pf.TypeId.{ TypeId }]
imports [pf.Types.{ Types }, pf.Shape.{ RocFn }, pf.File.{ File }, pf.TypeId.{ TypeId }]
provides [makeGlue] to pf
makeGlue : List Types -> Result (List File) Str
@ -8,7 +8,7 @@ makeGlue = \typesByArch ->
modFileContent =
List.walk typesByArch "" \content, types ->
arch = (Types.target types).architecture
archStr = archName arch
archStr = archName arch
Str.concat
content
@ -17,7 +17,7 @@ makeGlue = \typesByArch ->
mod \(archStr);
#[cfg(target_arch = "\(archStr)")]
pub use \(archStr)::*;
"""
typesByArch
@ -63,9 +63,11 @@ convertTypesToFile = \types ->
TagUnion (NonNullableUnwrapped { name, tagName, payload }) ->
generateRecursiveTagUnion buf types id name [{ name: tagName, payload: Some payload }] 0 0 None
Function _ ->
# TODO: actually generate glue functions.
buf
Function rocFn ->
if rocFn.isToplevel then
buf
else
generateFunction buf types rocFn
RecursivePointer _ ->
# This is recursively pointing to a type that should already have been added,
@ -88,13 +90,133 @@ convertTypesToFile = \types ->
buf
arch = (Types.target types).architecture
archStr = archName arch
archStr = archName arch
{
name: "\(archStr).rs",
content,
content: content |> generateEntryPoints types,
}
generateEntryPoints : Str, Types -> Str
generateEntryPoints = \buf, types ->
List.walk (Types.entryPoints types) buf \accum, T name id -> generateEntryPoint accum types name id
generateEntryPoint : Str, Types, Str, TypeId -> Str
generateEntryPoint = \buf, types, name, id ->
publicSignature =
when Types.shape types id is
Function rocFn ->
arguments =
rocFn.args
|> List.mapWithIndex \argId, i ->
type = typeName types argId
c = Num.toStr i
"arg\(c): \(type)"
|> Str.joinWith ", "
ret = typeName types rocFn.ret
"(\(arguments)) -> \(ret)"
_ ->
ret = typeName types id
"() -> \(ret)"
externSignature =
when Types.shape types id is
Function rocFn ->
arguments =
rocFn.args
|> List.map \argId ->
type = typeName types argId
"_: \(type)"
|> Str.joinWith ", "
ret = typeName types rocFn.ret
"(_: *mut \(ret), \(arguments))"
_ ->
ret = typeName types id
"(_: *mut \(ret))"
externArguments =
when Types.shape types id is
Function rocFn ->
rocFn.args
|> List.mapWithIndex \_, i ->
c = Num.toStr i
"arg\(c)"
|> Str.joinWith ", "
_ ->
""
"""
\(buf)
pub fn \(name)\(publicSignature) {
extern "C" {
fn roc__\(name)_1_exposed_generic\(externSignature);
}
let mut ret = std::mem::MaybeUninit::uninit();
unsafe { roc__\(name)_1_exposed_generic(ret.as_mut_ptr(), \(externArguments)) };
unsafe { ret.assume_init() }
}
"""
generateFunction : Str, Types, RocFn -> Str
generateFunction = \buf, types, rocFn ->
name = rocFn.functionName
externName = rocFn.externName
lambdaSet = typeName types rocFn.lambdaSet
publicArguments =
rocFn.args
|> List.mapWithIndex \argId, i ->
type = typeName types argId
c = Num.toStr i
"arg\(c): \(type)"
|> Str.joinWith ", "
externArguments =
rocFn.args
|> List.mapWithIndex \_, i ->
c = Num.toStr i
"arg\(c)"
|> Str.joinWith ", "
externComma = if Str.isEmpty publicArguments then "" else ", "
ret = typeName types rocFn.ret
"""
\(buf)
#[repr(C)]
pub struct \(name) {
closure_data: \(lambdaSet),
}
impl \(name) {
pub fn force_thunk(mut self, \(publicArguments)) -> \(ret) {
extern "C" {
fn \(externName)(\(publicArguments)\(externComma) closure_data: *mut u8, output: *mut \(ret));
}
let mut output = std::mem::MaybeUninit::uninit();
let ptr = &mut self.closure_data as *mut _ as *mut u8;
unsafe { \(externName)(\(externArguments)\(externComma) ptr, output.as_mut_ptr(), ) };
unsafe { output.assume_init() }
}
}
"""
generateStruct : Str, Types, TypeId, _, _, _ -> Str
generateStruct = \buf, types, id, name, structFields, visibility ->
escapedName = escapeKW name
@ -125,6 +247,7 @@ generateStructFields = \buf, types, visibility, structFields ->
when structFields is
HasNoClosure fields ->
List.walk fields buf (generateStructFieldWithoutClosure types visibility)
HasClosure _ ->
Str.concat buf "// TODO: Struct fields with closures"
@ -147,6 +270,7 @@ nameTagUnionPayloadFields = \payloadFields ->
HasNoClosure fields ->
renamedFields = List.map fields \{ name, id } -> { name: "f\(name)", id }
HasNoClosure renamedFields
HasClosure fields ->
renamedFields = List.map fields \{ name, id, accessors } -> { name: "f\(name)", id, accessors }
HasClosure renamedFields
@ -169,7 +293,7 @@ generateEnumeration = \buf, types, enumType, name, tags, tagBytes ->
impl core::fmt::Debug for \(escapedName) {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
"""
|> \b -> List.walk tags b (generateEnumTagsDebug name)
|> Str.concat "\(indent)\(indent)}\n\(indent)}\n}\n\n"
@ -222,7 +346,7 @@ generateNonRecursiveTagUnion = \buf, types, id, name, tags, discriminantSize, di
}
}
"""
|> Str.concat "// TODO: NonRecursive TagUnion constructor impls\n\n"
|> \b ->
@ -235,7 +359,7 @@ generateNonRecursiveTagUnion = \buf, types, id, name, tags, discriminantSize, di
impl Drop for \(escapedName) {
fn drop(&mut self) {
// Drop the payloads
"""
|> generateTagUnionDropPayload types selfMut tags discriminantName discriminantSize 2
|> Str.concat
@ -243,7 +367,7 @@ generateNonRecursiveTagUnion = \buf, types, id, name, tags, discriminantSize, di
}
}
"""
else
b
@ -371,7 +495,7 @@ generateUnionField = \types ->
else
typeStr
Str.concat accum "\(indent)\(escapedFieldName): \(fullTypeStr),\n"
Str.concat accum "\(indent)\(escapedFieldName): std::mem::ManuallyDrop<\(fullTypeStr)>,\n"
None ->
# If there's no payload, we don't need a discriminant for it.
@ -417,6 +541,7 @@ generateSingleTagStruct = \buf, types, name, tagName, payload ->
generateZeroElementSingleTagStruct b escapedName tagName
else
generateMultiElementSingleTagStruct b types escapedName tagName fields asStructFields
HasClosure _ ->
Str.concat buf "\\TODO: SingleTagStruct with closures"
@ -428,7 +553,7 @@ generateMultiElementSingleTagStruct = \buf, types, name, tagName, payloadFields,
|> Str.concat
"""
impl \(name) {
"""
|> \b ->
fieldTypes =
@ -475,7 +600,7 @@ generateMultiElementSingleTagStruct = \buf, types, name, tagName, payloadFields,
\(indent) }
\(indent)}
""",
fieldTypes,
fieldAccesses,
@ -494,7 +619,7 @@ generateMultiElementSingleTagStruct = \buf, types, name, tagName, payloadFields,
\(indent) \(retExpr)
\(indent)}
""",
fieldTypes,
fieldAccesses,
@ -517,7 +642,7 @@ generateMultiElementSingleTagStruct = \buf, types, name, tagName, payloadFields,
\(indent)pub fn as_\(tagName)(&self) -> \(retType) {
\(indent) \(retExpr)
\(indent)}
"""
|> Str.concat
"""
@ -527,7 +652,7 @@ generateMultiElementSingleTagStruct = \buf, types, name, tagName, payloadFields,
impl core::fmt::Debug for \(name) {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("\(name)::\(tagName)")
"""
|> \b ->
payloadFields
@ -542,7 +667,7 @@ generateMultiElementSingleTagStruct = \buf, types, name, tagName, payloadFields,
}
}
"""
asRustTuple = \list ->
@ -585,7 +710,7 @@ generateZeroElementSingleTagStruct = \buf, name, tagName ->
}
}
"""
generateDeriveStr = \buf, types, type, includeDebug ->
@ -854,7 +979,7 @@ fileHeader =
#![allow(clippy::clone_on_copy)]
"""
indent = " "

View File

@ -380,11 +380,8 @@ pub fn load_types(
// Get the variables for all the exposed_to_host symbols
let variables = (0..decls.len()).filter_map(|index| {
if exposed_to_host.contains_key(&decls.symbols[index].value) {
Some(decls.variables[index])
} else {
None
}
let symbol = decls.symbols[index].value;
exposed_to_host.get(&symbol).copied()
});
let operating_system = target_info.operating_system;
@ -453,14 +450,14 @@ pub fn load_types(
}
}
let types = Types::new(
let types = Types::new_with_entry_points(
arena,
subs,
variables.clone(),
arena.alloc(interns),
glue_procs_by_layout,
layout_cache,
target_info,
exposed_to_host.clone(),
);
arch_types.push(types);

View File

@ -42,6 +42,7 @@ pub struct File {
pub struct Types {
pub aligns: roc_std::RocList<u32>,
pub deps: roc_std::RocList<Tuple2>,
pub entrypoints: roc_std::RocList<Tuple1>,
pub sizes: roc_std::RocList<u32>,
pub types: roc_std::RocList<RocType>,
pub typesByName: roc_std::RocList<Tuple1>,
@ -567,6 +568,7 @@ pub struct RocFn {
pub functionName: roc_std::RocStr,
pub lambdaSet: u32,
pub ret: u32,
pub is_toplevel: bool,
}
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
@ -713,6 +715,7 @@ pub struct RocFn {
pub functionName: roc_std::RocStr,
pub lambdaSet: u64,
pub ret: u64,
pub isToplevel: bool,
}
impl Tuple1 {

View File

@ -21,7 +21,7 @@ use roc_mono::{
};
use roc_target::{Architecture, OperatingSystem, TargetInfo};
use roc_types::{
subs::{Content, FlatType, GetSubsSlice, Label, Subs, UnionLabels, Variable},
subs::{Content, FlatType, GetSubsSlice, Label, Subs, SubsSlice, UnionLabels, Variable},
types::{AliasKind, RecordField},
};
use std::convert::From;
@ -68,10 +68,16 @@ pub struct Types {
}
impl Types {
const UNIT: TypeId = TypeId(0);
pub fn with_capacity(cap: usize, target_info: TargetInfo) -> Self {
let mut types = Vec::with_capacity(cap);
types.push(RocType::Unit);
Self {
target: target_info,
types: Vec::with_capacity(cap),
types,
types_by_name: FnvHashMap::with_capacity_and_hasher(10, Default::default()),
entry_points: Vec::new(),
sizes: Vec::new(),
@ -81,16 +87,16 @@ impl Types {
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn new<'a, I: Iterator<Item = Variable>>(
pub(crate) fn new_with_entry_points<'a>(
arena: &'a Bump,
subs: &'a Subs,
variables: I,
interns: &'a Interns,
glue_procs_by_layout: MutMap<Layout<'a>, &'a [String]>,
layout_cache: LayoutCache<'a>,
target: TargetInfo,
mut entry_points: MutMap<Symbol, Variable>,
) -> Self {
let mut types = Self::with_capacity(variables.size_hint().0, target);
let mut types = Self::with_capacity(entry_points.len(), target);
let mut env = Env::new(
arena,
subs,
@ -100,11 +106,24 @@ impl Types {
target,
);
let variables: Vec<_> = entry_points.values().copied().collect();
for var in variables {
env.lambda_set_ids = env.find_lambda_sets(var);
env.add_type(var, &mut types);
let id = env.add_toplevel_type(var, &mut types);
let key = entry_points
.iter()
.find_map(|(k, v)| (*v == var).then_some((*k, id)));
if let Some((k, id)) = key {
let name = k.as_str(env.interns).to_string();
types.entry_points.push((name, id));
entry_points.remove(&k);
}
}
debug_assert!(entry_points.is_empty());
env.resolve_pending_recursive_types(&mut types);
types
@ -412,6 +431,7 @@ impl Types {
args: args_a,
lambda_set: lambda_a,
ret: ret_a,
is_toplevel: is_toplevel_a,
}),
Function(RocFn {
function_name: name_b,
@ -419,12 +439,14 @@ impl Types {
args: args_b,
lambda_set: lambda_b,
ret: ret_b,
is_toplevel: is_toplevel_b,
}),
) => {
// for functions, the name is actually important because two functions
// with the same type could have completely different implementations!
if name_a == name_b
&& extern_a == extern_b
&& is_toplevel_a == is_toplevel_b
&& args_a.len() == args_b.len()
&& self.is_equivalent_help(
self.get_type_or_pending(*lambda_a),
@ -626,9 +648,17 @@ impl From<&Types> for roc_type::Types {
.iter()
.map(|(k, v)| roc_type::Tuple1::T(k.as_str().into(), v.0 as _))
.collect();
let entrypoints = types
.entry_points()
.iter()
.map(|(k, v)| roc_type::Tuple1::T(k.as_str().into(), v.0 as _))
.collect();
roc_type::Types {
aligns: types.aligns.as_slice().into(),
deps,
entrypoints,
sizes: types.sizes.as_slice().into(),
types: types.types.iter().map(|t| t.into()).collect(),
typesByName: types_by_name,
@ -667,12 +697,14 @@ impl From<&RocType> for roc_type::RocType {
args,
lambda_set,
ret,
is_toplevel,
}) => roc_type::RocType::Function(roc_type::RocFn {
args: args.iter().map(|arg| arg.0 as _).collect(),
functionName: function_name.as_str().into(),
externName: extern_name.as_str().into(),
ret: ret.0 as _,
lambdaSet: lambda_set.0 as _,
isToplevel: *is_toplevel,
}),
RocType::Unit => roc_type::RocType::Unit,
RocType::Unsized => roc_type::RocType::Unsized,
@ -937,6 +969,7 @@ impl RocStructFields {
pub struct RocFn {
pub function_name: String,
pub extern_name: String,
pub is_toplevel: bool,
pub args: Vec<TypeId>,
pub lambda_set: TypeId,
pub ret: TypeId,
@ -1261,18 +1294,99 @@ impl<'a> Env<'a> {
result
}
fn add_type(&mut self, var: Variable, types: &mut Types) -> TypeId {
roc_tracing::debug!(content=?roc_types::subs::SubsFmtContent(self.subs.get_content_without_compacting(var), self.subs), "adding type");
fn add_toplevel_type(&mut self, var: Variable, types: &mut Types) -> TypeId {
roc_tracing::debug!(content=?roc_types::subs::SubsFmtContent(self.subs.get_content_without_compacting(var), self.subs), "adding toplevel type");
let layout = self
.layout_cache
.from_var(self.arena, var, self.subs)
.expect("Something weird ended up in the content");
add_type_help(self, layout, var, None, types)
match self.subs.get_content_without_compacting(var) {
Content::Structure(FlatType::Func(args, closure_var, ret_var)) => {
// this is a toplevel type, so the closure must be empty
let is_toplevel = true;
add_function_type(
self,
layout,
types,
args,
*closure_var,
*ret_var,
is_toplevel,
)
}
_ => add_type_help(self, layout, var, None, types),
}
}
}
fn add_function_type<'a>(
env: &mut Env<'a>,
layout: InLayout<'a>,
types: &mut Types,
args: &SubsSlice<Variable>,
closure_var: Variable,
ret_var: Variable,
is_toplevel: bool,
) -> TypeId {
let args = env.subs.get_subs_slice(*args);
let mut arg_type_ids = Vec::with_capacity(args.len());
let name = format!("RocFunction_{:?}", closure_var);
let id = env.lambda_set_ids.get(&closure_var).unwrap();
let extern_name = format!("roc__mainForHost_{}_caller", id.0);
for arg_var in args {
let arg_layout = env
.layout_cache
.from_var(env.arena, *arg_var, env.subs)
.expect("Something weird ended up in the content");
arg_type_ids.push(add_type_help(env, arg_layout, *arg_var, None, types));
}
let lambda_set_type_id = if is_toplevel {
Types::UNIT
} else {
let lambda_set_layout = env
.layout_cache
.from_var(env.arena, closure_var, env.subs)
.expect("Something weird ended up in the content");
add_type_help(env, lambda_set_layout, closure_var, None, types)
};
let ret_type_id = {
let ret_layout = env
.layout_cache
.from_var(env.arena, ret_var, env.subs)
.expect("Something weird ended up in the content");
add_type_help(env, ret_layout, ret_var, None, types)
};
let fn_type_id = add_function(env, name, types, layout, |name| {
RocType::Function(RocFn {
function_name: name,
extern_name,
args: arg_type_ids.clone(),
lambda_set: lambda_set_type_id,
ret: ret_type_id,
is_toplevel,
})
});
types.depends(fn_type_id, ret_type_id);
for arg_type_id in arg_type_ids {
types.depends(fn_type_id, arg_type_id);
}
fn_type_id
}
fn add_type_help<'a>(
env: &mut Env<'a>,
layout: InLayout<'a>,
@ -1352,62 +1466,17 @@ fn add_type_help<'a>(
}
},
Content::Structure(FlatType::Func(args, closure_var, ret_var)) => {
let args = env.subs.get_subs_slice(*args);
let mut arg_type_ids = Vec::with_capacity(args.len());
let is_toplevel = false; // or in any case, we cannot assume that we are
let name = format!("RocFunction_{:?}", closure_var);
let lambda_set_layout = env
.layout_cache
.from_var(env.arena, *closure_var, env.subs)
.expect("Something weird ended up in the content");
let _lambda_set = match env.layout_cache.interner.get(lambda_set_layout) {
Layout::LambdaSet(lambda_set) => lambda_set,
_ => unreachable!(),
};
let id = env.lambda_set_ids.get(closure_var).unwrap();
let extern_name = format!("roc__mainForHost_{}_caller", id.0);
for arg_var in args {
let arg_layout = env
.layout_cache
.from_var(env.arena, *arg_var, env.subs)
.expect("Something weird ended up in the content");
arg_type_ids.push(add_type_help(env, arg_layout, *arg_var, None, types));
}
let lambda_set_type_id =
add_type_help(env, lambda_set_layout, *closure_var, None, types);
let ret_type_id = {
let ret_layout = env
.layout_cache
.from_var(env.arena, *ret_var, env.subs)
.expect("Something weird ended up in the content");
add_type_help(env, ret_layout, *ret_var, None, types)
};
let fn_type_id = add_function(env, name, types, layout, |name| {
RocType::Function(RocFn {
function_name: name,
extern_name,
args: arg_type_ids.clone(),
lambda_set: lambda_set_type_id,
ret: ret_type_id,
})
});
types.depends(fn_type_id, ret_type_id);
for arg_type_id in arg_type_ids {
types.depends(fn_type_id, arg_type_id);
}
fn_type_id
add_function_type(
env,
layout,
types,
args,
*closure_var,
*ret_var,
is_toplevel,
)
}
Content::Structure(FlatType::FunctionOrTagUnion(_, _, _)) => {
todo!()

View File

@ -6,9 +6,9 @@ app "app"
main = {
default: Job {
command: Command {
tool: SystemTool { name: "test", num: 42 }
tool: SystemTool { name: "test", num: 42 },
},
inputFiles : ["foo"]
}
inputFiles: ["foo"],
},
}

View File

@ -7,7 +7,7 @@ platform "test-platform"
Tool : [
SystemTool { name : Str, num : U32 },
FromJob { job : Job, num : U32 }
FromJob { job : Job, num : U32 },
]
Command : [Command { tool : Tool }]
@ -20,7 +20,7 @@ Job : [
# WithTool Tool # Mutual recursion; Tool also references Job
]
Rbt : { default: Job }
Rbt : { default : Job }
mainForHost : Rbt
mainForHost = main

View File

@ -0,0 +1,7 @@
app "app"
packages { pf: "platform.roc" }
imports []
provides [main] to pf
main : I64 -> I64
main = \x -> 2 * x

View File

@ -0,0 +1,9 @@
platform "test-platform"
requires {} { main : I64 -> I64 }
exposes []
packages {}
imports []
provides [mainForHost]
mainForHost : I64 -> I64
mainForHost = \x -> main x

View File

@ -0,0 +1,60 @@
mod test_glue;
#[no_mangle]
pub extern "C" fn rust_main() -> i32 {
let answer = test_glue::mainForHost(42i64);
println!("Answer was: {:?}", answer); // Debug
// Exit code
0
}
// Externs required by roc_std and by the Roc app
use core::ffi::c_void;
use std::ffi::CStr;
use std::os::raw::c_char;
#[no_mangle]
pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
return libc::malloc(size);
}
#[no_mangle]
pub unsafe extern "C" fn roc_realloc(
c_ptr: *mut c_void,
new_size: usize,
_old_size: usize,
_alignment: u32,
) -> *mut c_void {
return libc::realloc(c_ptr, new_size);
}
#[no_mangle]
pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
return libc::free(c_ptr);
}
#[no_mangle]
pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
match tag_id {
0 => {
let slice = CStr::from_ptr(c_ptr as *const c_char);
let string = slice.to_str().unwrap();
eprintln!("Roc hit a panic: {}", string);
std::process::exit(1);
}
_ => todo!(),
}
}
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[no_mangle]
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
libc::memset(dst, c, n)
}

View File

@ -1,22 +1,11 @@
mod test_glue;
extern "C" {
#[link_name = "roc__mainForHost_1_exposed_generic"]
fn roc_main(_: *mut test_glue::MyRcd);
}
#[no_mangle]
pub extern "C" fn rust_main() -> i32 {
use std::cmp::Ordering;
use std::collections::hash_set::HashSet;
let record = unsafe {
let mut ret: core::mem::MaybeUninit<test_glue::MyRcd> = core::mem::MaybeUninit::uninit();
roc_main(ret.as_mut_ptr());
ret.assume_init()
};
let record = test_glue::mainForHost();
// Verify that the record has all the expected traits.

View File

@ -1,22 +1,11 @@
mod test_glue;
extern "C" {
#[link_name = "roc__mainForHost_1_exposed_generic"]
fn roc_main(_: *mut test_glue::MyEnum);
}
#[no_mangle]
pub extern "C" fn rust_main() -> i32 {
use std::cmp::Ordering;
use std::collections::hash_set::HashSet;
let tag_union = unsafe {
let mut ret: core::mem::MaybeUninit<test_glue::MyEnum> = core::mem::MaybeUninit::uninit();
roc_main(ret.as_mut_ptr());
ret.assume_init()
};
let tag_union = test_glue::mainForHost();
// Verify that it has all the expected traits.

View File

@ -10,7 +10,7 @@ main = {
args: [],
},
job: [],
inputFiles : ["foo"],
}
inputFiles: ["foo"],
},
}

View File

@ -5,13 +5,13 @@ platform "test-platform"
imports []
provides [mainForHost]
Tool : [SystemTool { name: Str }]
Tool : [SystemTool { name : Str }]
Command : [Command { tool : Tool, args: List Str }]
Command : [Command { tool : Tool, args : List Str }]
Job : [Job { command : Command, job: List Job, inputFiles : List Str }, Blah Str]
Job : [Job { command : Command, job : List Job, inputFiles : List Str }, Blah Str]
Rbt : { default: Job }
Rbt : { default : Job }
mainForHost : Rbt
mainForHost = main

View File

@ -1,5 +1,5 @@
interface Dep1 exposes [DepStr1, string] imports []
DepStr1 := [ S Str ]
DepStr1 := [S Str]
string = \s -> @DepStr1 (S s)

View File

@ -1,5 +1,5 @@
interface Dep2 exposes [DepStr2, string] imports []
DepStr2 := [ R Str ]
DepStr2 := [R Str]
string = \s -> @DepStr2 (R s)

View File

@ -3,4 +3,4 @@ app "app"
imports [pf.Dep1, pf.Dep2]
provides [main] to pf
main = {s1: Dep1.string "hello", s2: Dep2.string "world"}
main = { s1: Dep1.string "hello", s2: Dep2.string "world" }

View File

@ -5,7 +5,7 @@ platform "test-platform"
imports [Dep1, Dep2]
provides [mainForHost]
Combined : {s1: Dep1.DepStr1, s2: Dep2.DepStr2}
Combined : { s1 : Dep1.DepStr1, s2 : Dep2.DepStr2 }
mainForHost : Combined
mainForHost = main

View File

@ -1,25 +1,13 @@
mod test_glue;
use indoc::indoc;
use test_glue::Combined;
extern "C" {
#[link_name = "roc__mainForHost_1_exposed_generic"]
fn roc_main(_: *mut Combined);
}
#[no_mangle]
pub extern "C" fn rust_main() -> i32 {
use std::cmp::Ordering;
use std::collections::hash_set::HashSet;
let tag_union = unsafe {
let mut ret: core::mem::MaybeUninit<Combined> = core::mem::MaybeUninit::uninit();
roc_main(ret.as_mut_ptr());
ret.assume_init()
};
let tag_union = test_glue::mainForHost();
// Verify that it has all the expected traits.

View File

@ -1,21 +1,10 @@
mod test_glue;
extern "C" {
#[link_name = "roc__mainForHost_1_exposed_generic"]
fn roc_main(_: *mut test_glue::Outer);
}
#[no_mangle]
pub extern "C" fn rust_main() -> i32 {
use std::cmp::Ordering;
let outer = unsafe {
let mut ret: core::mem::MaybeUninit<test_glue::Outer> = core::mem::MaybeUninit::uninit();
roc_main(ret.as_mut_ptr());
ret.assume_init()
};
let outer = test_glue::mainForHost();
// Verify that `inner` has all the expected traits.
{

View File

@ -3,23 +3,12 @@ mod test_glue;
use indoc::indoc;
use test_glue::SingleTagUnion;
extern "C" {
#[link_name = "roc__mainForHost_1_exposed_generic"]
fn roc_main(_: *mut SingleTagUnion);
}
#[no_mangle]
pub extern "C" fn rust_main() -> i32 {
use std::cmp::Ordering;
use std::collections::hash_set::HashSet;
let tag_union = unsafe {
let mut ret: core::mem::MaybeUninit<SingleTagUnion> = core::mem::MaybeUninit::uninit();
roc_main(ret.as_mut_ptr());
ret.assume_init()
};
let tag_union = test_glue::mainForHost();
// Verify that it has all the expected traits.

View File

@ -122,6 +122,9 @@ mod glue_cli_run {
multiple_modules:"multiple-modules" => indoc!(r#"
combined was: Combined { s1: DepStr1::S("hello"), s2: DepStr2::R("world") }
"#),
arguments:"arguments" => indoc!(r#"
Answer was: 84
"#),
}
fn check_for_tests(all_fixtures: &mut roc_collections::VecSet<String>) {

View File

@ -10,10 +10,7 @@ use std::io::Write;
use std::mem::MaybeUninit;
use std::os::raw::c_char;
extern "C" {
#[link_name = "roc__mainForHost_1_exposed_generic"]
fn roc_main(_: *mut Op);
}
use glue::mainForHost as roc_main;
#[no_mangle]
pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
@ -93,13 +90,7 @@ pub extern "C" fn rust_main() -> i32 {
println!("Let's do things!");
let mut op: Op = unsafe {
let mut mem = MaybeUninit::uninit();
roc_main(mem.as_mut_ptr());
mem.assume_init()
};
let mut op: Op = roc_main();
loop {
match dbg!(op.discriminant()) {