Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

improve case exhaustiveness checks for ints and floats

author
pdewey
committer
Louis Pilfold
date (Nov 10, 2025, 12:52 PM UTC) commit 623aa9b2 parent 292b0a29 change-id rnwumlwz
+351 -30
+24 -25
compiler-core/src/exhaustiveness.rs
··· 71 71 self, AssignName, BitArraySize, Endianness, IntOperator, SrcSpan, TypedBitArraySize, 72 72 TypedClause, TypedPattern, TypedPatternBitArraySegment, 73 73 }, 74 + parse::LiteralFloatValue, 74 75 strings::{ 75 76 convert_string_escape_chars, length_utf16, length_utf32, string_to_utf16_bytes, 76 77 string_to_utf32_bytes, ··· 442 443 pub enum Pattern { 443 444 Discard, 444 445 Int { 445 - value: EcoString, 446 + int_value: BigInt, 446 447 }, 447 448 Float { 448 - value: EcoString, 449 + float_value: LiteralFloatValue, 449 450 }, 450 451 String { 451 452 value: EcoString, ··· 492 493 // out of a branch's checks. So there's no corresponding runtime check 493 494 // we can perform for them. 494 495 Pattern::Discard | Pattern::Variable { .. } | Pattern::Assign { .. } => return None, 495 - Pattern::Int { value } => RuntimeCheckKind::Int { 496 - value: value.clone(), 496 + Pattern::Int { int_value, .. } => RuntimeCheckKind::Int { 497 + int_value: int_value.clone(), 497 498 }, 498 - Pattern::Float { value } => RuntimeCheckKind::Float { 499 - value: value.clone(), 499 + Pattern::Float { float_value, .. } => RuntimeCheckKind::Float { 500 + float_value: *float_value, 500 501 }, 501 502 Pattern::String { value } => RuntimeCheckKind::String { 502 503 value: value.clone(), ··· 596 597 #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] 597 598 pub enum RuntimeCheck { 598 599 Int { 599 - value: EcoString, 600 + int_value: BigInt, 600 601 }, 601 602 Float { 602 - value: EcoString, 603 + float_value: LiteralFloatValue, 603 604 }, 604 605 String { 605 606 value: EcoString, ··· 631 632 impl RuntimeCheck { 632 633 fn kind(&self) -> Option<RuntimeCheckKind> { 633 634 let kind = match self { 634 - RuntimeCheck::Int { value } => RuntimeCheckKind::Int { 635 - value: value.clone(), 635 + RuntimeCheck::Int { int_value, .. } => RuntimeCheckKind::Int { 636 + int_value: int_value.clone(), 636 637 }, 637 - RuntimeCheck::Float { value } => RuntimeCheckKind::Float { 638 - value: value.clone(), 638 + RuntimeCheck::Float { float_value, .. } => RuntimeCheckKind::Float { 639 + float_value: *float_value, 639 640 }, 640 641 RuntimeCheck::String { value } => RuntimeCheckKind::String { 641 642 value: value.clone(), ··· 683 684 684 685 #[derive(Eq, PartialEq, Clone, Hash, Debug)] 685 686 pub enum RuntimeCheckKind { 686 - Int { value: EcoString }, 687 - Float { value: EcoString }, 687 + Int { int_value: BigInt }, 688 + Float { float_value: LiteralFloatValue }, 688 689 String { value: EcoString }, 689 690 StringPrefix { prefix: EcoString }, 690 691 Tuple { size: usize }, ··· 2486 2487 branch_mode: &BranchMode, 2487 2488 ) -> RuntimeCheck { 2488 2489 match (kind, branch_mode) { 2489 - (RuntimeCheckKind::Int { value }, _) => RuntimeCheck::Int { 2490 - value: value.clone(), 2491 - }, 2492 - (RuntimeCheckKind::Float { value }, _) => RuntimeCheck::Float { 2493 - value: value.clone(), 2490 + (RuntimeCheckKind::Int { int_value }, _) => RuntimeCheck::Int { 2491 + int_value: int_value.clone(), 2494 2492 }, 2493 + (RuntimeCheckKind::Float { float_value }, _) => RuntimeCheck::Float { float_value }, 2495 2494 (RuntimeCheckKind::String { value }, _) => RuntimeCheck::String { 2496 2495 value: value.clone(), 2497 2496 }, ··· 3296 3295 TypedPattern::Invalid { .. } => self.insert(Pattern::Discard), 3297 3296 TypedPattern::Discard { .. } => self.insert(Pattern::Discard), 3298 3297 3299 - TypedPattern::Int { value, .. } => { 3300 - let value = value.clone(); 3301 - self.insert(Pattern::Int { value }) 3298 + TypedPattern::Int { int_value, .. } => { 3299 + let int_value = int_value.clone(); 3300 + self.insert(Pattern::Int { int_value }) 3302 3301 } 3303 3302 3304 - TypedPattern::Float { value, .. } => { 3305 - let value = value.clone(); 3306 - self.insert(Pattern::Float { value }) 3303 + TypedPattern::Float { float_value, .. } => { 3304 + let float_value = *float_value; 3305 + self.insert(Pattern::Float { float_value }) 3307 3306 } 3308 3307 3309 3308 TypedPattern::String { value, .. } => {
+7 -3
compiler-core/src/javascript/decision.rs
··· 1 1 use super::{ 2 2 INDENT, bit_array_segment_int_value_to_bytes, 3 - expression::{self, Generator, Ordering, float, int}, 3 + expression::{self, Generator, Ordering, float}, 4 4 }; 5 5 use crate::{ 6 6 ast::{AssignmentKind, Endianness, SrcSpan, TypedClause, TypedExpr, TypedPattern}, ··· 1022 1022 1023 1023 match runtime_check { 1024 1024 RuntimeCheck::String { value: expected } => docvec![value, equality, string(expected)], 1025 - RuntimeCheck::Float { value: expected } => docvec![value, equality, float(expected)], 1026 - RuntimeCheck::Int { value: expected } => docvec![value, equality, int(expected)], 1025 + RuntimeCheck::Float { 1026 + float_value: expected, 1027 + } => docvec![value, equality, expected.value()], 1028 + RuntimeCheck::Int { 1029 + int_value: expected, 1030 + } => docvec![value, equality, expected.clone()], 1027 1031 RuntimeCheck::StringPrefix { prefix, .. } => { 1028 1032 docvec![value, ".startsWith(", string(prefix), ")"] 1029 1033 }
+1 -1
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__preceeding_zeros_float_pattern.snap
··· 15 15 const FILEPATH = "src/module.gleam"; 16 16 17 17 export function main(x) { 18 - if (!(x === 9_179.1)) { 18 + if (!(x === 9179.1)) { 19 19 throw makeError( 20 20 "let_assert", 21 21 FILEPATH,
+1 -1
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__numbers__preceeding_zeros_int_pattern.snap
··· 15 15 const FILEPATH = "src/module.gleam"; 16 16 17 17 export function main(x) { 18 - if (!(x === 9_179)) { 18 + if (!(x === 9179)) { 19 19 throw makeError( 20 20 "let_assert", 21 21 FILEPATH,
+31
compiler-core/src/parse.rs
··· 80 80 use error::{LexicalError, ParseError, ParseErrorType}; 81 81 use lexer::{LexResult, Spanned}; 82 82 use num_bigint::BigInt; 83 + use serde::{Deserialize, Serialize}; 83 84 use std::cmp::Ordering; 84 85 use std::collections::VecDeque; 86 + use std::hash::{Hash, Hasher}; 85 87 use std::str::FromStr; 86 88 pub use token::Token; 87 89 use vec1::{Vec1, vec1}; ··· 4757 4759 Some(self.cmp(other)) 4758 4760 } 4759 4761 } 4762 + 4763 + impl Hash for LiteralFloatValue { 4764 + fn hash<H: Hasher>(&self, state: &mut H) { 4765 + self.0.to_bits().hash(state) 4766 + } 4767 + } 4768 + 4769 + impl Serialize for LiteralFloatValue { 4770 + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 4771 + where 4772 + S: serde::Serializer, 4773 + { 4774 + serializer.serialize_f64(self.0) 4775 + } 4776 + } 4777 + 4778 + impl<'de> Deserialize<'de> for LiteralFloatValue { 4779 + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 4780 + where 4781 + D: serde::Deserializer<'de>, 4782 + { 4783 + let value = f64::deserialize(deserializer)?; 4784 + if value.is_nan() { 4785 + Err(serde::de::Error::custom("NaN is not allowed")) 4786 + } else { 4787 + Ok(LiteralFloatValue(value)) 4788 + } 4789 + } 4790 + }
+105
compiler-core/src/type_/tests/exhaustiveness.rs
··· 896 896 } 897 897 898 898 #[test] 899 + fn redundant_int_with_underscores() { 900 + assert_warning!( 901 + r#" 902 + pub fn main(x) { 903 + case x { 904 + 10 -> "ten" 905 + 1_0 -> "also ten" 906 + _ -> "other" 907 + } 908 + } 909 + "# 910 + ); 911 + } 912 + 913 + #[test] 914 + fn redundant_int_with_multiple_underscores() { 915 + assert_warning!( 916 + r#" 917 + pub fn main(x) { 918 + case x { 919 + 1_000_000 -> "one million" 920 + 1000000 -> "also one million" 921 + _ -> "other" 922 + } 923 + } 924 + "# 925 + ); 926 + } 927 + 928 + #[test] 929 + fn redundant_float_with_different_formatting() { 930 + assert_warning!( 931 + r#" 932 + pub fn main(x) { 933 + case x { 934 + 1.0 -> "one" 935 + 1.00 -> "also one" 936 + _ -> "other" 937 + } 938 + } 939 + "# 940 + ); 941 + } 942 + 943 + #[test] 944 + fn redundant_float_with_no_trailing_decimal() { 945 + assert_warning!( 946 + r#" 947 + pub fn main(x) { 948 + case x { 949 + 1.0 -> "one" 950 + 1. -> "another one" 951 + _ -> "other" 952 + } 953 + } 954 + "# 955 + ); 956 + } 957 + 958 + #[test] 959 + fn redundant_float_with_underscore() { 960 + assert_warning!( 961 + r#" 962 + pub fn main(x) { 963 + case x { 964 + 10.0 -> "ten" 965 + 1_0.0 -> "also ten" 966 + _ -> "other" 967 + } 968 + } 969 + "# 970 + ); 971 + } 972 + 973 + #[test] 974 + fn redundant_float_scientific_notation() { 975 + assert_warning!( 976 + r#" 977 + pub fn main(x) { 978 + case x { 979 + 10.0 -> "ten" 980 + 1.0e1 -> "also ten" 981 + _ -> "other" 982 + } 983 + } 984 + "# 985 + ); 986 + } 987 + 988 + #[test] 989 + fn redundant_float_scientific_notation_and_underscore() { 990 + assert_warning!( 991 + r#" 992 + pub fn main(x) { 993 + case x { 994 + 1.0e2 -> "one hundred" 995 + 1_0_0.0 -> "one hundred again" 996 + _ -> "other" 997 + } 998 + } 999 + "# 1000 + ); 1001 + } 1002 + 1003 + #[test] 899 1004 fn let_1() { 900 1005 assert_module_error!( 901 1006 r#"
+26
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__exhaustiveness__redundant_float_scientific_notation.snap
··· 1 + --- 2 + source: compiler-core/src/type_/tests/exhaustiveness.rs 3 + expression: "\npub fn main(x) {\n case x {\n 10.0 -> \"ten\"\n 1.0e1 -> \"also ten\"\n _ -> \"other\"\n }\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 7 + pub fn main(x) { 8 + case x { 9 + 10.0 -> "ten" 10 + 1.0e1 -> "also ten" 11 + _ -> "other" 12 + } 13 + } 14 + 15 + 16 + ----- WARNING 17 + warning: Unreachable pattern 18 + ┌─ /src/warning/wrn.gleam:5:5 19 + 20 + 5 │ 1.0e1 -> "also ten" 21 + │ ^^^^^ 22 + 23 + This pattern cannot be reached as a previous pattern matches the same 24 + values. 25 + 26 + Hint: It can be safely removed.
+26
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__exhaustiveness__redundant_float_scientific_notation_and_underscore.snap
··· 1 + --- 2 + source: compiler-core/src/type_/tests/exhaustiveness.rs 3 + expression: "\npub fn main(x) {\n case x {\n 1.0e2 -> \"one hundred\"\n 1_0_0.0 -> \"one hundred again\"\n _ -> \"other\"\n }\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 7 + pub fn main(x) { 8 + case x { 9 + 1.0e2 -> "one hundred" 10 + 1_0_0.0 -> "one hundred again" 11 + _ -> "other" 12 + } 13 + } 14 + 15 + 16 + ----- WARNING 17 + warning: Unreachable pattern 18 + ┌─ /src/warning/wrn.gleam:5:5 19 + 20 + 5 │ 1_0_0.0 -> "one hundred again" 21 + │ ^^^^^^^ 22 + 23 + This pattern cannot be reached as a previous pattern matches the same 24 + values. 25 + 26 + Hint: It can be safely removed.
+26
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__exhaustiveness__redundant_float_with_different_formatting.snap
··· 1 + --- 2 + source: compiler-core/src/type_/tests/exhaustiveness.rs 3 + expression: "\npub fn main(x) {\n case x {\n 1.0 -> \"one\"\n 1.00 -> \"also one\"\n _ -> \"other\"\n }\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 7 + pub fn main(x) { 8 + case x { 9 + 1.0 -> "one" 10 + 1.00 -> "also one" 11 + _ -> "other" 12 + } 13 + } 14 + 15 + 16 + ----- WARNING 17 + warning: Unreachable pattern 18 + ┌─ /src/warning/wrn.gleam:5:5 19 + 20 + 5 │ 1.00 -> "also one" 21 + │ ^^^^ 22 + 23 + This pattern cannot be reached as a previous pattern matches the same 24 + values. 25 + 26 + Hint: It can be safely removed.
+26
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__exhaustiveness__redundant_float_with_no_trailing_decimal.snap
··· 1 + --- 2 + source: compiler-core/src/type_/tests/exhaustiveness.rs 3 + expression: "\npub fn main(x) {\n case x {\n 1.0 -> \"one\"\n 1. -> \"another one\"\n _ -> \"other\"\n }\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 7 + pub fn main(x) { 8 + case x { 9 + 1.0 -> "one" 10 + 1. -> "another one" 11 + _ -> "other" 12 + } 13 + } 14 + 15 + 16 + ----- WARNING 17 + warning: Unreachable pattern 18 + ┌─ /src/warning/wrn.gleam:5:5 19 + 20 + 5 │ 1. -> "another one" 21 + │ ^^ 22 + 23 + This pattern cannot be reached as a previous pattern matches the same 24 + values. 25 + 26 + Hint: It can be safely removed.
+26
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__exhaustiveness__redundant_float_with_underscore.snap
··· 1 + --- 2 + source: compiler-core/src/type_/tests/exhaustiveness.rs 3 + expression: "\npub fn main(x) {\n case x {\n 10.0 -> \"ten\"\n 1_0.0 -> \"also ten\"\n _ -> \"other\"\n }\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 7 + pub fn main(x) { 8 + case x { 9 + 10.0 -> "ten" 10 + 1_0.0 -> "also ten" 11 + _ -> "other" 12 + } 13 + } 14 + 15 + 16 + ----- WARNING 17 + warning: Unreachable pattern 18 + ┌─ /src/warning/wrn.gleam:5:5 19 + 20 + 5 │ 1_0.0 -> "also ten" 21 + │ ^^^^^ 22 + 23 + This pattern cannot be reached as a previous pattern matches the same 24 + values. 25 + 26 + Hint: It can be safely removed.
+26
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__exhaustiveness__redundant_int_with_multiple_underscores.snap
··· 1 + --- 2 + source: compiler-core/src/type_/tests/exhaustiveness.rs 3 + expression: "\npub fn main(x) {\n case x {\n 1_000_000 -> \"one million\"\n 1000000 -> \"also one million\"\n _ -> \"other\"\n }\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 7 + pub fn main(x) { 8 + case x { 9 + 1_000_000 -> "one million" 10 + 1000000 -> "also one million" 11 + _ -> "other" 12 + } 13 + } 14 + 15 + 16 + ----- WARNING 17 + warning: Unreachable pattern 18 + ┌─ /src/warning/wrn.gleam:5:5 19 + 20 + 5 │ 1000000 -> "also one million" 21 + │ ^^^^^^^ 22 + 23 + This pattern cannot be reached as a previous pattern matches the same 24 + values. 25 + 26 + Hint: It can be safely removed.
+26
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__exhaustiveness__redundant_int_with_underscores.snap
··· 1 + --- 2 + source: compiler-core/src/type_/tests/exhaustiveness.rs 3 + expression: "\npub fn main(x) {\n case x {\n 10 -> \"ten\"\n 1_0 -> \"also ten\"\n _ -> \"other\"\n }\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 7 + pub fn main(x) { 8 + case x { 9 + 10 -> "ten" 10 + 1_0 -> "also ten" 11 + _ -> "other" 12 + } 13 + } 14 + 15 + 16 + ----- WARNING 17 + warning: Unreachable pattern 18 + ┌─ /src/warning/wrn.gleam:5:5 19 + 20 + 5 │ 1_0 -> "also ten" 21 + │ ^^^ 22 + 23 + This pattern cannot be reached as a previous pattern matches the same 24 + values. 25 + 26 + Hint: It can be safely removed.