mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +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_F32 | Symbol::NUM_BINARY32 => Ok(Immediate(Symbol::ENCODE_F32)),
|
||||
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
|
||||
// by the backend, and the backend treats opaques like structural aliases.
|
||||
_ => Self::from_var(subs, real_var),
|
||||
|
|
|
@ -8,8 +8,8 @@ use roc_error_macros::internal_error;
|
|||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_solve_problem::{
|
||||
NotDerivableContext, NotDerivableDecode, NotDerivableEq, TypeError, UnderivableReason,
|
||||
Unfulfilled,
|
||||
NotDerivableContext, NotDerivableDecode, NotDerivableEncode, NotDerivableEq, TypeError,
|
||||
UnderivableReason, Unfulfilled,
|
||||
};
|
||||
use roc_types::num::NumericRange;
|
||||
use roc_types::subs::{
|
||||
|
@ -451,9 +451,9 @@ impl ObligationCache {
|
|||
|
||||
#[inline(always)]
|
||||
#[rustfmt::skip]
|
||||
fn is_builtin_int_alias(symbol: Symbol) -> bool {
|
||||
fn is_builtin_fixed_int_alias(symbol: Symbol) -> bool {
|
||||
matches!(symbol,
|
||||
Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8
|
||||
| Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8
|
||||
| Symbol::NUM_U16 | Symbol::NUM_UNSIGNED16
|
||||
| Symbol::NUM_U32 | Symbol::NUM_UNSIGNED32
|
||||
| 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_I64 | Symbol::NUM_SIGNED64
|
||||
| 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)]
|
||||
#[rustfmt::skip]
|
||||
fn is_builtin_float_alias(symbol: Symbol) -> bool {
|
||||
|
@ -477,17 +481,16 @@ fn is_builtin_float_alias(symbol: Symbol) -> bool {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[rustfmt::skip]
|
||||
fn is_builtin_dec_alias(symbol: Symbol) -> bool {
|
||||
matches!(symbol,
|
||||
| Symbol::NUM_DEC | Symbol::NUM_DECIMAL,
|
||||
)
|
||||
matches!(symbol, Symbol::NUM_DEC | Symbol::NUM_DECIMAL,)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[rustfmt::skip]
|
||||
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 {
|
||||
|
@ -826,7 +829,7 @@ impl DerivableVisitor for DeriveEncoding {
|
|||
|
||||
#[inline(always)]
|
||||
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)]
|
||||
|
@ -884,9 +887,16 @@ impl DerivableVisitor for DeriveEncoding {
|
|||
}
|
||||
|
||||
#[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) {
|
||||
Ok(Descend(false))
|
||||
if is_builtin_nat_alias(symbol) {
|
||||
Err(NotDerivable {
|
||||
var,
|
||||
context: NotDerivableContext::Encode(NotDerivableEncode::Nat),
|
||||
})
|
||||
} else {
|
||||
Ok(Descend(false))
|
||||
}
|
||||
} else {
|
||||
Ok(Descend(true))
|
||||
}
|
||||
|
@ -914,7 +924,7 @@ impl DerivableVisitor for DeriveDecoding {
|
|||
|
||||
#[inline(always)]
|
||||
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)]
|
||||
|
@ -983,9 +993,16 @@ impl DerivableVisitor for DeriveDecoding {
|
|||
}
|
||||
|
||||
#[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) {
|
||||
Ok(Descend(false))
|
||||
if is_builtin_nat_alias(symbol) {
|
||||
Err(NotDerivable {
|
||||
var,
|
||||
context: NotDerivableContext::Decode(NotDerivableDecode::Nat),
|
||||
})
|
||||
} else {
|
||||
Ok(Descend(false))
|
||||
}
|
||||
} else {
|
||||
Ok(Descend(true))
|
||||
}
|
||||
|
@ -1112,7 +1129,9 @@ impl DerivableVisitor for DeriveEq {
|
|||
|
||||
#[inline(always)]
|
||||
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)]
|
||||
|
|
|
@ -87,12 +87,19 @@ pub enum NotDerivableContext {
|
|||
Function,
|
||||
UnboundVar,
|
||||
Opaque(Symbol),
|
||||
Encode(NotDerivableEncode),
|
||||
Decode(NotDerivableDecode),
|
||||
Eq(NotDerivableEq),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub enum NotDerivableEncode {
|
||||
Nat,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub enum NotDerivableDecode {
|
||||
Nat,
|
||||
OptionalRecordField(Lowercase),
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ use roc_module::symbol::Symbol;
|
|||
use roc_problem::Severity;
|
||||
use roc_region::all::{LineInfo, Region};
|
||||
use roc_solve_problem::{
|
||||
NotDerivableContext, NotDerivableDecode, NotDerivableEq, TypeError, UnderivableReason,
|
||||
Unfulfilled,
|
||||
NotDerivableContext, NotDerivableDecode, NotDerivableEncode, NotDerivableEq, TypeError,
|
||||
UnderivableReason, Unfulfilled,
|
||||
};
|
||||
use roc_std::RocDec;
|
||||
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 {
|
||||
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) => {
|
||||
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 "),
|
||||
|
|
|
@ -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
|
||||
expect
|
||||
State : { answer : Nat }
|
||||
State : { answer : U32 }
|
||||
|
||||
diffStateBefore : DiffState State
|
||||
diffStateBefore = {
|
||||
|
@ -778,7 +778,7 @@ expect
|
|||
|
||||
# initClientAppHelp
|
||||
expect
|
||||
State : { answer : Nat }
|
||||
State : { answer : U32 }
|
||||
|
||||
init = \result ->
|
||||
when result is
|
||||
|
|
|
@ -68,20 +68,20 @@ initServerApp = \app, initData, hostJavaScript ->
|
|||
|
||||
insertRocScript : Html [], initData, Str, Str -> Result (Html []) [InvalidDocument] | initData has Encoding
|
||||
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.
|
||||
# JSON won't have invalid UTF-8 in it, since it would be escaped as part of JSON encoding.
|
||||
jsInitData =
|
||||
initData
|
||||
|> Encode.toBytes Json.toUtf8
|
||||
|> Str.fromUtf8
|
||||
|> Encode.toBytes Json.toUtf8
|
||||
|> Str.fromUtf8
|
||||
|> Result.withDefault ""
|
||||
initData |> encode |> encode
|
||||
|
||||
jsWasmUrl =
|
||||
wasmUrl
|
||||
|> Encode.toBytes Json.toUtf8
|
||||
|> Str.fromUtf8
|
||||
|> Result.withDefault ""
|
||||
encode wasmUrl
|
||||
|
||||
script : Html []
|
||||
script = (element "script") [] [
|
||||
|
|
|
@ -43,7 +43,7 @@ Attribute state : [
|
|||
|
||||
CyclicStructureAccessor : [
|
||||
ObjectField Str CyclicStructureAccessor,
|
||||
ArrayIndex Nat CyclicStructureAccessor,
|
||||
ArrayIndex U32 CyclicStructureAccessor,
|
||||
SerializableValue,
|
||||
]
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue