mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 21:39:07 +00:00
Merge pull request #4974 from roc-lang/no-encode-decode-nat
Display Encode/Decode of `Nat`s
This commit is contained in:
commit
9a5a77ee3a
8 changed files with 146 additions and 33 deletions
|
@ -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),
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 "),
|
||||||
|
|
|
@ -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`.
|
||||||
|
"###
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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") [] [
|
||||||
|
|
|
@ -43,7 +43,7 @@ Attribute state : [
|
||||||
|
|
||||||
CyclicStructureAccessor : [
|
CyclicStructureAccessor : [
|
||||||
ObjectField Str CyclicStructureAccessor,
|
ObjectField Str CyclicStructureAccessor,
|
||||||
ArrayIndex Nat CyclicStructureAccessor,
|
ArrayIndex U32 CyclicStructureAccessor,
|
||||||
SerializableValue,
|
SerializableValue,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue