mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 15:51:12 +00:00
Merge pull request #5284 from roc-lang/i5131
Handle record updates of imported module thunks
This commit is contained in:
commit
526cd25611
9 changed files with 359 additions and 83 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -5084,8 +5084,12 @@ dependencies = [
|
|||
"pretty_assertions",
|
||||
"regex",
|
||||
"roc_builtins",
|
||||
"roc_collections",
|
||||
"roc_derive",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
"roc_packaging",
|
||||
"roc_parse",
|
||||
"roc_problem",
|
||||
"roc_reporting",
|
||||
|
|
|
@ -5049,9 +5049,12 @@ pub fn with_hole<'a>(
|
|||
);
|
||||
}
|
||||
CopyExisting(index) => {
|
||||
let record_needs_specialization =
|
||||
procs.ability_member_aliases.get(structure).is_some();
|
||||
let specialized_structure_sym = if record_needs_specialization {
|
||||
let structure_needs_specialization =
|
||||
procs.ability_member_aliases.get(structure).is_some()
|
||||
|| procs.is_module_thunk(structure)
|
||||
|| procs.is_imported_module_thunk(structure);
|
||||
|
||||
let specialized_structure_sym = if structure_needs_specialization {
|
||||
// We need to specialize the record now; create a new one for it.
|
||||
// TODO: reuse this symbol for all updates
|
||||
env.unique_symbol()
|
||||
|
@ -5068,10 +5071,7 @@ pub fn with_hole<'a>(
|
|||
stmt =
|
||||
Stmt::Let(*symbol, access_expr, *field_layout, arena.alloc(stmt));
|
||||
|
||||
// If the records needs specialization or it's a thunk, we need to
|
||||
// create the specialized definition or force the thunk, respectively.
|
||||
// Both cases are handled below.
|
||||
if record_needs_specialization || procs.is_module_thunk(structure) {
|
||||
if structure_needs_specialization {
|
||||
stmt = specialize_symbol(
|
||||
env,
|
||||
procs,
|
||||
|
|
|
@ -31,7 +31,7 @@ mod solve_expr {
|
|||
..
|
||||
},
|
||||
src,
|
||||
) = run_load_and_infer(src, false)?;
|
||||
) = run_load_and_infer(src, [], false)?;
|
||||
|
||||
let mut can_problems = can_problems.remove(&home).unwrap_or_default();
|
||||
let type_problems = type_problems.remove(&home).unwrap_or_default();
|
||||
|
@ -103,7 +103,7 @@ mod solve_expr {
|
|||
interns,
|
||||
abilities_store,
|
||||
..
|
||||
} = run_load_and_infer(src, false).unwrap().0;
|
||||
} = run_load_and_infer(src, [], false).unwrap().0;
|
||||
|
||||
let can_problems = can_problems.remove(&home).unwrap_or_default();
|
||||
let type_problems = type_problems.remove(&home).unwrap_or_default();
|
||||
|
|
|
@ -44,8 +44,9 @@ fn promote_expr_to_module(src: &str) -> String {
|
|||
buffer
|
||||
}
|
||||
|
||||
pub fn run_load_and_infer(
|
||||
pub fn run_load_and_infer<'a>(
|
||||
src: &str,
|
||||
dependencies: impl IntoIterator<Item = (&'a str, &'a str)>,
|
||||
no_promote: bool,
|
||||
) -> Result<(LoadedModule, String), std::io::Error> {
|
||||
use tempfile::tempdir;
|
||||
|
@ -65,6 +66,11 @@ pub fn run_load_and_infer(
|
|||
|
||||
let loaded = {
|
||||
let dir = tempdir()?;
|
||||
|
||||
for (file, source) in dependencies {
|
||||
std::fs::write(dir.path().join(format!("{file}.roc")), source)?;
|
||||
}
|
||||
|
||||
let filename = PathBuf::from("Test.roc");
|
||||
let file_path = dir.path().join(filename);
|
||||
let result = roc_load::load_and_typecheck_str(
|
||||
|
@ -338,7 +344,11 @@ impl InferredProgram {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn infer_queries(src: &str, options: InferOptions) -> Result<InferredProgram, Box<dyn Error>> {
|
||||
pub fn infer_queries<'a>(
|
||||
src: &str,
|
||||
dependencies: impl IntoIterator<Item = (&'a str, &'a str)>,
|
||||
options: InferOptions,
|
||||
) -> Result<InferredProgram, Box<dyn Error>> {
|
||||
let (
|
||||
LoadedModule {
|
||||
module_id: home,
|
||||
|
@ -351,7 +361,7 @@ pub fn infer_queries(src: &str, options: InferOptions) -> Result<InferredProgram
|
|||
..
|
||||
},
|
||||
src,
|
||||
) = run_load_and_infer(src, options.no_promote)?;
|
||||
) = run_load_and_infer(src, dependencies, options.no_promote)?;
|
||||
|
||||
let declarations = declarations_by_id.remove(&home).unwrap();
|
||||
let subs = solved.inner_mut();
|
||||
|
@ -373,9 +383,6 @@ pub fn infer_queries(src: &str, options: InferOptions) -> Result<InferredProgram
|
|||
|
||||
let line_info = LineInfo::new(&src);
|
||||
let queries = parse_queries(&src, &line_info);
|
||||
if queries.is_empty() {
|
||||
return Err("No queries provided!".into());
|
||||
}
|
||||
|
||||
let mut inferred_queries = Vec::with_capacity(queries.len());
|
||||
let exposed_by_module = ExposedByModule::default();
|
||||
|
@ -561,40 +568,3 @@ impl<'a> QueryCtx<'a> {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn infer_queries_help(src: &str, expected: impl FnOnce(&str), options: InferOptions) {
|
||||
let InferredProgram {
|
||||
program,
|
||||
inferred_queries,
|
||||
} = infer_queries(src, options).unwrap();
|
||||
|
||||
let mut output_parts = Vec::with_capacity(inferred_queries.len() + 2);
|
||||
|
||||
if options.print_can_decls {
|
||||
use roc_can::debug::{pretty_print_declarations, PPCtx};
|
||||
let ctx = PPCtx {
|
||||
home: program.home,
|
||||
interns: &program.interns,
|
||||
print_lambda_names: true,
|
||||
};
|
||||
let pretty_decls = pretty_print_declarations(&ctx, &program.declarations);
|
||||
output_parts.push(pretty_decls);
|
||||
output_parts.push("\n".to_owned());
|
||||
}
|
||||
|
||||
for InferredQuery { elaboration, .. } in inferred_queries {
|
||||
let output_part = match elaboration {
|
||||
Elaboration::Specialization {
|
||||
specialized_name,
|
||||
typ,
|
||||
} => format!("{specialized_name} : {typ}"),
|
||||
Elaboration::Source { source, typ } => format!("{source} : {typ}"),
|
||||
Elaboration::Instantiation { .. } => panic!("Use uitest instead"),
|
||||
};
|
||||
output_parts.push(output_part);
|
||||
}
|
||||
|
||||
let pretty_output = output_parts.join("\n");
|
||||
|
||||
expected(&pretty_output);
|
||||
}
|
||||
|
|
|
@ -14,8 +14,12 @@ harness = false
|
|||
|
||||
[dev-dependencies]
|
||||
roc_builtins = { path = "../builtins" }
|
||||
roc_collections = { path = "../collections" }
|
||||
roc_derive = { path = "../derive", features = ["debug-derived-symbols"] }
|
||||
roc_load = { path = "../load" }
|
||||
roc_packaging = { path = "../../packaging" }
|
||||
roc_module = { path = "../module", features = ["debug-symbols"] }
|
||||
roc_mono = { path = "../mono" }
|
||||
roc_parse = { path = "../parse" }
|
||||
roc_problem = { path = "../problem" }
|
||||
roc_reporting = { path = "../../reporting" }
|
||||
|
|
135
crates/compiler/uitest/src/mono.rs
Normal file
135
crates/compiler/uitest/src/mono.rs
Normal file
|
@ -0,0 +1,135 @@
|
|||
use std::io;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use roc_collections::MutMap;
|
||||
use roc_load::{ExecutionMode, LoadConfig, LoadMonomorphizedError, Threading};
|
||||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_mono::{
|
||||
ir::{Proc, ProcLayout},
|
||||
layout::STLayoutInterner,
|
||||
};
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MonoOptions {
|
||||
pub no_check: bool,
|
||||
}
|
||||
|
||||
pub fn write_compiled_ir<'a>(
|
||||
writer: &mut impl io::Write,
|
||||
test_module: &str,
|
||||
dependencies: impl IntoIterator<Item = (&'a str, &'a str)>,
|
||||
options: MonoOptions,
|
||||
) -> io::Result<()> {
|
||||
use roc_packaging::cache::RocCacheDir;
|
||||
use std::path::PathBuf;
|
||||
|
||||
let exec_mode = ExecutionMode::Executable;
|
||||
|
||||
let arena = &Bump::new();
|
||||
|
||||
let dir = tempdir()?;
|
||||
|
||||
for (file, source) in dependencies {
|
||||
std::fs::write(dir.path().join(format!("{file}.roc")), source)?;
|
||||
}
|
||||
|
||||
let filename = PathBuf::from("Test.roc");
|
||||
let file_path = dir.path().join(filename);
|
||||
|
||||
let load_config = LoadConfig {
|
||||
target_info: roc_target::TargetInfo::default_x86_64(),
|
||||
threading: Threading::Single,
|
||||
render: roc_reporting::report::RenderTarget::Generic,
|
||||
palette: roc_reporting::report::DEFAULT_PALETTE,
|
||||
exec_mode,
|
||||
};
|
||||
let loaded = roc_load::load_and_monomorphize_from_str(
|
||||
arena,
|
||||
file_path,
|
||||
test_module,
|
||||
dir.path().to_path_buf(),
|
||||
RocCacheDir::Disallowed,
|
||||
load_config,
|
||||
);
|
||||
|
||||
let loaded = match loaded {
|
||||
Ok(x) => x,
|
||||
Err(LoadMonomorphizedError::LoadingProblem(roc_load::LoadingProblem::FormattedReport(
|
||||
report,
|
||||
))) => {
|
||||
println!("{}", report);
|
||||
panic!();
|
||||
}
|
||||
Err(e) => panic!("{:?}", e),
|
||||
};
|
||||
|
||||
use roc_load::MonomorphizedModule;
|
||||
let MonomorphizedModule {
|
||||
procedures,
|
||||
exposed_to_host,
|
||||
mut layout_interner,
|
||||
interns,
|
||||
..
|
||||
} = loaded;
|
||||
|
||||
let main_fn_symbol = exposed_to_host.top_level_values.keys().copied().next();
|
||||
|
||||
if !options.no_check {
|
||||
check_procedures(arena, &interns, &mut layout_interner, &procedures);
|
||||
}
|
||||
|
||||
write_procedures(writer, layout_interner, procedures, main_fn_symbol)
|
||||
}
|
||||
|
||||
fn check_procedures<'a>(
|
||||
arena: &'a Bump,
|
||||
interns: &Interns,
|
||||
interner: &mut STLayoutInterner<'a>,
|
||||
procedures: &MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||
) {
|
||||
use roc_mono::debug::{check_procs, format_problems};
|
||||
let problems = check_procs(arena, interner, procedures);
|
||||
if problems.is_empty() {
|
||||
return;
|
||||
}
|
||||
let formatted = format_problems(interns, interner, problems);
|
||||
panic!("IR problems found:\n{formatted}");
|
||||
}
|
||||
|
||||
fn write_procedures<'a>(
|
||||
writer: &mut impl io::Write,
|
||||
interner: STLayoutInterner<'a>,
|
||||
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||
opt_main_fn_symbol: Option<Symbol>,
|
||||
) -> io::Result<()> {
|
||||
let mut procs_strings = procedures
|
||||
.values()
|
||||
.map(|proc| proc.to_pretty(&interner, 200, false))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let opt_main_fn = opt_main_fn_symbol.map(|main_fn_symbol| {
|
||||
let index = procedures
|
||||
.keys()
|
||||
.position(|(s, _)| *s == main_fn_symbol)
|
||||
.unwrap();
|
||||
procs_strings.swap_remove(index)
|
||||
});
|
||||
|
||||
procs_strings.sort();
|
||||
|
||||
if let Some(main_fn) = opt_main_fn {
|
||||
procs_strings.push(main_fn);
|
||||
}
|
||||
|
||||
let mut procs = procs_strings.iter().peekable();
|
||||
while let Some(proc) = procs.next() {
|
||||
if procs.peek().is_some() {
|
||||
writeln!(writer, "{}", proc)?;
|
||||
} else {
|
||||
write!(writer, "{}", proc)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -8,11 +8,15 @@ use std::{
|
|||
|
||||
use lazy_static::lazy_static;
|
||||
use libtest_mimic::{run, Arguments, Failed, Trial};
|
||||
use mono::MonoOptions;
|
||||
use regex::Regex;
|
||||
use roc_collections::VecMap;
|
||||
use test_solve_helpers::{
|
||||
infer_queries, Elaboration, InferOptions, InferredProgram, InferredQuery, MUTLILINE_MARKER,
|
||||
};
|
||||
|
||||
mod mono;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let args = Arguments::from_args();
|
||||
|
||||
|
@ -36,9 +40,17 @@ lazy_static! {
|
|||
static ref RE_OPT_INFER: Regex =
|
||||
Regex::new(r#"# \+opt infer:(?P<opt>.*)"#).unwrap();
|
||||
|
||||
/// # +opt print:<opt>
|
||||
static ref RE_OPT_PRINT: Regex =
|
||||
Regex::new(r#"# \+opt print:(?P<opt>.*)"#).unwrap();
|
||||
/// # +opt mono:<opt>
|
||||
static ref RE_OPT_MONO: Regex =
|
||||
Regex::new(r#"# \+opt mono:(?P<opt>.*)"#).unwrap();
|
||||
|
||||
/// # +emit:<opt>
|
||||
static ref RE_EMIT: Regex =
|
||||
Regex::new(r#"# \+emit:(?P<opt>.*)"#).unwrap();
|
||||
|
||||
/// ## module <name>
|
||||
static ref RE_MODULE: Regex =
|
||||
Regex::new(r#"## module (?P<name>.*)"#).unwrap();
|
||||
}
|
||||
|
||||
fn collect_uitest_files() -> io::Result<Vec<PathBuf>> {
|
||||
|
@ -79,11 +91,19 @@ fn run_test(path: PathBuf) -> Result<(), Failed> {
|
|||
let data = std::fs::read_to_string(&path)?;
|
||||
let TestCase {
|
||||
infer_options,
|
||||
print_options,
|
||||
source,
|
||||
} = TestCase::parse(data)?;
|
||||
emit_options,
|
||||
mono_options,
|
||||
program,
|
||||
} = TestCase::parse(&data)?;
|
||||
|
||||
let inferred_program = infer_queries(&source, infer_options)?;
|
||||
let inferred_program = infer_queries(
|
||||
program.test_module,
|
||||
program
|
||||
.other_modules
|
||||
.iter()
|
||||
.map(|(md, src)| (&**md, &**src)),
|
||||
infer_options,
|
||||
)?;
|
||||
|
||||
{
|
||||
let mut fd = fs::OpenOptions::new()
|
||||
|
@ -91,7 +111,13 @@ fn run_test(path: PathBuf) -> Result<(), Failed> {
|
|||
.truncate(true)
|
||||
.open(&path)?;
|
||||
|
||||
assemble_query_output(&mut fd, &source, inferred_program, print_options)?;
|
||||
assemble_query_output(
|
||||
&mut fd,
|
||||
program,
|
||||
inferred_program,
|
||||
mono_options,
|
||||
emit_options,
|
||||
)?;
|
||||
}
|
||||
|
||||
check_for_changes(&path)?;
|
||||
|
@ -101,32 +127,93 @@ fn run_test(path: PathBuf) -> Result<(), Failed> {
|
|||
|
||||
const EMIT_HEADER: &str = "# -emit:";
|
||||
|
||||
struct TestCase {
|
||||
struct Modules<'a> {
|
||||
before_any: &'a str,
|
||||
other_modules: VecMap<&'a str, &'a str>,
|
||||
test_module: &'a str,
|
||||
}
|
||||
|
||||
struct TestCase<'a> {
|
||||
infer_options: InferOptions,
|
||||
print_options: PrintOptions,
|
||||
source: String,
|
||||
mono_options: MonoOptions,
|
||||
emit_options: EmitOptions,
|
||||
program: Modules<'a>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct PrintOptions {
|
||||
struct EmitOptions {
|
||||
can_decls: bool,
|
||||
mono: bool,
|
||||
}
|
||||
|
||||
impl TestCase {
|
||||
fn parse(mut data: String) -> Result<Self, Failed> {
|
||||
impl<'a> TestCase<'a> {
|
||||
fn parse(mut data: &'a str) -> Result<Self, Failed> {
|
||||
// Drop anything following `# -emit:` header lines; that's the output.
|
||||
if let Some(drop_at) = data.find(EMIT_HEADER) {
|
||||
data.truncate(drop_at);
|
||||
data.truncate(data.trim_end().len());
|
||||
data = data[..drop_at].trim_end();
|
||||
}
|
||||
|
||||
let infer_options = Self::parse_infer_options(data)?;
|
||||
let mono_options = Self::parse_mono_options(data)?;
|
||||
let emit_options = Self::parse_emit_options(data)?;
|
||||
|
||||
let program = Self::parse_modules(data);
|
||||
|
||||
Ok(TestCase {
|
||||
infer_options: Self::parse_infer_options(&data)?,
|
||||
print_options: Self::parse_print_options(&data)?,
|
||||
source: data,
|
||||
infer_options,
|
||||
mono_options,
|
||||
emit_options,
|
||||
program,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_modules(data: &'a str) -> Modules<'a> {
|
||||
let mut module_starts = RE_MODULE.captures_iter(data).peekable();
|
||||
|
||||
let first_module_start = match module_starts.peek() {
|
||||
None => {
|
||||
// This is just a single module with no name; it is the test module.
|
||||
return Modules {
|
||||
before_any: Default::default(),
|
||||
other_modules: Default::default(),
|
||||
test_module: data,
|
||||
};
|
||||
}
|
||||
Some(p) => p.get(0).unwrap().start(),
|
||||
};
|
||||
|
||||
let before_any = data[..first_module_start].trim();
|
||||
|
||||
let mut test_module = None;
|
||||
let mut other_modules = VecMap::new();
|
||||
|
||||
while let Some(module_start) = module_starts.next() {
|
||||
let module_name = module_start.name("name").unwrap().as_str();
|
||||
let module_start = module_start.get(0).unwrap().end();
|
||||
let module = match module_starts.peek() {
|
||||
None => &data[module_start..],
|
||||
Some(next_module_start) => {
|
||||
let module_end = next_module_start.get(0).unwrap().start();
|
||||
&data[module_start..module_end]
|
||||
}
|
||||
}
|
||||
.trim();
|
||||
|
||||
if module_name == "Test" {
|
||||
test_module = Some(module);
|
||||
} else {
|
||||
other_modules.insert(module_name, module);
|
||||
}
|
||||
}
|
||||
|
||||
let test_module = test_module.expect("no Test module found");
|
||||
Modules {
|
||||
before_any,
|
||||
other_modules,
|
||||
test_module,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_infer_options(data: &str) -> Result<InferOptions, Failed> {
|
||||
let mut infer_opts = InferOptions {
|
||||
no_promote: true,
|
||||
|
@ -146,19 +233,35 @@ impl TestCase {
|
|||
Ok(infer_opts)
|
||||
}
|
||||
|
||||
fn parse_print_options(data: &str) -> Result<PrintOptions, Failed> {
|
||||
let mut print_opts = PrintOptions::default();
|
||||
fn parse_mono_options(data: &str) -> Result<MonoOptions, Failed> {
|
||||
let mut mono_opts = MonoOptions::default();
|
||||
|
||||
let found_infer_opts = RE_OPT_PRINT.captures_iter(data);
|
||||
let found_infer_opts = RE_OPT_MONO.captures_iter(data);
|
||||
for infer_opt in found_infer_opts {
|
||||
let opt = infer_opt.name("opt").unwrap().as_str();
|
||||
match opt.trim() {
|
||||
"can_decls" => print_opts.can_decls = true,
|
||||
other => return Err(format!("unknown print option: {other:?}").into()),
|
||||
"no_check" => mono_opts.no_check = true,
|
||||
other => return Err(format!("unknown mono option: {other:?}").into()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(print_opts)
|
||||
Ok(mono_opts)
|
||||
}
|
||||
|
||||
fn parse_emit_options(data: &str) -> Result<EmitOptions, Failed> {
|
||||
let mut emit_opts = EmitOptions::default();
|
||||
|
||||
let found_infer_opts = RE_EMIT.captures_iter(data);
|
||||
for infer_opt in found_infer_opts {
|
||||
let opt = infer_opt.name("opt").unwrap().as_str();
|
||||
match opt.trim() {
|
||||
"can_decls" => emit_opts.can_decls = true,
|
||||
"mono" => emit_opts.mono = true,
|
||||
other => return Err(format!("unknown emit option: {other:?}").into()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(emit_opts)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,25 +287,53 @@ fn check_for_changes(path: &Path) -> Result<(), Failed> {
|
|||
/// Assemble the output for a test, with queries elaborated in-line.
|
||||
fn assemble_query_output(
|
||||
writer: &mut impl io::Write,
|
||||
source: &str,
|
||||
program: Modules<'_>,
|
||||
inferred_program: InferredProgram,
|
||||
print_options: PrintOptions,
|
||||
mono_options: MonoOptions,
|
||||
emit_options: EmitOptions,
|
||||
) -> io::Result<()> {
|
||||
let Modules {
|
||||
before_any,
|
||||
other_modules,
|
||||
test_module,
|
||||
} = program;
|
||||
|
||||
if !before_any.is_empty() {
|
||||
writeln!(writer, "{before_any}\n")?;
|
||||
}
|
||||
|
||||
for (module, source) in other_modules.iter() {
|
||||
writeln!(writer, "## module {module}")?;
|
||||
writeln!(writer, "{}\n", source)?;
|
||||
}
|
||||
|
||||
if !other_modules.is_empty() {
|
||||
writeln!(writer, "## module Test")?;
|
||||
}
|
||||
|
||||
// Reverse the queries so that we can pop them off the end as we pass through the lines.
|
||||
let (queries, program) = inferred_program.decompose();
|
||||
let mut sorted_queries = queries.into_sorted();
|
||||
sorted_queries.reverse();
|
||||
|
||||
let mut reflow = Reflow::new_unindented(writer);
|
||||
write_source_with_answers(&mut reflow, source, sorted_queries, 0)?;
|
||||
write_source_with_answers(&mut reflow, test_module, sorted_queries, 0)?;
|
||||
|
||||
// Finish up with any remaining print options we were asked to provide.
|
||||
let PrintOptions { can_decls } = print_options;
|
||||
// Finish up with any remaining emit options we were asked to provide.
|
||||
let EmitOptions { can_decls, mono } = emit_options;
|
||||
if can_decls {
|
||||
writeln!(writer, "\n{EMIT_HEADER}can_decls")?;
|
||||
program.write_can_decls(writer)?;
|
||||
}
|
||||
|
||||
if mono {
|
||||
writeln!(writer, "\n{EMIT_HEADER}mono")?;
|
||||
// Unfortunately, with the current setup we must now recompile into the IR.
|
||||
// TODO: extend the data returned by a monomorphized module to include
|
||||
// that of a solved module.
|
||||
mono::write_compiled_ir(writer, test_module, other_modules, mono_options)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# +opt print:can_decls
|
||||
# +opt infer:print_only_under_alias
|
||||
# +emit:can_decls
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
Parser a : {} -> a
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
# +emit:mono
|
||||
# +opt mono:no_check
|
||||
|
||||
## module Dep
|
||||
interface Dep exposes [defaultRequest] imports []
|
||||
|
||||
defaultRequest = {
|
||||
url: "",
|
||||
body: "",
|
||||
}
|
||||
|
||||
## module Test
|
||||
app "test" imports [Dep.{ defaultRequest }] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
{ defaultRequest & url: "http://www.example.com" }
|
||||
|
||||
# -emit:mono
|
||||
procedure Dep.0 ():
|
||||
let Dep.2 : Str = "";
|
||||
let Dep.3 : Str = "";
|
||||
let Dep.1 : {Str, Str} = Struct {Dep.2, Dep.3};
|
||||
ret Dep.1;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.3 : Str = "http://www.example.com";
|
||||
let Test.4 : {Str, Str} = CallByName Dep.0;
|
||||
let Test.2 : Str = StructAtIndex 0 Test.4;
|
||||
inc Test.2;
|
||||
dec Test.4;
|
||||
let Test.1 : {Str, Str} = Struct {Test.2, Test.3};
|
||||
ret Test.1;
|
Loading…
Add table
Add a link
Reference in a new issue