diff --git a/compiler-core/src/analyse.rs b/compiler-core/src/analyse.rs index 6ee38d98869..dbdc1dde8ec 100644 --- a/compiler-core/src/analyse.rs +++ b/compiler-core/src/analyse.rs @@ -51,9 +51,10 @@ use vec1::Vec1; use self::imports::Importer; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] pub enum Inferred { Known(T), + #[default] Unknown, } diff --git a/compiler-core/src/ast.rs b/compiler-core/src/ast.rs index e651c961239..a4039a2bc6f 100644 --- a/compiler-core/src/ast.rs +++ b/compiler-core/src/ast.rs @@ -2478,12 +2478,6 @@ impl BitArraySize { } } -impl Default for Inferred<()> { - fn default() -> Self { - Self::Unknown - } -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum AssignName { Variable(EcoString), diff --git a/compiler-core/src/javascript/expression.rs b/compiler-core/src/javascript/expression.rs index 25e345fefb2..0bd5cc158d3 100644 --- a/compiler-core/src/javascript/expression.rs +++ b/compiler-core/src/javascript/expression.rs @@ -1536,14 +1536,75 @@ impl<'module, 'a> Generator<'module, 'a> { return docvec![left_doc, operator, right_doc]; } + // For comparison with singleton custom types, ie, one with no fields. + // If you have some code like this + // ```gleam + // pub type Wibble { + // Wibble + // Wobble + // } + + // pub fn is_wibble(w: Wibble) -> Bool { + // w == Wibble + // } + // ``` + // Instead of `isEqual(w, new Wibble())`, generate `w instanceof Wibble` + // because the first approach needs to construct a new Wibble, and then call the isEqual function, + // which supports any shape of data, and so does a lot of extra logic which isn't necessary. + + if let Some(doc) = self.singleton_variant_equality(left, right, should_be_equal) { + return doc; + } + + if let Some(doc) = self.singleton_variant_equality(right, left, should_be_equal) { + return doc; + } + // Other types must be compared using structural equality let left = self.not_in_tail_position(Some(Ordering::Strict), |this| this.wrap_expression(left)); let right = self.not_in_tail_position(Some(Ordering::Strict), |this| this.wrap_expression(right)); + self.prelude_equal_call(should_be_equal, left, right) } + fn singleton_variant_equality( + &mut self, + left: &'a TypedExpr, + 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 + } + } + + fn singleton_equal( + &self, + value: Document<'a>, + tag: &EcoString, + should_be_equal: bool, + ) -> Document<'a> { + if should_be_equal { + docvec![value, " instanceof ", tag.to_doc()] + } else { + docvec!["!(", value, " instanceof ", tag.to_doc(), ")"] + } + } + fn equal_with_doc_operands( &mut self, left: Document<'a>, @@ -1966,16 +2027,26 @@ impl<'module, 'a> Generator<'module, 'a> { docvec![left, " !== ", right] } - ClauseGuard::Equals { left, right, .. } => { - let left = self.guard(left); - let right = self.guard(right); - self.prelude_equal_call(true, left, right) - } + ClauseGuard::Equals { left, right, .. } + | ClauseGuard::NotEquals { left, right, .. } => { + let should_be_equal = matches!(guard, ClauseGuard::Equals { .. }); + + // Handle singleton equality optimization for guards + if let Some(doc) = + self.singleton_variant_guard_equality(left, right, should_be_equal) + { + return doc; + } - ClauseGuard::NotEquals { left, right, .. } => { - let left = self.guard(left); - let right = self.guard(right); - self.prelude_equal_call(false, left, right) + if let Some(doc) = + self.singleton_variant_guard_equality(right, left, should_be_equal) + { + return doc; + } + + let left_doc = self.guard(left); + let right_doc = self.guard(right); + self.prelude_equal_call(should_be_equal, left_doc, right_doc) } ClauseGuard::GtFloat { left, right, .. } | ClauseGuard::GtInt { left, right, .. } => { @@ -2078,6 +2149,26 @@ impl<'module, 'a> Generator<'module, 'a> { } } + fn singleton_variant_guard_equality( + &mut self, + left: &'a TypedClauseGuard, + right: &'a TypedClauseGuard, + should_be_equal: bool, + ) -> Option> { + if let ClauseGuard::Constant(Constant::Record { + arguments, name, .. + }) = right + && arguments.is_empty() + && right.type_().is_named() + && let ClauseGuard::Var { type_, .. } = left + && !matches!(&**type_, Type::Fn { .. }) + { + let left_doc = self.guard(left); + return Some(self.singleton_equal(left_doc, name, should_be_equal)); + } + None + } + fn wrapped_guard(&mut self, guard: &'a TypedClauseGuard) -> Document<'a> { match guard { ClauseGuard::Var { .. } diff --git a/compiler-core/src/javascript/tests/custom_types.rs b/compiler-core/src/javascript/tests/custom_types.rs index 2fcecfef056..9a5f9920b04 100644 --- a/compiler-core/src/javascript/tests/custom_types.rs +++ b/compiler-core/src/javascript/tests/custom_types.rs @@ -687,3 +687,253 @@ pub type Wibble { "# ); } + +#[test] +fn singleton_record_equality() { + assert_js!( + r#" +pub type Wibble { + Wibble + Wobble +} + +pub fn is_wibble(w: Wibble) -> Bool { + w == Wibble +} +"#, + ); +} + +#[test] +fn singleton_record_inequality() { + assert_js!( + r#" +pub type Wibble { + Wibble + Wobble +} + +pub fn is_not_wibble(w: Wibble) -> Bool { + w != Wibble +} +"#, + ); +} + +#[test] +fn singleton_record_reverse_order() { + assert_js!( + r#" +pub type Wibble { + Wibble + Wobble +} + +pub fn is_wibble_reverse(w: Wibble) -> Bool { + Wibble == w +} +"#, + ); +} + +#[test] +fn non_singleton_record_equality() { + assert_js!( + r#" +pub type Person { + Person(name: String, age: Int) +} + +pub fn same_person(p1: Person, p2: Person) -> Bool { + p1 == p2 +} +"#, + ); +} + +#[test] +fn multiple_singleton_constructors() { + assert_js!( + r#" +pub type Status { + Loading + Success + Error +} + +pub fn is_loading(s: Status) -> Bool { + s == Loading +} + +pub fn is_success(s: Status) -> Bool { + s == Success +} +"#, + ); +} + +#[test] +fn mixed_singleton_and_non_singleton() { + assert_js!( + r#" +pub type Result { + Ok(value: Int) + Error +} + +pub fn is_error(r: Result) -> Bool { + r == Error +} +"#, + ); +} + +#[test] +fn singleton_in_case_guard() { + assert_js!( + r#" +pub type State { + Active + Inactive +} + +pub fn process(s: State) -> String { + case s { + state if state == Active -> "active" + _ -> "inactive" + } +} +"#, + ); +} + +#[test] +fn equality_with_non_singleton_variant() { + assert_js!( + r#" +pub type Thing { + Variant + Other(String) +} + +pub fn check_other(x: Thing) -> Bool { + x == Other("hello") +} +"#, + ); +} + +#[test] +fn guard_equality_with_non_singleton_variant() { + assert_js!( + r#" +pub type Thing { + Variant + Other(String) +} + +pub fn process(e: Thing) -> String { + case e { + value if value == Other("hello") -> "match" + _ -> "no match" + } +} +"#, + ); +} + +#[test] +fn variant_defined_in_another_module_qualified_expression() { + assert_js!( + ( + "other_module", + r#"pub type Thingy { Variant OtherVariant }"# + ), + r#" +import other_module + +pub fn check(x) -> Bool { + x == other_module.Variant +} +"#, + ); +} + +#[test] +fn variant_defined_in_another_module_unqualified_expression() { + assert_js!( + ("other_module", r#"pub type Thingy { Variant Other(Int) }"#), + r#" +import other_module.{Variant} + +pub fn check(x) -> Bool { + x == Variant +} +"#, + ); +} + +#[test] +fn variant_defined_in_another_module_aliased_expression() { + assert_js!( + ("other_module", r#"pub type Thingy { Variant Other(Int) }"#), + r#" +import other_module.{Variant as Aliased} + +pub fn check(x) -> Bool { + x == Aliased +} +"#, + ); +} + +#[test] +fn variant_defined_in_another_module_qualified_clause_guard() { + assert_js!( + ("other_module", r#"pub type Thingy { Variant Other(Int) }"#), + r#" +import other_module + +pub fn process(e) -> String { + case e { + value if value == other_module.Variant -> "match" + _ -> "no match" + } +} +"#, + ); +} + +#[test] +fn variant_defined_in_another_module_unqualified_clause_guard() { + assert_js!( + ("other_module", r#"pub type Thingy { Variant Other(Int) }"#), + r#" +import other_module.{Variant} + +pub fn process(e) -> String { + case e { + value if value == Variant -> "match" + _ -> "no match" + } +} +"#, + ); +} + +#[test] +fn variant_defined_in_another_module_aliased_clause_guard() { + assert_js!( + ("other_module", r#"pub type Thingy { Variant Other(Int) }"#), + r#" +import other_module.{Variant as Aliased} + +pub fn process(e) -> String { + case e { + value if value == Aliased -> "match" + _ -> "no match" + } +} +"#, + ); +} diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__case_clause_guards__custom_type_constructor_imported_and_aliased.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__case_clause_guards__custom_type_constructor_imported_and_aliased.snap index 002d9a563b8..33963a20fb3 100644 --- a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__case_clause_guards__custom_type_constructor_imported_and_aliased.snap +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__case_clause_guards__custom_type_constructor_imported_and_aliased.snap @@ -1,8 +1,6 @@ --- source: compiler-core/src/javascript/tests/case_clause_guards.rs -assertion_line: 474 expression: "import other_module.{A as B}\npub fn func() {\n case B {\n x if x == B -> True\n _ -> False\n }\n}\n" -snapshot_kind: text --- ----- SOURCE CODE import other_module.{A as B} @@ -17,12 +15,11 @@ pub fn func() { ----- COMPILED JAVASCRIPT import * as $other_module from "../../package/other_module.mjs"; import { A as B } from "../../package/other_module.mjs"; -import { isEqual } from "../gleam.mjs"; export function func() { let $ = new B(); let x = $; - if (isEqual(x, new B())) { + if (x instanceof B) { return true; } else { return false; diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__equality_with_non_singleton_variant.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__equality_with_non_singleton_variant.snap new file mode 100644 index 00000000000..80ab92302c6 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__equality_with_non_singleton_variant.snap @@ -0,0 +1,36 @@ +--- +source: compiler-core/src/javascript/tests/custom_types.rs +expression: "\npub type Thing {\n Variant\n Other(String)\n}\n\npub fn check_other(x: Thing) -> Bool {\n x == Other(\"hello\")\n}\n" +--- +----- SOURCE CODE + +pub type Thing { + Variant + Other(String) +} + +pub fn check_other(x: Thing) -> Bool { + x == Other("hello") +} + + +----- COMPILED JAVASCRIPT +import { CustomType as $CustomType, isEqual } from "../gleam.mjs"; + +export class Variant extends $CustomType {} +export const Thing$Variant = () => new Variant(); +export const Thing$isVariant = (value) => value instanceof Variant; + +export class Other extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Thing$Other = ($0) => new Other($0); +export const Thing$isOther = (value) => value instanceof Other; +export const Thing$Other$0 = (value) => value[0]; + +export function check_other(x) { + return isEqual(x, new Other("hello")); +} diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__guard_equality_with_non_singleton_variant.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__guard_equality_with_non_singleton_variant.snap new file mode 100644 index 00000000000..93f3e8229d3 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__guard_equality_with_non_singleton_variant.snap @@ -0,0 +1,44 @@ +--- +source: compiler-core/src/javascript/tests/custom_types.rs +expression: "\npub type Thing {\n Variant\n Other(String)\n}\n\npub fn process(e: Thing) -> String {\n case e {\n value if value == Other(\"hello\") -> \"match\"\n _ -> \"no match\"\n }\n}\n" +--- +----- SOURCE CODE + +pub type Thing { + Variant + Other(String) +} + +pub fn process(e: Thing) -> String { + case e { + value if value == Other("hello") -> "match" + _ -> "no match" + } +} + + +----- COMPILED JAVASCRIPT +import { CustomType as $CustomType, isEqual } from "../gleam.mjs"; + +export class Variant extends $CustomType {} +export const Thing$Variant = () => new Variant(); +export const Thing$isVariant = (value) => value instanceof Variant; + +export class Other extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Thing$Other = ($0) => new Other($0); +export const Thing$isOther = (value) => value instanceof Other; +export const Thing$Other$0 = (value) => value[0]; + +export function process(e) { + let value = e; + if (isEqual(value, new Other("hello"))) { + return "match"; + } else { + return "no match"; + } +} diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__mixed_singleton_and_non_singleton.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__mixed_singleton_and_non_singleton.snap new file mode 100644 index 00000000000..facec826ad1 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__mixed_singleton_and_non_singleton.snap @@ -0,0 +1,37 @@ +--- +source: compiler-core/src/javascript/tests/custom_types.rs +expression: "\npub type Result {\n Ok(value: Int)\n Error\n}\n\npub fn is_error(r: Result) -> Bool {\n r == Error\n}\n" +--- +----- SOURCE CODE + +pub type Result { + Ok(value: Int) + Error +} + +pub fn is_error(r: Result) -> Bool { + r == Error +} + + +----- COMPILED JAVASCRIPT +import { CustomType as $CustomType } from "../gleam.mjs"; + +export class Ok extends $CustomType { + constructor(value) { + super(); + this.value = value; + } +} +export const Result$Ok = (value) => new Ok(value); +export const Result$isOk = (value) => value instanceof Ok; +export const Result$Ok$value = (value) => value.value; +export const Result$Ok$0 = (value) => value.value; + +export class Error extends $CustomType {} +export const Result$Error = () => new Error(); +export const Result$isError = (value) => value instanceof Error; + +export function is_error(r) { + return r instanceof Error; +} diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__multiple_singleton_constructors.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__multiple_singleton_constructors.snap new file mode 100644 index 00000000000..b0d7056d28c --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__multiple_singleton_constructors.snap @@ -0,0 +1,43 @@ +--- +source: compiler-core/src/javascript/tests/custom_types.rs +expression: "\npub type Status {\n Loading\n Success\n Error\n}\n\npub fn is_loading(s: Status) -> Bool {\n s == Loading\n}\n\npub fn is_success(s: Status) -> Bool {\n s == Success\n}\n" +--- +----- SOURCE CODE + +pub type Status { + Loading + Success + Error +} + +pub fn is_loading(s: Status) -> Bool { + s == Loading +} + +pub fn is_success(s: Status) -> Bool { + s == Success +} + + +----- COMPILED JAVASCRIPT +import { CustomType as $CustomType } from "../gleam.mjs"; + +export class Loading extends $CustomType {} +export const Status$Loading = () => new Loading(); +export const Status$isLoading = (value) => value instanceof Loading; + +export class Success extends $CustomType {} +export const Status$Success = () => new Success(); +export const Status$isSuccess = (value) => value instanceof Success; + +export class Error extends $CustomType {} +export const Status$Error = () => new Error(); +export const Status$isError = (value) => value instanceof Error; + +export function is_loading(s) { + return s instanceof Loading; +} + +export function is_success(s) { + return s instanceof Success; +} diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__non_singleton_record_equality.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__non_singleton_record_equality.snap new file mode 100644 index 00000000000..fae6e7e4234 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__non_singleton_record_equality.snap @@ -0,0 +1,35 @@ +--- +source: compiler-core/src/javascript/tests/custom_types.rs +expression: "\npub type Person {\n Person(name: String, age: Int)\n}\n\npub fn same_person(p1: Person, p2: Person) -> Bool {\n p1 == p2\n}\n" +--- +----- SOURCE CODE + +pub type Person { + Person(name: String, age: Int) +} + +pub fn same_person(p1: Person, p2: Person) -> Bool { + p1 == p2 +} + + +----- COMPILED JAVASCRIPT +import { CustomType as $CustomType, isEqual } from "../gleam.mjs"; + +export class Person extends $CustomType { + constructor(name, age) { + super(); + this.name = name; + this.age = age; + } +} +export const Person$Person = (name, age) => new Person(name, age); +export const Person$isPerson = (value) => value instanceof Person; +export const Person$Person$name = (value) => value.name; +export const Person$Person$0 = (value) => value.name; +export const Person$Person$age = (value) => value.age; +export const Person$Person$1 = (value) => value.age; + +export function same_person(p1, p2) { + return isEqual(p1, p2); +} diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__singleton_in_case_guard.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__singleton_in_case_guard.snap new file mode 100644 index 00000000000..69e79adb08c --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__singleton_in_case_guard.snap @@ -0,0 +1,38 @@ +--- +source: compiler-core/src/javascript/tests/custom_types.rs +expression: "\npub type State {\n Active\n Inactive\n}\n\npub fn process(s: State) -> String {\n case s {\n state if state == Active -> \"active\"\n _ -> \"inactive\"\n }\n}\n" +--- +----- SOURCE CODE + +pub type State { + Active + Inactive +} + +pub fn process(s: State) -> String { + case s { + state if state == Active -> "active" + _ -> "inactive" + } +} + + +----- COMPILED JAVASCRIPT +import { CustomType as $CustomType } from "../gleam.mjs"; + +export class Active extends $CustomType {} +export const State$Active = () => new Active(); +export const State$isActive = (value) => value instanceof Active; + +export class Inactive extends $CustomType {} +export const State$Inactive = () => new Inactive(); +export const State$isInactive = (value) => value instanceof Inactive; + +export function process(s) { + let state = s; + if (state instanceof Active) { + return "active"; + } else { + return "inactive"; + } +} diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__singleton_record_equality.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__singleton_record_equality.snap new file mode 100644 index 00000000000..2933bedefc6 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__singleton_record_equality.snap @@ -0,0 +1,30 @@ +--- +source: compiler-core/src/javascript/tests/custom_types.rs +expression: "\npub type Wibble {\n Wibble\n Wobble\n}\n\npub fn is_wibble(w: Wibble) -> Bool {\n w == Wibble\n}\n" +--- +----- SOURCE CODE + +pub type Wibble { + Wibble + Wobble +} + +pub fn is_wibble(w: Wibble) -> Bool { + w == Wibble +} + + +----- COMPILED JAVASCRIPT +import { CustomType as $CustomType } from "../gleam.mjs"; + +export class Wibble extends $CustomType {} +export const Wibble$Wibble = () => new Wibble(); +export const Wibble$isWibble = (value) => value instanceof Wibble; + +export class Wobble extends $CustomType {} +export const Wibble$Wobble = () => new Wobble(); +export const Wibble$isWobble = (value) => value instanceof Wobble; + +export function is_wibble(w) { + return w instanceof Wibble; +} diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__singleton_record_inequality.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__singleton_record_inequality.snap new file mode 100644 index 00000000000..e062790629d --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__singleton_record_inequality.snap @@ -0,0 +1,30 @@ +--- +source: compiler-core/src/javascript/tests/custom_types.rs +expression: "\npub type Wibble {\n Wibble\n Wobble\n}\n\npub fn is_not_wibble(w: Wibble) -> Bool {\n w != Wibble\n}\n" +--- +----- SOURCE CODE + +pub type Wibble { + Wibble + Wobble +} + +pub fn is_not_wibble(w: Wibble) -> Bool { + w != Wibble +} + + +----- COMPILED JAVASCRIPT +import { CustomType as $CustomType } from "../gleam.mjs"; + +export class Wibble extends $CustomType {} +export const Wibble$Wibble = () => new Wibble(); +export const Wibble$isWibble = (value) => value instanceof Wibble; + +export class Wobble extends $CustomType {} +export const Wibble$Wobble = () => new Wobble(); +export const Wibble$isWobble = (value) => value instanceof Wobble; + +export function is_not_wibble(w) { + return !(w instanceof Wibble); +} diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__singleton_record_reverse_order.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__singleton_record_reverse_order.snap new file mode 100644 index 00000000000..41ac8f2d288 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__singleton_record_reverse_order.snap @@ -0,0 +1,30 @@ +--- +source: compiler-core/src/javascript/tests/custom_types.rs +expression: "\npub type Wibble {\n Wibble\n Wobble\n}\n\npub fn is_wibble_reverse(w: Wibble) -> Bool {\n Wibble == w\n}\n" +--- +----- SOURCE CODE + +pub type Wibble { + Wibble + Wobble +} + +pub fn is_wibble_reverse(w: Wibble) -> Bool { + Wibble == w +} + + +----- COMPILED JAVASCRIPT +import { CustomType as $CustomType } from "../gleam.mjs"; + +export class Wibble extends $CustomType {} +export const Wibble$Wibble = () => new Wibble(); +export const Wibble$isWibble = (value) => value instanceof Wibble; + +export class Wobble extends $CustomType {} +export const Wibble$Wobble = () => new Wobble(); +export const Wibble$isWobble = (value) => value instanceof Wobble; + +export function is_wibble_reverse(w) { + return w instanceof Wibble; +} diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_aliased_clause_guard.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_aliased_clause_guard.snap new file mode 100644 index 00000000000..eaec979a3f5 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_aliased_clause_guard.snap @@ -0,0 +1,32 @@ +--- +source: compiler-core/src/javascript/tests/custom_types.rs +expression: "\nimport other_module.{Variant as Aliased}\n\npub fn process(e) -> String {\n case e {\n value if value == Aliased -> \"match\"\n _ -> \"no match\"\n }\n}\n" +--- +----- SOURCE CODE +-- other_module.gleam +pub type Thingy { Variant Other(Int) } + +-- main.gleam + +import other_module.{Variant as Aliased} + +pub fn process(e) -> String { + case e { + value if value == Aliased -> "match" + _ -> "no match" + } +} + + +----- COMPILED JAVASCRIPT +import * as $other_module from "../other_module.mjs"; +import { Variant as Aliased } from "../other_module.mjs"; + +export function process(e) { + let value = e; + if (value instanceof Aliased) { + 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_aliased_expression.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_aliased_expression.snap new file mode 100644 index 00000000000..17e25679264 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_aliased_expression.snap @@ -0,0 +1,24 @@ +--- +source: compiler-core/src/javascript/tests/custom_types.rs +expression: "\nimport other_module.{Variant as Aliased}\n\npub fn check(x) -> Bool {\n x == Aliased\n}\n" +--- +----- SOURCE CODE +-- other_module.gleam +pub type Thingy { Variant Other(Int) } + +-- main.gleam + +import other_module.{Variant as Aliased} + +pub fn check(x) -> Bool { + x == Aliased +} + + +----- COMPILED JAVASCRIPT +import * as $other_module from "../other_module.mjs"; +import { Variant as Aliased } from "../other_module.mjs"; + +export function check(x) { + return x instanceof Variant; +} 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 new file mode 100644 index 00000000000..6e6003ccc47 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_qualified_clause_guard.snap @@ -0,0 +1,31 @@ +--- +source: compiler-core/src/javascript/tests/custom_types.rs +expression: "\nimport other_module\n\npub fn process(e) -> String {\n case e {\n value if value == other_module.Variant -> \"match\"\n _ -> \"no match\"\n }\n}\n" +--- +----- SOURCE CODE +-- other_module.gleam +pub type Thingy { Variant Other(Int) } + +-- main.gleam + +import other_module + +pub fn process(e) -> String { + case e { + value if value == other_module.Variant -> "match" + _ -> "no match" + } +} + + +----- COMPILED JAVASCRIPT +import * as $other_module from "../other_module.mjs"; + +export function process(e) { + let value = e; + if (value instanceof 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 new file mode 100644 index 00000000000..7ac37916696 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_qualified_expression.snap @@ -0,0 +1,24 @@ +--- +source: compiler-core/src/javascript/tests/custom_types.rs +expression: "\nimport other_module\n\npub fn check(x) -> Bool {\n x == other_module.Variant\n}\n" +--- +----- SOURCE CODE +-- other_module.gleam +pub type Thingy { Variant OtherVariant } + +-- main.gleam + +import other_module + +pub fn check(x) -> Bool { + x == other_module.Variant +} + + +----- 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()); +} diff --git a/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_unqualified_clause_guard.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_unqualified_clause_guard.snap new file mode 100644 index 00000000000..6e898796329 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_unqualified_clause_guard.snap @@ -0,0 +1,32 @@ +--- +source: compiler-core/src/javascript/tests/custom_types.rs +expression: "\nimport other_module.{Variant}\n\npub fn process(e) -> String {\n case e {\n value if value == Variant -> \"match\"\n _ -> \"no match\"\n }\n}\n" +--- +----- SOURCE CODE +-- other_module.gleam +pub type Thingy { Variant Other(Int) } + +-- main.gleam + +import other_module.{Variant} + +pub fn process(e) -> String { + case e { + value if value == Variant -> "match" + _ -> "no match" + } +} + + +----- COMPILED JAVASCRIPT +import * as $other_module from "../other_module.mjs"; +import { Variant } from "../other_module.mjs"; + +export function process(e) { + let value = e; + if (value instanceof 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_unqualified_expression.snap b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_unqualified_expression.snap new file mode 100644 index 00000000000..a51477e0c6e --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__variant_defined_in_another_module_unqualified_expression.snap @@ -0,0 +1,24 @@ +--- +source: compiler-core/src/javascript/tests/custom_types.rs +expression: "\nimport other_module.{Variant}\n\npub fn check(x) -> Bool {\n x == Variant\n}\n" +--- +----- SOURCE CODE +-- other_module.gleam +pub type Thingy { Variant Other(Int) } + +-- main.gleam + +import other_module.{Variant} + +pub fn check(x) -> Bool { + x == Variant +} + + +----- COMPILED JAVASCRIPT +import * as $other_module from "../other_module.mjs"; +import { Variant } from "../other_module.mjs"; + +export function check(x) { + return x instanceof Variant; +} diff --git a/compiler-core/src/type_.rs b/compiler-core/src/type_.rs index 6c9d04e99f3..ba8f9b10436 100644 --- a/compiler-core/src/type_.rs +++ b/compiler-core/src/type_.rs @@ -131,6 +131,13 @@ impl Type { } } + pub fn is_named(&self) -> bool { + match self { + Self::Named { .. } => true, + _ => false, + } + } + pub fn result_ok_type(&self) -> Option> { match self { Self::Named { @@ -1375,10 +1382,13 @@ pub struct ValueConstructor { pub type_: Arc, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)] pub enum Deprecation { + #[default] NotDeprecated, - Deprecated { message: EcoString }, + Deprecated { + message: EcoString, + }, } impl Deprecation { @@ -1391,12 +1401,6 @@ impl Deprecation { } } -impl Default for Deprecation { - fn default() -> Self { - Self::NotDeprecated - } -} - impl ValueConstructor { pub fn local_variable(location: SrcSpan, origin: VariableOrigin, type_: Arc) -> Self { Self {