A couple hacks to get deriving of records working.. but it's working?

This commit is contained in:
Ayaz Hafiz 2022-07-08 11:24:06 -04:00
parent 801803d813
commit 63adb901b4
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
8 changed files with 156 additions and 104 deletions

View file

@ -102,38 +102,70 @@ impl DerivedModule {
debug_assert!(!self.stolen, "attempting to add to stolen symbols!"); debug_assert!(!self.stolen, "attempting to add to stolen symbols!");
} }
// TODO: can we get rid of the clone? match self.map.get(&key) {
let entry = self.map.entry(key.clone()); Some(entry) => {
// Might happen that during rollback in specializing the Derived module, we wink
// away the variables associated with a derived impl. In this case, just rebuild
// the impl.
dbg!(entry.0);
if entry.2.iter().all(|(_, var)| self.subs.contains(*var)) {
// rustc won't let us return an immutable reference *and* continue using
// `self.map` immutably below, but this is safe, because we are not returning
// an immutable reference to the entry.
return unsafe { std::mem::transmute(entry) };
}
dbg!(entry.0);
}
None => {}
}
entry.or_insert_with(|| { let ident_id = if cfg!(debug_assertions) || cfg!(feature = "debug-derived-symbols") {
let ident_id = if cfg!(debug_assertions) || cfg!(feature = "debug-derived-symbols") { let debug_name = key.debug_name();
let debug_name = key.debug_name(); // debug_assert!(
debug_assert!( // self.derived_ident_ids.get_id(&debug_name).is_none(),
self.derived_ident_ids.get_id(&debug_name).is_none(), // "duplicate debug name for different derive key"
"duplicate debug name for different derive key" // );
); let ident_id = self.derived_ident_ids.get_or_insert(&debug_name);
let ident_id = self.derived_ident_ids.get_or_insert(&debug_name);
// This is expensive, but yields much better symbols when debugging. // This is expensive, but yields much better symbols when debugging.
// TODO: hide behind debug_flags? // TODO: hide behind debug_flags?
DERIVED_MODULE.register_debug_idents(&self.derived_ident_ids); DERIVED_MODULE.register_debug_idents(&self.derived_ident_ids);
ident_id ident_id
} else { } else {
self.derived_ident_ids.gen_unique() // TODO this is WRONG when we're re-instantiating the derived impl
}; self.derived_ident_ids.gen_unique()
};
let derived_symbol = Symbol::new(DERIVED_MODULE, ident_id); let derived_symbol = Symbol::new(DERIVED_MODULE, ident_id);
let (derived_def, specialization_lsets) = build_derived_body( let (derived_def, specialization_lsets) = build_derived_body(
&mut self.subs, &mut self.subs,
&mut self.derived_ident_ids, &mut self.derived_ident_ids,
exposed_by_module, exposed_by_module,
derived_symbol, derived_symbol,
key.clone(), key.clone(),
); );
(derived_symbol, derived_def, specialization_lsets) let triple = (derived_symbol, derived_def, specialization_lsets);
}) self.map.remove(&key);
self.map.entry(key).or_insert(triple)
}
/// TERRIBLE HACK to deal with rollbacks: in specializing the Derived module, we snapshot and
/// rollback Subs every time we specialize a particular procedure. However in specializing a
/// procedure we may have added a new derived impl to the [`DerivedModule`] whose types are
/// then rolled back! Hence, we need to re-initialize the derived impl in such cases.
///
/// But there must be a better way!!
pub fn refresh_stale_specializations(&mut self, exposed_by_module: &ExposedByModule) {
let all_derived: Vec<_> = self.map.keys().cloned().collect();
//dbg!(&all_derived);
all_derived
.into_iter()
// get_or_insert will handle re-initializing stale defs
.for_each(|derive_key| {
let _ = self.get_or_insert(exposed_by_module, derive_key);
});
} }
pub fn iter_all( pub fn iter_all(
@ -190,9 +222,11 @@ impl DerivedModule {
self.derived_ident_ids = ident_ids; self.derived_ident_ids = ident_ids;
} }
// TODO: just pass and copy the ambient function directly, don't pass the lambda set var.
pub fn copy_lambda_set_ambient_function_to_subs( pub fn copy_lambda_set_ambient_function_to_subs(
&self, &self,
lambda_set_var: Variable, lambda_set_var: Variable,
target_subs_home: ModuleId,
target: &mut Subs, target: &mut Subs,
target_rank: Rank, target_rank: Rank,
) -> Variable { ) -> Variable {
@ -203,23 +237,27 @@ impl DerivedModule {
let ambient_function_var = self.subs.get_lambda_set(lambda_set_var).ambient_function; let ambient_function_var = self.subs.get_lambda_set(lambda_set_var).ambient_function;
let copied_import = copy_import_to( if target_subs_home == ModuleId::DERIVED {
&self.subs, ambient_function_var
target, } else {
// bookkeep unspecialized lambda sets of var - I think we want this here let copied_import = copy_import_to(
true, &self.subs,
ambient_function_var, target,
// TODO: I think this is okay because the only use of `copy_lambda_set_var_to_subs` // bookkeep unspecialized lambda sets of var - I think we want this here
// (at least right now) is for lambda set compaction, which will automatically unify true,
// and lower ranks, and never generalize. ambient_function_var,
// // TODO: I think this is okay because the only use of `copy_lambda_set_var_to_subs`
// However this is a bad coupling and maybe not a good assumption, we should revisit // (at least right now) is for lambda set compaction, which will automatically unify
// this when possible. // and lower ranks, and never generalize.
Rank::import(), //
// target_rank, // However this is a bad coupling and maybe not a good assumption, we should revisit
); // this when possible.
Rank::import(),
// target_rank,
);
copied_import.variable copied_import.variable
}
} }
} }

View file

@ -5021,7 +5021,26 @@ fn load_derived_partial_procs<'a>(
let mut update_mode_ids = UpdateModeIds::new(); let mut update_mode_ids = UpdateModeIds::new();
let derives_to_add: Vec<_> = { let derives_to_add: Vec<_> = {
let derived_module = derived_module.lock().unwrap(); let mut derived_module = derived_module.lock().unwrap();
// TERRIBLE HACK remove me
// TODO HACK FIXME
dbg!(derived_module
.iter_all()
.map(|(_, (s, _, _))| *s)
.collect::<Vec<_>>());
//derived_module.refresh_stale_specializations(exposed_by_module);
derived_module.get_or_insert(
exposed_by_module,
roc_derive_key::DeriveKey::ToEncoder(
roc_derive_key::encoding::FlatEncodableKey::String,
),
);
dbg!(derived_module
.iter_all()
.map(|(_, (s, _, _))| *s)
.collect::<Vec<_>>());
derived_module derived_module
.iter_all() .iter_all()

View file

@ -162,7 +162,6 @@ impl<'a> DeclarationToIndex<'a> {
} }
} }
} }
dbg!(&self.elements);
unreachable!( unreachable!(
"symbol/layout {:?} {:#?} combo must be in DeclarationToIndex", "symbol/layout {:?} {:#?} combo must be in DeclarationToIndex",
needle_symbol, needle_layout needle_symbol, needle_layout

View file

@ -2932,16 +2932,7 @@ fn specialize_external<'a>(
let snapshot = env.subs.snapshot(); let snapshot = env.subs.snapshot();
let cache_snapshot = layout_cache.snapshot(); let cache_snapshot = layout_cache.snapshot();
let ann = roc_types::subs::SubsFmtContent(
env.subs
.get_content_without_compacting(partial_proc.annotation),
env.subs,
);
let spec =
roc_types::subs::SubsFmtContent(env.subs.get_content_without_compacting(fn_var), env.subs);
dbg!(ann, spec);
let _unified = env.unify(partial_proc.annotation, fn_var); let _unified = env.unify(partial_proc.annotation, fn_var);
dbg!("done");
// This will not hold for programs with type errors // This will not hold for programs with type errors
// let is_valid = matches!(unified, roc_unify::unify::Unified::Success(_)); // let is_valid = matches!(unified, roc_unify::unify::Unified::Success(_));
@ -4758,12 +4749,6 @@ pub fn with_hole<'a>(
match loc_expr.value { match loc_expr.value {
roc_can::expr::Expr::Var(proc_name) if is_known(proc_name) => { roc_can::expr::Expr::Var(proc_name) if is_known(proc_name) => {
dbg!(proc_name);
dbg!(roc_types::subs::SubsFmtContent(
env.subs.get_content_without_compacting(fn_var),
&env.subs
));
// a call by a known name // a call by a known name
call_by_name( call_by_name(
env, env,
@ -7586,6 +7571,10 @@ fn call_by_name<'a>(
hole: &'a Stmt<'a>, hole: &'a Stmt<'a>,
) -> Stmt<'a> { ) -> Stmt<'a> {
// Register a pending_specialization for this function // Register a pending_specialization for this function
// dbg!(roc_types::subs::SubsFmtContent(
// env.subs.get_content_without_compacting(fn_var),
// env.subs
// ));
match layout_cache.raw_from_var(env.arena, fn_var, env.subs) { match layout_cache.raw_from_var(env.arena, fn_var, env.subs) {
Err(LayoutProblem::UnresolvedTypeVar(var)) => { Err(LayoutProblem::UnresolvedTypeVar(var)) => {
let msg = format!( let msg = format!(
@ -7811,7 +7800,11 @@ fn call_by_name_help<'a>(
assigned, assigned,
hole, hole,
) )
} else if env.is_imported_symbol(proc_name.name()) { } else if env.is_imported_symbol(proc_name.name())
// TODO HACK FIXME
|| (proc_name.name().module_id() == ModuleId::DERIVED
&& !procs.partial_procs.contains_key(proc_name.name()))
{
add_needed_external(procs, env, original_fn_var, proc_name); add_needed_external(procs, env, original_fn_var, proc_name);
debug_assert_ne!(proc_name.name().module_id(), ModuleId::ATTR); debug_assert_ne!(proc_name.name().module_id(), ModuleId::ATTR);

View file

@ -1236,20 +1236,6 @@ impl<'a> Layout<'a> {
match content { match content {
FlexVar(_) | RigidVar(_) => Err(LayoutProblem::UnresolvedTypeVar(var)), FlexVar(_) | RigidVar(_) => Err(LayoutProblem::UnresolvedTypeVar(var)),
FlexAbleVar(_, _) | RigidAbleVar(_, _) => { FlexAbleVar(_, _) | RigidAbleVar(_, _) => {
unsafe {
dbg!(
roc_types::subs::SubsFmtContent(
env.subs
.get_content_without_compacting(Variable::from_index(1217)),
&env.subs
),
roc_types::subs::SubsFmtContent(
env.subs
.get_content_without_compacting(Variable::from_index(1224)),
&env.subs
),
);
}
todo_abilities!("Not reachable yet") todo_abilities!("Not reachable yet")
} }
RecursionVar { structure, .. } => { RecursionVar { structure, .. } => {
@ -2115,13 +2101,6 @@ fn layout_from_flat_type<'a>(
} }
} }
Func(_, closure_var, _) => { Func(_, closure_var, _) => {
dbg!(
closure_var,
roc_types::subs::SubsFmtContent(
env.subs.get_content_without_compacting(closure_var),
&subs
)
);
let lambda_set = let lambda_set =
LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info)?; LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info)?;
@ -2139,13 +2118,6 @@ fn layout_from_flat_type<'a>(
for (label, field) in it { for (label, field) in it {
match field { match field {
RecordField::Required(field_var) | RecordField::Demanded(field_var) => { RecordField::Required(field_var) | RecordField::Demanded(field_var) => {
dbg!(
label,
roc_types::subs::SubsFmtContent(
subs.get_content_without_compacting(field_var),
subs
)
);
sortables.push((label, Layout::from_var(env, field_var)?)); sortables.push((label, Layout::from_var(env, field_var)?));
} }
RecordField::Optional(_) => { RecordField::Optional(_) => {

View file

@ -1817,18 +1817,13 @@ impl<'a> SubsProxy<'a> {
lambda_set_var_in_derived: Variable, lambda_set_var_in_derived: Variable,
target_rank: Rank, target_rank: Rank,
) -> Variable { ) -> Variable {
if self.home == ModuleId::DERIVED { let derived_module = self.derived_module.lock().unwrap();
self.subs derived_module.copy_lambda_set_ambient_function_to_subs(
.get_lambda_set(lambda_set_var_in_derived) lambda_set_var_in_derived,
.ambient_function self.home,
} else { self.subs,
let derived_module = self.derived_module.lock().unwrap(); target_rank,
derived_module.copy_lambda_set_ambient_function_to_subs( )
lambda_set_var_in_derived,
self.subs,
target_rank,
)
}
} }
} }

View file

@ -427,9 +427,8 @@ fn encode_derived_string() {
} }
#[test] #[test]
#[ignore]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm"))]
fn encode_derived_record() { fn encode_derived_record_one_field_string() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
@ -444,7 +443,7 @@ fn encode_derived_record() {
_ -> "<bad>" _ -> "<bad>"
"# "#
), ),
RocStr::from("foo"), RocStr::from(r#"{"a":"foo",}"#),
RocStr RocStr
) )
} }

View file

@ -1797,3 +1797,40 @@ fn instantiate_annotated_as_recursive_alias_multiple_polymorphic_expr() {
"# "#
) )
} }
#[mono_test]
fn encode_derived_record_one_field_string() {
indoc!(
r#"
app "test"
imports [Encode.{ toEncoder }, Json]
provides [main] to "./platform"
main =
result = Str.fromUtf8 (Encode.toBytes {a: "foo"} Json.format)
when result is
Ok s -> s
_ -> "<bad>"
"#
)
}
#[mono_test]
#[ignore = "TODO make this work (one ULS var is missing)"]
fn encode_derived_tag_one_field_string() {
indoc!(
r#"
app "test"
imports [Encode.{ toEncoder }, Json]
provides [main] to "./platform"
main =
x : [A Str]
x = A "foo"
result = Str.fromUtf8 (Encode.toBytes x Json.format)
when result is
Ok s -> s
_ -> "<bad>"
"#
)
}