Merge pull request #3680 from rtfeldman/derive-decoders-1

Derive immediate decoders for basic types
This commit is contained in:
Folkert de Vries 2022-08-05 00:07:52 +02:00 committed by GitHub
commit 510016bf6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 443 additions and 183 deletions

1
Cargo.lock generated
View File

@ -3859,6 +3859,7 @@ dependencies = [
"roc_collections",
"roc_debug_flags",
"roc_derive",
"roc_derive_key",
"roc_error_macros",
"roc_exhaustive",
"roc_late_solve",

View File

@ -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

View File

@ -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]);

View File

@ -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 {

View 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!")
}
}
}
}

View 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),
}
}
}

View File

@ -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))),
},
}
}
}

View File

@ -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 }

View File

@ -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")
}
}
}
}
}

View File

@ -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)
}
};

View File

@ -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))

View 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);
}

View File

@ -1,5 +1,6 @@
#![cfg(test)]
mod decoding;
mod encoding;
mod pretty_print;

View File

@ -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"),
),
}
}

View File

@ -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
)
}
}