diff --git a/CHANGELOG.md b/CHANGELOG.md index 8785830db09..a2e7a43d9c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -207,6 +207,11 @@ ([Richard Viney](https://github.com/richard-viney)) +- When targeting JavaScript the compiler now generates faster and smaller code + for `Int` values in bit array expressions and patterns by evaluating them at + compile time where possible. + ([Richard Viney](https://github.com/richard-viney)) + ### Formatter - The formatter no longer removes the first argument from a function diff --git a/Cargo.lock b/Cargo.lock index 19186ac9d9e..fd1141a559a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -876,6 +876,7 @@ dependencies = [ "lsp-server", "lsp-types", "num-bigint", + "num-traits", "pathdiff", "petgraph", "pretty_assertions", @@ -1440,9 +1441,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] diff --git a/compiler-core/Cargo.toml b/compiler-core/Cargo.toml index aa98c855614..281d5044be1 100644 --- a/compiler-core/Cargo.toml +++ b/compiler-core/Cargo.toml @@ -46,6 +46,7 @@ unicode-segmentation = "1.12.0" bimap = "0.6.3" # Parsing of arbitrary width int values num-bigint = "0.4.6" +num-traits = "0.2.19" async-trait.workspace = true base16.workspace = true bytes.workspace = true diff --git a/compiler-core/src/javascript.rs b/compiler-core/src/javascript.rs index 82becb1ecf3..a670455820d 100644 --- a/compiler-core/src/javascript.rs +++ b/compiler-core/src/javascript.rs @@ -6,6 +6,9 @@ mod pattern; mod tests; mod typescript; +use num_bigint::BigInt; +use num_traits::{One, ToPrimitive}; + use crate::analyse::TargetSupport; use crate::build::Target; use crate::codegen::TypeScriptDeclarations; @@ -785,3 +788,56 @@ fn bool(bool: bool) -> Document<'static> { false => "false".to_doc(), } } + +/// Int segments <= 48 bits wide in bit arrays are within JavaScript's safe range and are evaluated +/// at compile time when all inputs are known. This is done for both bit array expressions and +/// pattern matching. +/// +/// Int segments of any size could be evaluated at compile time, but currently aren't due to the +/// potential for causing large generated JS for inputs such as `<<0:8192>>`. +/// +pub(crate) const SAFE_INT_SEGMENT_MAX_SIZE: usize = 48; + +/// Evaluates the value of an Int segment in a bit array into its corresponding bytes. This avoids +/// needing to do the evaluation at runtime when all inputs are known at compile-time. +/// +pub(crate) fn bit_array_segment_int_value_to_bytes( + mut value: BigInt, + size: BigInt, + endianness: endianness::Endianness, + location: SrcSpan, +) -> Result, Error> { + // Clamp negative sizes to zero + let size = size.max(BigInt::ZERO); + + // The segment size in bits is not allowed to exceed the range of a u32. At runtime the + // limit is lower than this and depends on the JS engine. V8's limit is currently + // 2^30-1 bits. + let size = size.to_u32().ok_or_else(|| Error::Unsupported { + feature: "Integer segment size greater than 2^32-1".into(), + location, + })?; + + // Convert negative number to two's complement representation + if value < BigInt::ZERO { + let value_modulus = BigInt::one() << size; + value = &value_modulus + (value % &value_modulus); + } + + let byte_mask = BigInt::from(0xFF); + + // Convert value to the desired number of bytes + let mut bytes = vec![0u8; size as usize / 8]; + for byte in bytes.iter_mut() { + *byte = (&value & &byte_mask) + .to_u8() + .expect("bitwise and result to be a u8"); + value >>= 8; + } + + if endianness.is_big() { + bytes.reverse(); + } + + Ok(bytes) +} diff --git a/compiler-core/src/javascript/expression.rs b/compiler-core/src/javascript/expression.rs index 5892fabe5b8..0bd616e1903 100644 --- a/compiler-core/src/javascript/expression.rs +++ b/compiler-core/src/javascript/expression.rs @@ -1,3 +1,4 @@ +use num_bigint::BigInt; use vec1::Vec1; use super::{ @@ -232,19 +233,36 @@ impl<'module> Generator<'module> { let details = self.sized_bit_array_segment_details(segment)?; if segment.type_ == crate::type_::int() { - if details.has_explicit_size { - self.tracker.sized_integer_segment_used = true; - Ok(docvec![ - "sizedInt(", - value, - ", ", - details.size, - ", ", - bool(details.endianness.is_big()), - ")" - ]) - } else { - Ok(value) + match (details.size_value, segment.value.as_ref()) { + (Some(size_value), TypedExpr::Int { int_value, .. }) + if size_value <= SAFE_INT_SEGMENT_MAX_SIZE.into() => + { + let bytes = bit_array_segment_int_value_to_bytes( + int_value.clone(), + size_value, + details.endianness, + segment.location, + )?; + + Ok(u8_slice(&bytes)) + } + + (Some(size_value), _) if size_value == 8.into() => Ok(value), + + (Some(size_value), _) if size_value <= 0.into() => Ok(docvec![]), + + _ => { + self.tracker.sized_integer_segment_used = true; + Ok(docvec![ + "sizedInt(", + value, + ", ", + details.size, + ", ", + bool(details.endianness.is_big()), + ")" + ]) + } } } else { self.tracker.float_bit_array_segment_used = true; @@ -319,43 +337,41 @@ impl<'module> Generator<'module> { .iter() .find(|x| matches!(x, Opt::Size { .. })); - let has_explicit_size = size.is_some(); - - let size = match size { + let (size_value, size) = match size { Some(Opt::Size { value: size, .. }) => { - let size_int = match *size.clone() { - TypedExpr::Int { - location: _, - type_: _, - value, - int_value: _, - } => value.parse().unwrap_or(0), - _ => 0, + let size_value = match *size.clone() { + TypedExpr::Int { int_value, .. } => Some(int_value), + _ => None, }; - if size_int > 0 && size_int % 8 != 0 { - return Err(Error::Unsupported { - feature: "Non byte aligned array".into(), - location: segment.location, - }); + if let Some(size_value) = size_value.as_ref() { + if *size_value > BigInt::ZERO && size_value % 8 != BigInt::ZERO { + return Err(Error::Unsupported { + feature: "Non byte aligned array".into(), + location: segment.location, + }); + } } - self.not_in_tail_position(|gen| gen.wrap_expression(size))? + ( + size_value, + self.not_in_tail_position(|gen| gen.wrap_expression(size))?, + ) } _ => { - let default_size = if segment.type_ == crate::type_::int() { + let size_value = if segment.type_ == crate::type_::int() { 8usize } else { 64usize }; - docvec![default_size] + (Some(BigInt::from(size_value)), docvec![size_value]) } }; Ok(SizedBitArraySegmentDetails { - has_explicit_size, size, + size_value, endianness, }) } @@ -1428,19 +1444,36 @@ fn bit_array<'a>( sized_bit_array_segment_details(segment, tracker, &mut constant_expr_fun)?; if segment.type_ == crate::type_::int() { - if details.has_explicit_size { - tracker.sized_integer_segment_used = true; - Ok(docvec![ - "sizedInt(", - value, - ", ", - details.size, - ", ", - bool(details.endianness.is_big()), - ")" - ]) - } else { - Ok(value) + match (details.size_value, segment.value.as_ref()) { + (Some(size_value), Constant::Int { int_value, .. }) + if size_value <= SAFE_INT_SEGMENT_MAX_SIZE.into() => + { + let bytes = bit_array_segment_int_value_to_bytes( + int_value.clone(), + size_value, + details.endianness, + segment.location, + )?; + + Ok(u8_slice(&bytes)) + } + + (Some(size_value), _) if size_value == 8.into() => Ok(value), + + (Some(size_value), _) if size_value <= 0.into() => Ok(docvec![]), + + _ => { + tracker.sized_integer_segment_used = true; + Ok(docvec![ + "sizedInt(", + value, + ", ", + details.size, + ", ", + bool(details.endianness.is_big()), + ")" + ]) + } } } else { tracker.float_bit_array_segment_used = true; @@ -1485,8 +1518,8 @@ fn bit_array<'a>( #[derive(Debug)] struct SizedBitArraySegmentDetails<'a> { - has_explicit_size: bool, size: Document<'a>, + size_value: Option, // This is set when the segment's size is known at compile time endianness: Endianness, } @@ -1523,41 +1556,38 @@ fn sized_bit_array_segment_details<'a>( .iter() .find(|x| matches!(x, Opt::Size { .. })); - let has_explicit_size = size.is_some(); - - let size = match size { + let (size_value, size) = match size { Some(Opt::Size { value: size, .. }) => { - let size_int = match *size.clone() { - Constant::Int { - location: _, - value, - int_value: _, - } => value.parse().unwrap_or(0), - _ => 0, + let size_value = match *size.clone() { + Constant::Int { int_value, .. } => Some(int_value), + _ => None, }; - if size_int > 0 && size_int % 8 != 0 { - return Err(Error::Unsupported { - feature: "Non byte aligned array".into(), - location: segment.location, - }); + + if let Some(size_value) = size_value.as_ref() { + if *size_value > BigInt::ZERO && size_value % 8 != BigInt::ZERO { + return Err(Error::Unsupported { + feature: "Non byte aligned array".into(), + location: segment.location, + }); + } } - constant_expr_fun(tracker, size)? + (size_value, constant_expr_fun(tracker, size)?) } _ => { - let default_size = if segment.type_ == crate::type_::int() { + let size_value = if segment.type_ == crate::type_::int() { 8usize } else { 64usize }; - docvec![default_size] + (Some(BigInt::from(size_value)), docvec![size_value]) } }; Ok(SizedBitArraySegmentDetails { - has_explicit_size, size, + size_value, endianness, }) } @@ -1797,3 +1827,14 @@ fn record_constructor<'a>( ) } } + +fn u8_slice<'a>(bytes: &[u8]) -> Document<'a> { + let s: EcoString = bytes + .iter() + .map(u8::to_string) + .collect::>() + .join(", ") + .into(); + + docvec![s] +} diff --git a/compiler-core/src/javascript/pattern.rs b/compiler-core/src/javascript/pattern.rs index 6e5ff66534d..8cfb1157327 100644 --- a/compiler-core/src/javascript/pattern.rs +++ b/compiler-core/src/javascript/pattern.rs @@ -1,3 +1,4 @@ +use num_bigint::BigInt; use std::sync::OnceLock; use super::{expression::is_js_scalar, *}; @@ -576,28 +577,50 @@ impl<'module_ctx, 'expression_gen, 'a> Generator<'module_ctx, 'expression_gen, ' { let details = Self::sized_bit_array_segment_details(segment)?; - let start = offset.bytes; - let increment = details.size / 8; - let end = offset.bytes + increment; - - if segment.type_ == crate::type_::int() { - if details.size == 8 && !details.is_signed { - self.push_byte_at(offset.bytes); - } else { - self.push_int_from_slice( - start, - end, + match segment.value.as_ref() { + Pattern::Int { int_value, .. } + if details.size <= SAFE_INT_SEGMENT_MAX_SIZE => + { + let bytes = bit_array_segment_int_value_to_bytes( + (*int_value).clone(), + BigInt::from(details.size), details.endianness, - details.is_signed, - ); + segment.location, + )?; + + for byte in bytes { + self.push_byte_at(offset.bytes); + self.push_equality_check(subject.clone(), docvec![byte]); + self.pop(); + offset.increment(1); + } } - } else { - self.push_float_from_slice(start, end, details.endianness); - } - self.traverse_pattern(subject, &segment.value)?; - self.pop(); - offset.increment(increment); + _ => { + let start = offset.bytes; + let increment = details.size / 8; + let end = offset.bytes + increment; + + if segment.type_ == crate::type_::int() { + if details.size == 8 && !details.is_signed { + self.push_byte_at(offset.bytes); + } else { + self.push_int_from_slice( + start, + end, + details.endianness, + details.is_signed, + ); + } + } else { + self.push_float_from_slice(start, end, details.endianness); + } + + self.traverse_pattern(subject, &segment.value)?; + self.pop(); + offset.increment(increment); + } + } } else { match segment.options.as_slice() { [Opt::Bytes { .. }] => { diff --git a/compiler-core/src/javascript/tests/bit_arrays.rs b/compiler-core/src/javascript/tests/bit_arrays.rs index 47966e40b63..2c34ffc10cc 100644 --- a/compiler-core/src/javascript/tests/bit_arrays.rs +++ b/compiler-core/src/javascript/tests/bit_arrays.rs @@ -122,11 +122,27 @@ fn go() { } #[test] -fn sized_big_endian() { +fn sized_with_compile_time_evaluation() { assert_js!( r#" fn go() { + <<0:8>> + <<4000:16>> + <<80_000:16>> + <<-80_000:16>> + <<-1:48>> +} +"#, + ); +} + +#[test] +fn sized_big_endian() { + assert_js!( + r#" +fn go(i: Int) { <<256:16-big>> + <> } "#, ); @@ -136,8 +152,9 @@ fn go() { fn sized_little_endian() { assert_js!( r#" -fn go() { +fn go(i: Int) { <<256:16-little>> + <> } "#, ); @@ -147,8 +164,9 @@ fn go() { fn explicit_sized() { assert_js!( r#" -fn go() { - <<256:size(64)>> +fn go(i: Int) { + <<256:size(32)>> + <> } "#, ); @@ -292,6 +310,7 @@ fn match_sized() { r#" fn go(x) { let assert <> = x + let assert <<1234:16, 123:8>> = x } "#, ); @@ -303,6 +322,7 @@ fn match_unsigned() { r#" fn go(x) { let assert <> = x + let assert <<-2:unsigned>> = x } "#, ); @@ -314,6 +334,7 @@ fn match_signed() { r#" fn go(x) { let assert <> = x + let assert <<-1:signed>> = x } "#, ); @@ -325,6 +346,7 @@ fn match_sized_big_endian() { r#" fn go(x) { let assert <> = x + let assert <<1234:16-big>> = x } "#, ); @@ -336,6 +358,7 @@ fn match_sized_little_endian() { r#" fn go(x) { let assert <> = x + let assert <<1234:16-little>> = x } "#, ); @@ -347,6 +370,7 @@ fn match_sized_big_endian_unsigned() { r#" fn go(x) { let assert <> = x + let assert <<1234:16-big-unsigned>> = x } "#, ); @@ -358,6 +382,7 @@ fn match_sized_big_endian_signed() { r#" fn go(x) { let assert <> = x + let assert <<1234:16-big-signed>> = x } "#, ); @@ -369,6 +394,7 @@ fn match_sized_little_endian_unsigned() { r#" fn go(x) { let assert <> = x + let assert <<1234:16-little-unsigned>> = x } "#, ); @@ -380,6 +406,7 @@ fn match_sized_little_endian_signed() { r#" fn go(x) { let assert <> = x + let assert <<1234:16-little-signed>> = x } "#, ); @@ -426,6 +453,7 @@ fn match_sized_value() { r#" fn go(x) { let assert <<258:16>> = x + let assert <> = x } "#, ); @@ -555,6 +583,7 @@ fn as_module_const() { "Gleam":utf8, 4.2:float, 4.2:32-float, + -1:64, << <<1, 2, 3>>:bits, "Gleam":utf8, @@ -569,8 +598,9 @@ fn as_module_const() { fn negative_size() { assert_js!( r#" -fn go() { +fn go(x: Int) { <<1:size(-1)>> + <> } "#, ); diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__as_module_const.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__as_module_const.snap index 1b975c8feeb..534cb13749e 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__as_module_const.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__as_module_const.snap @@ -1,6 +1,6 @@ --- source: compiler-core/src/javascript/tests/bit_arrays.rs -expression: "\n pub const data = <<\n 0x1,\n 2,\n 2:size(16),\n 0x4:size(32),\n -1:32,\n \"Gleam\":utf8,\n 4.2:float,\n 4.2:32-float,\n <<\n <<1, 2, 3>>:bits,\n \"Gleam\":utf8,\n 1024\n >>:bits\n >>\n " +expression: "\n pub const data = <<\n 0x1,\n 2,\n 2:size(16),\n 0x4:size(32),\n -1:32,\n \"Gleam\":utf8,\n 4.2:float,\n 4.2:32-float,\n -1:64,\n <<\n <<1, 2, 3>>:bits,\n \"Gleam\":utf8,\n 1024\n >>:bits\n >>\n " --- ----- SOURCE CODE @@ -13,6 +13,7 @@ expression: "\n pub const data = <<\n 0x1,\n 2,\n "Gleam":utf8, 4.2:float, 4.2:32-float, + -1:64, << <<1, 2, 3>>:bits, "Gleam":utf8, @@ -25,17 +26,18 @@ expression: "\n pub const data = <<\n 0x1,\n 2,\n import { toBitArray, sizedInt, stringBits, sizedFloat } from "../gleam.mjs"; export const data = /* @__PURE__ */ toBitArray([ - 0x1, + 1, 2, - sizedInt(2, 16, true), - sizedInt(0x4, 32, true), - sizedInt(-1, 32, true), + 0, 2, + 0, 0, 0, 4, + 255, 255, 255, 255, stringBits("Gleam"), sizedFloat(4.2, 64, true), sizedFloat(4.2, 32, true), + sizedInt(-1, 64, true), /* @__PURE__ */ toBitArray([ /* @__PURE__ */ toBitArray([1, 2, 3]).buffer, stringBits("Gleam"), - 1024, + 0, ]).buffer, ]); diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__explicit_sized.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__explicit_sized.snap index 5228f025510..b64f51b0c6f 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__explicit_sized.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__explicit_sized.snap @@ -1,17 +1,19 @@ --- source: compiler-core/src/javascript/tests/bit_arrays.rs -expression: "\nfn go() {\n <<256:size(64)>>\n}\n" +expression: "\nfn go(i: Int) {\n <<256:size(32)>>\n <>\n}\n" --- ----- SOURCE CODE -fn go() { - <<256:size(64)>> +fn go(i: Int) { + <<256:size(32)>> + <> } ----- COMPILED JAVASCRIPT import { toBitArray, sizedInt } from "../gleam.mjs"; -function go() { - return toBitArray([sizedInt(256, 64, true)]); +function go(i) { + toBitArray([0, 0, 1, 0]); + return toBitArray([sizedInt(i, 32, true)]); } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__integer.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__integer.snap index 3ea815bd1ed..9d26a08ed6c 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__integer.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__integer.snap @@ -13,5 +13,5 @@ fn go() { import { toBitArray } from "../gleam.mjs"; function go() { - return toBitArray([256]); + return toBitArray([0]); } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_signed.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_signed.snap index dd2bcb2277b..6c439e270aa 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_signed.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_signed.snap @@ -1,11 +1,12 @@ --- source: compiler-core/src/javascript/tests/bit_arrays.rs -expression: "\nfn go(x) {\n let assert <> = x\n}\n" +expression: "\nfn go(x) {\n let assert <> = x\n let assert <<-1:signed>> = x\n}\n" --- ----- SOURCE CODE fn go(x) { let assert <> = x + let assert <<-1:signed>> = x } @@ -24,5 +25,15 @@ function go(x) { ) } let a = x.intFromSlice(0, 1, true, true); + if (x.byteAt(0) !== 255 || !(x.length == 1)) { + throw makeError( + "let_assert", + "my/mod", + 4, + "go", + "Pattern match failed, no pattern matched the value.", + { value: x } + ) + } return x; } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized.snap index 9a6d55e94cc..7fa937bf384 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized.snap @@ -1,11 +1,12 @@ --- source: compiler-core/src/javascript/tests/bit_arrays.rs -expression: "\nfn go(x) {\n let assert <> = x\n}\n" +expression: "\nfn go(x) {\n let assert <> = x\n let assert <<1234:16, 123:8>> = x\n}\n" --- ----- SOURCE CODE fn go(x) { let assert <> = x + let assert <<1234:16, 123:8>> = x } @@ -25,5 +26,20 @@ function go(x) { } let a = x.intFromSlice(0, 2, true, false); let b = x.byteAt(2); + if ( + x.byteAt(0) !== 4 || + x.byteAt(1) !== 210 || + x.byteAt(2) !== 123 || + !(x.length == 3) + ) { + throw makeError( + "let_assert", + "my/mod", + 4, + "go", + "Pattern match failed, no pattern matched the value.", + { value: x } + ) + } return x; } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_big_endian.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_big_endian.snap index e34dea32041..073c8bd0526 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_big_endian.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_big_endian.snap @@ -1,11 +1,12 @@ --- source: compiler-core/src/javascript/tests/bit_arrays.rs -expression: "\nfn go(x) {\n let assert <> = x\n}\n" +expression: "\nfn go(x) {\n let assert <> = x\n let assert <<1234:16-big>> = x\n}\n" --- ----- SOURCE CODE fn go(x) { let assert <> = x + let assert <<1234:16-big>> = x } @@ -24,5 +25,15 @@ function go(x) { ) } let a = x.intFromSlice(0, 2, true, false); + if (x.byteAt(0) !== 4 || x.byteAt(1) !== 210 || !(x.length == 2)) { + throw makeError( + "let_assert", + "my/mod", + 4, + "go", + "Pattern match failed, no pattern matched the value.", + { value: x } + ) + } return x; } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_big_endian_signed.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_big_endian_signed.snap index 7304a635800..6e3d928ea2a 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_big_endian_signed.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_big_endian_signed.snap @@ -1,11 +1,12 @@ --- source: compiler-core/src/javascript/tests/bit_arrays.rs -expression: "\nfn go(x) {\n let assert <> = x\n}\n" +expression: "\nfn go(x) {\n let assert <> = x\n let assert <<1234:16-big-signed>> = x\n}\n" --- ----- SOURCE CODE fn go(x) { let assert <> = x + let assert <<1234:16-big-signed>> = x } @@ -24,5 +25,15 @@ function go(x) { ) } let a = x.intFromSlice(0, 2, true, true); + if (x.byteAt(0) !== 4 || x.byteAt(1) !== 210 || !(x.length == 2)) { + throw makeError( + "let_assert", + "my/mod", + 4, + "go", + "Pattern match failed, no pattern matched the value.", + { value: x } + ) + } return x; } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_big_endian_unsigned.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_big_endian_unsigned.snap index 2f846cce409..24f2f895a09 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_big_endian_unsigned.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_big_endian_unsigned.snap @@ -1,11 +1,12 @@ --- source: compiler-core/src/javascript/tests/bit_arrays.rs -expression: "\nfn go(x) {\n let assert <> = x\n}\n" +expression: "\nfn go(x) {\n let assert <> = x\n let assert <<1234:16-big-unsigned>> = x\n}\n" --- ----- SOURCE CODE fn go(x) { let assert <> = x + let assert <<1234:16-big-unsigned>> = x } @@ -24,5 +25,15 @@ function go(x) { ) } let a = x.intFromSlice(0, 2, true, false); + if (x.byteAt(0) !== 4 || x.byteAt(1) !== 210 || !(x.length == 2)) { + throw makeError( + "let_assert", + "my/mod", + 4, + "go", + "Pattern match failed, no pattern matched the value.", + { value: x } + ) + } return x; } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_little_endian.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_little_endian.snap index 3e4b2095cc4..8b085684999 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_little_endian.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_little_endian.snap @@ -1,11 +1,12 @@ --- source: compiler-core/src/javascript/tests/bit_arrays.rs -expression: "\nfn go(x) {\n let assert <> = x\n}\n" +expression: "\nfn go(x) {\n let assert <> = x\n let assert <<1234:16-little>> = x\n}\n" --- ----- SOURCE CODE fn go(x) { let assert <> = x + let assert <<1234:16-little>> = x } @@ -24,5 +25,15 @@ function go(x) { ) } let a = x.intFromSlice(0, 2, false, false); + if (x.byteAt(0) !== 210 || x.byteAt(1) !== 4 || !(x.length == 2)) { + throw makeError( + "let_assert", + "my/mod", + 4, + "go", + "Pattern match failed, no pattern matched the value.", + { value: x } + ) + } return x; } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_little_endian_signed.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_little_endian_signed.snap index b4fbced8316..2d89895afd8 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_little_endian_signed.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_little_endian_signed.snap @@ -1,11 +1,12 @@ --- source: compiler-core/src/javascript/tests/bit_arrays.rs -expression: "\nfn go(x) {\n let assert <> = x\n}\n" +expression: "\nfn go(x) {\n let assert <> = x\n let assert <<1234:16-little-signed>> = x\n}\n" --- ----- SOURCE CODE fn go(x) { let assert <> = x + let assert <<1234:16-little-signed>> = x } @@ -24,5 +25,15 @@ function go(x) { ) } let a = x.intFromSlice(0, 2, false, true); + if (x.byteAt(0) !== 210 || x.byteAt(1) !== 4 || !(x.length == 2)) { + throw makeError( + "let_assert", + "my/mod", + 4, + "go", + "Pattern match failed, no pattern matched the value.", + { value: x } + ) + } return x; } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_little_endian_unsigned.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_little_endian_unsigned.snap index 5f43c02847d..96265933751 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_little_endian_unsigned.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_little_endian_unsigned.snap @@ -1,11 +1,12 @@ --- source: compiler-core/src/javascript/tests/bit_arrays.rs -expression: "\nfn go(x) {\n let assert <> = x\n}\n" +expression: "\nfn go(x) {\n let assert <> = x\n let assert <<1234:16-little-unsigned>> = x\n}\n" --- ----- SOURCE CODE fn go(x) { let assert <> = x + let assert <<1234:16-little-unsigned>> = x } @@ -24,5 +25,15 @@ function go(x) { ) } let a = x.intFromSlice(0, 2, false, false); + if (x.byteAt(0) !== 210 || x.byteAt(1) !== 4 || !(x.length == 2)) { + throw makeError( + "let_assert", + "my/mod", + 4, + "go", + "Pattern match failed, no pattern matched the value.", + { value: x } + ) + } return x; } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_value.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_value.snap index 87bd9fb726c..35bac9728ba 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_value.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_sized_value.snap @@ -1,11 +1,12 @@ --- source: compiler-core/src/javascript/tests/bit_arrays.rs -expression: "\nfn go(x) {\n let assert <<258:16>> = x\n}\n" +expression: "\nfn go(x) {\n let assert <<258:16>> = x\n let assert <> = x\n}\n" --- ----- SOURCE CODE fn go(x) { let assert <<258:16>> = x + let assert <> = x } @@ -13,7 +14,7 @@ fn go(x) { import { makeError } from "../gleam.mjs"; function go(x) { - if (x.intFromSlice(0, 2, true, false) !== 258 || !(x.length == 2)) { + if (x.byteAt(0) !== 1 || x.byteAt(1) !== 2 || !(x.length == 2)) { throw makeError( "let_assert", "my/mod", @@ -23,5 +24,16 @@ function go(x) { { value: x } ) } + if (!(x.length == 2)) { + throw makeError( + "let_assert", + "my/mod", + 4, + "go", + "Pattern match failed, no pattern matched the value.", + { value: x } + ) + } + let i = x.intFromSlice(0, 2, true, false); return x; } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_unsigned.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_unsigned.snap index ec4c7692d18..a52b1db8fdc 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_unsigned.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__match_unsigned.snap @@ -1,11 +1,12 @@ --- source: compiler-core/src/javascript/tests/bit_arrays.rs -expression: "\nfn go(x) {\n let assert <> = x\n}\n" +expression: "\nfn go(x) {\n let assert <> = x\n let assert <<-2:unsigned>> = x\n}\n" --- ----- SOURCE CODE fn go(x) { let assert <> = x + let assert <<-2:unsigned>> = x } @@ -24,5 +25,15 @@ function go(x) { ) } let a = x.byteAt(0); + if (x.byteAt(0) !== 254 || !(x.length == 1)) { + throw makeError( + "let_assert", + "my/mod", + 4, + "go", + "Pattern match failed, no pattern matched the value.", + { value: x } + ) + } return x; } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__negative_size.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__negative_size.snap index 674d5e27f3d..d3043f4036b 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__negative_size.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__negative_size.snap @@ -1,17 +1,19 @@ --- source: compiler-core/src/javascript/tests/bit_arrays.rs -expression: "\nfn go() {\n <<1:size(-1)>>\n}\n" +expression: "\nfn go(x: Int) {\n <<1:size(-1)>>\n <>\n}\n" --- ----- SOURCE CODE -fn go() { +fn go(x: Int) { <<1:size(-1)>> + <> } ----- COMPILED JAVASCRIPT -import { toBitArray, sizedInt } from "../gleam.mjs"; +import { toBitArray } from "../gleam.mjs"; -function go() { - return toBitArray([sizedInt(1, -1, true)]); +function go(x) { + toBitArray([]); + return toBitArray([]); } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__one.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__one.snap index 4ac2ffe70e9..f7b52c937f8 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__one.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__one.snap @@ -13,5 +13,5 @@ fn go() { import { toBitArray } from "../gleam.mjs"; function go() { - return toBitArray([256]); + return toBitArray([0]); } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__sized_big_endian.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__sized_big_endian.snap index ddee63365f2..92d8dee7c28 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__sized_big_endian.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__sized_big_endian.snap @@ -1,17 +1,19 @@ --- source: compiler-core/src/javascript/tests/bit_arrays.rs -expression: "\nfn go() {\n <<256:16-big>>\n}\n" +expression: "\nfn go(i: Int) {\n <<256:16-big>>\n <>\n}\n" --- ----- SOURCE CODE -fn go() { +fn go(i: Int) { <<256:16-big>> + <> } ----- COMPILED JAVASCRIPT import { toBitArray, sizedInt } from "../gleam.mjs"; -function go() { - return toBitArray([sizedInt(256, 16, true)]); +function go(i) { + toBitArray([1, 0]); + return toBitArray([sizedInt(i, 16, true)]); } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__sized_little_endian.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__sized_little_endian.snap index 40e8ca4ab75..10c6ea883d0 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__sized_little_endian.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__sized_little_endian.snap @@ -1,17 +1,19 @@ --- source: compiler-core/src/javascript/tests/bit_arrays.rs -expression: "\nfn go() {\n <<256:16-little>>\n}\n" +expression: "\nfn go(i: Int) {\n <<256:16-little>>\n <>\n}\n" --- ----- SOURCE CODE -fn go() { +fn go(i: Int) { <<256:16-little>> + <> } ----- COMPILED JAVASCRIPT import { toBitArray, sizedInt } from "../gleam.mjs"; -function go() { - return toBitArray([sizedInt(256, 16, false)]); +function go(i) { + toBitArray([0, 1]); + return toBitArray([sizedInt(i, 16, false)]); } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__sized_with_compile_time_evaluation.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__sized_with_compile_time_evaluation.snap new file mode 100644 index 00000000000..04c590c80fa --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__sized_with_compile_time_evaluation.snap @@ -0,0 +1,25 @@ +--- +source: compiler-core/src/javascript/tests/bit_arrays.rs +expression: "\nfn go() {\n <<0:8>>\n <<4000:16>>\n <<80_000:16>>\n <<-80_000:16>>\n <<-1:48>>\n}\n" +--- +----- SOURCE CODE + +fn go() { + <<0:8>> + <<4000:16>> + <<80_000:16>> + <<-80_000:16>> + <<-1:48>> +} + + +----- COMPILED JAVASCRIPT +import { toBitArray } from "../gleam.mjs"; + +function go() { + toBitArray([0]); + toBitArray([15, 160]); + toBitArray([56, 128]); + toBitArray([199, 128]); + return toBitArray([255, 255, 255, 255, 255, 255]); +} diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__two.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__two.snap index cf06fa8ced6..dc209d9be15 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__two.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__two.snap @@ -13,5 +13,5 @@ fn go() { import { toBitArray } from "../gleam.mjs"; function go() { - return toBitArray([256, 4]); + return toBitArray([0, 4]); } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__utf8.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__utf8.snap index 361bcefab21..1511aeeb4b4 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__utf8.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__utf8.snap @@ -13,5 +13,5 @@ fn go(x) { import { toBitArray, stringBits } from "../gleam.mjs"; function go(x) { - return toBitArray([256, 4, x, stringBits("Gleam")]); + return toBitArray([0, 4, x, stringBits("Gleam")]); } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__variable.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__variable.snap index 398e22f9449..fd6ec2566d1 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__variable.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__variable.snap @@ -13,5 +13,5 @@ fn go(x) { import { toBitArray } from "../gleam.mjs"; function go(x) { - return toBitArray([256, 4, x]); + return toBitArray([0, 4, x]); }