Deriving string encoders works

This commit is contained in:
Ayaz Hafiz 2022-06-24 17:50:46 -04:00
parent a17748ea01
commit 9826253785
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
11 changed files with 372 additions and 33 deletions

View file

@ -205,7 +205,7 @@ impl DerivedModule {
&self.subs,
target,
// bookkeep unspecialized lambda sets of var - I think we want this here
true,
false,
var,
// TODO: I think this is okay because the only use of `copy_lambda_set_var_to_subs`
// (at least right now) is for lambda set compaction, which will automatically unify
@ -213,8 +213,8 @@ impl DerivedModule {
//
// However this is a bad coupling and maybe not a good assumption, we should revisit
// this when possible.
// Rank::toplevel(),
target_rank,
Rank::import(),
// target_rank,
);
copied_import.variable

View file

@ -10,7 +10,7 @@ use roc_collections::MutMap;
use roc_derive::SharedDerivedModule;
use roc_error_macros::internal_error;
use roc_module::symbol::ModuleId;
use roc_solve::solve::{compact_lambda_sets_of_vars, Phase, Pools};
use roc_solve::solve::{compact_lambda_sets_of_vars, Phase, Pools, SubsProxy};
use roc_types::subs::{Content, FlatType, LambdaSet};
use roc_types::subs::{ExposedTypesStorageSubs, Subs, Variable};
use roc_unify::unify::{unify as unify_unify, Mode, Unified};
@ -174,7 +174,22 @@ pub fn unify(
left: Variable,
right: Variable,
) -> Result<(), UnificationFailed> {
let unified = unify_unify(subs, left, right, Mode::EQ);
let unified = if home == ModuleId::DERIVED {
// TODO: can we be smarter so more stuff can happen in parallel without us having to lock
// and steal from the derived module here?
let mut derived_module = derived_module.lock().unwrap();
let mut stolen = derived_module.steal();
let unified = unify_unify(&mut stolen.subs, left, right, Mode::EQ);
derived_module.return_stolen(stolen);
unified
} else {
unify_unify(subs, left, right, Mode::EQ)
};
match unified {
Unified::Success {
vars: _,
@ -186,14 +201,14 @@ pub fn unify(
let late_phase = LatePhase { home, abilities };
let mut subs_proxy = SubsProxy::new(home, subs, derived_module);
let must_implement_constraints = compact_lambda_sets_of_vars(
subs,
&mut subs_proxy,
arena,
&mut pools,
lambda_sets_to_specialize,
&late_phase,
exposed_by_module,
derived_module,
);
// At this point we can't do anything with must-implement constraints, since we're no
// longer solving. We must assume that they were totally caught during solving.
@ -210,3 +225,26 @@ pub fn unify(
Unified::Failure(..) | Unified::BadType(..) => Err(UnificationFailed),
}
}
pub fn hack(
lset: Variable,
home: ModuleId,
arena: &Bump,
subs: &mut Subs,
abilities: &AbilitiesView,
derived_module: &SharedDerivedModule,
exposed_by_module: &ExposedByModule,
) {
let mut subs_proxy = SubsProxy::new(home, subs, derived_module);
let late_phase = LatePhase { home, abilities };
let mut uls_of_var = roc_types::subs::UlsOfVar::default();
uls_of_var.add(lset, lset);
compact_lambda_sets_of_vars(
&mut subs_proxy,
arena,
&mut Pools::default(),
uls_of_var,
&late_phase,
exposed_by_module,
);
}

View file

@ -2706,7 +2706,15 @@ fn finish_specialization(
.into_inner()
.into_module_ids();
let all_ident_ids = state.constrained_ident_ids;
let mut all_ident_ids = state.constrained_ident_ids;
// Steal the derived symbols and put them in the global ident ids. Since we're done, we won't
// need them again.
let StolenFromDerived {
ident_ids: derived_ident_ids,
subs: _,
} = state.derived_module.lock().unwrap().steal();
ModuleId::DERIVED.register_debug_idents(&derived_ident_ids);
all_ident_ids.insert(ModuleId::DERIVED, derived_ident_ids);
let interns = Interns {
module_ids,
@ -4113,7 +4121,10 @@ fn run_solve_solve(
}
let (solved_subs, solved_specializations, exposed_vars_by_symbol, problems, abilities_store) = {
let module_id = module.module_id;
let (solved_subs, solved_env, problems, abilities_store) = roc_solve::module::run_solve(
module_id,
&constraints,
actual_constraint,
rigid_variables,
@ -4125,7 +4136,6 @@ fn run_solve_solve(
derived_module,
);
let module_id = module.module_id;
// Figure out what specializations belong to this module
let solved_specializations: ResolvedSpecializations = abilities_store
.iter_specializations()

View file

@ -2923,6 +2923,7 @@ fn specialize_external<'a>(
host_exposed_variables: &[(Symbol, Variable)],
partial_proc_id: PartialProcId,
) -> Result<Proc<'a>, LayoutProblem> {
dbg!(("spec ", proc_name));
let partial_proc = procs.partial_procs.get_id(partial_proc_id);
let captured_symbols = partial_proc.captured_symbols;
@ -2930,7 +2931,16 @@ fn specialize_external<'a>(
let snapshot = env.subs.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);
dbg!("done");
// This will not hold for programs with type errors
// let is_valid = matches!(unified, roc_unify::unify::Unified::Success(_));
@ -4747,6 +4757,12 @@ pub fn with_hole<'a>(
match loc_expr.value {
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
call_by_name(
env,

View file

@ -1235,7 +1235,23 @@ impl<'a> Layout<'a> {
use roc_types::subs::Content::*;
match content {
FlexVar(_) | RigidVar(_) => Err(LayoutProblem::UnresolvedTypeVar(var)),
FlexAbleVar(_, _) | RigidAbleVar(_, _) => todo_abilities!("Not reachable yet"),
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")
}
RecursionVar { structure, .. } => {
let structure_content = env.subs.get_content_without_compacting(structure);
Self::new_help(env, structure, *structure_content)
@ -2099,6 +2115,13 @@ fn layout_from_flat_type<'a>(
}
}
Func(_, closure_var, _) => {
dbg!(
closure_var,
roc_types::subs::SubsFmtContent(
env.subs.get_content_without_compacting(closure_var),
&subs
)
);
let lambda_set =
LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info)?;
@ -2116,6 +2139,13 @@ fn layout_from_flat_type<'a>(
for (label, field) in it {
match field {
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)?));
}
RecordField::Optional(_) => {

View file

@ -5,9 +5,9 @@ use roc_can::expr::PendingDerives;
use roc_can::module::{ExposedByModule, RigidVariables};
use roc_collections::all::MutMap;
use roc_collections::VecMap;
use roc_derive_key::GlobalDerivedSymbols;
use roc_derive::SharedDerivedModule;
use roc_error_macros::internal_error;
use roc_module::symbol::Symbol;
use roc_module::symbol::{ModuleId, Symbol};
use roc_types::subs::{Content, ExposedTypesStorageSubs, FlatType, StorageSubs, Subs, Variable};
use roc_types::types::Alias;
@ -54,6 +54,7 @@ pub struct SolvedModule {
#[allow(clippy::too_many_arguments)] // TODO: put params in a context/env var
pub fn run_solve(
home: ModuleId,
constraints: &Constraints,
constraint: ConstraintSoa,
rigid_variables: RigidVariables,
@ -87,6 +88,7 @@ pub fn run_solve(
// Run the solver to populate Subs.
let (solved_subs, solved_env) = solve::run(
home,
constraints,
&mut problems,
subs,

View file

@ -14,8 +14,8 @@ use roc_collections::all::MutMap;
use roc_debug_flags::dbg_do;
#[cfg(debug_assertions)]
use roc_debug_flags::{ROC_TRACE_COMPACTION, ROC_VERIFY_RIGID_LET_GENERALIZED};
use roc_derive::SharedDerivedModule;
use roc_derive_key::{DeriveError, Derived};
use roc_derive::{DerivedModule, SharedDerivedModule};
use roc_derive_key::{DeriveError, DeriveKey};
use roc_error_macros::internal_error;
use roc_module::ident::TagName;
use roc_module::symbol::{ModuleId, Symbol};
@ -545,6 +545,7 @@ struct State {
#[allow(clippy::too_many_arguments)] // TODO: put params in a context/env var
pub fn run(
home: ModuleId,
constraints: &Constraints,
problems: &mut Vec<TypeError>,
mut subs: Subs,
@ -556,6 +557,7 @@ pub fn run(
derived_module: SharedDerivedModule,
) -> (Solved<Subs>, Env) {
let env = run_in_place(
home,
constraints,
problems,
&mut subs,
@ -573,6 +575,7 @@ pub fn run(
/// Modify an existing subs in-place instead
#[allow(clippy::too_many_arguments)] // TODO: put params in a context/env var
fn run_in_place(
home: ModuleId,
constraints: &Constraints,
problems: &mut Vec<TypeError>,
subs: &mut Subs,
@ -617,14 +620,15 @@ fn run_in_place(
// Now that the module has been solved, we can run through and check all
// types claimed to implement abilities. This will also tell us what derives
// are legal, which we need to register.
let mut subs_proxy = SubsProxy::new(home, subs, &derived_module);
let new_must_implement = compact_lambda_sets_of_vars(
subs,
&mut subs_proxy,
&arena,
&mut pools,
deferred_uls_to_resolve,
&SolvePhase { abilities_store },
exposed_by_module,
&derived_module,
);
deferred_obligations.add(new_must_implement, AbilityImplError::IncompleteAbility);
@ -1759,6 +1763,72 @@ fn check_ability_specialization(
}
}
/// A proxy for managing [`Subs`] and the derived module, in the presence of possibly having to
/// solve within the derived module's subs.
pub struct SubsProxy<'a> {
home: ModuleId,
subs: &'a mut Subs,
derived_module: &'a SharedDerivedModule,
}
impl<'a> SubsProxy<'a> {
/// Creates a new subs proxy with the derived module.
/// The contract is:
/// - `subs` is for the `home` module
/// - if `home` is the derived module, then the derived module is not in a stolen state
pub fn new(
home: ModuleId,
subs: &'a mut Subs,
derived_module: &'a SharedDerivedModule,
) -> Self {
Self {
home,
subs,
derived_module,
}
}
fn with_subs<T, F>(&mut self, f: F) -> T
where
F: FnOnce(&mut Subs) -> T,
{
if self.home != ModuleId::DERIVED {
f(self.subs)
} else {
let mut derived_module = self.derived_module.lock().unwrap();
let mut stolen = derived_module.steal();
let result = f(&mut stolen.subs);
derived_module.return_stolen(stolen);
result
}
}
fn with_derived<T, F>(&mut self, f: F) -> T
where
F: FnOnce(&mut DerivedModule) -> T,
{
let mut derived_module = self.derived_module.lock().unwrap();
f(&mut derived_module)
}
fn copy_lambda_set_var_from_derived_to_subs(
&mut self,
lambda_set_var_in_derived: Variable,
target_rank: Rank,
) -> Variable {
if self.home == ModuleId::DERIVED {
lambda_set_var_in_derived
} else {
let derived_module = self.derived_module.lock().unwrap();
derived_module.copy_lambda_set_var_to_subs(
lambda_set_var_in_derived,
self.subs,
target_rank,
)
}
}
}
#[cfg(debug_assertions)]
fn trace_compaction_step_1(subs: &Subs, c_a: Variable, uls_a: &[Variable]) {
let c_a = roc_types::subs::SubsFmtContent(subs.get_content_without_compacting(c_a), subs);
@ -1881,7 +1951,7 @@ fn unique_unspecialized_lambda(subs: &Subs, c_a: Variable, uls: &[Uls]) -> Optio
#[must_use]
pub fn compact_lambda_sets_of_vars<P: Phase>(
subs: &mut Subs,
subs: &mut SubsProxy,
arena: &Bump,
pools: &mut Pools,
uls_of_var: UlsOfVar,
@ -2307,13 +2377,14 @@ fn make_specialization_decision(subs: &Subs, var: Variable) -> SpecializeDecisio
}
fn get_specialization_lambda_set<P: Phase>(
subs: &mut Subs,
subs: &mut SubsProxy,
phase: &P,
ability_member: Symbol,
lset_region: u8,
specialization_key: SpecializationTypeKey,
exposed_by_module: &ExposedByModule,
derived_module: &SharedDerivedModule,
target_rank: Rank,
) -> Result<Variable, ()> {
match specialization_key {
SpecializationTypeKey::Opaque(opaque) => {
@ -2344,27 +2415,25 @@ fn get_specialization_lambda_set<P: Phase>(
}
})?;
let local_lset = phase.copy_lambda_set_var_to_home_subs(
external_specialized_lset,
opaque_home,
subs,
);
let local_lset = subs.with_subs(|subs| {
phase.copy_lambda_set_var_to_home_subs(external_specialized_lset, opaque_home, subs)
});
Ok(local_lset)
}
SpecializationTypeKey::Derived(derive_key) => {
let mut derived_module = derived_module.lock().unwrap();
let specialized_lambda_set = subs.with_derived(|derived_module| {
let (_, _, specialization_lambda_sets) =
derived_module.get_or_insert(exposed_by_module, derive_key);
let (_, _, specialization_lambda_sets) =
derived_module.get_or_insert(exposed_by_module, derive_key);
let &specialized_lambda_set = specialization_lambda_sets
.get(&lset_region)
.expect("lambda set region not resolved");
*specialization_lambda_sets
.get(&lset_region)
.expect("lambda set region not resolved")
});
let local_lset =
derived_module.copy_lambda_set_var_to_subs(specialized_lambda_set, subs);
subs.copy_lambda_set_var_from_derived_to_subs(specialized_lambda_set, target_rank);
Ok(local_lset)
}

View file

@ -403,3 +403,47 @@ fn to_encoder_encode_custom_has_capture() {
RocStr
)
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn encode_derived_string() {
assert_evals_to!(
indoc!(
r#"
app "test"
imports [Encode.{ toEncoder }, Json]
provides [main] to "./platform"
main =
result = Str.fromUtf8 (Encode.toBytes "foo" Json.format)
when result is
Ok s -> s
_ -> "<bad>"
"#
),
RocStr::from("\"foo\""),
RocStr
)
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn encode_derived_record() {
assert_evals_to!(
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>"
"#
),
RocStr::from("foo"),
RocStr
)
}

View file

@ -0,0 +1,106 @@
procedure #Derived.0 (#Derived.1):
let #Derived.6 : {Str} = Struct {#Derived.1};
let #Derived.5 : {Str} = CallByName Encode.22 #Derived.6;
ret #Derived.5;
procedure #Derived.2 (#Derived.3, #Derived.4, #Attr.12):
let #Derived.1 : Str = StructAtIndex 0 #Attr.12;
inc #Derived.1;
dec #Attr.12;
let #Derived.9 : {Str} = CallByName Json.17 #Derived.1;
let #Derived.8 : List U8 = CallByName Encode.23 #Derived.3 #Derived.9 #Derived.4;
ret #Derived.8;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.107 : List U8 = CallByName #Derived.2 Encode.94 Encode.96 Encode.102;
ret Encode.107;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.114 : List U8 = CallByName Json.65 Encode.94 Encode.96 Encode.102;
ret Encode.114;
procedure Encode.25 (Encode.100, Encode.101):
let Encode.104 : List U8 = Array [];
let Encode.105 : {Str} = CallByName #Derived.0 Encode.100;
let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101;
ret Encode.103;
procedure Json.1 ():
let Json.102 : {} = Struct {};
ret Json.102;
procedure Json.17 (Json.64):
let Json.104 : {Str} = Struct {Json.64};
let Json.103 : {Str} = CallByName Encode.22 Json.104;
ret Json.103;
procedure Json.65 (Json.66, Json.105, #Attr.12):
let Json.64 : Str = StructAtIndex 0 #Attr.12;
inc Json.64;
dec #Attr.12;
let Json.114 : I32 = 34i64;
let Json.113 : U8 = CallByName Num.122 Json.114;
let Json.111 : List U8 = CallByName List.4 Json.66 Json.113;
let Json.112 : List U8 = CallByName Str.12 Json.64;
let Json.108 : List U8 = CallByName List.8 Json.111 Json.112;
let Json.110 : I32 = 34i64;
let Json.109 : U8 = CallByName Num.122 Json.110;
let Json.107 : List U8 = CallByName List.4 Json.108 Json.109;
ret Json.107;
procedure List.4 (#Attr.2, #Attr.3):
let List.141 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3;
ret List.141;
procedure List.8 (#Attr.2, #Attr.3):
let List.142 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.142;
procedure Num.122 (#Attr.2):
let Num.272 : U8 = lowlevel NumIntCast #Attr.2;
ret Num.272;
procedure Str.12 (#Attr.2):
let Str.73 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.73;
procedure Str.9 (#Attr.2):
let #Attr.3 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
let Str.69 : Int1 = StructAtIndex 2 #Attr.3;
if Str.69 then
let Str.71 : Str = StructAtIndex 1 #Attr.3;
inc Str.71;
dec #Attr.3;
let Str.70 : [C {U64, U8}, C Str] = Ok Str.71;
ret Str.70;
else
let Str.67 : U8 = StructAtIndex 3 #Attr.3;
let Str.68 : U64 = StructAtIndex 0 #Attr.3;
dec #Attr.3;
let Str.66 : {U64, U8} = Struct {Str.68, Str.67};
let Str.65 : [C {U64, U8}, C Str] = Err Str.66;
ret Str.65;
procedure Test.0 ():
let Test.9 : Str = "abc";
let Test.10 : {} = CallByName Json.1;
let Test.8 : List U8 = CallByName Encode.25 Test.9 Test.10;
let Test.1 : [C {U64, U8}, C Str] = CallByName Str.9 Test.8;
let Test.5 : U8 = 1i64;
let Test.6 : U8 = GetTagId Test.1;
let Test.7 : Int1 = lowlevel Eq Test.5 Test.6;
if Test.7 then
let Test.2 : Str = UnionAtIndex (Id 1) (Index 0) Test.1;
inc Test.2;
dec Test.1;
ret Test.2;
else
dec Test.1;
let Test.4 : Str = "<bad>";
ret Test.4;

View file

@ -1467,6 +1467,24 @@ fn encode_custom_type() {
}
#[mono_test]
fn encode_derived_string() {
indoc!(
r#"
app "test"
imports [Encode.{ toEncoder }, Json]
provides [main] to "./platform"
main =
result = Str.fromUtf8 (Encode.toBytes "abc" Json.format)
when result is
Ok s -> s
_ -> "<bad>"
"#
)
}
#[mono_test]
#[ignore = "TODO"]
fn encode_derived_record() {
indoc!(
r#"
@ -1475,7 +1493,7 @@ fn encode_derived_record() {
provides [main] to "./platform"
main =
result = Str.fromUtf8 (Encode.toBytes {a: "fieldA", b: "fieldB"} Json.format)
result = Str.fromUtf8 (Encode.toBytes {a: "a"} Json.format)
when result is
Ok s -> s
_ -> "<bad>"

View file

@ -873,7 +873,13 @@ fn subs_fmt_flat_type(this: &FlatType, subs: &Subs, f: &mut fmt::Formatter) -> f
FlatType::Apply(name, arguments) => {
let slice = subs.get_subs_slice(*arguments);
write!(f, "Apply({:?}, {:?})", name, slice)
write!(f, "Apply({:?} ", name)?;
for var in slice {
let content = subs.get_content_without_compacting(*var);
write!(f, ", <{:?}>{:?}", *var, SubsFmtContent(content, subs))?;
}
write!(f, ")")
// write!(f, "Apply({:?}, {:?})", name, slice)
}
FlatType::Func(arguments, lambda_set, result) => {
let slice = subs.get_subs_slice(*arguments);