Propagate multiple entrypoints to alias analysis

This commit is contained in:
Richard Feldman 2022-12-06 16:09:34 -05:00
parent 236cebacc1
commit 8b463686bc
No known key found for this signature in database
GPG key ID: F1F21AA5B1D9E43B
10 changed files with 282 additions and 352 deletions

View file

@ -11,7 +11,7 @@ use roc_module::symbol::Symbol;
use roc_mono::ir::{ use roc_mono::ir::{
Call, CallType, Expr, HigherOrderLowLevel, HostExposedLayouts, ListLiteralElement, Literal, Call, CallType, Expr, HigherOrderLowLevel, HostExposedLayouts, ListLiteralElement, Literal,
ModifyRc, OptLevel, Proc, Stmt, ModifyRc, OptLevel, Proc, ProcLayout, Stmt,
}; };
use roc_mono::layout::{ use roc_mono::layout::{
Builtin, CapturesNiche, Layout, RawFunctionLayout, STLayoutInterner, UnionLayout, Builtin, CapturesNiche, Layout, RawFunctionLayout, STLayoutInterner, UnionLayout,
@ -136,12 +136,15 @@ pub fn spec_program<'a, I>(
arena: &'a Bump, arena: &'a Bump,
interner: &STLayoutInterner<'a>, interner: &STLayoutInterner<'a>,
opt_level: OptLevel, opt_level: OptLevel,
opt_entry_point: Option<roc_mono::ir::EntryPoint<'a>>, entry_points: &'a [(Symbol, ProcLayout<'a>)],
procs: I, procs: I,
) -> Result<morphic_lib::Solutions> ) -> Result<morphic_lib::Solutions>
where where
I: Iterator<Item = &'a Proc<'a>>, I: Iterator<Item = &'a Proc<'a>>,
{ {
// TODO support multiple entry points here!
let opt_entry_point = entry_points.first();
let main_module = { let main_module = {
let mut m = ModDefBuilder::new(); let mut m = ModDefBuilder::new();
@ -226,13 +229,13 @@ where
m.add_func(func_name, spec)?; m.add_func(func_name, spec)?;
} }
if let Some(entry_point) = opt_entry_point { if let Some((symbol, proc_layout)) = opt_entry_point {
// the entry point wrapper // the entry point wrapper
let roc_main_bytes = func_name_bytes_help( let roc_main_bytes = func_name_bytes_help(
entry_point.symbol, *symbol,
entry_point.layout.arguments.iter().copied(), proc_layout.arguments.iter().copied(),
CapturesNiche::no_niche(), CapturesNiche::no_niche(),
&entry_point.layout.result, &proc_layout.result,
); );
let roc_main = FuncName(&roc_main_bytes); let roc_main = FuncName(&roc_main_bytes);
@ -241,7 +244,7 @@ where
let entry_point_function = build_entry_point( let entry_point_function = build_entry_point(
&mut env, &mut env,
interner, interner,
entry_point.layout, *proc_layout,
roc_main, roc_main,
&host_exposed_functions, &host_exposed_functions,
)?; )?;

View file

@ -188,18 +188,18 @@ fn gen_from_mono_module_llvm<'a>(
// expects that would confuse the surgical linker // expects that would confuse the surgical linker
add_default_roc_externs(&env); add_default_roc_externs(&env);
let opt_entry_point = match loaded.entry_point { let exposed_to_host = match loaded.entry_point {
EntryPoint::Executable { symbol, layout, .. } => { EntryPoint::Executable {
Some(roc_mono::ir::EntryPoint { symbol, layout }) exposed_to_host, ..
} } => exposed_to_host,
EntryPoint::Test => None, EntryPoint::Test => &[],
}; };
roc_gen_llvm::llvm::build::build_procedures( roc_gen_llvm::llvm::build::build_procedures(
&env, &env,
opt_level, opt_level,
loaded.procedures, loaded.procedures,
opt_entry_point, exposed_to_host,
Some(&app_ll_file), Some(&app_ll_file),
); );

View file

@ -40,8 +40,7 @@ use roc_debug_flags::ROC_PRINT_LLVM_FN_VERIFICATION;
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_mono::ir::{ use roc_mono::ir::{
BranchInfo, CallType, CrashTag, EntryPoint, JoinPointId, ListLiteralElement, ModifyRc, BranchInfo, CallType, CrashTag, JoinPointId, ListLiteralElement, ModifyRc, OptLevel, ProcLayout,
OptLevel, ProcLayout,
}; };
use roc_mono::layout::{ use roc_mono::layout::{
Builtin, CapturesNiche, LambdaName, LambdaSet, Layout, LayoutIds, RawFunctionLayout, Builtin, CapturesNiche, LambdaName, LambdaSet, Layout, LayoutIds, RawFunctionLayout,
@ -4164,50 +4163,60 @@ pub fn build_procedures<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
opt_level: OptLevel, opt_level: OptLevel,
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>, procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
opt_entry_point: Option<EntryPoint<'a>>, entry_points: &[(Symbol, ProcLayout<'a>)],
debug_output_file: Option<&Path>, debug_output_file: Option<&Path>,
) { ) {
build_procedures_help( build_procedures_help(env, opt_level, procedures, entry_points, debug_output_file);
env,
opt_level,
procedures,
opt_entry_point,
debug_output_file,
);
} }
pub fn build_wasm_test_wrapper<'a, 'ctx, 'env>( pub fn build_wasm_test_wrapper<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
opt_level: OptLevel, opt_level: OptLevel,
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>, procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
entry_point: EntryPoint<'a>, entry_points: &'a [(Symbol, ProcLayout<'a>)],
) -> (&'static str, FunctionValue<'ctx>) { ) -> (&'static str, FunctionValue<'ctx>) {
assert_eq!(
entry_points.len(),
1,
"Currently, build_wasm_test_wrapper() only supports receiving one entrypoint."
);
let mod_solutions = build_procedures_help( let mod_solutions = build_procedures_help(
env, env,
opt_level, opt_level,
procedures, procedures,
Some(entry_point), entry_points,
Some(&std::env::temp_dir().join("test.ll")), Some(&std::env::temp_dir().join("test.ll")),
); );
promote_to_wasm_test_wrapper(env, mod_solutions, entry_point.symbol, entry_point.layout) let (symbol, proc_layout) = entry_points[0];
promote_to_wasm_test_wrapper(env, mod_solutions, symbol, proc_layout)
} }
pub fn build_procedures_return_main<'a, 'ctx, 'env>( pub fn build_procedures_return_main<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
opt_level: OptLevel, opt_level: OptLevel,
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>, procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
entry_point: EntryPoint<'a>, entry_points: &'a [(Symbol, ProcLayout<'a>)],
) -> (&'static str, FunctionValue<'ctx>) { ) -> (&'static str, FunctionValue<'ctx>) {
assert_eq!(
entry_points.len(),
1,
"Currently, build_procedures_return_main() only supports receiving one entrypoint."
);
let mod_solutions = build_procedures_help( let mod_solutions = build_procedures_help(
env, env,
opt_level, opt_level,
procedures, procedures,
Some(entry_point), entry_points,
Some(&std::env::temp_dir().join("test.ll")), Some(&std::env::temp_dir().join("test.ll")),
); );
promote_to_main_function(env, mod_solutions, entry_point.symbol, entry_point.layout) let (symbol, proc_layout) = entry_points[0];
promote_to_main_function(env, mod_solutions, symbol, proc_layout)
} }
pub fn build_procedures_expose_expects<'a, 'ctx, 'env>( pub fn build_procedures_expose_expects<'a, 'ctx, 'env>(
@ -4215,13 +4224,13 @@ pub fn build_procedures_expose_expects<'a, 'ctx, 'env>(
opt_level: OptLevel, opt_level: OptLevel,
expects: &[Symbol], expects: &[Symbol],
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>, procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
opt_entry_point: Option<EntryPoint<'a>>, entry_points: &'a [(Symbol, ProcLayout<'a>)],
) -> Vec<'a, &'a str> { ) -> Vec<'a, &'a str> {
let mod_solutions = build_procedures_help( let mod_solutions = build_procedures_help(
env, env,
opt_level, opt_level,
procedures, procedures,
opt_entry_point, entry_points,
Some(&std::env::temp_dir().join("test.ll")), Some(&std::env::temp_dir().join("test.ll")),
); );
@ -4283,7 +4292,7 @@ fn build_procedures_help<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
opt_level: OptLevel, opt_level: OptLevel,
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>, procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
opt_entry_point: Option<EntryPoint<'a>>, entry_points: &[(Symbol, ProcLayout<'a>)],
debug_output_file: Option<&Path>, debug_output_file: Option<&Path>,
) -> &'a ModSolutions { ) -> &'a ModSolutions {
let mut layout_ids = roc_mono::layout::LayoutIds::default(); let mut layout_ids = roc_mono::layout::LayoutIds::default();
@ -4295,7 +4304,7 @@ fn build_procedures_help<'a, 'ctx, 'env>(
env.arena, env.arena,
env.layout_interner, env.layout_interner,
opt_level, opt_level,
opt_entry_point, entry_points,
it, it,
) { ) {
Err(e) => panic!("Error in alias analysis: {}", e), Err(e) => panic!("Error in alias analysis: {}", e),

View file

@ -42,9 +42,8 @@ use roc_packaging::cache::{self, RocCacheDir};
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
use roc_packaging::https::PackageMetadata; use roc_packaging::https::PackageMetadata;
use roc_parse::ast::{self, Defs, ExtractSpaces, Spaced, StrLiteral, TypeAnnotation}; use roc_parse::ast::{self, Defs, ExtractSpaces, Spaced, StrLiteral, TypeAnnotation};
use roc_parse::header::{ExposedName, ImportsEntry, PackageEntry, PlatformHeader, To, TypedIdent}; use roc_parse::header::{ExposedName, ImportsEntry, PackageEntry, PlatformHeader, To};
use roc_parse::header::{HeaderType, ModuleNameEnum, PackageName}; use roc_parse::header::{HeaderType, ModuleNameEnum, PackageName};
use roc_parse::ident::UppercaseIdent;
use roc_parse::module::module_defs; use roc_parse::module::module_defs;
use roc_parse::parser::{FileError, Parser, SourceError, SyntaxError}; use roc_parse::parser::{FileError, Parser, SourceError, SyntaxError};
use roc_problem::Severity; use roc_problem::Severity;
@ -650,7 +649,7 @@ struct ModuleHeader<'a> {
exposes: Vec<Symbol>, exposes: Vec<Symbol>,
exposed_imports: MutMap<Ident, (Symbol, Region)>, exposed_imports: MutMap<Ident, (Symbol, Region)>,
parse_state: roc_parse::state::State<'a>, parse_state: roc_parse::state::State<'a>,
header_for: HeaderType<'a>, header_type: HeaderType<'a>,
symbols_from_requires: Vec<(Loc<Symbol>, Loc<TypeAnnotation<'a>>)>, symbols_from_requires: Vec<(Loc<Symbol>, Loc<TypeAnnotation<'a>>)>,
module_timing: ModuleTiming, module_timing: ModuleTiming,
} }
@ -737,8 +736,7 @@ pub struct ExpectMetadata<'a> {
#[derive(Debug)] #[derive(Debug)]
pub enum EntryPoint<'a> { pub enum EntryPoint<'a> {
Executable { Executable {
symbol: Symbol, exposed_to_host: &'a [(Symbol, ProcLayout<'a>)],
layout: ProcLayout<'a>,
platform_path: PathBuf, platform_path: PathBuf,
}, },
Test, Test,
@ -884,9 +882,9 @@ enum PlatformPath<'a> {
} }
#[derive(Debug)] #[derive(Debug)]
struct PlatformData { struct PlatformData<'a> {
module_id: ModuleId, module_id: ModuleId,
provides: Symbol, provides: &'a [Loc<ExposedName<'a>>],
is_prebuilt: bool, is_prebuilt: bool,
} }
@ -919,7 +917,7 @@ struct State<'a> {
pub root_id: ModuleId, pub root_id: ModuleId,
pub root_subs: Option<Subs>, pub root_subs: Option<Subs>,
pub cache_dir: PathBuf, pub cache_dir: PathBuf,
pub platform_data: Option<PlatformData>, pub platform_data: Option<PlatformData<'a>>,
pub exposed_types: ExposedByModule, pub exposed_types: ExposedByModule,
pub output_path: Option<&'a str>, pub output_path: Option<&'a str>,
pub platform_path: PlatformPath<'a>, pub platform_path: PlatformPath<'a>,
@ -1712,8 +1710,13 @@ fn state_thread_step<'a>(
// We're done! There should be no more messages pending. // We're done! There should be no more messages pending.
debug_assert!(msg_rx.is_empty()); debug_assert!(msg_rx.is_empty());
let monomorphized = let monomorphized = finish_specialization(
finish_specialization(state, subs, layout_interner, exposed_to_host)?; arena,
state,
subs,
layout_interner,
exposed_to_host,
)?;
Ok(ControlFlow::Break(LoadResult::Monomorphized(monomorphized))) Ok(ControlFlow::Break(LoadResult::Monomorphized(monomorphized)))
} }
@ -2349,14 +2352,14 @@ fn update<'a>(
shorthands.insert(shorthand, shorthand_path); shorthands.insert(shorthand, shorthand_path);
} }
match header.header_for { match header.header_type {
App { to_platform } => { App { to_platform } => {
debug_assert!(matches!(state.platform_path, PlatformPath::NotSpecified)); debug_assert!(matches!(state.platform_path, PlatformPath::NotSpecified));
state.platform_path = PlatformPath::Valid(to_platform); state.platform_path = PlatformPath::Valid(to_platform);
} }
Platform { Platform {
main_for_host,
config_shorthand, config_shorthand,
provides,
.. ..
} => { } => {
debug_assert!(matches!(state.platform_data, None)); debug_assert!(matches!(state.platform_data, None));
@ -2383,7 +2386,7 @@ fn update<'a>(
state.platform_data = Some(PlatformData { state.platform_data = Some(PlatformData {
module_id: header.module_id, module_id: header.module_id,
provides: main_for_host, provides,
is_prebuilt, is_prebuilt,
}); });
} }
@ -2507,7 +2510,7 @@ fn update<'a>(
todo!("TODO gracefully handle a malformed string literal after `app` keyword."); todo!("TODO gracefully handle a malformed string literal after `app` keyword.");
} }
}, },
ModuleNameEnum::Platform ModuleNameEnum::Platform(_)
| ModuleNameEnum::Interface(_) | ModuleNameEnum::Interface(_)
| ModuleNameEnum::Hosted(_) => {} | ModuleNameEnum::Hosted(_) => {}
} }
@ -3092,6 +3095,7 @@ fn log_layout_stats(module_id: ModuleId, layout_cache: &LayoutCache) {
} }
fn finish_specialization<'a>( fn finish_specialization<'a>(
arena: &'a Bump,
state: State<'a>, state: State<'a>,
subs: Subs, subs: Subs,
layout_interner: STLayoutInterner<'a>, layout_interner: STLayoutInterner<'a>,
@ -3119,7 +3123,7 @@ fn finish_specialization<'a>(
ModuleId::DERIVED_SYNTH.register_debug_idents(&derived_synth_ident_ids); ModuleId::DERIVED_SYNTH.register_debug_idents(&derived_synth_ident_ids);
all_ident_ids.insert(ModuleId::DERIVED_SYNTH, derived_synth_ident_ids); all_ident_ids.insert(ModuleId::DERIVED_SYNTH, derived_synth_ident_ids);
let interns = Interns { let mut interns = Interns {
module_ids, module_ids,
all_ident_ids, all_ident_ids,
}; };
@ -3153,6 +3157,7 @@ fn finish_specialization<'a>(
ExecutionMode::Test => EntryPoint::Test, ExecutionMode::Test => EntryPoint::Test,
ExecutionMode::Executable | ExecutionMode::ExecutableIfCheck => { ExecutionMode::Executable | ExecutionMode::ExecutableIfCheck => {
use PlatformPath::*; use PlatformPath::*;
let platform_path = match platform_path { let platform_path = match platform_path {
Valid(To::ExistingPackage(shorthand)) => { Valid(To::ExistingPackage(shorthand)) => {
match (*state.arc_shorthands).lock().get(shorthand) { match (*state.arc_shorthands).lock().get(shorthand) {
@ -3167,33 +3172,43 @@ fn finish_specialization<'a>(
} }
}; };
let symbol = match platform_data { let exposed_symbols_and_layouts = match platform_data {
None => { None => {
debug_assert_eq!(exposed_to_host.values.len(), 1); let src = &exposed_to_host.values;
*exposed_to_host.values.iter().next().unwrap().0 let mut buf = bumpalo::collections::Vec::with_capacity_in(src.len(), arena);
for &symbol in src.keys() {
let proc_layout = proc_layout_for(procedures.keys().copied(), symbol);
buf.push((symbol, proc_layout));
}
buf.into_bump_slice()
}
Some(PlatformData {
module_id,
provides,
..
}) => {
let ident_ids = interns.all_ident_ids.get_mut(&module_id).unwrap();
let mut buf =
bumpalo::collections::Vec::with_capacity_in(provides.len(), arena);
for loc_name in provides {
let ident_id = ident_ids.get_or_insert(loc_name.value.as_str());
let symbol = Symbol::new(module_id, ident_id);
let proc_layout = proc_layout_for(procedures.keys().copied(), symbol);
buf.push((symbol, proc_layout));
}
buf.into_bump_slice()
} }
Some(PlatformData { provides, .. }) => provides,
}; };
match procedures.keys().find(|(s, _)| *s == symbol) { EntryPoint::Executable {
Some((_, layout)) => EntryPoint::Executable { exposed_to_host: exposed_symbols_and_layouts,
layout: *layout, platform_path,
symbol,
platform_path,
},
None => {
// the entry point is not specialized. This can happen if the repl output
// is a function value
EntryPoint::Executable {
layout: roc_mono::ir::ProcLayout {
arguments: &[],
result: Layout::struct_no_name_order(&[]),
captures_niche: CapturesNiche::no_niche(),
},
symbol,
platform_path,
}
}
} }
} }
ExecutionMode::Check => unreachable!(), ExecutionMode::Check => unreachable!(),
@ -3231,6 +3246,24 @@ fn finish_specialization<'a>(
}) })
} }
fn proc_layout_for<'a>(
mut proc_symbols: impl Iterator<Item = (Symbol, ProcLayout<'a>)>,
symbol: Symbol,
) -> ProcLayout<'a> {
match proc_symbols.find(|(s, _)| *s == symbol) {
Some((_, layout)) => layout,
None => {
// the entry point is not specialized. This can happen if the repl output
// is a function value
roc_mono::ir::ProcLayout {
arguments: &[],
result: Layout::struct_no_name_order(&[]),
captures_niche: CapturesNiche::no_niche(),
}
}
}
}
fn finish( fn finish(
mut state: State, mut state: State,
solved: Solved<Subs>, solved: Solved<Subs>,
@ -3359,7 +3392,7 @@ fn load_platform_module<'a>(
parser_state, parser_state,
)) => { )) => {
// make a `platform` module that ultimately exposes `main` to the host // make a `platform` module that ultimately exposes `main` to the host
let platform_module_msg = fabricate_platform_module( let (_, _, platform_module_msg) = build_platform_header(
arena, arena,
Some(shorthand), Some(shorthand),
Some(app_module_id), Some(app_module_id),
@ -3369,10 +3402,9 @@ fn load_platform_module<'a>(
ident_ids_by_module, ident_ids_by_module,
&header, &header,
pkg_module_timing, pkg_module_timing,
) );
.1;
Ok(platform_module_msg) Ok(Msg::Header(platform_module_msg))
} }
Err(fail) => Err(LoadingProblem::ParsingFailed( Err(fail) => Err(LoadingProblem::ParsingFailed(
fail.map_problem(SyntaxError::Header) fail.map_problem(SyntaxError::Header)
@ -3935,17 +3967,21 @@ fn parse_header<'a>(
.. ..
}, },
parse_state, parse_state,
)) => Ok(fabricate_platform_module( )) => {
arena, let (module_id, _, header) = build_platform_header(
None, arena,
None, None,
filename, None,
parse_state, filename.to_path_buf(),
module_ids.clone(), parse_state,
ident_ids_by_module, module_ids.clone(),
&header, ident_ids_by_module,
module_timing, &header,
)), module_timing,
);
Ok((module_id, Msg::Header(header)))
}
Err(fail) => Err(LoadingProblem::ParsingFailed( Err(fail) => Err(LoadingProblem::ParsingFailed(
fail.map_problem(SyntaxError::Header) fail.map_problem(SyntaxError::Header)
@ -4053,8 +4089,8 @@ fn build_header<'a>(
} = info; } = info;
let declared_name: ModuleName = match &loc_name.value { let declared_name: ModuleName = match &loc_name.value {
Platform => unreachable!(),
App(_) => ModuleName::APP.into(), App(_) => ModuleName::APP.into(),
Platform(package_name) => package_name.as_str().into(),
Interface(module_name) | Hosted(module_name) => { Interface(module_name) | Hosted(module_name) => {
// TODO check to see if module_name is consistent with filename. // TODO check to see if module_name is consistent with filename.
// If it isn't, report a problem! // If it isn't, report a problem!
@ -4127,6 +4163,7 @@ fn build_header<'a>(
}; };
let module_id = module_ids.get_or_insert(&pq_module_name); let module_id = module_ids.get_or_insert(&pq_module_name);
imported_modules.insert(module_id, region); imported_modules.insert(module_id, region);
deps_by_name.insert(pq_module_name, module_id); deps_by_name.insert(pq_module_name, module_id);
@ -4151,7 +4188,96 @@ fn build_header<'a>(
} }
} }
let ident_ids = ident_ids_by_module.get_mut(&home).unwrap(); let mut ident_ids = if let HeaderType::Platform {
provides,
requires,
requires_types,
config_shorthand,
platform_main_type,
opt_app_module_id,
} = header_type
{
// If we don't have an app module id (e.g. because we're doing
// `roc check myplatform.roc` or because we're generating glue code),
// insert the `requires` symbols into the platform module's IdentIds.
//
// Otherwise, get them from the app module's IdentIds, because it
// should already have a symbol for each `requires` entry, and we
// want to make sure we're referencing the same symbols!
let module_id = opt_app_module_id.unwrap_or(home);
let ident_ids = ident_ids_by_module.get_or_insert(module_id);
let num_provides = provides.len();
let mut provided: Vec<Symbol> = Vec::with_capacity(num_provides);
let mut symbols_from_requires = Vec::with_capacity(requires.len());
// Add standard imports, if there is an app module.
// (There might not be, e.g. when running `roc check myplatform.roc` or
// when generating bindings.)
if let Some(app_module_id) = opt_app_module_id {
imported_modules.insert(app_module_id, Region::zero());
deps_by_name.insert(
PQModuleName::Unqualified(ModuleName::APP.into()),
app_module_id,
);
}
// If we don't have an app module id (e.g. because we're doing
// `roc check myplatform.roc` or because we're generating glue code),
// insert the `requires` symbols into the platform module's IdentIds.
//
// Otherwise, get them from the app module's IdentIds, because it
// should already have a symbol for each `requires` entry, and we
// want to make sure we're referencing the same symbols!
for entry in requires.iter() {
let entry = entry.value;
let ident: Ident = entry.ident.value.into();
let ident_id = ident_ids.get_or_insert(entry.ident.value);
let symbol = Symbol::new(module_id, ident_id);
// Since this value is exposed, add it to our module's default scope.
debug_assert!(!scope.contains_key(&ident.clone()));
scope.insert(ident, (symbol, entry.ident.region));
symbols_from_requires.push((Loc::at(entry.ident.region, symbol), entry.ann));
}
for entry in requires_types {
let string: &str = entry.value.into();
let ident: Ident = string.into();
let ident_id = ident_ids.get_or_insert(string);
let symbol = Symbol::new(module_id, ident_id);
// Since this value is exposed, add it to our module's default scope.
debug_assert!(!scope.contains_key(&ident));
scope.insert(ident, (symbol, entry.region));
}
// Generate IdentIds entries for all values this module exposes.
// This way, when we encounter them in Defs later, they already
// have an IdentIds entry.
//
// We must *not* add them to scope yet, or else the Defs will
// incorrectly think they're shadowing them!
for loc_provides in provides.iter() {
// Use get_or_insert here because the ident_ids may already
// created an IdentId for this, when it was imported exposed
// in a dependent module.
//
// For example, if module A has [B.{ foo }], then
// when we get here for B, `foo` will already have
// an IdentId. We must reuse that!
let ident_id = ident_ids.get_or_insert(loc_provides.value.as_str());
let symbol = Symbol::new(home, ident_id);
provided.push(symbol);
}
ident_ids.clone()
} else {
let ident_ids = ident_ids_by_module.get_mut(&home).unwrap();
ident_ids.clone()
};
// Generate IdentIds entries for all values this module exposes. // Generate IdentIds entries for all values this module exposes.
// This way, when we encounter them in Defs later, they already // This way, when we encounter them in Defs later, they already
@ -4174,10 +4300,10 @@ fn build_header<'a>(
} }
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
home.register_debug_idents(ident_ids); home.register_debug_idents(&ident_ids);
} }
ident_ids.clone() ident_ids
}; };
let package_entries = packages let package_entries = packages
@ -4211,7 +4337,7 @@ fn build_header<'a>(
// make sure when we run the bulitin modules in /compiler/builtins/roc that we // make sure when we run the bulitin modules in /compiler/builtins/roc that we
// mark these modules as Builtin. Otherwise the builtin functions are not instantiated // mark these modules as Builtin. Otherwise the builtin functions are not instantiated
// and we just have a bunch of definitions with runtime errors in their bodies // and we just have a bunch of definitions with runtime errors in their bodies
let extra = { let header_type = {
match header_type { match header_type {
HeaderType::Interface if home.is_builtin() => HeaderType::Builtin { HeaderType::Interface if home.is_builtin() => HeaderType::Builtin {
generates_with: &[], generates_with: &[],
@ -4237,7 +4363,7 @@ fn build_header<'a>(
parse_state, parse_state,
exposed_imports: scope, exposed_imports: scope,
symbols_from_requires: Vec::new(), symbols_from_requires: Vec::new(),
header_for: extra, header_type,
module_timing, module_timing,
}, },
) )
@ -4756,7 +4882,7 @@ fn unspace<'a, T: Copy>(arena: &'a Bump, items: &[Loc<Spaced<'a, T>>]) -> &'a [L
.into_bump_slice() .into_bump_slice()
} }
fn fabricate_platform_module<'a>( fn build_platform_header<'a>(
arena: &'a Bump, arena: &'a Bump,
opt_shorthand: Option<&'a str>, opt_shorthand: Option<&'a str>,
opt_app_module_id: Option<ModuleId>, opt_app_module_id: Option<ModuleId>,
@ -4766,7 +4892,7 @@ fn fabricate_platform_module<'a>(
ident_ids_by_module: SharedIdentIdsByModule, ident_ids_by_module: SharedIdentIdsByModule,
header: &PlatformHeader<'a>, header: &PlatformHeader<'a>,
module_timing: ModuleTiming, module_timing: ModuleTiming,
) -> (ModuleId, Msg<'a>) { ) -> (ModuleId, PQModuleName<'a>, ModuleHeader<'a>) {
// If we have an app module, then it's the root module; // If we have an app module, then it's the root module;
// otherwise, we must be the root. // otherwise, we must be the root.
let is_root_module = opt_app_module_id.is_none(); let is_root_module = opt_app_module_id.is_none();
@ -4779,237 +4905,37 @@ fn fabricate_platform_module<'a>(
let requires_types = unspace(arena, header.requires.item.rigids.items); let requires_types = unspace(arena, header.requires.item.rigids.items);
let imports = unspace(arena, header.imports.item.items); let imports = unspace(arena, header.imports.item.items);
{ let header_type = HeaderType::Platform {
let declared_name: ModuleName = "".into(); // A config_shorthand of "" should be fine
let mut symbols_from_requires = Vec::with_capacity(requires.len()); config_shorthand: opt_shorthand.unwrap_or_default(),
platform_main_type: requires[0].value,
opt_app_module_id,
provides,
requires,
requires_types,
};
let mut imported: Vec<(QualifiedModuleName, Vec<Loc<Ident>>, Region)> = let info = HeaderInfo {
Vec::with_capacity(imports.len()); loc_name: Loc {
let mut imported_modules: MutMap<ModuleId, Region> = MutMap::default(); region: header.name.region,
value: ModuleNameEnum::Platform(header.name.value),
},
filename,
is_root_module,
opt_shorthand,
packages: &[],
exposes: &[], // These are exposed values. TODO move this into header_type!
imports: unspace(arena, header.imports.item.items),
header_type,
};
let num_exposes = provides.len(); build_header(
let mut deps_by_name: MutMap<PQModuleName, ModuleId> = info,
HashMap::with_capacity_and_hasher(num_exposes, default_hasher()); parse_state,
module_ids,
// Add standard imports, if there is an app module. ident_ids_by_module,
// (There might not be, e.g. when running `roc check myplatform.roc` or module_timing,
// when generating bindings.) )
if let Some(app_module_id) = opt_app_module_id {
imported_modules.insert(app_module_id, Region::zero());
deps_by_name.insert(
PQModuleName::Unqualified(ModuleName::APP.into()),
app_module_id,
);
}
let mut scope_size = 0;
for loc_entry in imports {
let (qualified_module_name, exposed) = exposed_from_import(&loc_entry.value);
scope_size += exposed.len();
imported.push((qualified_module_name, exposed, loc_entry.region));
}
let mut exposed: Vec<Symbol> = Vec::with_capacity(num_exposes);
// Make sure the module_ids has ModuleIds for all our deps,
// then record those ModuleIds in can_module_ids for later.
let mut scope: MutMap<Ident, (Symbol, Region)> =
HashMap::with_capacity_and_hasher(scope_size, default_hasher());
let home: ModuleId;
let mut ident_ids = {
// Lock just long enough to perform the minimal operations necessary.
let mut module_ids = (*module_ids).lock();
let mut ident_ids_by_module = (*ident_ids_by_module).lock();
let name = match opt_shorthand {
Some(shorthand) => PQModuleName::Qualified(shorthand, declared_name),
None => PQModuleName::Unqualified(declared_name),
};
home = module_ids.get_or_insert(&name);
// Ensure this module has an entry in the exposed_ident_ids map.
ident_ids_by_module.get_or_insert(home);
// For each of our imports, add an entry to deps_by_name
//
// e.g. for `imports [pf.Foo.{ bar }]`, add `Foo` to deps_by_name
//
// Also build a list of imported_values_to_expose (like `bar` above.)
for (qualified_module_name, exposed_idents, region) in imported.into_iter() {
let cloned_module_name = qualified_module_name.module.clone();
let pq_module_name = if qualified_module_name.is_builtin() {
// If this is a builtin, it must be unqualified, and we should *never* prefix it
// with the package shorthand! The user intended to import the module as-is here.
debug_assert!(qualified_module_name.opt_package.is_none());
PQModuleName::Unqualified(qualified_module_name.module)
} else {
match qualified_module_name.opt_package {
None => match opt_shorthand {
Some(shorthand) => {
PQModuleName::Qualified(shorthand, qualified_module_name.module)
}
None => PQModuleName::Unqualified(qualified_module_name.module),
},
Some(package) => PQModuleName::Qualified(package, cloned_module_name),
}
};
let module_id = module_ids.get_or_insert(&pq_module_name);
imported_modules.insert(module_id, region);
deps_by_name.insert(pq_module_name, module_id);
// Add the new exposed idents to the dep module's IdentIds, so
// once that module later gets loaded, its lookups will resolve
// to the same symbols as the ones we're using here.
let ident_ids = ident_ids_by_module.get_or_insert(module_id);
for Loc {
region,
value: ident,
} in exposed_idents
{
let ident_id = ident_ids.get_or_insert(ident.as_str());
let symbol = Symbol::new(module_id, ident_id);
// Since this value is exposed, add it to our module's default scope.
debug_assert!(!scope.contains_key(&ident.clone()));
scope.insert(ident, (symbol, region));
}
}
{
// If we don't have an app module id (e.g. because we're doing
// `roc check myplatform.roc` or because we're generating glue code),
// insert the `requires` symbols into the platform module's IdentIds.
//
// Otherwise, get them from the app module's IdentIds, because it
// should already have a symbol for each `requires` entry, and we
// want to make sure we're referencing the same symbols!
let module_id = opt_app_module_id.unwrap_or(home);
let ident_ids = ident_ids_by_module.get_or_insert(module_id);
for entry in requires.iter() {
let entry = entry.value;
let ident: Ident = entry.ident.value.into();
let ident_id = ident_ids.get_or_insert(entry.ident.value);
let symbol = Symbol::new(module_id, ident_id);
// Since this value is exposed, add it to our module's default scope.
debug_assert!(!scope.contains_key(&ident.clone()));
scope.insert(ident, (symbol, entry.ident.region));
symbols_from_requires.push((Loc::at(entry.ident.region, symbol), entry.ann));
}
for entry in requires_types {
let string: &str = entry.value.into();
let ident: Ident = string.into();
let ident_id = ident_ids.get_or_insert(string);
let symbol = Symbol::new(module_id, ident_id);
// Since this value is exposed, add it to our module's default scope.
debug_assert!(!scope.contains_key(&ident));
scope.insert(ident, (symbol, entry.region));
}
}
let ident_ids = ident_ids_by_module.get_mut(&home).unwrap();
// Generate IdentIds entries for all values this module exposes.
// This way, when we encounter them in Defs later, they already
// have an IdentIds entry.
//
// We must *not* add them to scope yet, or else the Defs will
// incorrectly think they're shadowing them!
for loc_exposed in provides.iter() {
// Use get_or_insert here because the ident_ids may already
// created an IdentId for this, when it was imported exposed
// in a dependent module.
//
// For example, if module A has [B.{ foo }], then
// when we get here for B, `foo` will already have
// an IdentId. We must reuse that!
let ident_id = ident_ids.get_or_insert(loc_exposed.value.as_str());
let symbol = Symbol::new(home, ident_id);
exposed.push(symbol);
}
if cfg!(debug_assertions) {
home.register_debug_idents(ident_ids);
}
ident_ids.clone()
};
let package_entries = MutMap::default(); // TODO support packages in platforms!
// Send the deps to the coordinator thread for processing,
// then continue on to parsing and canonicalizing defs.
//
// We always need to send these, even if deps is empty,
// because the coordinator thread needs to receive this message
// to decrement its "pending" count.
let module_name = ModuleNameEnum::Platform;
let main_for_host = {
let ident_id = ident_ids.get_or_insert(provides[0].value.as_str());
Symbol::new(home, ident_id)
};
let extra = HeaderType::Platform {
// A config_shorthand of "" should be fine
config_shorthand: opt_shorthand.unwrap_or_default(),
platform_main_type: requires[0].value,
main_for_host,
opt_app_module_id,
provides,
requires,
requires_types,
};
let mut package_qualified_imported_modules = MutSet::default();
for (pq_module_name, module_id) in &deps_by_name {
match pq_module_name {
PackageQualified::Unqualified(_) => {
package_qualified_imported_modules
.insert(PackageQualified::Unqualified(*module_id));
}
PackageQualified::Qualified(shorthand, _) => {
package_qualified_imported_modules
.insert(PackageQualified::Qualified(shorthand, *module_id));
}
}
}
(
home,
Msg::Header(ModuleHeader {
module_id: home,
module_path: filename,
is_root_module,
exposed_ident_ids: ident_ids,
module_name,
packages: package_entries,
imported_modules,
package_qualified_imported_modules,
deps_by_name,
exposes: exposed,
parse_state: parse_state,
exposed_imports: scope,
module_timing: module_timing,
symbols_from_requires,
header_for: extra,
}),
)
}
} }
#[allow(clippy::unnecessary_wraps)] #[allow(clippy::unnecessary_wraps)]
@ -5080,8 +5006,7 @@ fn canonicalize_and_constrain<'a>(
// Generate documentation information // Generate documentation information
// TODO: store timing information? // TODO: store timing information?
let module_docs = match module_name { let module_docs = match module_name {
ModuleNameEnum::Platform => None, ModuleNameEnum::Platform(_) | ModuleNameEnum::App(_) => None,
ModuleNameEnum::App(_) => None,
ModuleNameEnum::Interface(name) | ModuleNameEnum::Hosted(name) => { ModuleNameEnum::Interface(name) | ModuleNameEnum::Hosted(name) => {
let mut scope = module_output.scope.clone(); let mut scope = module_output.scope.clone();
scope.add_docs_imports(); scope.add_docs_imports();
@ -5222,7 +5147,7 @@ fn parse<'a>(arena: &'a Bump, header: ModuleHeader<'a>) -> Result<Msg<'a>, Loadi
exposed_ident_ids, exposed_ident_ids,
exposed_imports, exposed_imports,
module_path, module_path,
header_for, header_type: header_for,
symbols_from_requires, symbols_from_requires,
.. ..
} = header; } = header;

View file

@ -75,6 +75,7 @@ impl ModuleName {
// NOTE: After adding one of these, go to `impl ModuleId` and // NOTE: After adding one of these, go to `impl ModuleId` and
// add a corresponding ModuleId to there! // add a corresponding ModuleId to there!
pub const APP: &'static str = "#UserApp"; // app modules have this hardcoded name pub const APP: &'static str = "#UserApp"; // app modules have this hardcoded name
pub const PLATFORM: &'static str = "#Platform"; // platform modules have this hardcoded name
pub const BOOL: &'static str = "Bool"; pub const BOOL: &'static str = "Bool";
pub const STR: &'static str = "Str"; pub const STR: &'static str = "Str";
pub const NUM: &'static str = "Num"; pub const NUM: &'static str = "Num";

View file

@ -118,12 +118,6 @@ pub enum OptLevel {
Optimize, Optimize,
} }
#[derive(Debug, Clone, Copy)]
pub struct EntryPoint<'a> {
pub symbol: Symbol,
pub layout: ProcLayout<'a>,
}
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct PartialProcId(usize); pub struct PartialProcId(usize);

View file

@ -34,8 +34,6 @@ pub enum HeaderType<'a> {
/// (currently unused) /// (currently unused)
#[allow(dead_code)] #[allow(dead_code)]
platform_main_type: TypedIdent<'a>, platform_main_type: TypedIdent<'a>,
/// provided symbol to host (commonly `mainForHost`)
main_for_host: Symbol,
}, },
Interface, Interface,
} }
@ -107,7 +105,7 @@ pub enum ModuleNameEnum<'a> {
App(StrLiteral<'a>), App(StrLiteral<'a>),
Interface(ModuleName<'a>), Interface(ModuleName<'a>),
Hosted(ModuleName<'a>), Hosted(ModuleName<'a>),
Platform, Platform(PackageName<'a>),
} }
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]

