mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-11 10:55:31 +03:00
Merge pull request #3680 from rtfeldman/derive-decoders-1
Derive immediate decoders for basic types
This commit is contained in:
commit
510016bf6e
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -3859,6 +3859,7 @@ dependencies = [
|
||||
"roc_collections",
|
||||
"roc_debug_flags",
|
||||
"roc_derive",
|
||||
"roc_derive_key",
|
||||
"roc_error_macros",
|
||||
"roc_exhaustive",
|
||||
"roc_late_solve",
|
||||
|
@ -197,7 +197,7 @@ takeFloat = \bytes ->
|
||||
|
||||
when List.get rest 0 is
|
||||
Ok 46 -> # 46 = .
|
||||
{ taken: floatPart, rest: afterAll } = takeDigits rest
|
||||
{ taken: floatPart, rest: afterAll } = takeDigits (List.split rest 1).others
|
||||
builtFloat =
|
||||
List.concat (List.append intPart (asciiByte '.')) floatPart
|
||||
|
||||
|
@ -2,173 +2,24 @@
|
||||
|
||||
use std::iter::once;
|
||||
|
||||
use roc_can::abilities::SpecializationLambdaSets;
|
||||
use roc_can::expr::{
|
||||
AnnotatedMark, ClosureData, Expr, Field, Recursive, WhenBranch, WhenBranchPattern,
|
||||
};
|
||||
use roc_can::module::ExposedByModule;
|
||||
use roc_can::pattern::Pattern;
|
||||
use roc_collections::SendMap;
|
||||
use roc_derive_key::encoding::FlatEncodableKey;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{
|
||||
instantiate_rigids, Content, ExhaustiveMark, FlatType, GetSubsSlice, LambdaSet, OptVariable,
|
||||
RecordFields, RedundantMark, Subs, SubsSlice, UnionLambdas, UnionTags, Variable,
|
||||
VariableSubsSlice,
|
||||
Content, ExhaustiveMark, FlatType, GetSubsSlice, LambdaSet, OptVariable, RecordFields,
|
||||
RedundantMark, SubsSlice, UnionLambdas, UnionTags, Variable, VariableSubsSlice,
|
||||
};
|
||||
use roc_types::types::RecordField;
|
||||
|
||||
use crate::{synth_var, DerivedBody, DERIVED_SYNTH};
|
||||
|
||||
pub(crate) struct Env<'a> {
|
||||
/// NB: This **must** be subs for the derive module!
|
||||
pub subs: &'a mut Subs,
|
||||
pub exposed_types: &'a ExposedByModule,
|
||||
pub derived_ident_ids: &'a mut IdentIds,
|
||||
}
|
||||
|
||||
impl Env<'_> {
|
||||
fn new_symbol(&mut self, name_hint: &str) -> Symbol {
|
||||
if cfg!(any(
|
||||
debug_assertions,
|
||||
test,
|
||||
feature = "debug-derived-symbols"
|
||||
)) {
|
||||
let mut i = 0;
|
||||
let debug_name = loop {
|
||||
i += 1;
|
||||
let name = if i == 1 {
|
||||
name_hint.to_owned()
|
||||
} else {
|
||||
format!("{}{}", name_hint, i)
|
||||
};
|
||||
if self.derived_ident_ids.get_id(&name).is_none() {
|
||||
break name;
|
||||
}
|
||||
};
|
||||
|
||||
let ident_id = self.derived_ident_ids.get_or_insert(&debug_name);
|
||||
|
||||
Symbol::new(DERIVED_SYNTH, ident_id)
|
||||
} else {
|
||||
self.unique_symbol()
|
||||
}
|
||||
}
|
||||
|
||||
fn unique_symbol(&mut self) -> Symbol {
|
||||
let ident_id = self.derived_ident_ids.gen_unique();
|
||||
Symbol::new(DERIVED_SYNTH, ident_id)
|
||||
}
|
||||
|
||||
fn import_encode_symbol(&mut self, symbol: Symbol) -> Variable {
|
||||
debug_assert_eq!(symbol.module_id(), ModuleId::ENCODE);
|
||||
|
||||
let encode_types = &self
|
||||
.exposed_types
|
||||
.get(&ModuleId::ENCODE)
|
||||
.unwrap()
|
||||
.exposed_types_storage_subs;
|
||||
let storage_var = encode_types.stored_vars_by_symbol.get(&symbol).unwrap();
|
||||
let imported = encode_types
|
||||
.storage_subs
|
||||
.export_variable_to_directly_to_use_site(self.subs, *storage_var);
|
||||
|
||||
instantiate_rigids(self.subs, imported.variable);
|
||||
|
||||
imported.variable
|
||||
}
|
||||
|
||||
fn unify(&mut self, left: Variable, right: Variable) {
|
||||
use roc_unify::unify::{unify, Env, Mode, Unified};
|
||||
|
||||
let unified = unify(&mut Env::new(self.subs), left, right, Mode::EQ);
|
||||
|
||||
match unified {
|
||||
Unified::Success {
|
||||
vars: _,
|
||||
must_implement_ability: _,
|
||||
lambda_sets_to_specialize,
|
||||
extra_metadata: _,
|
||||
} => {
|
||||
if !lambda_sets_to_specialize.is_empty() {
|
||||
internal_error!("Did not expect derivers to need to specialize unspecialized lambda sets, but we got some: {:?}", lambda_sets_to_specialize)
|
||||
}
|
||||
}
|
||||
Unified::Failure(..) | Unified::BadType(..) => {
|
||||
internal_error!("Unification failed in deriver - that's a deriver bug!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_specialization_lambda_sets(
|
||||
&mut self,
|
||||
specialization_type: Variable,
|
||||
ability_member: Symbol,
|
||||
) -> SpecializationLambdaSets {
|
||||
use roc_unify::unify::{unify_introduced_ability_specialization, Env, Mode, Unified};
|
||||
|
||||
let member_signature = self.import_encode_symbol(ability_member);
|
||||
|
||||
let unified = unify_introduced_ability_specialization(
|
||||
&mut Env::new(self.subs),
|
||||
member_signature,
|
||||
specialization_type,
|
||||
Mode::EQ,
|
||||
);
|
||||
|
||||
match unified {
|
||||
Unified::Success {
|
||||
vars: _,
|
||||
must_implement_ability: _,
|
||||
lambda_sets_to_specialize: _lambda_sets_to_specialize,
|
||||
extra_metadata: specialization_lsets,
|
||||
} => {
|
||||
let specialization_lsets: SpecializationLambdaSets = specialization_lsets
|
||||
.0
|
||||
.into_iter()
|
||||
.map(|((spec_member, region), var)| {
|
||||
debug_assert_eq!(spec_member, ability_member);
|
||||
(region, var)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Since we're doing `{foo} ~ a | a has Encoding`, we may see "lambda sets to
|
||||
// specialize" for e.g. `{foo}:toEncoder:1`, but these are actually just the
|
||||
// specialization lambda sets, so we don't need to do any extra work!
|
||||
//
|
||||
// If there are other lambda sets to specialize in here, that's unexpected, because
|
||||
// that means we would have been deriving something like `toEncoder {foo: bar}`,
|
||||
// and now seen that we needed `toEncoder bar` where `bar` is a concrete type. But
|
||||
// we only expect `bar` to polymorphic at this stage!
|
||||
//
|
||||
// TODO: it would be better if `unify` could prune these for us. See also
|
||||
// https://github.com/rtfeldman/roc/issues/3207; that is a blocker for this TODO.
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
for (spec_var, lambda_sets) in _lambda_sets_to_specialize.drain() {
|
||||
for lambda_set in lambda_sets {
|
||||
let belongs_to_specialized_lambda_sets =
|
||||
specialization_lsets.iter().any(|(_, var)| {
|
||||
self.subs.get_root_key_without_compacting(*var)
|
||||
== self.subs.get_root_key_without_compacting(lambda_set)
|
||||
});
|
||||
debug_assert!(belongs_to_specialized_lambda_sets,
|
||||
"Did not expect derivers to need to specialize unspecialized lambda sets, but we got one: {:?} for {:?}", lambda_set, spec_var)
|
||||
}
|
||||
}
|
||||
}
|
||||
specialization_lsets
|
||||
}
|
||||
Unified::Failure(..) | Unified::BadType(..) => {
|
||||
internal_error!("Unification failed in deriver - that's a deriver bug!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::util::Env;
|
||||
use crate::{synth_var, DerivedBody};
|
||||
|
||||
pub(crate) fn derive_to_encoder(
|
||||
env: &mut Env<'_>,
|
||||
@ -253,7 +104,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) {
|
||||
|
||||
// build `toEncoder elem` type
|
||||
// val -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
let to_encoder_fn_var = env.import_encode_symbol(Symbol::ENCODE_TO_ENCODER);
|
||||
let to_encoder_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TO_ENCODER);
|
||||
|
||||
// elem -[clos]-> t1
|
||||
let to_encoder_clos_var = env.subs.fresh_unnamed_flex_var(); // clos
|
||||
@ -333,7 +184,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) {
|
||||
|
||||
// build `Encode.list lst (\elem -> Encode.toEncoder elem)` type
|
||||
// List e, (e -> Encoder fmt) -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
let encode_list_fn_var = env.import_encode_symbol(Symbol::ENCODE_LIST);
|
||||
let encode_list_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_LIST);
|
||||
|
||||
// List elem, to_elem_encoder_fn_var -[clos]-> t1
|
||||
let this_encode_list_args_slice =
|
||||
@ -469,7 +320,7 @@ fn to_encoder_record(
|
||||
|
||||
// build `toEncoder rcd.a` type
|
||||
// val -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
let to_encoder_fn_var = env.import_encode_symbol(Symbol::ENCODE_TO_ENCODER);
|
||||
let to_encoder_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TO_ENCODER);
|
||||
|
||||
// (typeof rcd.a) -[clos]-> t1
|
||||
let to_encoder_clos_var = env.subs.fresh_unnamed_flex_var(); // clos
|
||||
@ -549,7 +400,7 @@ fn to_encoder_record(
|
||||
|
||||
// build `Encode.record [ { key: .., value: ..}, .. ]` type
|
||||
// List { key : Str, value : Encoder fmt } -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
let encode_record_fn_var = env.import_encode_symbol(Symbol::ENCODE_RECORD);
|
||||
let encode_record_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_RECORD);
|
||||
|
||||
// fields_list_var -[clos]-> t1
|
||||
let fields_list_var_slice =
|
||||
@ -687,7 +538,8 @@ fn to_encoder_tag_union(
|
||||
.map(|(&sym, &sym_var)| {
|
||||
// build `toEncoder v1` type
|
||||
// expected: val -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
let to_encoder_fn_var = env.import_encode_symbol(Symbol::ENCODE_TO_ENCODER);
|
||||
let to_encoder_fn_var =
|
||||
env.import_builtin_symbol_var(Symbol::ENCODE_TO_ENCODER);
|
||||
|
||||
// wanted: t1 -[clos]-> t'
|
||||
let var_slice_of_sym_var =
|
||||
@ -747,7 +599,7 @@ fn to_encoder_tag_union(
|
||||
|
||||
// build `Encode.tag "A" [ ... ]` type
|
||||
// expected: Str, List (Encoder fmt) -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
let encode_tag_fn_var = env.import_encode_symbol(Symbol::ENCODE_TAG);
|
||||
let encode_tag_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TAG);
|
||||
|
||||
// wanted: Str, List whole_encoders_var -[clos]-> t'
|
||||
let this_encode_tag_args_var_slice = VariableSubsSlice::insert_into_subs(
|
||||
@ -904,7 +756,7 @@ fn wrap_in_encode_custom(
|
||||
|
||||
// build `Encode.appendWith bytes encoder fmt` type
|
||||
// expected: Encode.appendWith : List U8, Encoder fmt, fmt -[appendWith]-> List U8 | fmt has EncoderFormatting
|
||||
let append_with_fn_var = env.import_encode_symbol(Symbol::ENCODE_APPEND_WITH);
|
||||
let append_with_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_APPEND_WITH);
|
||||
|
||||
// wanted: Encode.appendWith : List U8, encoder_var, fmt -[clos]-> List U8 | fmt has EncoderFormatting
|
||||
let this_append_with_args_var_slice =
|
||||
@ -995,7 +847,7 @@ fn wrap_in_encode_custom(
|
||||
// Encode.custom \bytes, fmt -> Encode.appendWith bytes encoder fmt
|
||||
//
|
||||
// expected: Encode.custom : (List U8, fmt -> List U8) -> Encoder fmt | fmt has EncoderFormatting
|
||||
let custom_fn_var = env.import_encode_symbol(Symbol::ENCODE_CUSTOM);
|
||||
let custom_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_CUSTOM);
|
||||
|
||||
// wanted: Encode.custom : fn_var -[clos]-> t'
|
||||
let this_custom_args_var_slice = VariableSubsSlice::insert_into_subs(env.subs, [fn_var]);
|
||||
|
@ -14,9 +14,12 @@ use roc_region::all::Loc;
|
||||
use roc_types::subs::{
|
||||
copy_import_to, Content, Descriptor, Mark, OptVariable, Rank, Subs, Variable,
|
||||
};
|
||||
use util::Env;
|
||||
|
||||
mod encoding;
|
||||
|
||||
mod util;
|
||||
|
||||
pub(crate) const DERIVED_SYNTH: ModuleId = ModuleId::DERIVED_SYNTH;
|
||||
|
||||
pub fn synth_var(subs: &mut Subs, content: Content) -> Variable {
|
||||
@ -62,14 +65,14 @@ fn build_derived_body(
|
||||
specialization_lambda_sets,
|
||||
} = match derive_key {
|
||||
DeriveKey::ToEncoder(to_encoder_key) => {
|
||||
let mut env = encoding::Env {
|
||||
let mut env = Env {
|
||||
subs: derived_subs,
|
||||
exposed_types: exposed_by_module,
|
||||
derived_ident_ids,
|
||||
};
|
||||
encoding::derive_to_encoder(&mut env, to_encoder_key, derived_symbol)
|
||||
}
|
||||
DeriveKey::Decoding => todo!(),
|
||||
DeriveKey::Decoder(_decoder_key) => todo!(),
|
||||
};
|
||||
|
||||
let def = Def {
|
||||
|
155
crates/compiler/derive/src/util.rs
Normal file
155
crates/compiler/derive/src/util.rs
Normal file
@ -0,0 +1,155 @@
|
||||
use roc_can::{abilities::SpecializationLambdaSets, module::ExposedByModule};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::symbol::{IdentIds, Symbol};
|
||||
use roc_types::subs::{instantiate_rigids, Subs, Variable};
|
||||
|
||||
use crate::DERIVED_SYNTH;
|
||||
|
||||
/// An environment representing the Derived_synth module, for use in building derived
|
||||
/// implementations.
|
||||
pub(crate) struct Env<'a> {
|
||||
/// NB: This **must** be subs for the derive module!
|
||||
pub subs: &'a mut Subs,
|
||||
pub exposed_types: &'a ExposedByModule,
|
||||
pub derived_ident_ids: &'a mut IdentIds,
|
||||
}
|
||||
|
||||
impl Env<'_> {
|
||||
pub fn new_symbol(&mut self, name_hint: &str) -> Symbol {
|
||||
if cfg!(any(
|
||||
debug_assertions,
|
||||
test,
|
||||
feature = "debug-derived-symbols"
|
||||
)) {
|
||||
let mut i = 0;
|
||||
let debug_name = loop {
|
||||
i += 1;
|
||||
let name = if i == 1 {
|
||||
name_hint.to_owned()
|
||||
} else {
|
||||
format!("{}{}", name_hint, i)
|
||||
};
|
||||
if self.derived_ident_ids.get_id(&name).is_none() {
|
||||
break name;
|
||||
}
|
||||
};
|
||||
|
||||
let ident_id = self.derived_ident_ids.get_or_insert(&debug_name);
|
||||
|
||||
Symbol::new(DERIVED_SYNTH, ident_id)
|
||||
} else {
|
||||
self.unique_symbol()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unique_symbol(&mut self) -> Symbol {
|
||||
let ident_id = self.derived_ident_ids.gen_unique();
|
||||
Symbol::new(DERIVED_SYNTH, ident_id)
|
||||
}
|
||||
|
||||
pub fn import_builtin_symbol_var(&mut self, symbol: Symbol) -> Variable {
|
||||
let module_id = symbol.module_id();
|
||||
debug_assert!(module_id.is_builtin());
|
||||
|
||||
let module_types = &self
|
||||
.exposed_types
|
||||
.get(&module_id)
|
||||
.unwrap()
|
||||
.exposed_types_storage_subs;
|
||||
let storage_var = module_types.stored_vars_by_symbol.get(&symbol).unwrap();
|
||||
let imported = module_types
|
||||
.storage_subs
|
||||
.export_variable_to_directly_to_use_site(self.subs, *storage_var);
|
||||
|
||||
instantiate_rigids(self.subs, imported.variable);
|
||||
|
||||
imported.variable
|
||||
}
|
||||
|
||||
pub fn unify(&mut self, left: Variable, right: Variable) {
|
||||
use roc_unify::unify::{unify, Env, Mode, Unified};
|
||||
|
||||
let unified = unify(&mut Env::new(self.subs), left, right, Mode::EQ);
|
||||
|
||||
match unified {
|
||||
Unified::Success {
|
||||
vars: _,
|
||||
must_implement_ability: _,
|
||||
lambda_sets_to_specialize,
|
||||
extra_metadata: _,
|
||||
} => {
|
||||
if !lambda_sets_to_specialize.is_empty() {
|
||||
internal_error!("Did not expect derivers to need to specialize unspecialized lambda sets, but we got some: {:?}", lambda_sets_to_specialize)
|
||||
}
|
||||
}
|
||||
Unified::Failure(..) | Unified::BadType(..) => {
|
||||
internal_error!("Unification failed in deriver - that's a deriver bug!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_specialization_lambda_sets(
|
||||
&mut self,
|
||||
specialization_type: Variable,
|
||||
ability_member: Symbol,
|
||||
) -> SpecializationLambdaSets {
|
||||
use roc_unify::unify::{unify_introduced_ability_specialization, Env, Mode, Unified};
|
||||
|
||||
let member_signature = self.import_builtin_symbol_var(ability_member);
|
||||
|
||||
let unified = unify_introduced_ability_specialization(
|
||||
&mut Env::new(self.subs),
|
||||
member_signature,
|
||||
specialization_type,
|
||||
Mode::EQ,
|
||||
);
|
||||
|
||||
match unified {
|
||||
Unified::Success {
|
||||
vars: _,
|
||||
must_implement_ability: _,
|
||||
lambda_sets_to_specialize: _lambda_sets_to_specialize,
|
||||
extra_metadata: specialization_lsets,
|
||||
} => {
|
||||
let specialization_lsets: SpecializationLambdaSets = specialization_lsets
|
||||
.0
|
||||
.into_iter()
|
||||
.map(|((spec_member, region), var)| {
|
||||
debug_assert_eq!(spec_member, ability_member);
|
||||
(region, var)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Since we're doing `{foo} ~ a | a has Encoding`, we may see "lambda sets to
|
||||
// specialize" for e.g. `{foo}:toEncoder:1`, but these are actually just the
|
||||
// specialization lambda sets, so we don't need to do any extra work!
|
||||
//
|
||||
// If there are other lambda sets to specialize in here, that's unexpected, because
|
||||
// that means we would have been deriving something like `toEncoder {foo: bar}`,
|
||||
// and now seen that we needed `toEncoder bar` where `bar` is a concrete type. But
|
||||
// we only expect `bar` to polymorphic at this stage!
|
||||
//
|
||||
// TODO: it would be better if `unify` could prune these for us. See also
|
||||
// https://github.com/rtfeldman/roc/issues/3207; that is a blocker for this TODO.
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
for (spec_var, lambda_sets) in _lambda_sets_to_specialize.drain() {
|
||||
for lambda_set in lambda_sets {
|
||||
let belongs_to_specialized_lambda_sets =
|
||||
specialization_lsets.iter().any(|(_, var)| {
|
||||
self.subs.get_root_key_without_compacting(*var)
|
||||
== self.subs.get_root_key_without_compacting(lambda_set)
|
||||
});
|
||||
debug_assert!(belongs_to_specialized_lambda_sets,
|
||||
"Did not expect derivers to need to specialize unspecialized lambda sets, but we got one: {:?} for {:?}", lambda_set, spec_var)
|
||||
}
|
||||
}
|
||||
}
|
||||
specialization_lsets
|
||||
}
|
||||
Unified::Failure(..) | Unified::BadType(..) => {
|
||||
internal_error!("Unification failed in deriver - that's a deriver bug!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
84
crates/compiler/derive_key/src/decoding.rs
Normal file
84
crates/compiler/derive_key/src/decoding.rs
Normal file
@ -0,0 +1,84 @@
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_types::subs::{Content, FlatType, Subs, Variable};
|
||||
|
||||
use crate::DeriveError;
|
||||
|
||||
#[derive(Hash)]
|
||||
pub enum FlatDecodable {
|
||||
Immediate(Symbol),
|
||||
Key(FlatDecodableKey),
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq, Debug, Clone)]
|
||||
pub enum FlatDecodableKey {
|
||||
List(/* takes one variable */),
|
||||
}
|
||||
|
||||
impl FlatDecodableKey {
|
||||
pub(crate) fn debug_name(&self) -> String {
|
||||
match self {
|
||||
FlatDecodableKey::List() => "list".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FlatDecodable {
|
||||
pub(crate) fn from_var(subs: &Subs, var: Variable) -> Result<FlatDecodable, DeriveError> {
|
||||
use DeriveError::*;
|
||||
use FlatDecodable::*;
|
||||
match *subs.get_content_without_compacting(var) {
|
||||
Content::Structure(flat_type) => match flat_type {
|
||||
FlatType::Apply(sym, _) => match sym {
|
||||
Symbol::LIST_LIST => Ok(Key(FlatDecodableKey::List())),
|
||||
Symbol::STR_STR => Ok(Immediate(Symbol::DECODE_STRING)),
|
||||
_ => Err(Underivable),
|
||||
},
|
||||
FlatType::Record(_fields, _ext) => {
|
||||
Err(Underivable) // yet
|
||||
}
|
||||
FlatType::TagUnion(_tags, _ext) | FlatType::RecursiveTagUnion(_, _tags, _ext) => {
|
||||
Err(Underivable) // yet
|
||||
}
|
||||
FlatType::FunctionOrTagUnion(_name_index, _, _) => {
|
||||
Err(Underivable) // yet
|
||||
}
|
||||
FlatType::EmptyRecord => {
|
||||
Err(Underivable) // yet
|
||||
}
|
||||
FlatType::EmptyTagUnion => {
|
||||
Err(Underivable) // yet
|
||||
}
|
||||
//
|
||||
FlatType::Erroneous(_) => Err(Underivable),
|
||||
FlatType::Func(..) => Err(Underivable),
|
||||
},
|
||||
Content::Alias(sym, _, real_var, _) => match sym {
|
||||
Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8 => Ok(Immediate(Symbol::DECODE_U8)),
|
||||
Symbol::NUM_U16 | Symbol::NUM_UNSIGNED16 => Ok(Immediate(Symbol::DECODE_U16)),
|
||||
Symbol::NUM_U32 | Symbol::NUM_UNSIGNED32 => Ok(Immediate(Symbol::DECODE_U32)),
|
||||
Symbol::NUM_U64 | Symbol::NUM_UNSIGNED64 => Ok(Immediate(Symbol::DECODE_U64)),
|
||||
Symbol::NUM_U128 | Symbol::NUM_UNSIGNED128 => Ok(Immediate(Symbol::DECODE_U128)),
|
||||
Symbol::NUM_I8 | Symbol::NUM_SIGNED8 => Ok(Immediate(Symbol::DECODE_I8)),
|
||||
Symbol::NUM_I16 | Symbol::NUM_SIGNED16 => Ok(Immediate(Symbol::DECODE_I16)),
|
||||
Symbol::NUM_I32 | Symbol::NUM_SIGNED32 => Ok(Immediate(Symbol::DECODE_I32)),
|
||||
Symbol::NUM_I64 | Symbol::NUM_SIGNED64 => Ok(Immediate(Symbol::DECODE_I64)),
|
||||
Symbol::NUM_I128 | Symbol::NUM_SIGNED128 => Ok(Immediate(Symbol::DECODE_I128)),
|
||||
Symbol::NUM_DEC | Symbol::NUM_DECIMAL => Ok(Immediate(Symbol::DECODE_DEC)),
|
||||
Symbol::NUM_F32 | Symbol::NUM_BINARY32 => Ok(Immediate(Symbol::DECODE_F32)),
|
||||
Symbol::NUM_F64 | Symbol::NUM_BINARY64 => Ok(Immediate(Symbol::DECODE_F64)),
|
||||
// NB: I believe it is okay to unwrap opaques here because derivers are only used
|
||||
// by the backend, and the backend treats opaques like structural aliases.
|
||||
_ => Self::from_var(subs, real_var),
|
||||
},
|
||||
Content::RangedNumber(_) => Err(Underivable),
|
||||
//
|
||||
Content::RecursionVar { .. } => Err(Underivable),
|
||||
Content::Error => Err(Underivable),
|
||||
Content::FlexVar(_)
|
||||
| Content::RigidVar(_)
|
||||
| Content::FlexAbleVar(_, _)
|
||||
| Content::RigidAbleVar(_, _) => Err(UnboundVar),
|
||||
Content::LambdaSet(_) => Err(Underivable),
|
||||
}
|
||||
}
|
||||
}
|
@ -13,8 +13,10 @@
|
||||
//! For these reasons the content keying is based on a strategy as well, which are the variants of
|
||||
//! [`DeriveKey`].
|
||||
|
||||
pub mod decoding;
|
||||
pub mod encoding;
|
||||
|
||||
use decoding::{FlatDecodable, FlatDecodableKey};
|
||||
use encoding::{FlatEncodable, FlatEncodableKey};
|
||||
|
||||
use roc_module::symbol::Symbol;
|
||||
@ -33,15 +35,14 @@ pub enum DeriveError {
|
||||
#[repr(u8)]
|
||||
pub enum DeriveKey {
|
||||
ToEncoder(FlatEncodableKey),
|
||||
#[allow(unused)]
|
||||
Decoding,
|
||||
Decoder(FlatDecodableKey),
|
||||
}
|
||||
|
||||
impl DeriveKey {
|
||||
pub fn debug_name(&self) -> String {
|
||||
match self {
|
||||
DeriveKey::ToEncoder(key) => format!("toEncoder_{}", key.debug_name()),
|
||||
DeriveKey::Decoding => todo!(),
|
||||
DeriveKey::Decoder(key) => format!("decoder_{}", key.debug_name()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -61,6 +62,19 @@ pub enum Derived {
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum DeriveBuiltin {
|
||||
ToEncoder,
|
||||
Decoder,
|
||||
}
|
||||
|
||||
impl TryFrom<Symbol> for DeriveBuiltin {
|
||||
type Error = Symbol;
|
||||
|
||||
fn try_from(value: Symbol) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Symbol::ENCODE_TO_ENCODER => Ok(DeriveBuiltin::ToEncoder),
|
||||
Symbol::DECODE_DECODER => Ok(DeriveBuiltin::Decoder),
|
||||
_ => Err(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Derived {
|
||||
@ -74,6 +88,10 @@ impl Derived {
|
||||
FlatEncodable::Immediate(imm) => Ok(Derived::Immediate(imm)),
|
||||
FlatEncodable::Key(repr) => Ok(Derived::Key(DeriveKey::ToEncoder(repr))),
|
||||
},
|
||||
DeriveBuiltin::Decoder => match decoding::FlatDecodable::from_var(subs, var)? {
|
||||
FlatDecodable::Immediate(imm) => Ok(Derived::Immediate(imm)),
|
||||
FlatDecodable::Key(repr) => Ok(Derived::Key(DeriveKey::Decoder(repr))),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ roc_region = { path = "../region" }
|
||||
roc_module = { path = "../module" }
|
||||
roc_types = { path = "../types" }
|
||||
roc_can = { path = "../can" }
|
||||
roc_derive_key = { path = "../derive_key" }
|
||||
roc_derive = { path = "../derive" }
|
||||
roc_late_solve = { path = "../late_solve" }
|
||||
roc_std = { path = "../../roc_std", default-features = false }
|
||||
|
@ -4921,7 +4921,7 @@ pub fn with_hole<'a>(
|
||||
|
||||
let resolved_proc = match resolved_proc {
|
||||
Resolved::Specialization(symbol) => symbol,
|
||||
Resolved::NeedsGenerated => {
|
||||
Resolved::NeedsGenerated(_) => {
|
||||
todo_abilities!("Generate impls for structural types")
|
||||
}
|
||||
};
|
||||
@ -5236,8 +5236,22 @@ fn late_resolve_ability_specialization<'a>(
|
||||
|
||||
match specialization {
|
||||
Resolved::Specialization(symbol) => symbol,
|
||||
Resolved::NeedsGenerated => {
|
||||
todo_abilities!("Generate impls for structural types")
|
||||
Resolved::NeedsGenerated(var) => {
|
||||
let derive_key = roc_derive_key::Derived::builtin(
|
||||
roc_derive_key::DeriveBuiltin::Decoder,
|
||||
env.subs,
|
||||
var,
|
||||
)
|
||||
.expect("not a builtin");
|
||||
match derive_key {
|
||||
roc_derive_key::Derived::Immediate(imm) => {
|
||||
// The immediate is an ability member itself, so it must be resolved!
|
||||
late_resolve_ability_specialization(env, imm, None, specialization_var)
|
||||
}
|
||||
roc_derive_key::Derived::Key(_) => {
|
||||
todo_abilities!("support derived specializations that aren't immediates")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use roc_can::abilities::AbilitiesStore;
|
||||
use roc_can::expr::PendingDerives;
|
||||
use roc_collections::{VecMap, VecSet};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_error_macros::{internal_error, todo_abilities};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_solve_problem::{TypeError, UnderivableReason, Unfulfilled};
|
||||
@ -825,8 +825,8 @@ pub fn type_implementing_specialization(
|
||||
pub enum Resolved {
|
||||
/// A user-defined specialization should be used.
|
||||
Specialization(Symbol),
|
||||
/// A specialization must be generated.
|
||||
NeedsGenerated,
|
||||
/// A specialization must be generated for the given type variable.
|
||||
NeedsGenerated(Variable),
|
||||
}
|
||||
|
||||
/// An [`AbilityResolver`] is a shell of an abilities store that answers questions needed for
|
||||
@ -910,15 +910,17 @@ pub fn resolve_ability_specialization<R: AbilityResolver>(
|
||||
roc_types::types::MemberImpl::Impl(spec_symbol) => {
|
||||
Resolved::Specialization(spec_symbol)
|
||||
}
|
||||
roc_types::types::MemberImpl::Derived => Resolved::NeedsGenerated,
|
||||
roc_types::types::MemberImpl::Derived => {
|
||||
todo_abilities!("get type from obligated opaque")
|
||||
}
|
||||
// TODO this is not correct. We can replace `Resolved` with `MemberImpl` entirely,
|
||||
// which will make this simpler.
|
||||
roc_types::types::MemberImpl::Error => Resolved::Specialization(Symbol::UNDERSCORE),
|
||||
}
|
||||
}
|
||||
Obligated::Adhoc(_) => {
|
||||
Obligated::Adhoc(variable) => {
|
||||
// TODO: more rules need to be validated here, like is this a builtin ability?
|
||||
Resolved::NeedsGenerated
|
||||
Resolved::NeedsGenerated(variable)
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -12,7 +12,7 @@ use roc_debug_flags::dbg_do;
|
||||
#[cfg(debug_assertions)]
|
||||
use roc_debug_flags::ROC_TRACE_COMPACTION;
|
||||
use roc_derive::SharedDerivedModule;
|
||||
use roc_derive_key::{DeriveBuiltin, DeriveError, DeriveKey};
|
||||
use roc_derive_key::{DeriveError, DeriveKey};
|
||||
use roc_error_macros::{internal_error, todo_abilities};
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_types::{
|
||||
@ -655,13 +655,16 @@ fn make_specialization_decision<P: Phase>(
|
||||
}
|
||||
}
|
||||
Structure(_) | Alias(_, _, _, _) => {
|
||||
// This is a structural type, find the name of the derived ability function it
|
||||
// should use.
|
||||
match roc_derive_key::Derived::builtin(DeriveBuiltin::ToEncoder, subs, var) {
|
||||
let builtin = match ability_member.try_into() {
|
||||
Ok(builtin) => builtin,
|
||||
Err(_) => return SpecializeDecision::Drop,
|
||||
};
|
||||
|
||||
// This is a structural type, find the derived ability function it should use.
|
||||
match roc_derive_key::Derived::builtin(builtin, subs, var) {
|
||||
Ok(derived) => match derived {
|
||||
roc_derive_key::Derived::Immediate(imm) => {
|
||||
SpecializeDecision::Specialize(Immediate(imm))
|
||||
// todo!("deal with lambda set extraction from immediates")
|
||||
}
|
||||
roc_derive_key::Derived::Key(derive_key) => {
|
||||
SpecializeDecision::Specialize(Derived(derive_key))
|
||||
|
29
crates/compiler/test_derive/src/decoding.rs
Normal file
29
crates/compiler/test_derive/src/decoding.rs
Normal file
@ -0,0 +1,29 @@
|
||||
#![cfg(test)]
|
||||
// Even with #[allow(non_snake_case)] on individual idents, rust-analyzer issues diagnostics.
|
||||
// See https://github.com/rust-lang/rust-analyzer/issues/6541.
|
||||
// For the `v!` macro we use uppercase variables when constructing tag unions.
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::{util::check_immediate, v};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_types::subs::{Subs, Variable};
|
||||
|
||||
use roc_derive_key::DeriveBuiltin::Decoder;
|
||||
|
||||
#[test]
|
||||
fn immediates() {
|
||||
check_immediate(Decoder, v!(U8), Symbol::DECODE_U8);
|
||||
check_immediate(Decoder, v!(U16), Symbol::DECODE_U16);
|
||||
check_immediate(Decoder, v!(U32), Symbol::DECODE_U32);
|
||||
check_immediate(Decoder, v!(U64), Symbol::DECODE_U64);
|
||||
check_immediate(Decoder, v!(U128), Symbol::DECODE_U128);
|
||||
check_immediate(Decoder, v!(I8), Symbol::DECODE_I8);
|
||||
check_immediate(Decoder, v!(I16), Symbol::DECODE_I16);
|
||||
check_immediate(Decoder, v!(I32), Symbol::DECODE_I32);
|
||||
check_immediate(Decoder, v!(I64), Symbol::DECODE_I64);
|
||||
check_immediate(Decoder, v!(I128), Symbol::DECODE_I128);
|
||||
check_immediate(Decoder, v!(DEC), Symbol::DECODE_DEC);
|
||||
check_immediate(Decoder, v!(F32), Symbol::DECODE_F32);
|
||||
check_immediate(Decoder, v!(F64), Symbol::DECODE_F64);
|
||||
check_immediate(Decoder, v!(STR), Symbol::DECODE_STRING);
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#![cfg(test)]
|
||||
|
||||
mod decoding;
|
||||
mod encoding;
|
||||
|
||||
mod pretty_print;
|
||||
|
@ -45,6 +45,11 @@ fn module_source_and_path(builtin: DeriveBuiltin) -> (ModuleId, &'static str, Pa
|
||||
module_source(ModuleId::ENCODE),
|
||||
builtins_path.join("Encode.roc"),
|
||||
),
|
||||
DeriveBuiltin::Decoder => (
|
||||
ModuleId::DECODE,
|
||||
module_source(ModuleId::DECODE),
|
||||
builtins_path.join("Decode.roc"),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -437,7 +437,7 @@ mod encode_immediate {
|
||||
macro_rules! num_immediate {
|
||||
($($num:expr, $typ:ident)*) => {$(
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn $typ() {
|
||||
assert_evals_to!(
|
||||
&format!(indoc!(
|
||||
@ -780,3 +780,95 @@ fn decode_use_stdlib_json_list() {
|
||||
RocList<u8>
|
||||
)
|
||||
}
|
||||
|
||||
mod decode_immediate {
|
||||
#[cfg(feature = "gen-llvm")]
|
||||
use crate::helpers::llvm::assert_evals_to;
|
||||
|
||||
#[cfg(feature = "gen-wasm")]
|
||||
use crate::helpers::wasm::assert_evals_to;
|
||||
|
||||
#[cfg(all(test, any(feature = "gen-llvm", feature = "gen-wasm")))]
|
||||
use indoc::indoc;
|
||||
|
||||
#[cfg(all(test, any(feature = "gen-llvm", feature = "gen-wasm")))]
|
||||
use roc_std::RocStr;
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn string() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Decode, Json] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
when Str.toUtf8 "\"foo\"" |> Decode.fromBytes Json.fromUtf8 is
|
||||
Ok s -> s
|
||||
_ -> "<bad>"
|
||||
"#
|
||||
),
|
||||
RocStr::from("foo"),
|
||||
RocStr
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! num_immediate {
|
||||
($($num:expr, $typ:ident)*) => {$(
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn $typ() {
|
||||
assert_evals_to!(
|
||||
&format!(indoc!(
|
||||
r#"
|
||||
app "test" imports [Decode, Json] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
when Num.toStr {}{} |> Str.toUtf8 |> Decode.fromBytes Json.fromUtf8 is
|
||||
Ok n -> n
|
||||
_ -> 101{}
|
||||
"#
|
||||
), $num, stringify!($typ), stringify!($typ)),
|
||||
$num,
|
||||
$typ
|
||||
)
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
num_immediate! {
|
||||
17, i8
|
||||
17, i16
|
||||
17, i32
|
||||
17, i64
|
||||
17, i128
|
||||
17, u8
|
||||
17, u16
|
||||
17, u32
|
||||
17, u64
|
||||
17, u128
|
||||
17.23, f32
|
||||
17.23, f64
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn dec() {
|
||||
use roc_std::RocDec;
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" imports [Decode, Json] provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
when Num.toStr 17.23dec |> Str.toUtf8 |> Decode.fromBytes Json.fromUtf8 is
|
||||
Ok n -> n
|
||||
_ -> 101dec
|
||||
"#
|
||||
),
|
||||
RocDec::from_str("17.23").unwrap(),
|
||||
RocDec
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user