Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion compiler-core/src/analyse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> {
Known(T),
#[default]
Unknown,
}

Expand Down
6 changes: 0 additions & 6 deletions compiler-core/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2473,12 +2473,6 @@ impl<T> BitArraySize<T> {
}
}

impl Default for Inferred<()> {
fn default() -> Self {
Self::Unknown
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum AssignName {
Variable(EcoString),
Expand Down
2 changes: 1 addition & 1 deletion compiler-core/src/erlang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,7 @@ fn string_inner(value: &str) -> Document<'_> {
.replace_all(value, |caps: &Captures<'_>| {
let slashes = caps.get(1).map_or("", |m| m.as_str());

if slashes.len() % 2 == 0 {
if slashes.len().is_multiple_of(2) {
format!("{slashes}u")
} else {
format!("{slashes}x")
Expand Down
119 changes: 110 additions & 9 deletions compiler-core/src/javascript/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1536,14 +1536,83 @@ 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(result) = self.handle_singleton_equality(left, right, should_be_equal) {
return result;
}

// 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 handle_singleton_equality(
&mut self,
left: &'a TypedExpr,
right: &'a TypedExpr,
should_be_equal: bool,
) -> Option<Document<'a>> {
fn singleton_record_constructor_name(expr: &TypedExpr) -> Option<&EcoString> {
match expr {
TypedExpr::Var {
constructor:
ValueConstructor {
variant: ValueConstructorVariant::Record { arity: 0, name, .. },
..
},
..
} => Some(name),
_ => None,
}
}

if let Some(name) = singleton_record_constructor_name(right) {
let left_doc = self
.not_in_tail_position(Some(Ordering::Strict), |this| this.wrap_expression(left));
return Some(self.singleton_equal(left_doc, name, should_be_equal));
}

if let Some(name) = singleton_record_constructor_name(left) {
let right_doc = self
.not_in_tail_position(Some(Ordering::Strict), |this| this.wrap_expression(right));
return Some(self.singleton_equal(right_doc, name, should_be_equal));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we extract the duplicated bits to helper methods please 🙏

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

surely

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hasn't been done yet! The patterns are still duplicated. Could we write something like this?:

if let Some(document) = self.zero_arity_variant_equality(left, right, should_be_equal) {
    return document;
}
if let Some(document) = self.zero_arity_variant_equality(right, left, should_be_equal) {
    return document;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still outstanding 🙏

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still hasn't been done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once again, this still needs to be done


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>,
Expand Down Expand Up @@ -1966,16 +2035,20 @@ 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 { .. });

ClauseGuard::NotEquals { left, right, .. } => {
let left = self.guard(left);
let right = self.guard(right);
self.prelude_equal_call(false, left, right)
// Handle singleton equality optimization for guards
if let Some(result) =
self.handle_guard_singleton_equality(left, right, should_be_equal)
{
return result;
}

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, .. } => {
Expand Down Expand Up @@ -2078,6 +2151,34 @@ impl<'module, 'a> Generator<'module, 'a> {
}
}

fn handle_guard_singleton_equality(
&mut self,
left: &'a TypedClauseGuard,
right: &'a TypedClauseGuard,
should_be_equal: bool,
) -> Option<Document<'a>> {
fn singleton_record_constructor_name(guard: &TypedClauseGuard) -> Option<&EcoString> {
match guard {
ClauseGuard::Constant(Constant::Record {
arguments, name, ..
}) if arguments.is_empty() => Some(name),
_ => None,
}
}

if let Some(name) = singleton_record_constructor_name(right) {
let left_doc = self.guard(left);
return Some(self.singleton_equal(left_doc, name, should_be_equal));
}

if let Some(name) = singleton_record_constructor_name(left) {
let right_doc = self.guard(right);
return Some(self.singleton_equal(right_doc, name, should_be_equal));
}

None
}

fn wrapped_guard(&mut self, guard: &'a TypedClauseGuard) -> Document<'a> {
match guard {
ClauseGuard::Var { .. }
Expand Down
Loading
Loading