⭐️ A friendly language for building type-safe, scalable systems!

Fix compiler crash for UTF-codepoint segments on the JavaScript target

authored by gearsco.de and committed by Louis Pilfold 639d3d7a 76dd4cdd

+4
CHANGELOG.md
··· 184 184 "Extract variable" code action was used on a `use` expression. 185 185 ([Surya Rose](https://github.com/GearsDatapacks)) 186 186 187 + - Fixed a bug where the compiler would crash when using the `utf8_codepoint` 188 + bit array segment on the JavaScript target. 189 + ([Surya Rose](https://github.com/GearsDatapacks)) 190 + 187 191 ## v1.11.1 - 2025-06-05 188 192 189 193 ### Compiler
+61 -8
compiler-core/src/bit_array.rs
··· 2 2 use num_bigint::BigInt; 3 3 4 4 use crate::ast::{self, BitArrayOption, SrcSpan}; 5 + use crate::build::Target; 5 6 use crate::type_::Type; 6 7 use std::sync::Arc; 7 8 ··· 11 12 12 13 pub fn type_options_for_value<TypedValue>( 13 14 input_options: &[BitArrayOption<TypedValue>], 15 + target: Target, 14 16 ) -> Result<Arc<Type>, Error> 15 17 where 16 18 TypedValue: GetLiteralValue, 17 19 { 18 - type_options(input_options, TypeOptionsMode::Expression, false) 20 + type_options(input_options, TypeOptionsMode::Expression, false, target) 19 21 } 20 22 21 23 pub fn type_options_for_pattern<TypedValue>( 22 24 input_options: &[BitArrayOption<TypedValue>], 23 25 must_have_size: bool, 26 + target: Target, 24 27 ) -> Result<Arc<Type>, Error> 25 28 where 26 29 TypedValue: GetLiteralValue, 27 30 { 28 - type_options(input_options, TypeOptionsMode::Pattern, must_have_size) 31 + type_options( 32 + input_options, 33 + TypeOptionsMode::Pattern, 34 + must_have_size, 35 + target, 36 + ) 29 37 } 30 38 31 39 struct SegmentOptionCategories<'a, T> { ··· 86 94 input_options: &[BitArrayOption<TypedValue>], 87 95 mode: TypeOptionsMode, 88 96 must_have_size: bool, 97 + target: Target, 89 98 ) -> Result<Arc<Type>, Error> 90 99 where 91 100 TypedValue: GetLiteralValue, ··· 96 105 // Basic category checking 97 106 for option in input_options { 98 107 match option { 108 + Utf8Codepoint { .. } | Utf16Codepoint { .. } | Utf32Codepoint { .. } 109 + if mode == TypeOptionsMode::Pattern && target == Target::JavaScript => 110 + { 111 + return err( 112 + ErrorType::OptionNotSupportedForTarget { 113 + target, 114 + option: UnsupportedOption::UtfCodepointPattern, 115 + }, 116 + option.location(), 117 + ); 118 + } 119 + 99 120 Bytes { .. } 100 121 | Int { .. } 101 122 | Float { .. } ··· 129 150 } else { 130 151 categories.signed = Some(option); 131 152 } 153 + } 154 + 155 + Native { .. } if target == Target::JavaScript => { 156 + return err( 157 + ErrorType::OptionNotSupportedForTarget { 158 + target, 159 + option: UnsupportedOption::NativeEndianness, 160 + }, 161 + option.location(), 162 + ); 132 163 } 133 164 134 165 Big { .. } | Little { .. } | Native { .. } => { ··· 337 368 338 369 #[derive(Debug, PartialEq, Eq, Clone)] 339 370 pub enum ErrorType { 340 - ConflictingEndiannessOptions { existing_endianness: EcoString }, 341 - ConflictingSignednessOptions { existing_signed: EcoString }, 371 + ConflictingEndiannessOptions { 372 + existing_endianness: EcoString, 373 + }, 374 + ConflictingSignednessOptions { 375 + existing_signed: EcoString, 376 + }, 342 377 ConflictingSizeOptions, 343 - ConflictingTypeOptions { existing_type: EcoString }, 378 + ConflictingTypeOptions { 379 + existing_type: EcoString, 380 + }, 344 381 ConflictingUnitOptions, 345 382 FloatWithSize, 346 383 InvalidEndianness, 347 384 OptionNotAllowedInValue, 348 385 SegmentMustHaveSize, 349 - SignednessUsedOnNonInt { type_: EcoString }, 350 - TypeDoesNotAllowSize { type_: EcoString }, 351 - TypeDoesNotAllowUnit { type_: EcoString }, 386 + SignednessUsedOnNonInt { 387 + type_: EcoString, 388 + }, 389 + TypeDoesNotAllowSize { 390 + type_: EcoString, 391 + }, 392 + TypeDoesNotAllowUnit { 393 + type_: EcoString, 394 + }, 352 395 UnitMustHaveSize, 353 396 VariableUtfSegmentInPattern, 354 397 ConstantSizeNotPositive, 398 + OptionNotSupportedForTarget { 399 + target: Target, 400 + option: UnsupportedOption, 401 + }, 402 + } 403 + 404 + #[derive(Debug, PartialEq, Eq, Clone, Copy)] 405 + pub enum UnsupportedOption { 406 + UtfCodepointPattern, 407 + NativeEndianness, 355 408 }
+19
compiler-core/src/error.rs
··· 1 1 #![allow(clippy::unwrap_used, clippy::expect_used)] 2 + use crate::bit_array::UnsupportedOption; 2 3 use crate::build::{Origin, Outcome, Runtime, Target}; 3 4 use crate::dependency::{PackageFetcher, ResolutionError}; 4 5 use crate::diagnostic::{Diagnostic, ExtraLabel, Label, Location}; ··· 3017 3018 bit_array::ErrorType::ConstantSizeNotPositive => { 3018 3019 ("A constant size must be a positive number", vec![]) 3019 3020 } 3021 + bit_array::ErrorType::OptionNotSupportedForTarget { 3022 + target, 3023 + option: UnsupportedOption::NativeEndianness, 3024 + } => ( 3025 + "Unsupported endianness", 3026 + vec![ 3027 + wrap_format!("The {target} target does not support the `native` endianness option.") 3028 + ], 3029 + ), 3030 + bit_array::ErrorType::OptionNotSupportedForTarget { 3031 + target, 3032 + option: UnsupportedOption::UtfCodepointPattern, 3033 + } => ( 3034 + "UTF-codepoint pattern matching is not supported", 3035 + vec![ 3036 + wrap_format!("The {target} target does not support UTF-codepoint pattern matching.") 3037 + ], 3038 + ), 3020 3039 }; 3021 3040 extra.push("See: https://tour.gleam.run/data-types/bit-arrays/".into()); 3022 3041 let text = extra.join("\n");
-14
compiler-core/src/javascript/expression.rs
··· 395 395 this.wrap_expression(&segment.value) 396 396 })?; 397 397 398 - if segment.has_native_option() { 399 - return Err(Error::Unsupported { 400 - feature: "This bit array segment option".into(), 401 - location: segment.location, 402 - }); 403 - } 404 - 405 398 match segment.options.as_slice() { 406 399 // Int segment 407 400 _ if segment.type_.is_int() => { ··· 1826 1819 self.tracker.bit_array_literal_used = true; 1827 1820 let segments_array = array(segments.iter().map(|segment| { 1828 1821 let value = self.constant_expression(Context::Constant, &segment.value)?; 1829 - 1830 - if segment.has_native_option() { 1831 - return Err(Error::Unsupported { 1832 - feature: "This bit array segment option".into(), 1833 - location: segment.location, 1834 - }); 1835 - } 1836 1822 1837 1823 match segment.options.as_slice() { 1838 1824 // Int segment
+4 -4
compiler-core/src/type_/expression.rs
··· 1518 1518 1519 1519 let options: Vec<_> = options.into_iter().map(infer_option).try_collect()?; 1520 1520 1521 - let type_ = bit_array::type_options_for_value(&options).map_err(|error| { 1522 - Error::BitArraySegmentError { 1521 + let type_ = bit_array::type_options_for_value(&options, self.environment.target).map_err( 1522 + |error| Error::BitArraySegmentError { 1523 1523 error: error.error, 1524 1524 location: error.location, 1525 - } 1526 - })?; 1525 + }, 1526 + )?; 1527 1527 1528 1528 // Track usage of the unaligned bit arrays feature on JavaScript so that 1529 1529 // warnings can be emitted if the Gleam version constraint is too low
+5 -1
compiler-core/src/type_/pattern.rs
··· 460 460 .try_collect() 461 461 .expect("The function always returns Ok"); 462 462 463 - let segment_type = match bit_array::type_options_for_pattern(&options, !is_last_segment) { 463 + let segment_type = match bit_array::type_options_for_pattern( 464 + &options, 465 + !is_last_segment, 466 + self.environment.target, 467 + ) { 464 468 Ok(type_) => type_, 465 469 Err(error) => { 466 470 self.error(Error::BitArraySegmentError {
+45 -1
compiler-core/src/type_/tests/errors.rs
··· 1 1 use crate::{ 2 - assert_error, assert_internal_module_error, assert_module_error, assert_module_syntax_error, 2 + assert_error, assert_internal_module_error, assert_js_module_error, assert_module_error, 3 + assert_module_syntax_error, 3 4 }; 4 5 5 6 #[test] ··· 3224 3225 " 3225 3226 ); 3226 3227 } 3228 + #[test] 3229 + fn native_endianness_javascript_target() { 3230 + assert_js_module_error!( 3231 + " 3232 + pub fn main() { 3233 + let assert <<a:native>> = <<10>> 3234 + } 3235 + " 3236 + ); 3237 + } 3238 + 3239 + #[test] 3240 + fn utf8_codepoint_javascript_target() { 3241 + assert_js_module_error!( 3242 + " 3243 + pub fn main() { 3244 + let assert <<a:utf8_codepoint>> = <<10>> 3245 + } 3246 + " 3247 + ); 3248 + } 3249 + 3250 + #[test] 3251 + fn utf16_codepoint_javascript_target() { 3252 + assert_js_module_error!( 3253 + " 3254 + pub fn main() { 3255 + let assert <<a:utf16_codepoint>> = <<10>> 3256 + } 3257 + " 3258 + ); 3259 + } 3260 + 3261 + #[test] 3262 + fn utf32_codepoint_javascript_target() { 3263 + assert_js_module_error!( 3264 + " 3265 + pub fn main() { 3266 + let assert <<a:utf32_codepoint>> = <<10>> 3267 + } 3268 + " 3269 + ); 3270 + }
+20
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__native_endianness_javascript_target.snap
··· 1 + --- 2 + source: compiler-core/src/type_/tests/errors.rs 3 + expression: "\npub fn main() {\n let assert <<a:native>> = <<10>>\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 7 + pub fn main() { 8 + let assert <<a:native>> = <<10>> 9 + } 10 + 11 + 12 + ----- ERROR 13 + error: Invalid bit array segment 14 + ┌─ /src/one/two.gleam:3:18 15 + 16 + 3 │ let assert <<a:native>> = <<10>> 17 + │ ^^^^^^ Unsupported endianness 18 + 19 + The javascript target does not support the `native` endianness option. 20 + See: https://tour.gleam.run/data-types/bit-arrays/
+20
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__utf16_codepoint_javascript_target.snap
··· 1 + --- 2 + source: compiler-core/src/type_/tests/errors.rs 3 + expression: "\npub fn main() {\n let assert <<a:utf16_codepoint>> = <<10>>\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 7 + pub fn main() { 8 + let assert <<a:utf16_codepoint>> = <<10>> 9 + } 10 + 11 + 12 + ----- ERROR 13 + error: Invalid bit array segment 14 + ┌─ /src/one/two.gleam:3:18 15 + 16 + 3 │ let assert <<a:utf16_codepoint>> = <<10>> 17 + │ ^^^^^^^^^^^^^^^ UTF-codepoint pattern matching is not supported 18 + 19 + The javascript target does not support UTF-codepoint pattern matching. 20 + See: https://tour.gleam.run/data-types/bit-arrays/
+20
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__utf32_codepoint_javascript_target.snap
··· 1 + --- 2 + source: compiler-core/src/type_/tests/errors.rs 3 + expression: "\npub fn main() {\n let assert <<a:utf32_codepoint>> = <<10>>\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 7 + pub fn main() { 8 + let assert <<a:utf32_codepoint>> = <<10>> 9 + } 10 + 11 + 12 + ----- ERROR 13 + error: Invalid bit array segment 14 + ┌─ /src/one/two.gleam:3:18 15 + 16 + 3 │ let assert <<a:utf32_codepoint>> = <<10>> 17 + │ ^^^^^^^^^^^^^^^ UTF-codepoint pattern matching is not supported 18 + 19 + The javascript target does not support UTF-codepoint pattern matching. 20 + See: https://tour.gleam.run/data-types/bit-arrays/
+20
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__utf8_codepoint_javascript_target.snap
··· 1 + --- 2 + source: compiler-core/src/type_/tests/errors.rs 3 + expression: "\npub fn main() {\n let assert <<a:utf8_codepoint>> = <<10>>\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 7 + pub fn main() { 8 + let assert <<a:utf8_codepoint>> = <<10>> 9 + } 10 + 11 + 12 + ----- ERROR 13 + error: Invalid bit array segment 14 + ┌─ /src/one/two.gleam:3:18 15 + 16 + 3 │ let assert <<a:utf8_codepoint>> = <<10>> 17 + │ ^^^^^^^^^^^^^^ UTF-codepoint pattern matching is not supported 18 + 19 + The javascript target does not support UTF-codepoint pattern matching. 20 + See: https://tour.gleam.run/data-types/bit-arrays/