Record with optionally-typed fields cannot be derived for decoding

This commit is contained in:
Ayaz Hafiz 2022-08-09 08:15:09 -07:00
parent 958f64c8fc
commit d2b9cc056f
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
2 changed files with 64 additions and 7 deletions

View file

@ -6,8 +6,10 @@ use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region};
use roc_solve_problem::{TypeError, UnderivableReason, Unfulfilled};
use roc_types::num::NumericRange;
use roc_types::subs::{instantiate_rigids, Content, FlatType, GetSubsSlice, Rank, Subs, Variable};
use roc_types::types::{AliasKind, Category, MemberImpl, PatternCategory};
use roc_types::subs::{
instantiate_rigids, Content, FlatType, GetSubsSlice, Rank, RecordFields, Subs, Variable,
};
use roc_types::types::{AliasKind, Category, MemberImpl, PatternCategory, RecordField};
use roc_unify::unify::{Env, MustImplementConstraints};
use roc_unify::unify::{MustImplementAbility, Obligated};
@ -476,7 +478,11 @@ trait DerivableVisitor {
}
#[inline(always)]
fn visit_record(var: Variable) -> Result<Descend, DerivableError> {
fn visit_record(
_subs: &Subs,
var: Variable,
_fields: RecordFields,
) -> Result<Descend, DerivableError> {
Err(DerivableError::NotDerivable(var))
}
@ -574,7 +580,7 @@ trait DerivableVisitor {
}
}
Record(fields, ext) => {
let descend = Self::visit_record(var)?;
let descend = Self::visit_record(subs, var, fields)?;
if descend.0 {
push_var_slice!(fields.variables());
if !matches!(
@ -682,7 +688,11 @@ impl DerivableVisitor for DeriveEncoding {
}
#[inline(always)]
fn visit_record(_var: Variable) -> Result<Descend, DerivableError> {
fn visit_record(
_subs: &Subs,
_var: Variable,
_fields: RecordFields,
) -> Result<Descend, DerivableError> {
Ok(Descend(true))
}
@ -753,8 +763,21 @@ impl DerivableVisitor for DeriveDecoding {
}
#[inline(always)]
fn visit_record(_var: Variable) -> Result<Descend, DerivableError> {
Ok(Descend(true))
fn visit_record(
subs: &Subs,
var: Variable,
fields: RecordFields,
) -> Result<Descend, DerivableError> {
let has_optional_field = subs
.get_subs_slice(fields.record_fields())
.iter()
.any(|field| matches!(field, RecordField::Optional(..)));
if has_optional_field {
Err(DerivableError::NotDerivable(var))
} else {
Ok(Descend(true))
}
}
#[inline(always)]

View file

@ -10507,4 +10507,38 @@ All branches in an `if` must have the same type!
Note: `Decoding` cannot be generated for functions.
"###
);
test_report!(
record_with_optional_field_types_cannot_derive_decoding,
indoc!(
r#"
app "test" imports [Decode.{Decoder, DecoderFormatting, decoder}] provides [main] to "./platform"
main =
myDecoder : Decoder {x : Str, y ? Str} fmt | fmt has DecoderFormatting
myDecoder = decoder
myDecoder
"#
),
@r###"
TYPE MISMATCH /code/proj/Main.roc
This expression has a type that does not implement the abilities it's expected to:
5 myDecoder = decoder
^^^^^^^
Roc can't generate an implementation of the `Decode.Decoding` ability
for
{ x : Str, y ? Str }
Note: I can't derive decoding for a record with an optional field,
which in this case is `.y`. Optional record fields are polymorphic over
records that may or may not contain them at compile time, but are not
a concept that extends to runtime!
Maybe you wanted to use a `Result`?
"###
);
}