···218218 patterns and guards of a case arm.
219219 ([Giacomo Cavalieri](https://github.com/giacomocavalieri))
220220221221+- Fixed a bug where the compiler would not warn for integers over the safe
222222+ JavaScript limit in `BitArray` segments with a unit option.
223223+ ([Giacomo Cavalieri](https://github.com/giacomocavalieri))
224224+225225+- Fixed a bug where the compiler would incorrectly warn for integers over the
226226+ safe JavaScript limit in `BitArray` byte segments that aren't integers.
227227+ ([Giacomo Cavalieri](https://github.com/giacomocavalieri))
228228+221229- Fixed a bug where enabling `javascript.typescript_declarations` or
222230 `javascript.source_maps` wouldn't generate their additional files unless the
223231 build directory was manually deleted. The compiler now automatically rebuilds
···494494 .expect("The function always returns Ok");
495495496496 self.check_pattern_segment_size_expression(&options);
497497- self.check_matched_int_is_in_js_bounds(segment.value.as_ref(), &options);
498497499498 let segment_type = match bit_array::type_options_for_pattern(
500499 &options,
···640639 | Pattern::Invalid { .. } => {}
641640 };
642641643643- BitArraySegment {
642642+ let typed_segment = BitArraySegment {
644643 location: segment.location,
645644 value: Box::new(typed_value),
646645 options,
647646 type_,
648648- }
647647+ };
648648+649649+ self.check_matched_int_is_in_js_bounds(&typed_segment);
650650+651651+ typed_segment
649652 }
650653651654 /// When we have an assignment or a case expression we unify the pattern with the
···15321535 /// That's the maximum size of ints on the JS target, matching on anything
15331536 /// bigger would result in the number being truncated.
15341537 ///
15351535- fn check_matched_int_is_in_js_bounds(
15361536- &mut self,
15371537- pattern: &UntypedPattern,
15381538- options: &[BitArrayOption<TypedPattern>],
15391539- ) {
15381538+ fn check_matched_int_is_in_js_bounds(&mut self, segment: &TypedPatternBitArraySegment) {
15401539 // This only makes sense to check on the js target
15411540 // And if the segment actually defines a variable that would end up
15421542- // being truncated.
15431543- if self.environment.target != Target::JavaScript {
15411541+ // being truncated and that is an integer!
15421542+ if self.environment.target != Target::JavaScript || !segment.type_.is_int() {
15441543 return;
15451544 }
15461546- let (Pattern::Assign { .. } | Pattern::Variable { .. }) = pattern else {
15471547- return;
15481548- };
15491549-15501550- // We need to check if there's an explicit size value for the segment.
15511551- let Some((location, size_value)) = options.iter().find_map(|option| match option {
15521552- BitArrayOption::Size {
15531553- value, location, ..
15541554- } => Some((location, value)),
15551555-15561556- BitArrayOption::Bytes { .. }
15571557- | BitArrayOption::Int { .. }
15581558- | BitArrayOption::Float { .. }
15591559- | BitArrayOption::Bits { .. }
15601560- | BitArrayOption::Utf8 { .. }
15611561- | BitArrayOption::Utf16 { .. }
15621562- | BitArrayOption::Utf32 { .. }
15631563- | BitArrayOption::Utf8Codepoint { .. }
15641564- | BitArrayOption::Utf16Codepoint { .. }
15651565- | BitArrayOption::Utf32Codepoint { .. }
15661566- | BitArrayOption::Signed { .. }
15671567- | BitArrayOption::Unsigned { .. }
15681568- | BitArrayOption::Big { .. }
15691569- | BitArrayOption::Little { .. }
15701570- | BitArrayOption::Native { .. }
15711571- | BitArrayOption::Unit { .. } => None,
15721572- }) else {
15451545+ let (Pattern::Assign { .. } | Pattern::Variable { .. }) = segment.value.as_ref() else {
15731546 return;
15741547 };
1575154815761549 // The size must be a compile time known number, otherwise there's not
15771550 // much we can tell about it.
15781578- let Pattern::BitArraySize(size) = size_value.as_ref() else {
15511551+ let Some(Pattern::BitArraySize(size)) = segment.size() else {
15791552 return;
15801553 };
15541554+15551555+ // We need to check if there's an explicit size value for the segment.
15811556 let Some(size) = size.compile_time_number() else {
15821557 return;
15831558 };
15591559+15601560+ let unit = segment.unit();
15611561+ let bits = size.clone() * unit;
1584156215851563 // If the size is above the JS limit we raise a warning.
15861586- if size > BigInt::from(52) {
15641564+ if bits > BigInt::from(52) {
15871565 self.problems.warning(Warning::JavaScriptBitArrayUnsafeInt {
15881588- location: *location,
15891589- size,
15661566+ location: segment.location,
15671567+ size: bits,
15901568 });
15911569 }
15921570 }
···12121313----- WARNING
1414warning: Truncated bit array segment
1515- ┌─ /src/warning/wrn.gleam:3:18
1515+ ┌─ /src/warning/wrn.gleam:3:11
1616 │
17173 │ let <<number:123>> = x
1818- │ ^^^
1818+ │ ^^^^^^^^^^
19192020This segment is a 123-bit long int, but on the JavaScript target numbers
2121have at most 52 bits. It would be truncated to its first 52 bits.
···14141515----- WARNING
1616warning: Truncated bit array segment
1717- ┌─ /src/warning/wrn.gleam:4:9
1717+ ┌─ /src/warning/wrn.gleam:4:7
1818 │
19194 │ <<n:size(53)>> -> n
2020- │ ^^^^^^^^
2020+ │ ^^^^^^^^^^
21212222This segment is a 53-bit long int, but on the JavaScript target numbers
2323have at most 52 bits. It would be truncated to its first 52 bits.
···11+---
22+source: compiler-core/src/type_/tests/warnings.rs
33+expression: "\npub fn go(x: BitArray) {\n case x {\n <<n:2-unit(250)>> -> n\n _ -> 1\n }\n}\n"
44+---
55+----- SOURCE CODE
66+77+pub fn go(x: BitArray) {
88+ case x {
99+ <<n:2-unit(250)>> -> n
1010+ _ -> 1
1111+ }
1212+}
1313+1414+1515+----- WARNING
1616+warning: Truncated bit array segment
1717+ ┌─ /src/warning/wrn.gleam:4:7
1818+ │
1919+4 │ <<n:2-unit(250)>> -> n
2020+ │ ^^^^^^^^^^^^^
2121+2222+This segment is a 500-bit long int, but on the JavaScript target numbers
2323+have at most 52 bits. It would be truncated to its first 52 bits.
2424+2525+Hint: Did you mean to use the `bytes` segment option?