View file

@ -238,10 +238,10 @@ fn create_llvm_module<'a>(
// platform to provide them. // platform to provide them.
add_default_roc_externs(&env); add_default_roc_externs(&env);
let entry_point = match entry_point { let entry_points = match entry_point {
EntryPoint::Executable { symbol, layout, .. } => { EntryPoint::Executable {
roc_mono::ir::EntryPoint { symbol, layout } exposed_to_host, ..
} } => exposed_to_host,
EntryPoint::Test => { EntryPoint::Test => {
unreachable!() unreachable!()
} }
@ -254,13 +254,13 @@ fn create_llvm_module<'a>(
&env, &env,
config.opt_level, config.opt_level,
procedures, procedures,
entry_point, entry_points,
), ),
LlvmBackendMode::GenTest => roc_gen_llvm::llvm::build::build_procedures_return_main( LlvmBackendMode::GenTest => roc_gen_llvm::llvm::build::build_procedures_return_main(
&env, &env,
config.opt_level, config.opt_level,
procedures, procedures,
entry_point, entry_points,
), ),
}; };

View file

@ -233,10 +233,10 @@ fn mono_module_to_dylib<'a>(
// platform to provide them. // platform to provide them.
add_default_roc_externs(&env); add_default_roc_externs(&env);
let entry_point = match entry_point { let entry_points = match entry_point {
EntryPoint::Executable { symbol, layout, .. } => { EntryPoint::Executable {
roc_mono::ir::EntryPoint { symbol, layout } exposed_to_host, ..
} } => exposed_to_host,
EntryPoint::Test => { EntryPoint::Test => {
unreachable!() unreachable!()
} }
@ -246,7 +246,7 @@ fn mono_module_to_dylib<'a>(
&env, &env,
opt_level, opt_level,
procedures, procedures,
entry_point, entry_points,
); );
env.dibuilder.finalize(); env.dibuilder.finalize();

View file

@ -708,11 +708,11 @@ pub fn expect_mono_module_to_dylib<'a>(
// platform to provide them. // platform to provide them.
add_default_roc_externs(&env); add_default_roc_externs(&env);
let opt_entry_point = match entry_point { let entry_points = match entry_point {
EntryPoint::Executable { symbol, layout, .. } => { EntryPoint::Executable {
Some(roc_mono::ir::EntryPoint { symbol, layout }) exposed_to_host, ..
} } => exposed_to_host,
EntryPoint::Test => None, EntryPoint::Test => &[],
}; };
let capacity = toplevel_expects.pure.len() + toplevel_expects.fx.len(); let capacity = toplevel_expects.pure.len() + toplevel_expects.fx.len();
@ -726,7 +726,7 @@ pub fn expect_mono_module_to_dylib<'a>(
opt_level, opt_level,
&expect_symbols, &expect_symbols,
procedures, procedures,
opt_entry_point, entry_points,
); );
let expects_fx = bumpalo::collections::Vec::from_iter_in( let expects_fx = bumpalo::collections::Vec::from_iter_in(