mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
commit
fbcde5dd9e
10 changed files with 340 additions and 209 deletions
|
@ -149,7 +149,8 @@ pub fn gen_from_mono_module(
|
|||
// env.module.print_to_stderr();
|
||||
// NOTE: If this fails, uncomment the above println to debug.
|
||||
panic!(
|
||||
"Non-main function failed LLVM verification. Uncomment the above println to debug!"
|
||||
r"Non-main function {:?} failed LLVM verification. Uncomment the above println to debug!",
|
||||
fn_val.get_name(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ pub enum ExposedModuleTypes {
|
|||
}
|
||||
|
||||
pub struct ConstrainedModule {
|
||||
pub unused_imports: MutSet<ModuleId>,
|
||||
pub unused_imports: MutMap<ModuleId, Region>,
|
||||
pub constraint: Constraint,
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ pub fn constrain_imports(
|
|||
pub struct ConstrainableImports {
|
||||
pub imported_symbols: Vec<Import>,
|
||||
pub imported_aliases: MutMap<Symbol, Alias>,
|
||||
pub unused_imports: MutSet<ModuleId>,
|
||||
pub unused_imports: MutMap<ModuleId, Region>,
|
||||
}
|
||||
|
||||
/// Run this before constraining imports.
|
||||
|
@ -143,7 +143,7 @@ pub struct ConstrainableImports {
|
|||
pub fn pre_constrain_imports(
|
||||
home: ModuleId,
|
||||
references: &MutSet<Symbol>,
|
||||
imported_modules: MutSet<ModuleId>,
|
||||
imported_modules: MutMap<ModuleId, Region>,
|
||||
exposed_types: &mut SubsByModule,
|
||||
stdlib: &StdLib,
|
||||
) -> ConstrainableImports {
|
||||
|
|
|
@ -2249,12 +2249,10 @@ pub fn build_closure_caller<'a, 'ctx, 'env>(
|
|||
let mut argument_types = Vec::with_capacity_in(arguments.len() + 3, env.arena);
|
||||
|
||||
for layout in arguments {
|
||||
argument_types.push(basic_type_from_layout(
|
||||
arena,
|
||||
context,
|
||||
layout,
|
||||
env.ptr_bytes,
|
||||
));
|
||||
let arg_type = basic_type_from_layout(arena, context, layout, env.ptr_bytes);
|
||||
let arg_ptr_type = arg_type.ptr_type(AddressSpace::Generic);
|
||||
|
||||
argument_types.push(arg_ptr_type.into());
|
||||
}
|
||||
|
||||
let function_pointer_type = {
|
||||
|
@ -2310,6 +2308,12 @@ pub fn build_closure_caller<'a, 'ctx, 'env>(
|
|||
let closure_data = builder.build_load(closure_data_ptr, "load_closure_data");
|
||||
|
||||
let mut parameters = parameters;
|
||||
|
||||
for param in parameters.iter_mut() {
|
||||
debug_assert!(param.is_pointer_value());
|
||||
*param = builder.build_load(param.into_pointer_value(), "load_param");
|
||||
}
|
||||
|
||||
parameters.push(closure_data);
|
||||
|
||||
let call_result = invoke_and_catch(env, function_value, function_ptr, ¶meters, result_type);
|
||||
|
@ -2357,12 +2361,10 @@ pub fn build_function_caller<'a, 'ctx, 'env>(
|
|||
let mut argument_types = Vec::with_capacity_in(arguments.len() + 3, env.arena);
|
||||
|
||||
for layout in arguments {
|
||||
argument_types.push(basic_type_from_layout(
|
||||
arena,
|
||||
context,
|
||||
layout,
|
||||
env.ptr_bytes,
|
||||
));
|
||||
let arg_type = basic_type_from_layout(arena, context, layout, env.ptr_bytes);
|
||||
let arg_ptr_type = arg_type.ptr_type(AddressSpace::Generic);
|
||||
|
||||
argument_types.push(arg_ptr_type.into());
|
||||
}
|
||||
|
||||
let function_pointer_type = {
|
||||
|
@ -2429,6 +2431,13 @@ pub fn build_function_caller<'a, 'ctx, 'env>(
|
|||
.build_bitcast(function_ptr, actual_function_type, "cast")
|
||||
.into_pointer_value();
|
||||
|
||||
let mut parameters = parameters;
|
||||
|
||||
for param in parameters.iter_mut() {
|
||||
debug_assert!(param.is_pointer_value());
|
||||
*param = builder.build_load(param.into_pointer_value(), "load_param");
|
||||
}
|
||||
|
||||
let call_result = invoke_and_catch(env, function_value, function_ptr, ¶meters, result_type);
|
||||
|
||||
builder.build_store(output, call_result);
|
||||
|
|
|
@ -106,12 +106,12 @@ impl Dependencies {
|
|||
&mut self,
|
||||
module_id: ModuleId,
|
||||
opt_effect_module: Option<ModuleId>,
|
||||
dependencies: &MutSet<ModuleId>,
|
||||
dependencies: &MutMap<ModuleId, Region>,
|
||||
goal_phase: Phase,
|
||||
) -> MutSet<(ModuleId, Phase)> {
|
||||
use Phase::*;
|
||||
|
||||
for dep in dependencies.iter().copied() {
|
||||
for dep in dependencies.keys().copied() {
|
||||
// to parse and generate constraints, the headers of all dependencies must be loaded!
|
||||
// otherwise, we don't know whether an imported symbol is actually exposed
|
||||
self.add_dependency_help(module_id, dep, Phase::Parse, Phase::LoadHeader);
|
||||
|
@ -146,7 +146,7 @@ impl Dependencies {
|
|||
let mut output = MutSet::default();
|
||||
|
||||
// all the dependencies can be loaded
|
||||
for dep in dependencies {
|
||||
for dep in dependencies.keys() {
|
||||
// TODO figure out how to "load" (because it doesn't exist on the file system) the Effect module
|
||||
|
||||
if Some(*dep) != opt_effect_module {
|
||||
|
@ -420,7 +420,7 @@ fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) ->
|
|||
|
||||
let mut aliases = MutMap::default();
|
||||
|
||||
for imported in parsed.imported_modules.iter() {
|
||||
for imported in parsed.imported_modules.keys() {
|
||||
match state.module_cache.aliases.get(imported) {
|
||||
None => unreachable!(
|
||||
r"imported module {:?} did not register its aliases, so {:?} cannot use them",
|
||||
|
@ -556,7 +556,7 @@ struct ModuleHeader<'a> {
|
|||
exposed_ident_ids: IdentIds,
|
||||
deps_by_name: MutMap<PQModuleName<'a>, ModuleId>,
|
||||
packages: MutMap<&'a str, PackageOrPath<'a>>,
|
||||
imported_modules: MutSet<ModuleId>,
|
||||
imported_modules: MutMap<ModuleId, Region>,
|
||||
exposes: Vec<Symbol>,
|
||||
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
||||
src: &'a [u8],
|
||||
|
@ -574,7 +574,7 @@ enum HeaderFor<'a> {
|
|||
struct ConstrainedModule {
|
||||
module: Module,
|
||||
declarations: Vec<Declaration>,
|
||||
imported_modules: MutSet<ModuleId>,
|
||||
imported_modules: MutMap<ModuleId, Region>,
|
||||
constraint: Constraint,
|
||||
ident_ids: IdentIds,
|
||||
var_store: VarStore,
|
||||
|
@ -631,7 +631,7 @@ struct ParsedModule<'a> {
|
|||
src: &'a str,
|
||||
module_timing: ModuleTiming,
|
||||
deps_by_name: MutMap<PQModuleName<'a>, ModuleId>,
|
||||
imported_modules: MutSet<ModuleId>,
|
||||
imported_modules: MutMap<ModuleId, Region>,
|
||||
exposed_ident_ids: IdentIds,
|
||||
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
||||
parsed_defs: &'a [Located<roc_parse::ast::Def<'a>>],
|
||||
|
@ -659,6 +659,7 @@ enum Msg<'a> {
|
|||
solved_subs: Solved<Subs>,
|
||||
decls: Vec<Declaration>,
|
||||
module_timing: ModuleTiming,
|
||||
unused_imports: MutMap<ModuleId, Region>,
|
||||
},
|
||||
FinishedAllTypeChecking {
|
||||
solved_subs: Solved<Subs>,
|
||||
|
@ -870,6 +871,7 @@ enum BuildTask<'a> {
|
|||
constraint: Constraint,
|
||||
var_store: VarStore,
|
||||
declarations: Vec<Declaration>,
|
||||
unused_imports: MutMap<ModuleId, Region>,
|
||||
},
|
||||
BuildPendingSpecializations {
|
||||
module_timing: ModuleTiming,
|
||||
|
@ -1607,6 +1609,7 @@ fn update<'a>(
|
|||
solved_subs,
|
||||
decls,
|
||||
mut module_timing,
|
||||
mut unused_imports,
|
||||
} => {
|
||||
log!("solved types for {:?}", module_id);
|
||||
module_timing.end_time = SystemTime::now();
|
||||
|
@ -1616,6 +1619,15 @@ fn update<'a>(
|
|||
.type_problems
|
||||
.insert(module_id, solved_module.problems);
|
||||
|
||||
let existing = match state.module_cache.can_problems.entry(module_id) {
|
||||
Vacant(entry) => entry.insert(std::vec::Vec::new()),
|
||||
Occupied(entry) => entry.into_mut(),
|
||||
};
|
||||
|
||||
for (unused, region) in unused_imports.drain() {
|
||||
existing.push(roc_problem::can::Problem::UnusedImport(unused, region));
|
||||
}
|
||||
|
||||
let work = state.dependencies.notify(module_id, Phase::SolveTypes);
|
||||
|
||||
// if there is a platform, the Pkg-Config module provides host-exposed,
|
||||
|
@ -2350,7 +2362,7 @@ fn send_header<'a>(
|
|||
|
||||
let mut imported: Vec<(QualifiedModuleName, Vec<Ident>, Region)> =
|
||||
Vec::with_capacity(imports.len());
|
||||
let mut imported_modules: MutSet<ModuleId> = MutSet::default();
|
||||
let mut imported_modules: MutMap<ModuleId, Region> = MutMap::default();
|
||||
let mut scope_size = 0;
|
||||
|
||||
for loc_entry in imports {
|
||||
|
@ -2410,7 +2422,7 @@ fn send_header<'a>(
|
|||
};
|
||||
|
||||
let module_id = module_ids.get_or_insert(&pq_module_name);
|
||||
imported_modules.insert(module_id);
|
||||
imported_modules.insert(module_id, region);
|
||||
|
||||
deps_by_name.insert(pq_module_name, module_id);
|
||||
|
||||
|
@ -2534,7 +2546,7 @@ fn send_header_two<'a>(
|
|||
|
||||
let mut imported: Vec<(QualifiedModuleName, Vec<Ident>, Region)> =
|
||||
Vec::with_capacity(imports.len());
|
||||
let mut imported_modules: MutSet<ModuleId> = MutSet::default();
|
||||
let mut imported_modules: MutMap<ModuleId, Region> = MutMap::default();
|
||||
|
||||
let num_exposes = provides.len();
|
||||
let mut deps_by_name: MutMap<PQModuleName, ModuleId> =
|
||||
|
@ -2542,7 +2554,7 @@ fn send_header_two<'a>(
|
|||
|
||||
// add standard imports
|
||||
// TODO add Effect by default
|
||||
imported_modules.insert(app_module_id);
|
||||
imported_modules.insert(app_module_id, Region::zero());
|
||||
deps_by_name.insert(
|
||||
PQModuleName::Unqualified(ModuleName::APP.into()),
|
||||
app_module_id,
|
||||
|
@ -2594,7 +2606,7 @@ fn send_header_two<'a>(
|
|||
};
|
||||
|
||||
let module_id = module_ids.get_or_insert(&pq_module_name);
|
||||
imported_modules.insert(module_id);
|
||||
imported_modules.insert(module_id, region);
|
||||
|
||||
deps_by_name.insert(pq_module_name, module_id);
|
||||
|
||||
|
@ -2720,7 +2732,7 @@ impl<'a> BuildTask<'a> {
|
|||
module_timing: ModuleTiming,
|
||||
constraint: Constraint,
|
||||
var_store: VarStore,
|
||||
imported_modules: MutSet<ModuleId>,
|
||||
imported_modules: MutMap<ModuleId, Region>,
|
||||
exposed_types: &mut SubsByModule,
|
||||
stdlib: &StdLib,
|
||||
declarations: Vec<Declaration>,
|
||||
|
@ -2742,14 +2754,6 @@ impl<'a> BuildTask<'a> {
|
|||
stdlib,
|
||||
);
|
||||
|
||||
if !unused_imports.is_empty() {
|
||||
todo!(
|
||||
"TODO gracefully handle unused import {:?} from module {:?}",
|
||||
&unused_imports,
|
||||
home,
|
||||
);
|
||||
}
|
||||
|
||||
// Next, solve this module in the background.
|
||||
Self::Solve {
|
||||
module,
|
||||
|
@ -2759,6 +2763,7 @@ impl<'a> BuildTask<'a> {
|
|||
var_store,
|
||||
declarations,
|
||||
module_timing,
|
||||
unused_imports,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2772,6 +2777,7 @@ fn run_solve<'a>(
|
|||
constraint: Constraint,
|
||||
mut var_store: VarStore,
|
||||
decls: Vec<Declaration>,
|
||||
unused_imports: MutMap<ModuleId, Region>,
|
||||
) -> Msg<'a> {
|
||||
// We have more constraining work to do now, so we'll add it to our timings.
|
||||
let constrain_start = SystemTime::now();
|
||||
|
@ -2819,6 +2825,7 @@ fn run_solve<'a>(
|
|||
decls,
|
||||
solved_module,
|
||||
module_timing,
|
||||
unused_imports,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3071,7 +3078,7 @@ fn fabricate_effects_module<'a>(
|
|||
rigid_variables: module_output.rigid_variables,
|
||||
};
|
||||
|
||||
let imported_modules = MutSet::default();
|
||||
let imported_modules = MutMap::default();
|
||||
|
||||
// Should a effect module ever have a ModuleDocumentation?
|
||||
let module_docs = ModuleDocumentation {
|
||||
|
@ -3615,6 +3622,7 @@ fn run_task<'a>(
|
|||
var_store,
|
||||
ident_ids,
|
||||
declarations,
|
||||
unused_imports,
|
||||
} => Ok(run_solve(
|
||||
module,
|
||||
ident_ids,
|
||||
|
@ -3623,6 +3631,7 @@ fn run_task<'a>(
|
|||
constraint,
|
||||
var_store,
|
||||
declarations,
|
||||
unused_imports,
|
||||
)),
|
||||
BuildPendingSpecializations {
|
||||
module_id,
|
||||
|
|
|
@ -64,7 +64,7 @@ impl TagName {
|
|||
impl ModuleName {
|
||||
// NOTE: After adding one of these, go to `impl ModuleId` and
|
||||
// add a corresponding ModuleId to there!
|
||||
pub const APP: &'static str = ""; // app modules have no module name
|
||||
pub const APP: &'static str = "#UserApp"; // app modules have this hardcoded name
|
||||
pub const BOOL: &'static str = "Bool";
|
||||
pub const STR: &'static str = "Str";
|
||||
pub const NUM: &'static str = "Num";
|
||||
|
|
|
@ -571,6 +571,12 @@ impl<'a> Procs<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
// If this is an imported symbol, let its home module make this specialization
|
||||
if env.is_imported_symbol(name) {
|
||||
add_needed_external(self, env, fn_var, name);
|
||||
return;
|
||||
}
|
||||
|
||||
// We're done with that tuple, so move layout back out to avoid cloning it.
|
||||
let (name, layout) = tuple;
|
||||
|
||||
|
@ -589,7 +595,7 @@ impl<'a> Procs<'a> {
|
|||
// TODO should pending_procs hold a Rc<Proc>?
|
||||
let partial_proc = match self.partial_procs.get(&symbol) {
|
||||
Some(p) => p.clone(),
|
||||
None => panic!("no partial_proc for {:?}", symbol),
|
||||
None => panic!("no partial_proc for {:?} in module {:?}", symbol, env.home),
|
||||
};
|
||||
|
||||
// Mark this proc as in-progress, so if we're dealing with
|
||||
|
@ -695,6 +701,10 @@ impl<'a, 'i> Env<'a, 'i> {
|
|||
|
||||
Symbol::new(self.home, ident_id)
|
||||
}
|
||||
|
||||
pub fn is_imported_symbol(&self, symbol: Symbol) -> bool {
|
||||
symbol.module_id() != self.home && !symbol.is_builtin()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Copy, Eq, Hash)]
|
||||
|
@ -904,13 +914,16 @@ where
|
|||
D::Doc: Clone,
|
||||
A: Clone,
|
||||
{
|
||||
use roc_module::ident::ModuleName;
|
||||
|
||||
if PRETTY_PRINT_IR_SYMBOLS {
|
||||
alloc.text(format!("{:?}", symbol))
|
||||
} else {
|
||||
let text = format!("{}", symbol);
|
||||
|
||||
if text.starts_with('.') {
|
||||
alloc.text("Test").append(text)
|
||||
if text.starts_with(ModuleName::APP) {
|
||||
let name: String = text.trim_start_matches(ModuleName::APP).into();
|
||||
alloc.text("Test").append(name)
|
||||
} else {
|
||||
alloc.text(text)
|
||||
}
|
||||
|
@ -2129,6 +2142,96 @@ impl<'a> FunctionLayouts<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn specialize_naked_symbol<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
variable: Variable,
|
||||
procs: &mut Procs<'a>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
assigned: Symbol,
|
||||
hole: &'a Stmt<'a>,
|
||||
symbol: Symbol,
|
||||
) -> Stmt<'a> {
|
||||
if procs.module_thunks.contains(&symbol) {
|
||||
let partial_proc = procs.partial_procs.get(&symbol).unwrap();
|
||||
let fn_var = partial_proc.annotation;
|
||||
|
||||
// This is a top-level declaration, which will code gen to a 0-arity thunk.
|
||||
let result = call_by_name(
|
||||
env,
|
||||
procs,
|
||||
fn_var,
|
||||
symbol,
|
||||
std::vec::Vec::new(),
|
||||
layout_cache,
|
||||
assigned,
|
||||
env.arena.alloc(Stmt::Ret(assigned)),
|
||||
);
|
||||
|
||||
return result;
|
||||
} else if env.is_imported_symbol(symbol) {
|
||||
match layout_cache.from_var(env.arena, variable, env.subs) {
|
||||
Err(e) => panic!("invalid layout {:?}", e),
|
||||
Ok(layout @ Layout::FunctionPointer(_, _)) => {
|
||||
add_needed_external(procs, env, variable, symbol);
|
||||
|
||||
match hole {
|
||||
Stmt::Jump(_, _) => todo!("not sure what to do in this case yet"),
|
||||
_ => {
|
||||
let expr = Expr::FunctionPointer(symbol, layout.clone());
|
||||
let new_symbol = env.unique_symbol();
|
||||
return Stmt::Let(
|
||||
new_symbol,
|
||||
expr,
|
||||
layout,
|
||||
env.arena.alloc(Stmt::Ret(new_symbol)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(_) => {
|
||||
// this is a 0-arity thunk
|
||||
let result = call_by_name(
|
||||
env,
|
||||
procs,
|
||||
variable,
|
||||
symbol,
|
||||
std::vec::Vec::new(),
|
||||
layout_cache,
|
||||
assigned,
|
||||
env.arena.alloc(Stmt::Ret(assigned)),
|
||||
);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A bit ugly, but it does the job
|
||||
match hole {
|
||||
Stmt::Jump(id, _) => Stmt::Jump(*id, env.arena.alloc([symbol])),
|
||||
_ => {
|
||||
let result = Stmt::Ret(symbol);
|
||||
let original = symbol;
|
||||
|
||||
// we don't have a more accurate variable available, which means the variable
|
||||
// from the partial_proc will be used. So far that has given the correct
|
||||
// result, but I'm not sure this will continue to be the case in more complex
|
||||
// examples.
|
||||
let opt_fn_var = None;
|
||||
|
||||
// if this is a function symbol, ensure that it's properly specialized!
|
||||
reuse_function_symbol(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
opt_fn_var,
|
||||
symbol,
|
||||
result,
|
||||
original,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_hole<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
can_expr: roc_can::expr::Expr,
|
||||
|
@ -2387,85 +2490,7 @@ pub fn with_hole<'a>(
|
|||
)
|
||||
}
|
||||
Var(symbol) => {
|
||||
if procs.module_thunks.contains(&symbol) {
|
||||
let partial_proc = procs.partial_procs.get(&symbol).unwrap();
|
||||
let fn_var = partial_proc.annotation;
|
||||
|
||||
// This is a top-level declaration, which will code gen to a 0-arity thunk.
|
||||
let result = call_by_name(
|
||||
env,
|
||||
procs,
|
||||
fn_var,
|
||||
symbol,
|
||||
std::vec::Vec::new(),
|
||||
layout_cache,
|
||||
assigned,
|
||||
env.arena.alloc(Stmt::Ret(assigned)),
|
||||
);
|
||||
|
||||
return result;
|
||||
} else if symbol.module_id() != env.home && symbol.module_id() != ModuleId::ATTR {
|
||||
match layout_cache.from_var(env.arena, variable, env.subs) {
|
||||
Err(e) => panic!("invalid layout {:?}", e),
|
||||
Ok(layout @ Layout::FunctionPointer(_, _)) => {
|
||||
add_needed_external(procs, env, variable, symbol);
|
||||
|
||||
match hole {
|
||||
Stmt::Jump(_, _) => todo!("not sure what to do in this case yet"),
|
||||
_ => {
|
||||
let expr = Expr::FunctionPointer(symbol, layout.clone());
|
||||
let new_symbol = env.unique_symbol();
|
||||
return Stmt::Let(
|
||||
new_symbol,
|
||||
expr,
|
||||
layout,
|
||||
env.arena.alloc(Stmt::Ret(new_symbol)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(_) => {
|
||||
// this is a 0-arity thunk
|
||||
let result = call_by_name(
|
||||
env,
|
||||
procs,
|
||||
variable,
|
||||
symbol,
|
||||
std::vec::Vec::new(),
|
||||
layout_cache,
|
||||
assigned,
|
||||
env.arena.alloc(Stmt::Ret(assigned)),
|
||||
);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A bit ugly, but it does the job
|
||||
match hole {
|
||||
Stmt::Jump(id, _) => Stmt::Jump(*id, env.arena.alloc([symbol])),
|
||||
_ => {
|
||||
let result = Stmt::Ret(symbol);
|
||||
let original = symbol;
|
||||
|
||||
// we don't have a more accurate variable available, which means the variable
|
||||
// from the partial_proc will be used. So far that has given the correct
|
||||
// result, but I'm not sure this will continue to be the case in more complex
|
||||
// examples.
|
||||
let opt_fn_var = None;
|
||||
|
||||
// if this is a function symbol, ensure that it's properly specialized!
|
||||
reuse_function_symbol(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
opt_fn_var,
|
||||
symbol,
|
||||
result,
|
||||
original,
|
||||
)
|
||||
}
|
||||
}
|
||||
specialize_naked_symbol(env, variable, procs, layout_cache, assigned, hole, symbol)
|
||||
}
|
||||
Tag {
|
||||
variant_var,
|
||||
|
@ -2636,17 +2661,28 @@ pub fn with_hole<'a>(
|
|||
let mut field_symbols = Vec::with_capacity_in(fields.len(), env.arena);
|
||||
let mut can_fields = Vec::with_capacity_in(fields.len(), env.arena);
|
||||
|
||||
for (label, _, _) in sorted_fields.into_iter() {
|
||||
// TODO how should function pointers be handled here?
|
||||
match fields.remove(&label) {
|
||||
Some(field) => match can_reuse_symbol(procs, &field.loc_expr.value) {
|
||||
Some(reusable) => {
|
||||
field_symbols.push(reusable);
|
||||
can_fields.push(None);
|
||||
enum Field {
|
||||
Function(Symbol, Variable),
|
||||
ValueSymbol,
|
||||
Field(roc_can::expr::Field),
|
||||
}
|
||||
None => {
|
||||
|
||||
for (label, variable, _) in sorted_fields.into_iter() {
|
||||
// TODO how should function pointers be handled here?
|
||||
use ReuseSymbol::*;
|
||||
match fields.remove(&label) {
|
||||
Some(field) => match can_reuse_symbol(env, procs, &field.loc_expr.value) {
|
||||
Imported(symbol) | LocalFunction(symbol) => {
|
||||
field_symbols.push(symbol);
|
||||
can_fields.push(Field::Function(symbol, variable));
|
||||
}
|
||||
Value(reusable) => {
|
||||
field_symbols.push(reusable);
|
||||
can_fields.push(Field::ValueSymbol);
|
||||
}
|
||||
NotASymbol => {
|
||||
field_symbols.push(env.unique_symbol());
|
||||
can_fields.push(Some(field));
|
||||
can_fields.push(Field::Field(field));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
|
@ -2666,7 +2702,22 @@ pub fn with_hole<'a>(
|
|||
|
||||
for (opt_field, symbol) in can_fields.into_iter().rev().zip(field_symbols.iter().rev())
|
||||
{
|
||||
if let Some(field) = opt_field {
|
||||
match opt_field {
|
||||
Field::ValueSymbol => {
|
||||
// this symbol is already defined; nothing to do
|
||||
}
|
||||
Field::Function(symbol, variable) => {
|
||||
stmt = reuse_function_symbol(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
Some(variable),
|
||||
symbol,
|
||||
stmt,
|
||||
symbol,
|
||||
);
|
||||
}
|
||||
Field::Field(field) => {
|
||||
stmt = with_hole(
|
||||
env,
|
||||
field.loc_expr.value,
|
||||
|
@ -2678,6 +2729,7 @@ pub fn with_hole<'a>(
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stmt
|
||||
}
|
||||
|
@ -3342,8 +3394,15 @@ pub fn with_hole<'a>(
|
|||
// if the function expression (loc_expr) is already a symbol,
|
||||
// re-use that symbol, and don't define its value again
|
||||
let mut result;
|
||||
match can_reuse_symbol(procs, &loc_expr.value) {
|
||||
Some(function_symbol) => {
|
||||
use ReuseSymbol::*;
|
||||
match can_reuse_symbol(env, procs, &loc_expr.value) {
|
||||
LocalFunction(_) => {
|
||||
unreachable!("if this was known to be a function, we would not be here")
|
||||
}
|
||||
Imported(_) => {
|
||||
unreachable!("an imported value is never an anonymous function")
|
||||
}
|
||||
Value(function_symbol) => {
|
||||
if let Layout::Closure(_, closure_fields, _) = full_layout {
|
||||
// we're invoking a closure
|
||||
let closure_record_symbol = env.unique_symbol();
|
||||
|
@ -3436,7 +3495,7 @@ pub fn with_hole<'a>(
|
|||
);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
NotASymbol => {
|
||||
let function_symbol = env.unique_symbol();
|
||||
|
||||
result = Stmt::Let(
|
||||
|
@ -4677,15 +4736,33 @@ fn store_record_destruct<'a>(
|
|||
/// We want to re-use symbols that are not function symbols
|
||||
/// for any other expression, we create a new symbol, and will
|
||||
/// later make sure it gets assigned the correct value.
|
||||
fn can_reuse_symbol<'a>(procs: &Procs<'a>, expr: &roc_can::expr::Expr) -> Option<Symbol> {
|
||||
|
||||
enum ReuseSymbol {
|
||||
Imported(Symbol),
|
||||
LocalFunction(Symbol),
|
||||
Value(Symbol),
|
||||
NotASymbol,
|
||||
}
|
||||
|
||||
fn can_reuse_symbol<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
procs: &Procs<'a>,
|
||||
expr: &roc_can::expr::Expr,
|
||||
) -> ReuseSymbol {
|
||||
use ReuseSymbol::*;
|
||||
|
||||
if let roc_can::expr::Expr::Var(symbol) = expr {
|
||||
if procs.partial_procs.contains_key(&symbol) {
|
||||
None
|
||||
let symbol = *symbol;
|
||||
|
||||
if env.is_imported_symbol(symbol) {
|
||||
Imported(symbol)
|
||||
} else if procs.partial_procs.contains_key(&symbol) {
|
||||
LocalFunction(symbol)
|
||||
} else {
|
||||
Some(*symbol)
|
||||
Value(symbol)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
NotASymbol
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4694,9 +4771,9 @@ fn possible_reuse_symbol<'a>(
|
|||
procs: &Procs<'a>,
|
||||
expr: &roc_can::expr::Expr,
|
||||
) -> Symbol {
|
||||
match can_reuse_symbol(procs, expr) {
|
||||
Some(s) => s,
|
||||
None => env.unique_symbol(),
|
||||
match can_reuse_symbol(env, procs, expr) {
|
||||
ReuseSymbol::Value(s) => s,
|
||||
_ => env.unique_symbol(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4752,8 +4829,7 @@ fn reuse_function_symbol<'a>(
|
|||
) -> Stmt<'a> {
|
||||
match procs.partial_procs.get(&original) {
|
||||
None => {
|
||||
let is_imported =
|
||||
!(env.home == original.module_id() || original.module_id() == ModuleId::ATTR);
|
||||
let is_imported = env.is_imported_symbol(original);
|
||||
|
||||
match arg_var {
|
||||
Some(arg_var) if is_imported => {
|
||||
|
@ -4761,8 +4837,6 @@ fn reuse_function_symbol<'a>(
|
|||
.from_var(env.arena, arg_var, env.subs)
|
||||
.expect("creating layout does not fail");
|
||||
|
||||
add_needed_external(procs, env, arg_var, original);
|
||||
|
||||
procs.insert_passed_by_name(
|
||||
env,
|
||||
arg_var,
|
||||
|
@ -4774,7 +4848,7 @@ fn reuse_function_symbol<'a>(
|
|||
// an imported symbol is always a function pointer:
|
||||
// either it's a function, or a top-level 0-argument thunk
|
||||
let expr = Expr::FunctionPointer(original, layout.clone());
|
||||
return Stmt::Let(original, expr, layout, env.arena.alloc(result));
|
||||
return Stmt::Let(symbol, expr, layout, env.arena.alloc(result));
|
||||
}
|
||||
_ => {
|
||||
// danger: a foreign symbol may not be specialized!
|
||||
|
@ -4914,8 +4988,10 @@ fn assign_to_symbol<'a>(
|
|||
symbol: Symbol,
|
||||
result: Stmt<'a>,
|
||||
) -> Stmt<'a> {
|
||||
// if this argument is already a symbol, we don't need to re-define it
|
||||
if let roc_can::expr::Expr::Var(original) = loc_arg.value {
|
||||
use ReuseSymbol::*;
|
||||
match can_reuse_symbol(env, procs, &loc_arg.value) {
|
||||
Imported(original) | LocalFunction(original) => {
|
||||
// for functions we must make sure they are specialized correctly
|
||||
reuse_function_symbol(
|
||||
env,
|
||||
procs,
|
||||
|
@ -4925,8 +5001,12 @@ fn assign_to_symbol<'a>(
|
|||
result,
|
||||
original,
|
||||
)
|
||||
} else {
|
||||
with_hole(
|
||||
}
|
||||
Value(_) => {
|
||||
// symbol is already defined; nothing else to do here
|
||||
result
|
||||
}
|
||||
NotASymbol => with_hole(
|
||||
env,
|
||||
loc_arg.value,
|
||||
arg_var,
|
||||
|
@ -4934,7 +5014,7 @@ fn assign_to_symbol<'a>(
|
|||
layout_cache,
|
||||
symbol,
|
||||
env.arena.alloc(result),
|
||||
)
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,15 +28,22 @@ pub fn can_problem<'b>(
|
|||
.append(alloc.reflow(line)),
|
||||
])
|
||||
}
|
||||
Problem::UnusedImport(module_id, region) => alloc.concat(vec![
|
||||
Problem::UnusedImport(module_id, region) => {
|
||||
alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("Nothing from "),
|
||||
alloc.module(module_id),
|
||||
alloc.reflow(" is used in this module."),
|
||||
]),
|
||||
alloc.region(region),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("Since "),
|
||||
alloc.module(module_id),
|
||||
alloc.reflow(" isn't used, you don't need to import it."),
|
||||
]),
|
||||
])
|
||||
])
|
||||
|
||||
}
|
||||
Problem::ExposedButNotDefined(symbol) => {
|
||||
alloc.stack(vec![
|
||||
alloc
|
||||
|
|
|
@ -3,5 +3,29 @@ app "effect-example"
|
|||
imports [base.Cmd]
|
||||
provides [ main ] to base
|
||||
|
||||
main : I64
|
||||
main = 42
|
||||
Model : I64
|
||||
|
||||
Msg : [ Line Str ]
|
||||
|
||||
main = { init, update }
|
||||
|
||||
init : {} -> { model : Model, cmd : Cmd.Cmd Msg }
|
||||
init = \{} ->
|
||||
cmd =
|
||||
Cmd.after (Cmd.putLine "Type a thing, and I'll say it back") \{} ->
|
||||
Cmd.getLine (\l -> Line l)
|
||||
|
||||
{ model: 42, cmd }
|
||||
|
||||
|
||||
update : Msg, Model -> { model : Model, cmd : Cmd.Cmd Msg }
|
||||
update = \msg, model ->
|
||||
when msg is
|
||||
Line line ->
|
||||
cmd =
|
||||
Cmd.after (Cmd.putLine "You said:") \{} ->
|
||||
Cmd.after (Cmd.putLine line) \{} ->
|
||||
Cmd.after (Cmd.putLine "Type another thing, and I'll say it back") \{} ->
|
||||
Cmd.getLine (\l -> Line l)
|
||||
|
||||
{ model: model + 1, cmd }
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
platform folkertdev/foo
|
||||
requires { main : Effect {} }
|
||||
requires { main : {} }
|
||||
exposes []
|
||||
packages {}
|
||||
imports [Cmd]
|
||||
|
@ -11,31 +11,9 @@ platform folkertdev/foo
|
|||
getLine : Effect Str
|
||||
}
|
||||
|
||||
|
||||
mainForHost :
|
||||
{
|
||||
init : ({} -> { model: I64, cmd : (Cmd.Cmd [ Line Str ]) as Fx }) as Init,
|
||||
update : ([ Line Str ], I64 -> { model: I64, cmd : Cmd.Cmd [ Line Str ] } ) as Update
|
||||
}
|
||||
mainForHost =
|
||||
{
|
||||
init : \{} ->
|
||||
{
|
||||
model: 42,
|
||||
cmd:
|
||||
Cmd.after (Cmd.putLine "Type a thing, and I'll say it back") \{} ->
|
||||
Cmd.getLine (\l -> Line l)
|
||||
},
|
||||
update : \msg, model ->
|
||||
when msg is
|
||||
Line line ->
|
||||
cmd =
|
||||
Cmd.after (Cmd.putLine "You said:") \{} ->
|
||||
Cmd.after (Cmd.putLine line) \{} ->
|
||||
Cmd.after (Cmd.putLine "Type another thing, and I'll say it back") \{} ->
|
||||
Cmd.getLine (\l -> Line l)
|
||||
|
||||
{ model: model + 1, cmd }
|
||||
}
|
||||
|
||||
|
||||
mainForHost = main
|
||||
|
|
|
@ -17,7 +17,12 @@ extern "C" {
|
|||
fn roc_main_size() -> i64;
|
||||
|
||||
#[link_name = "roc__mainForHost_1_Init_caller"]
|
||||
fn call_Init(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> ();
|
||||
fn _call_Init(
|
||||
flags: &(),
|
||||
function_pointer: *const u8,
|
||||
closure_data: *const u8,
|
||||
output: *mut u8,
|
||||
) -> ();
|
||||
|
||||
#[link_name = "roc__mainForHost_1_Init_size"]
|
||||
fn size_Init() -> i64;
|
||||
|
@ -27,8 +32,8 @@ extern "C" {
|
|||
|
||||
#[link_name = "roc__mainForHost_1_Update_caller"]
|
||||
fn call_Update(
|
||||
msg: Msg,
|
||||
model: Model,
|
||||
msg: &Msg,
|
||||
model: &Model,
|
||||
function_pointer: *const u8,
|
||||
closure_data: *const u8,
|
||||
output: *mut u8,
|
||||
|
@ -41,7 +46,12 @@ extern "C" {
|
|||
fn size_Update_result() -> i64;
|
||||
|
||||
#[link_name = "roc__mainForHost_1_Fx_caller"]
|
||||
fn call_Fx(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> ();
|
||||
fn _call_Fx(
|
||||
unit: &(),
|
||||
function_pointer: *const u8,
|
||||
closure_data: *const u8,
|
||||
output: *mut u8,
|
||||
) -> ();
|
||||
|
||||
#[link_name = "roc__mainForHost_1_Fx_size"]
|
||||
fn size_Fx() -> i64;
|
||||
|
@ -53,6 +63,17 @@ extern "C" {
|
|||
fn size_Model() -> i64;
|
||||
}
|
||||
|
||||
unsafe fn call_Fx(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> () {
|
||||
// Fx (or Cmd on the roc side) is a thunk, so we know its first argument is a (pointer to) unit
|
||||
_call_Fx(&(), function_pointer, closure_data, output)
|
||||
}
|
||||
|
||||
unsafe fn call_Init(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> () {
|
||||
// for now, we hardcode flags to be `()` (or `{}` on the roc side)
|
||||
let flags = ();
|
||||
_call_Init(&flags, function_pointer, closure_data, output)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn roc_fx_putChar(foo: i64) -> () {
|
||||
let character = foo as u8 as char;
|
||||
|
@ -152,9 +173,11 @@ unsafe fn run_update(
|
|||
let buffer: *mut std::ffi::c_void = buffer;
|
||||
let buffer: *mut u8 = buffer as *mut u8;
|
||||
|
||||
println!("let's try update!");
|
||||
|
||||
call_Update(
|
||||
msg,
|
||||
model,
|
||||
&msg,
|
||||
&model,
|
||||
function_pointer,
|
||||
closure_data_ptr,
|
||||
buffer as *mut u8,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue