Merge pull request #4974 from roc-lang/no-encode-decode-nat

Display Encode/Decode of `Nat`s
This commit is contained in:
Ayaz 2023-01-28 16:54:51 -06:00 committed by GitHub
commit 9a5a77ee3a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 146 additions and 33 deletions

View file

@ -126,6 +126,7 @@ impl FlatEncodable {
Symbol::NUM_DEC | Symbol::NUM_DECIMAL => Ok(Immediate(Symbol::ENCODE_DEC)), Symbol::NUM_DEC | Symbol::NUM_DECIMAL => Ok(Immediate(Symbol::ENCODE_DEC)),
Symbol::NUM_F32 | Symbol::NUM_BINARY32 => Ok(Immediate(Symbol::ENCODE_F32)), Symbol::NUM_F32 | Symbol::NUM_BINARY32 => Ok(Immediate(Symbol::ENCODE_F32)),
Symbol::NUM_F64 | Symbol::NUM_BINARY64 => Ok(Immediate(Symbol::ENCODE_F64)), Symbol::NUM_F64 | Symbol::NUM_BINARY64 => Ok(Immediate(Symbol::ENCODE_F64)),
Symbol::NUM_NAT | Symbol::NUM_NATURAL => Err(Underivable),
// TODO: I believe it is okay to unwrap opaques here because derivers are only used // TODO: 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. // by the backend, and the backend treats opaques like structural aliases.
_ => Self::from_var(subs, real_var), _ => Self::from_var(subs, real_var),

View file

@ -8,8 +8,8 @@ use roc_error_macros::internal_error;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
use roc_solve_problem::{ use roc_solve_problem::{
NotDerivableContext, NotDerivableDecode, NotDerivableEq, TypeError, UnderivableReason, NotDerivableContext, NotDerivableDecode, NotDerivableEncode, NotDerivableEq, TypeError,
Unfulfilled, UnderivableReason, Unfulfilled,
}; };
use roc_types::num::NumericRange; use roc_types::num::NumericRange;
use roc_types::subs::{ use roc_types::subs::{
@ -451,9 +451,9 @@ impl ObligationCache {
#[inline(always)] #[inline(always)]
#[rustfmt::skip] #[rustfmt::skip]
fn is_builtin_int_alias(symbol: Symbol) -> bool { fn is_builtin_fixed_int_alias(symbol: Symbol) -> bool {
matches!(symbol, matches!(symbol,
Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8 | Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8
| Symbol::NUM_U16 | Symbol::NUM_UNSIGNED16 | Symbol::NUM_U16 | Symbol::NUM_UNSIGNED16
| Symbol::NUM_U32 | Symbol::NUM_UNSIGNED32 | Symbol::NUM_U32 | Symbol::NUM_UNSIGNED32
| Symbol::NUM_U64 | Symbol::NUM_UNSIGNED64 | Symbol::NUM_U64 | Symbol::NUM_UNSIGNED64
@ -463,10 +463,14 @@ fn is_builtin_int_alias(symbol: Symbol) -> bool {
| Symbol::NUM_I32 | Symbol::NUM_SIGNED32 | Symbol::NUM_I32 | Symbol::NUM_SIGNED32
| Symbol::NUM_I64 | Symbol::NUM_SIGNED64 | Symbol::NUM_I64 | Symbol::NUM_SIGNED64
| Symbol::NUM_I128 | Symbol::NUM_SIGNED128 | Symbol::NUM_I128 | Symbol::NUM_SIGNED128
| Symbol::NUM_NAT | Symbol::NUM_NATURAL
) )
} }
#[inline(always)]
fn is_builtin_nat_alias(symbol: Symbol) -> bool {
matches!(symbol, Symbol::NUM_NAT | Symbol::NUM_NATURAL)
}
#[inline(always)] #[inline(always)]
#[rustfmt::skip] #[rustfmt::skip]
fn is_builtin_float_alias(symbol: Symbol) -> bool { fn is_builtin_float_alias(symbol: Symbol) -> bool {
@ -477,17 +481,16 @@ fn is_builtin_float_alias(symbol: Symbol) -> bool {
} }
#[inline(always)] #[inline(always)]
#[rustfmt::skip]
fn is_builtin_dec_alias(symbol: Symbol) -> bool { fn is_builtin_dec_alias(symbol: Symbol) -> bool {
matches!(symbol, matches!(symbol, Symbol::NUM_DEC | Symbol::NUM_DECIMAL,)
| Symbol::NUM_DEC | Symbol::NUM_DECIMAL,
)
} }
#[inline(always)] #[inline(always)]
#[rustfmt::skip]
fn is_builtin_number_alias(symbol: Symbol) -> bool { fn is_builtin_number_alias(symbol: Symbol) -> bool {
is_builtin_int_alias(symbol) || is_builtin_float_alias(symbol) || is_builtin_dec_alias(symbol) is_builtin_fixed_int_alias(symbol)
|| is_builtin_nat_alias(symbol)
|| is_builtin_float_alias(symbol)
|| is_builtin_dec_alias(symbol)
} }
struct NotDerivable { struct NotDerivable {
@ -826,7 +829,7 @@ impl DerivableVisitor for DeriveEncoding {
#[inline(always)] #[inline(always)]
fn is_derivable_builtin_opaque(symbol: Symbol) -> bool { fn is_derivable_builtin_opaque(symbol: Symbol) -> bool {
is_builtin_number_alias(symbol) is_builtin_number_alias(symbol) && !is_builtin_nat_alias(symbol)
} }
#[inline(always)] #[inline(always)]
@ -884,9 +887,16 @@ impl DerivableVisitor for DeriveEncoding {
} }
#[inline(always)] #[inline(always)]
fn visit_alias(_var: Variable, symbol: Symbol) -> Result<Descend, NotDerivable> { fn visit_alias(var: Variable, symbol: Symbol) -> Result<Descend, NotDerivable> {
if is_builtin_number_alias(symbol) { if is_builtin_number_alias(symbol) {
if is_builtin_nat_alias(symbol) {
Err(NotDerivable {
var,
context: NotDerivableContext::Encode(NotDerivableEncode::Nat),
})
} else {
Ok(Descend(false)) Ok(Descend(false))
}
} else { } else {
Ok(Descend(true)) Ok(Descend(true))
} }
@ -914,7 +924,7 @@ impl DerivableVisitor for DeriveDecoding {
#[inline(always)] #[inline(always)]
fn is_derivable_builtin_opaque(symbol: Symbol) -> bool { fn is_derivable_builtin_opaque(symbol: Symbol) -> bool {
is_builtin_number_alias(symbol) is_builtin_number_alias(symbol) && !is_builtin_nat_alias(symbol)
} }
#[inline(always)] #[inline(always)]
@ -983,9 +993,16 @@ impl DerivableVisitor for DeriveDecoding {
} }
#[inline(always)] #[inline(always)]
fn visit_alias(_var: Variable, symbol: Symbol) -> Result<Descend, NotDerivable> { fn visit_alias(var: Variable, symbol: Symbol) -> Result<Descend, NotDerivable> {
if is_builtin_number_alias(symbol) { if is_builtin_number_alias(symbol) {
if is_builtin_nat_alias(symbol) {
Err(NotDerivable {
var,
context: NotDerivableContext::Decode(NotDerivableDecode::Nat),
})
} else {
Ok(Descend(false)) Ok(Descend(false))
}
} else { } else {
Ok(Descend(true)) Ok(Descend(true))
} }
@ -1112,7 +1129,9 @@ impl DerivableVisitor for DeriveEq {
#[inline(always)] #[inline(always)]
fn is_derivable_builtin_opaque(symbol: Symbol) -> bool { fn is_derivable_builtin_opaque(symbol: Symbol) -> bool {
is_builtin_int_alias(symbol) || is_builtin_dec_alias(symbol) is_builtin_fixed_int_alias(symbol)
|| is_builtin_nat_alias(symbol)
|| is_builtin_dec_alias(symbol)
} }
#[inline(always)] #[inline(always)]

View file

@ -87,12 +87,19 @@ pub enum NotDerivableContext {
Function, Function,
UnboundVar, UnboundVar,
Opaque(Symbol), Opaque(Symbol),
Encode(NotDerivableEncode),
Decode(NotDerivableDecode), Decode(NotDerivableDecode),
Eq(NotDerivableEq), Eq(NotDerivableEq),
} }
#[derive(PartialEq, Eq, Debug, Clone)]
pub enum NotDerivableEncode {
Nat,
}
#[derive(PartialEq, Eq, Debug, Clone)] #[derive(PartialEq, Eq, Debug, Clone)]
pub enum NotDerivableDecode { pub enum NotDerivableDecode {
Nat,
OptionalRecordField(Lowercase), OptionalRecordField(Lowercase),
} }

View file

@ -13,8 +13,8 @@ use roc_module::symbol::Symbol;
use roc_problem::Severity; use roc_problem::Severity;
use roc_region::all::{LineInfo, Region}; use roc_region::all::{LineInfo, Region};
use roc_solve_problem::{ use roc_solve_problem::{
NotDerivableContext, NotDerivableDecode, NotDerivableEq, TypeError, UnderivableReason, NotDerivableContext, NotDerivableDecode, NotDerivableEncode, NotDerivableEq, TypeError,
Unfulfilled, UnderivableReason, Unfulfilled,
}; };
use roc_std::RocDec; use roc_std::RocDec;
use roc_types::pretty_print::{Parens, WILDCARD}; use roc_types::pretty_print::{Parens, WILDCARD};
@ -371,7 +371,29 @@ fn underivable_hint<'b>(
])), ])),
]))) ])))
} }
NotDerivableContext::Encode(reason) => match reason {
NotDerivableEncode::Nat => {
Some(alloc.note("").append(alloc.concat([
alloc.reflow("Encoding a "),
alloc.type_str("Nat"),
alloc.reflow(" is not supported. Consider using a fixed-sized unsigned integer, like a "),
alloc.type_str("U64"),
alloc.reflow(" instead."),
])))
}
},
NotDerivableContext::Decode(reason) => match reason { NotDerivableContext::Decode(reason) => match reason {
NotDerivableDecode::Nat => {
Some(alloc.note("").append(alloc.concat([
alloc.reflow("Decoding to a "),
alloc.type_str("Nat"),
alloc.reflow(" is not supported. Consider decoding to a fixed-sized unsigned integer, like "),
alloc.type_str("U64"),
alloc.reflow(", then converting to a "),
alloc.type_str("Nat"),
alloc.reflow(" if needed."),
])))
}
NotDerivableDecode::OptionalRecordField(field) => { NotDerivableDecode::OptionalRecordField(field) => {
Some(alloc.note("").append(alloc.concat([ Some(alloc.note("").append(alloc.concat([
alloc.reflow("I can't derive decoding for a record with an optional field, which in this case is "), alloc.reflow("I can't derive decoding for a record with an optional field, which in this case is "),

View file

@ -13028,4 +13028,68 @@ I recommend using camelCase. It's the standard style in Roc code!
"# "#
) )
); );
test_report!(
derive_decoding_for_nat,
indoc!(
r#"
app "test" imports [Decode.{decoder}] provides [main] to "./platform"
main =
myDecoder : Decoder Nat 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
^^^^^^^
I can't generate an implementation of the `Decoding` ability for
Nat
Note: Decoding to a Nat is not supported. Consider decoding to a
fixed-sized unsigned integer, like U64, then converting to a Nat if
needed.
"###
);
test_report!(
derive_encoding_for_nat,
indoc!(
r#"
app "test" imports [] provides [main] to "./platform"
x : Nat
main = Encode.toEncoder x
"#
),
@r###"
TYPE MISMATCH /code/proj/Main.roc
This expression has a type that does not implement the abilities it's expected to:
5 main = Encode.toEncoder x
^
I can't generate an implementation of the `Encoding` ability for
Int Natural
In particular, an implementation for
Natural
cannot be generated.
Tip: `Natural` does not implement `Encoding`.
"###
);
} }

View file

@ -723,7 +723,7 @@ expect
# diff # diff
expect expect
State : { answer : Nat } State : { answer : U32 }
diffStateBefore : DiffState State diffStateBefore : DiffState State
diffStateBefore = { diffStateBefore = {
@ -778,7 +778,7 @@ expect
# initClientAppHelp # initClientAppHelp
expect expect
State : { answer : Nat } State : { answer : U32 }
init = \result -> init = \result ->
when result is when result is

View file

@ -68,20 +68,20 @@ initServerApp = \app, initData, hostJavaScript ->
insertRocScript : Html [], initData, Str, Str -> Result (Html []) [InvalidDocument] | initData has Encoding insertRocScript : Html [], initData, Str, Str -> Result (Html []) [InvalidDocument] | initData has Encoding
insertRocScript = \document, initData, wasmUrl, hostJavaScript -> insertRocScript = \document, initData, wasmUrl, hostJavaScript ->
encode =
\value ->
value
|> Encode.toBytes Json.toUtf8
|> Str.fromUtf8
|> Result.withDefault ""
# Convert initData to JSON as a Roc Str, then convert the Roc Str to a JS string. # Convert initData to JSON as a Roc Str, then convert the Roc Str to a JS string.
# JSON won't have invalid UTF-8 in it, since it would be escaped as part of JSON encoding. # JSON won't have invalid UTF-8 in it, since it would be escaped as part of JSON encoding.
jsInitData = jsInitData =
initData initData |> encode |> encode
|> Encode.toBytes Json.toUtf8
|> Str.fromUtf8
|> Encode.toBytes Json.toUtf8
|> Str.fromUtf8
|> Result.withDefault ""
jsWasmUrl = jsWasmUrl =
wasmUrl encode wasmUrl
|> Encode.toBytes Json.toUtf8
|> Str.fromUtf8
|> Result.withDefault ""
script : Html [] script : Html []
script = (element "script") [] [ script = (element "script") [] [

View file

@ -43,7 +43,7 @@ Attribute state : [
CyclicStructureAccessor : [ CyclicStructureAccessor : [
ObjectField Str CyclicStructureAccessor, ObjectField Str CyclicStructureAccessor,
ArrayIndex Nat CyclicStructureAccessor, ArrayIndex U32 CyclicStructureAccessor,
SerializableValue, SerializableValue,
] ]