From bd57a03ba97ccc47902c3fbbdf3130c7bcb43fdb Mon Sep 17 00:00:00 2001 From: Gears Date: Sat, 20 Dec 2025 17:51:02 +0000 Subject: [PATCH] Fix qualification of class names for singleton inequality --- CHANGELOG.md | 4 + compiler-core/src/javascript/expression.rs | 85 ++++++++++++++----- ...another_module_qualified_clause_guard.snap | 2 +- ...n_another_module_qualified_expression.snap | 3 +- 4 files changed, 72 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56a2bf5a0..fdf450a6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ invalid code. ([Surya Rose](https://github.com/GearsDatapacks)) +- Fixed a bug where checking for equality with a variant with no fields using + qualified syntax would generate invalid code on the JavaScript target. + ([Surya Rose](https://github.com/GearsDatapacks)) + ## v1.14.0-rc1 - 2025-12-15 ### Compiler diff --git a/compiler-core/src/javascript/expression.rs b/compiler-core/src/javascript/expression.rs index 87019db4c..eef3197a5 100644 --- a/compiler-core/src/javascript/expression.rs +++ b/compiler-core/src/javascript/expression.rs @@ -1587,33 +1587,74 @@ impl<'module, 'a> Generator<'module, 'a> { right: &'a TypedExpr, should_be_equal: bool, ) -> Option> { - if let TypedExpr::Var { - constructor: - ValueConstructor { - variant: ValueConstructorVariant::Record { arity: 0, name, .. }, - .. - }, - .. - } = right - { - let left_doc = self - .not_in_tail_position(Some(Ordering::Strict), |this| this.wrap_expression(left)); - Some(self.singleton_equal(left_doc, name, should_be_equal)) - } else { - None + match right { + TypedExpr::Var { + constructor: + ValueConstructor { + variant: ValueConstructorVariant::Record { arity: 0, name, .. }, + .. + }, + .. + } => { + let left_doc = self.not_in_tail_position(Some(Ordering::Strict), |this| { + this.wrap_expression(left) + }); + Some(self.singleton_equal(left_doc, None, name, should_be_equal)) + } + TypedExpr::ModuleSelect { + module_alias, + constructor: ModuleValueConstructor::Record { arity: 0, name, .. }, + .. + } => { + let left_doc = self.not_in_tail_position(Some(Ordering::Strict), |this| { + this.wrap_expression(left) + }); + Some(self.singleton_equal(left_doc, Some(module_alias), name, should_be_equal)) + } + TypedExpr::Int { .. } + | TypedExpr::Float { .. } + | TypedExpr::String { .. } + | TypedExpr::Block { .. } + | TypedExpr::Pipeline { .. } + | TypedExpr::Var { .. } + | TypedExpr::Fn { .. } + | TypedExpr::List { .. } + | TypedExpr::Call { .. } + | TypedExpr::BinOp { .. } + | TypedExpr::Case { .. } + | TypedExpr::RecordAccess { .. } + | TypedExpr::PositionalAccess { .. } + | TypedExpr::ModuleSelect { .. } + | TypedExpr::Tuple { .. } + | TypedExpr::TupleIndex { .. } + | TypedExpr::Todo { .. } + | TypedExpr::Panic { .. } + | TypedExpr::Echo { .. } + | TypedExpr::BitArray { .. } + | TypedExpr::RecordUpdate { .. } + | TypedExpr::NegateBool { .. } + | TypedExpr::NegateInt { .. } + | TypedExpr::Invalid { .. } => None, } } fn singleton_equal( &self, value: Document<'a>, - tag: &EcoString, + module: Option<&'a str>, + name: &'a str, should_be_equal: bool, ) -> Document<'a> { - if should_be_equal { - docvec![value, " instanceof ", tag.to_doc()] + let record = if let Some(module) = module { + docvec!["$", module, ".", name] } else { - docvec!["!(", value, " instanceof ", tag.to_doc(), ")"] + name.to_doc() + }; + + if should_be_equal { + docvec![value, " instanceof ", record] + } else { + docvec!["!(", value, " instanceof ", record, ")"] } } @@ -2173,13 +2214,19 @@ impl<'module, 'a> Generator<'module, 'a> { ) -> Option> { if let ClauseGuard::Constant(Constant::Record { record_constructor: Some(constructor), + module, name, .. }) = right && let ValueConstructorVariant::Record { arity: 0, .. } = constructor.variant { let left_doc = self.guard(left); - return Some(self.singleton_equal(left_doc, name, should_be_equal)); + return Some(self.singleton_equal( + left_doc, + module.as_ref().map(|(module, _)| module.as_str()), + name, + should_be_equal, + )); } None } diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_qualified_clause_guard.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_qualified_clause_guard.snap index 6e6003ccc..557e5626a 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_qualified_clause_guard.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_qualified_clause_guard.snap @@ -23,7 +23,7 @@ import * as $other_module from "../other_module.mjs"; export function process(e) { let value = e; - if (value instanceof Variant) { + if (value instanceof $other_module.Variant) { return "match"; } else { return "no match"; diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_qualified_expression.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_qualified_expression.snap index 7ac379166..28bf0ff1f 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_qualified_expression.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_qualified_expression.snap @@ -16,9 +16,8 @@ pub fn check(x) -> Bool { ----- COMPILED JAVASCRIPT -import { isEqual } from "../gleam.mjs"; import * as $other_module from "../other_module.mjs"; export function check(x) { - return isEqual(x, new $other_module.Variant()); + return x instanceof $other_module.Variant; }