Fork of daniellemaywood.uk/gleam — Wasm codegen work
156 kB
4096 lines
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2018 The Gleam contributors
3
4mod pattern;
5#[cfg(test)]
6mod tests;
7
8use crate::build::Target;
9use crate::erlang::pattern::{AliasedLiteral, PatternGenerator};
10use crate::strings::to_snake_case;
11use crate::type_::{self, is_prelude_module};
12use crate::{
13 ast::*,
14 line_numbers::LineNumbers,
15 type_::{
16 ModuleValueConstructor, PatternConstructor, Type, TypeVar, TypedCallArg, ValueConstructor,
17 ValueConstructorVariant,
18 },
19};
20use camino::Utf8Path;
21use ecow::{EcoString, eco_format};
22use erlang_generation::{
23 BitArraySegmentSpecifier, DocContent, ErlangBuilder, ErlangModuleName, ErlangSourceBuilder,
24};
25use itertools::Itertools;
26use num_bigint::BigInt;
27use num_traits::Signed;
28use regex::Regex;
29use std::collections::VecDeque;
30use std::sync::OnceLock;
31use std::{collections::HashMap, ops::Deref, sync::Arc};
32
33/// This is an open runtime error to which more fields can still be added.
34#[must_use]
35struct RuntimeError<Map, Call> {
36 /// This is the map that is going to be thrown by the `erlang:error` call.
37 error_map: Map,
38 /// This is the call to `erlang:error` that will throw the error, with the
39 /// map as an argument.
40 erlang_error_call: Call,
41}
42
43/// Represents all the different kind of runtime errors that Gleam can raise.
44enum RuntimeErrorKind {
45 Todo,
46 Panic,
47 Assert,
48 LetAssert,
49}
50
51impl RuntimeErrorKind {
52 fn default_error_message(&self) -> &'static str {
53 match self {
54 RuntimeErrorKind::Panic => "`panic` expression evaluated.",
55 RuntimeErrorKind::Assert => "Assertion failed.",
56 RuntimeErrorKind::LetAssert => "Pattern match failed, no pattern matched the value.",
57 RuntimeErrorKind::Todo => {
58 "`todo` expression evaluated. This code has not yet been implemented."
59 }
60 }
61 }
62}
63
64enum EchoPrintedValue<'a> {
65 /// We're printing the result of a pipeline step.
66 PipeStep {
67 /// This is the name that was given to the variable holding the value
68 /// we have to print.
69 name: EcoString,
70 },
71 /// We're printing any arbitrary expression.
72 Expression { value: &'a TypedExpr },
73}
74
75/// This describes how an expression that is used in a Gleam's function call
76/// should be called in the Erlang generated code.
77enum FunctionCall<'a> {
78 /// We're calling a function from the given module.
79 /// It might be the same module we're generating code for, so the
80 /// qualification might not be needed at all; remember to check that!
81 ///
82 /// ```erl
83 /// io:println("wibble")
84 /// ```
85 ///
86 Call { module: &'a str, name: &'a str },
87
88 /// The expression is not a module level function and can be called directly
89 /// like thie:
90 ///
91 /// ```erl
92 /// SomeVariable("wibble"),
93 /// fun() -> nil end().
94 /// ```
95 DirectCall,
96
97 /// This is actually not a call but rather needs to build a tuple with the
98 /// given tag.
99 /// This is needed for records: those are function calls in Gleam, but
100 /// simple tuples on the Erlang side.
101 BuildRecord { name: &'a str },
102}
103
104/// This is a structure used to generate code for an Erlang module.
105#[derive(Debug)]
106pub struct Generator<'a> {
107 /// The module for which we're currently generating Erlang code.
108 module: &'a TypedModule,
109 line_numbers: &'a LineNumbers,
110
111 /// The relative source path to the module that's gonna be used in error
112 /// messages in the generated Erlang code.
113 module_source_path: EcoString,
114
115 /// Wether `echo` has been used in this module, we're gonna need to know
116 /// this in order to add the code needed by the pretty printing.
117 echo_used: bool,
118}
119
120/// This is a generator that takes care of generating the code for a single
121/// function, taking care of things like the scope, variable renaming, and
122/// generating the function's attributes and statements.
123struct FunctionGenerator<'a, 'generator> {
124 /// The name of the function we're generating code for.
125 function_name: &'a str,
126
127 /// A reference to the module generator, this is needed to take care of some
128 /// global state shared by all the functions.
129 module_generator: &'generator mut Generator<'a>,
130
131 /// This maps from variable origin in the Gleam code to the name it was
132 /// assigned to it in the generated Erlang code.
133 ///
134 /// Erlang doesn't allow shadowing existing variables, so it's not always
135 /// the case that a variable named `wibble` in Gleam is going to correspond
136 /// to the Erlang `Wibble` variable. For example:
137 ///
138 /// ```gleam
139 /// let a = 1
140 /// let a = a + 1
141 /// ```
142 ///
143 /// In Erlang this would become:
144 ///
145 /// ```erl
146 /// A = 1,
147 /// A@1 = A + 1,
148 /// ```
149 ///
150 /// So variables might need renaming.
151 /// Whenever we find a variable usage in Gleam we have to check "what is
152 /// the name that was given to the variable that comes from this location?"
153 /// Only then we'll know what's the correct name to use for it.
154 ///
155 variable_names: im::HashMap<SrcSpan, EcoString>,
156
157 /// This keeps track of the number of generated variables that have already
158 /// been generated in the current function.
159 /// For example if this is `2` it means we've already generated:
160 ///
161 /// ```erl
162 /// _value
163 /// _value@1
164 /// _value@2
165 /// ```
166 ///
167 /// We need this to make sure that every time we generate a new generated
168 /// variable it has a unique name not shadowing anything else.
169 ///
170 generated_variables: usize,
171
172 /// This keeps track of all the names that are taken for the current
173 /// function and can't be used when defining new variables.
174 /// For example if this is `hash_map![("wibble", 2), ("wobble", 1)]`
175 /// this means that all of these variables have already been defined
176 /// somewhere in the current function:
177 ///
178 /// ```erl
179 /// Wibble = ...,
180 /// Wibble@1 = ...,
181 /// Wibble@2 = ...,
182 ///
183 /// Wobble = ...,
184 /// Wobble@1 = ...,
185 /// ```
186 ///
187 /// This is handy whenever we run into a new variable assignment and have to
188 /// generate a new name for it in Erlang.
189 ///
190 taken_names: im::HashMap<String, usize>,
191}
192
193impl<'a> Generator<'a> {
194 pub fn new(
195 module: &'a TypedModule,
196 line_numbers: &'a LineNumbers,
197 module_root: &'a Utf8Path,
198 ) -> Self {
199 let module_source_path = module
200 .type_info
201 .src_path
202 .strip_prefix(module_root)
203 .unwrap_or(&module.type_info.src_path)
204 .as_str()
205 .replace("\\", "\\\\")
206 .into();
207
208 Self {
209 module,
210 module_source_path,
211 line_numbers,
212 echo_used: false,
213 }
214 }
215
216 fn module_document<Output>(&mut self, builder: &mut impl ErlangBuilder<Output>) {
217 // We need to know which private functions are referenced in importable
218 // constants so that we can export them anyway in the generated Erlang.
219 // This is because otherwise when the constant is used in another module it
220 // would result in an error as it tries to reference this private function.
221 let overridden_publicity =
222 find_private_functions_referenced_in_importable_constants(self.module);
223
224 // We add a `-compile` attribute at the top of each module to instruct
225 // the Erlang compiler.
226 builder.compile_attribute([
227 "no_auto_import",
228 "nowarn_ignored",
229 "nowarn_unused_vars",
230 "nowarn_unused_function",
231 "nowarn_nomatch",
232 "inline",
233 ]);
234
235 // We then need to add an `-export` attribute for all the module's
236 // public functions.
237 builder.export_attribute(
238 (self.module.definitions.functions.iter())
239 .filter_map(|function| function_export(function, &overridden_publicity)),
240 );
241 // We do the same but with types.
242 builder.export_type_attribute(self.module.definitions.custom_types.iter().map(type_export));
243
244 // We also add a `-module_doc` comment at the beginning of the module
245 // with its documentation.
246 self.module_documentation(builder);
247
248 // Then we generate `-type` definitions for the module's types.
249 for custom_type in &self.module.definitions.custom_types {
250 self.type_definition(builder, custom_type);
251 }
252
253 // And finally generate all the functions that the module defined.
254 for function in &self.module.definitions.functions {
255 FunctionGenerator::new(function, self).module_function(builder, function);
256 }
257 }
258
259 fn module_documentation<Output>(&mut self, builder: &mut impl ErlangBuilder<Output>) {
260 if self.module.type_info.is_internal {
261 // The module is internal so we need to add a `-moduledoc(false).`
262 // attribute to make sure its documentation is hidden.
263 builder.moduledoc_attribute(DocContent::False);
264 } else if self.module.documentation.is_empty() {
265 // The module is not internal, but it has no docs.
266 // We don't have to do anything.
267 } else {
268 // The module has some documentation that we're going to include
269 // with a `-moduledoc` attribute.
270 builder.moduledoc_attribute(DocContent::String(
271 &self.module.documentation.iter().join("\n"),
272 ));
273 }
274 }
275
276 fn type_definition<Output>(
277 &self,
278 builder: &mut impl ErlangBuilder<Output>,
279 custom_type: &TypedCustomType,
280 ) {
281 let TypedCustomType {
282 name,
283 constructors,
284 opaque,
285 typed_parameters,
286 external_erlang,
287 ..
288 } = custom_type;
289
290 let name = erl_safe_type_name(to_snake_case(name));
291
292 // We start the type spec.
293 let type_spec = builder.start_type_spec(
294 *opaque,
295 &name,
296 typed_parameters
297 .iter()
298 .map(|type_| type_parameter_name(type_)),
299 );
300
301 // Now we need to generate the type definition.
302 // Erlang doesn't allow to have phantom type variables, so if there's
303 // any type variable that is not used we will need to add one variant to
304 // the resulting type that is using all those phantom variables to avoid
305 // errors!
306 let phantom_type_variables = phantom_type_variables(custom_type);
307 let has_phantom_type_variables = !phantom_type_variables.is_empty();
308 match (constructors.as_slice(), has_phantom_type_variables) {
309 // This is an external type with an annotation telling us what type
310 // it corresponds to in Erlang.
311 // In that case all type variables are phantom type variables!
312 ([], _) if let Some((module, type_name, _)) = external_erlang => {
313 let type_ =
314 builder.start_remote_named_type(ErlangModuleName::new(module), type_name);
315 for type_variable in phantom_type_variables {
316 builder.type_variable(&type_variable);
317 }
318 builder.end_remote_named_type(type_);
319 }
320 // This is an external type with no external annotation and no
321 // phantom type variables. It is just `any()`.
322 ([], false) => {
323 let any = builder.start_named_type("any");
324 builder.end_named_type(any);
325 }
326 // This is an external type with no external annotation and some
327 // phantom type variables, we need to add an alternative to use
328 // them: `any() | {gleam_phantom, A, B, ...}`
329 ([], true) => {
330 let union = builder.start_union_type();
331 let any = builder.start_named_type("any");
332 builder.end_named_type(any);
333 self.phantom_type(builder, phantom_type_variables);
334 builder.end_union_type(union);
335 }
336 // This is an external type with a single constructor, no need to
337 // make it a union.
338 ([constructor], false) => self.constructor_type(builder, constructor),
339 // This is an external type with multiple constructors, we have to
340 // turn it into a union!
341 (constructors, has_phantom_type_variables) => {
342 let union = builder.start_union_type();
343 for constructor in constructors {
344 self.constructor_type(builder, constructor);
345 }
346 if has_phantom_type_variables {
347 self.phantom_type(builder, phantom_type_variables);
348 }
349 builder.end_union_type(union);
350 }
351 }
352
353 builder.end_type_spec(type_spec);
354 }
355
356 /// Given a constructor this generates its type. For example:
357 ///
358 /// ```gleam
359 /// Wibble(Int, String)
360 /// ```
361 ///
362 /// Would be turned into:
363 ///
364 /// ```erl
365 /// {wibble, integer(), binary()}.
366 /// ```
367 ///
368 fn constructor_type<Output>(
369 &self,
370 builder: &mut impl ErlangBuilder<Output>,
371 constructor: &RecordConstructor<Arc<Type>>,
372 ) {
373 let constructor_atom = to_snake_case(&constructor.name);
374 if constructor.arguments.is_empty() {
375 // A constructor with no fields becomes a regular atom on the Erlang
376 // target.
377 builder.literal_atom_type(&constructor_atom);
378 } else {
379 // Othwerwise, it is a tuple tagged with the atom with the
380 // constructor name.
381 let generator = TypeGenerator::new(&self.module.name);
382 let tuple = builder.start_tuple_type();
383 builder.literal_atom_type(&constructor_atom);
384 for argument in &constructor.arguments {
385 generator.type_(builder, &argument.type_);
386 }
387 builder.end_tuple_type(tuple);
388 }
389 }
390
391 /// Given a list of phantom type variabes, this generates a type using all
392 /// of those.
393 ///
394 /// Erlang doesn't allow having phantom type variables in type annotations,
395 /// so whenever there's any we need to manually add a type that uses them to
396 /// make sure we get no errors. For example:
397 ///
398 /// ```gleam
399 /// pub type Wibble(a, phantom) {
400 /// Wibble(a)
401 /// }
402 /// ```
403 ///
404 /// Will have to be turned into:
405 ///
406 /// ```erl
407 /// -type wibble(A) :: {wibble, A} | {gleam_phantom, Phantom}.
408 /// ```
409 ///
410 /// So the phantom type is nothing more than a tuple tagged with
411 /// `gleam_phantom`.
412 ///
413 fn phantom_type<Output>(
414 &self,
415 builder: &mut impl ErlangBuilder<Output>,
416 phantom_type_variables: Vec<EcoString>,
417 ) {
418 let phantom_tuple = builder.start_tuple_type();
419 builder.literal_atom_type("gleam_phantom");
420 for phantom_type_variable in phantom_type_variables {
421 builder.type_variable(&phantom_type_variable);
422 }
423 builder.end_tuple_type(phantom_tuple);
424 }
425}
426
427/// Given a custom type, this will return a vector with the names of all the
428/// phantom type varaibles that it has. The names returned are the names we can
429/// use in Erlang!
430fn phantom_type_variables(custom_type: &CustomType<Arc<Type>>) -> Vec<EcoString> {
431 // We first find all the variables that appear in the type definition
432 // itself: any of those that isn't used by any of the constructors is going
433 // to be a phantom type variable.
434 let mut definition_type_variables =
435 collect_type_var_usages(HashMap::new(), custom_type.typed_parameters.iter());
436
437 // So we need to gather all the type variables referenced by all the
438 // constructors.
439 let mut constructors_type_variables = HashMap::new();
440 for constructor in &custom_type.constructors {
441 constructors_type_variables = collect_type_var_usages(
442 constructors_type_variables,
443 constructor.arguments.iter().map(|argument| &argument.type_),
444 );
445 }
446
447 // The phantom ones are the ones in the definition that are not referenced
448 // by any constructor:
449 for used_type_variable in constructors_type_variables.keys() {
450 let _ = definition_type_variables.remove(used_type_variable);
451 }
452
453 definition_type_variables
454 .into_keys()
455 .map(id_to_type_var_str)
456 .sorted()
457 .collect_vec()
458}
459
460/// Given a custom type's type parameter (that is expected to be generic or
461/// unbound), this will return the name the corresponding type variable should
462/// have in the generated erlang code.
463///
464/// If the type passed is not generic this will panic!
465fn type_parameter_name(type_: &Type) -> EcoString {
466 let Type::Var { type_ } = type_ else {
467 panic!("non generic type as type parameter")
468 };
469 match &*type_.borrow() {
470 TypeVar::Unbound { id } | TypeVar::Generic { id } => id_to_type_var_str(*id),
471 TypeVar::Link { type_ } => type_parameter_name(type_),
472 }
473}
474
475impl<'a, 'generator> FunctionGenerator<'a, 'generator> {
476 pub fn new(
477 function: &'a TypedFunction,
478 module_generator: &'generator mut Generator<'a>,
479 ) -> Self {
480 let function_name = match function.name.as_ref() {
481 Some((_, function_name)) => function_name,
482 None => panic!("Module functions should have a name"),
483 };
484
485 Self {
486 function_name,
487 module_generator,
488 taken_names: im::HashMap::new(),
489 variable_names: im::HashMap::new(),
490 generated_variables: 0,
491 }
492 }
493
494 /// Given a variable name this returns a document with the name used to
495 /// reference such variable (names can change if a variable were to shadow
496 /// something with the same name!).
497 ///
498 /// ## Panics
499 /// This will panic if the variable is not in scope as that is most likely
500 /// the result of a bug in the compiler.
501 pub fn local_var_name(&self, variable_origin: &SrcSpan) -> EcoString {
502 self.variable_names
503 .get(variable_origin)
504 .expect("variable not in scope")
505 .clone()
506 }
507
508 /// Assigns a name to this new variable making sure it's not shadowing any
509 /// existing one.
510 ///
511 /// - `name` is the name of the variable as defined in the Gleam source code
512 /// - `location` is where that variable comes from, and it is used to then
513 /// get this newly generated name back.
514 ///
515 /// For example:
516 ///
517 /// ```gleam
518 /// let wibble = 1
519 /// ```
520 ///
521 /// When we run into this Gleam assignment we will need to decide how to
522 /// call it on the Erlang side. So we would call:
523 ///
524 /// ```ignore
525 /// let location = todo!("the location of this variable")
526 /// new_erlang_variable("wibble", location)
527 /// // and later we can tell what name was picked by calling
528 /// // `local_variable_name`
529 /// local_variable_name(location) // "Wibble"
530 /// ```
531 ///
532 ///
533 pub fn new_erlang_variable(&mut self, name: &str, location: SrcSpan) -> EcoString {
534 let next = self.taken_names.get(name).map_or(0, |i| i + 1);
535 let _ = self.taken_names.insert(name.to_string(), next);
536 let erlang_name = match next {
537 0 => variable_name(name),
538 _ => eco_format!("{}@{}", variable_name(name), next),
539 };
540 let _ = self.variable_names.insert(location, erlang_name.clone());
541 erlang_name
542 }
543
544 /// Sometimes during code generation we might need to create new variables
545 /// that were not accounted for during analysis.
546 /// Those variables don't really have an origin in the source code and are
547 /// usually generated and immediately used.
548 ///
549 /// For example:
550 ///
551 /// ```erl
552 /// _denominator = ...,
553 /// 1 / _denominator.
554 /// ```
555 ///
556 /// Any time you need one such variable you can create it with this method
557 /// instead of `new_erlang_variable` which is meant to be used for variables
558 /// generated from Gleam code (and so wants the source location of the
559 /// variable).
560 ///
561 /// The generated name is guaranteed to always be unique for the given
562 /// function.
563 ///
564 fn new_generated_variable(&mut self) -> EcoString {
565 let name = if self.generated_variables == 0 {
566 EcoString::from("_value")
567 } else {
568 eco_format!("_value@{}", self.generated_variables)
569 };
570 self.generated_variables += 1;
571 name
572 }
573
574 /// Generates code for an Erlang module function. This might return None
575 /// if there's no code to be generated at all!
576 /// For example if the function is unused, or if the function is a private
577 /// Erlang external (in which case, it would be inlined instead).
578 fn module_function<Output>(
579 &mut self,
580 builder: &mut impl ErlangBuilder<Output>,
581 function: &'a TypedFunction,
582 ) {
583 // We don't generate any code for unused functions.
584 if self
585 .module_generator
586 .module
587 .unused_definition_positions
588 .contains(&function.location.start)
589 {
590 return;
591 }
592
593 // Private external functions don't need to render anything, the
594 // underlying Erlang implementation is used directly at the call site.
595 if function.external_erlang.is_some() && function.publicity.is_private() {
596 return;
597 }
598
599 // If the function has no suitable Erlang implementation then there is
600 // nothing to generate for it.
601 if !function.implementations.supports(Target::Erlang) {
602 return;
603 }
604
605 let function_name = EcoString::from(escape_erlang_existing_name(self.function_name));
606
607 // Then we add the function's documentation and type annotation.
608 builder.file_attribute(
609 &self.module_generator.module_source_path,
610 self.module_generator
611 .line_numbers
612 .line_number(function.location.start),
613 );
614 self.function_spec_attribute(builder, &function_name, function);
615 self.function_doc_attribute(builder, function);
616
617 // Finally we start generating code for the function itself, how we do
618 // it depends if the function is external or not.
619 let arity = function.arguments.len();
620 match function.external_erlang.as_ref() {
621 // If the function is not external we generate the code for all of
622 // its statements.
623 None => {
624 let arguments = self.function_arguments_names(&function.arguments, false);
625 let open_function = builder.start_function(&function_name, arity, arguments);
626 self.statement_sequence(builder, &function.body);
627 builder.end_function(open_function);
628 }
629
630 // An external function consists of just a remote call being
631 // passed all of the function's arguments.
632 Some((module, external_function_name, _location)) => {
633 let arguments = self
634 .function_arguments_names(&function.arguments, true)
635 .collect_vec();
636 let open_function =
637 builder.start_function(&function_name, arity, arguments.clone());
638 let call = builder
639 .start_remote_call(ErlangModuleName::new(module), external_function_name);
640 for argument in arguments {
641 builder.variable(&argument);
642 }
643 builder.end_call(call);
644 builder.end_function(open_function);
645 }
646 }
647 }
648
649 /// This generates the `-spec` attribute for a function with the given name.
650 ///
651 fn function_spec_attribute<Output>(
652 &mut self,
653 builder: &mut impl ErlangBuilder<Output>,
654 function_name: &EcoString,
655 function: &'a Function<Arc<Type>, TypedExpr>,
656 ) {
657 // We start by getting all the type variable usages from this function,
658 // both in the argument types and return type.
659 let module_name = &self.module_generator.module.name;
660 let var_usages = &collect_type_var_usages(
661 HashMap::new(),
662 function
663 .arguments
664 .iter()
665 .map(|argument| &argument.type_)
666 .chain(std::iter::once(&function.return_type)),
667 );
668 let generator = TypeGenerator::new(module_name).with_var_usages(var_usages);
669
670 // We can then start generating the function spec.
671 let spec = builder.start_function_spec(function_name, function.arguments.len());
672 let function_type = builder.start_function_type();
673 for argument in &function.arguments {
674 generator.type_(builder, &argument.type_);
675 }
676 let function_type = builder.end_function_type_arguments(function_type);
677 generator.type_(builder, &function.return_type);
678 builder.end_function_type(function_type);
679 builder.end_function_spec(spec);
680 }
681
682 fn function_doc_attribute<Output>(
683 &self,
684 builder: &mut impl ErlangBuilder<Output>,
685 function: &TypedFunction,
686 ) {
687 // If a function is marked as internal or comes from an internal module
688 // we want to hide its documentation in the Erlang shell!
689 // So the doc directive will look like this: `-doc(false).`
690 let is_internal =
691 self.module_generator.module.type_info.is_internal || function.publicity.is_internal();
692
693 if is_internal {
694 builder.doc_attribute(DocContent::False);
695 } else if let Some((_, documentation)) = &function.documentation
696 && !documentation.is_empty()
697 {
698 builder.doc_attribute(DocContent::String(documentation));
699 }
700 }
701
702 /// Given a function, this will return the names of the arguments to be used
703 /// in this function's definition. This will also update the current scope
704 /// to add those names to the available local variables.
705 fn function_arguments_names(
706 &mut self,
707 arguments: &[TypedArg],
708 is_external: bool,
709 ) -> impl Iterator<Item = EcoString> {
710 arguments.iter().map(move |argument| match &argument.names {
711 // When the function is external we need to be careful with discarded
712 // arguments. _All_ of the function arguments are always used in an
713 // external function, regardless of them being discarded in Gleam:
714 //
715 // ```gleam
716 // @external(erlang, "io", "format")
717 // fn format(_string: String, _args: List(String)) -> Nil
718 // ```
719 //
720 // Becomes:
721 //
722 // ```erl
723 // format(_string, _args) ->
724 // io:format(_string, _args).
725 // ```
726 //
727 // If an argument is made of just underscores, then that would result
728 // in a syntax error in the generated Erlang, where the external
729 // function is called with a discard `io:format(_, _)`!
730 // So in this case we use a generated name to make sure the external
731 // function can be called correctly.
732 ArgNames::Discard { name, location }
733 | ArgNames::LabelledDiscard {
734 name,
735 name_location: location,
736 ..
737 } if is_external => self.new_generated_variable(),
738 ArgNames::Discard { .. } | ArgNames::LabelledDiscard { .. } => EcoString::from("_"),
739 ArgNames::Named { name, location }
740 | ArgNames::NamedLabelled {
741 name,
742 name_location: location,
743 ..
744 } => self.new_erlang_variable(name, *location),
745 })
746 }
747
748 fn statement_sequence<Output>(
749 &mut self,
750 builder: &mut impl ErlangBuilder<Output>,
751 statements: &'a [TypedStatement],
752 ) {
753 // We go over each statement one by one and produce the code they need.
754 for i in 0..statements.len() {
755 match statements.get(i).expect("statement in range") {
756 Statement::Expression(expression) => self.expression(builder, expression),
757 Statement::Use(use_) => self.expression(builder, &use_.call),
758 Statement::Assert(assert) => self.assert(builder, assert),
759 Statement::Assignment(assignment) => match &assignment.kind {
760 AssignmentKind::Let | AssignmentKind::Generated => {
761 self.let_(builder, &assignment.value, &assignment.pattern);
762 }
763 // Let asserts are slightly different from everything else:
764 // A let assert is compiled to a case expression where we
765 // have two branches:
766 //
767 // ```gleam
768 // let assert [a, b] = some_list
769 // // ... the remaining statements
770 // ```
771 //
772 // It will turn into something that looks like this:
773 //
774 // ```erl
775 // case SomeList of
776 // [a, b] ->
777 // % ... the remaining statements;
778 // _ ->
779 // erlang:error(...)
780 // end.
781 // ```
782 //
783 // So in case we find a let assert we need to break out of
784 // this cycle and pass it all the remaining statements so
785 // that it can put those under the correct branch of the
786 // case expression it's going to produce.
787 AssignmentKind::Assert {
788 message, location, ..
789 } => {
790 return self.let_assert(
791 builder,
792 &assignment.value,
793 &assignment.pattern,
794 message.as_ref(),
795 *location,
796 statements.get(i + 1..).unwrap_or_default(),
797 );
798 }
799 },
800 }
801 }
802 }
803
804 fn expression<Output>(
805 &mut self,
806 builder: &mut impl ErlangBuilder<Output>,
807 expression: &'a TypedExpr,
808 ) {
809 match expression {
810 //
811 // Simple scalar values, and blocks.
812 //
813 TypedExpr::Int { int_value, .. } => builder.int(int_value.clone()),
814 TypedExpr::Float { float_value, .. } => builder.float(float_value.value()),
815 TypedExpr::String { value, .. } => builder.string(value),
816 TypedExpr::Var {
817 name, constructor, ..
818 } => self.var(builder, name, constructor),
819 TypedExpr::Block { statements, .. } => {
820 // If the block has a single expression we don't bother wrapping
821 // it in an additional `begin ... end` block.
822 // It's going to be added only if strictly needed.
823 if statements.len() == 1
824 && let Statement::Expression(expression) = statements.first()
825 {
826 self.maybe_block_expr(builder, expression);
827 } else {
828 let block = builder.start_block();
829 self.statement_sequence(builder, statements);
830 builder.end_block(block);
831 }
832 }
833
834 //
835 // Operators.
836 //
837 TypedExpr::NegateBool { value, .. } => {
838 builder.unary_operator("not");
839 self.maybe_block_expr(builder, value);
840 }
841 TypedExpr::NegateInt { value, .. } => {
842 builder.unary_operator("-");
843 self.maybe_block_expr(builder, value);
844 }
845 TypedExpr::BinOp {
846 operator,
847 left,
848 right,
849 ..
850 } => self.binary_operator(builder, operator, left, right),
851
852 //
853 // BitArrays, Lists, and Tuples.
854 //
855 TypedExpr::BitArray { segments, .. } => {
856 let bit_array = builder.start_bit_array();
857 for segment in segments {
858 self.bit_array_expression_segment(builder, segment);
859 }
860 builder.end_bit_array(bit_array);
861 }
862 TypedExpr::List { elements, tail, .. } => {
863 // We generate all the items of the list as cons cells.
864 for element in elements {
865 builder.cons_list();
866 self.maybe_block_expr(builder, element);
867 }
868 // Finally we close the list with the tail, or an empty list
869 // (so that we're sure we're building proper Erlang lists).
870 if let Some(tail) = tail {
871 self.maybe_block_expr(builder, tail);
872 } else {
873 builder.empty_list();
874 }
875 }
876 TypedExpr::Tuple { elements, .. } => {
877 let tuple = builder.start_tuple();
878 for element in elements {
879 self.maybe_block_expr(builder, element);
880 }
881 builder.end_tuple(tuple);
882 }
883
884 //
885 // Accessing data inside tuples, and records.
886 // They're all tuple accesses at the end of the day!
887 //
888 TypedExpr::TupleIndex { tuple, index, .. } => self.tuple_index(builder, tuple, *index),
889 TypedExpr::RecordAccess { record, index, .. }
890 | TypedExpr::PositionalAccess { record, index, .. } => {
891 self.tuple_index(builder, record, index + 1);
892 }
893
894 //
895 // Records and record updates.
896 //
897 TypedExpr::ModuleSelect {
898 constructor: ModuleValueConstructor::Record { name, arity: 0, .. },
899 ..
900 } => builder.atom(&to_snake_case(name)),
901 TypedExpr::RecordUpdate {
902 updated_record_assigned_name,
903 updated_record,
904 constructor,
905 arguments,
906 ..
907 } => {
908 // If the record value itself needs to be bound to a variable
909 // before the update, we define it.
910 if let Some(name) = updated_record_assigned_name.as_ref() {
911 builder.match_operator();
912 builder.variable_pattern(
913 &self.new_erlang_variable(name, updated_record.location()),
914 );
915 self.maybe_block_expr(builder, updated_record);
916 }
917 // Then a record update is simply a call!
918 self.call(builder, constructor, arguments);
919 }
920
921 //
922 // All kinds of anonymous functions.
923 //
924 TypedExpr::Fn {
925 arguments, body, ..
926 } => {
927 let outer_scope = self.taken_names.clone();
928 let argument_names = self.function_arguments_names(arguments, false);
929 let function = builder.start_anonymous_function(argument_names);
930 self.statement_sequence(builder, body);
931 builder.end_function(function);
932 self.taken_names = outer_scope;
933 }
934 TypedExpr::ModuleSelect {
935 constructor: ModuleValueConstructor::Record { name, arity, .. },
936 ..
937 } => self.record_builder_anonymous_function(builder, name, *arity as usize),
938 TypedExpr::ModuleSelect {
939 type_,
940 constructor:
941 ModuleValueConstructor::Fn {
942 external_erlang: Some((module, name)),
943 ..
944 }
945 | ModuleValueConstructor::Fn { module, name, .. },
946 ..
947 } => match type_::collapse_links(type_.clone()).as_ref() {
948 Type::Fn { arguments, .. } => builder.function_reference(
949 Some(ErlangModuleName::new(module)),
950 escape_erlang_existing_name(name),
951 arguments.len(),
952 ),
953
954 Type::Named { .. } | Type::Var { .. } | Type::Tuple { .. } => {
955 let name = escape_erlang_existing_name(name);
956 let call = builder.start_remote_call(ErlangModuleName::new(module), name);
957 builder.end_call(call);
958 }
959 },
960
961 //
962 // Calling functions.
963 //
964 TypedExpr::Call { fun, arguments, .. } => self.call(builder, fun, arguments),
965 TypedExpr::Pipeline {
966 first_value,
967 assignments,
968 finally,
969 ..
970 } => self.pipeline(builder, first_value, assignments, finally),
971
972 //
973 // Todo, panic, and echo.
974 //
975 TypedExpr::Todo {
976 message, location, ..
977 } => {
978 let error = self.start_runtime_error(
979 builder,
980 RuntimeErrorKind::Todo,
981 *location,
982 message.as_deref(),
983 );
984 self.end_runtime_error(builder, error);
985 }
986 TypedExpr::Panic {
987 location, message, ..
988 } => {
989 let error = self.start_runtime_error(
990 builder,
991 RuntimeErrorKind::Panic,
992 *location,
993 message.as_deref(),
994 );
995 self.end_runtime_error(builder, error);
996 }
997 TypedExpr::Echo {
998 expression,
999 location,
1000 message,
1001 ..
1002 } => self.echo(
1003 builder,
1004 *location,
1005 message.as_deref(),
1006 EchoPrintedValue::Expression {
1007 value: expression
1008 .as_ref()
1009 .expect("echo with no expression outside of pipe"),
1010 },
1011 ),
1012
1013 //
1014 // Module constants.
1015 //
1016 TypedExpr::ModuleSelect {
1017 constructor: ModuleValueConstructor::Constant { literal, .. },
1018 ..
1019 } => self.inlined_constant(builder, literal),
1020
1021 //
1022 // Control flow.
1023 //
1024 TypedExpr::Case {
1025 subjects, clauses, ..
1026 } => self.case(builder, subjects, clauses),
1027
1028 //
1029 // Something went wrong!
1030 //
1031 TypedExpr::Invalid { .. } => {
1032 panic!("invalid expressions should not reach code generation")
1033 }
1034 }
1035 }
1036
1037 fn echo<Output>(
1038 &mut self,
1039 builder: &mut impl ErlangBuilder<Output>,
1040 echo_location: SrcSpan,
1041 message: Option<&'a TypedExpr>,
1042 printed_value: EchoPrintedValue<'a>,
1043 ) {
1044 self.module_generator.echo_used = true;
1045
1046 let call = builder.start_call();
1047 builder.atom("echo");
1048 let call = builder.end_called_expression(call);
1049
1050 // Echo has 4 arguments: the expression to print...
1051 match printed_value {
1052 EchoPrintedValue::PipeStep { name } => builder.variable(&name),
1053 EchoPrintedValue::Expression { value } => self.maybe_block_expr(builder, value),
1054 }
1055 // ...the message to print (or nil if there's no message)...
1056 if let Some(message) = message {
1057 self.maybe_block_expr(builder, message);
1058 } else {
1059 builder.atom("nil");
1060 }
1061
1062 // ...the filepath of this module...
1063 builder.string(&self.module_generator.module_source_path);
1064
1065 // ...and the line number of the expression.
1066 builder.int(
1067 self.module_generator
1068 .line_numbers
1069 .line_number(echo_location.start)
1070 .into(),
1071 );
1072
1073 builder.end_call(call);
1074 }
1075
1076 /// This starts a call to `erlang:error` with a map representing a Gleam
1077 /// runtime error of the given kind.
1078 /// Some fields are mandatory and always added, but if you need to add more
1079 /// fields you can still do so by calling `builder.map_field()`.
1080 ///
1081 /// After you're done generating those additional fields remember you _must_
1082 /// call `end_runtime_error` before generating any other piece of code!
1083 ///
1084 fn start_runtime_error<Output, Builder: ErlangBuilder<Output>>(
1085 &mut self,
1086 builder: &mut Builder,
1087 error_kind: RuntimeErrorKind,
1088 location: SrcSpan,
1089 message: Option<&'a TypedExpr>,
1090 ) -> RuntimeError<Builder::Map, Builder::Call> {
1091 let call = builder.start_remote_call(ErlangModuleName::erlang(), "error");
1092 let map = builder.start_map();
1093
1094 builder.map_field();
1095 builder.atom("gleam_error");
1096 builder.atom(match error_kind {
1097 RuntimeErrorKind::Todo => "todo",
1098 RuntimeErrorKind::Panic => "panic",
1099 RuntimeErrorKind::Assert => "assert",
1100 RuntimeErrorKind::LetAssert => "let_assert",
1101 });
1102
1103 builder.map_field();
1104 builder.atom("message");
1105 if let Some(message) = message {
1106 self.maybe_block_expr(builder, message);
1107 } else {
1108 builder.string(error_kind.default_error_message());
1109 }
1110
1111 builder.map_field();
1112 builder.atom("file");
1113 builder.string(&self.module_generator.module_source_path);
1114
1115 builder.map_field();
1116 builder.atom("module");
1117 builder.string(&self.module_generator.module.name);
1118
1119 builder.map_field();
1120 builder.atom("function");
1121 builder.string(self.function_name);
1122
1123 builder.map_field();
1124 builder.atom("line");
1125 builder.int(
1126 self.module_generator
1127 .line_numbers
1128 .line_number(location.start)
1129 .into(),
1130 );
1131
1132 RuntimeError {
1133 error_map: map,
1134 erlang_error_call: call,
1135 }
1136 }
1137
1138 /// This closes an open runtime error.
1139 fn end_runtime_error<Output, Builder: ErlangBuilder<Output>>(
1140 &self,
1141 builder: &mut Builder,
1142 runtime_error: RuntimeError<Builder::Map, Builder::Call>,
1143 ) {
1144 builder.end_map(runtime_error.error_map);
1145 builder.end_call(runtime_error.erlang_error_call);
1146 }
1147
1148 fn maybe_block_expr<Output>(
1149 &mut self,
1150 builder: &mut impl ErlangBuilder<Output>,
1151 expression: &'a TypedExpr,
1152 ) {
1153 if needs_begin_end_wrapping(expression) {
1154 let block = builder.start_block();
1155 self.expression(builder, expression);
1156 builder.end_block(block);
1157 } else {
1158 self.expression(builder, expression);
1159 }
1160 }
1161
1162 fn let_<Output>(
1163 &mut self,
1164 builder: &mut impl ErlangBuilder<Output>,
1165 value: &'a TypedExpr,
1166 pattern: &'a TypedPattern,
1167 ) {
1168 builder.match_operator();
1169 PatternGenerator::new(self).pattern(builder, pattern);
1170 self.maybe_block_expr(builder, value);
1171 }
1172
1173 fn let_assert<Output>(
1174 &mut self,
1175 builder: &mut impl ErlangBuilder<Output>,
1176 value: &'a TypedExpr,
1177 pattern: &'a TypedPattern,
1178 message: Option<&'a TypedExpr>,
1179 location: SrcSpan,
1180 following_statements: &'a [TypedStatement],
1181 ) {
1182 // If the pattern will never fail, like a tuple or a simple variable, we
1183 // simply treat it as if it were a `let` assignment.
1184 if pattern.always_matches() {
1185 self.let_(builder, value, pattern);
1186 self.statement_sequence(builder, following_statements);
1187 return;
1188 }
1189
1190 // Otherwise we turn the let assert into a case expression with two
1191 // branches: one for the asserted pattern, and one catch all to throw an
1192 // exception in case the pattern doesn't match.
1193 let case = builder.start_case();
1194 self.maybe_block_expr(builder, value);
1195 let case = builder.end_case_subject(case);
1196
1197 // This is the first branch for when the asserted pattern matches: it's
1198 // going to run all the remaining statements in its body.
1199 if !following_statements.is_empty() {
1200 // If there's statements after this let assert we want to generate
1201 // them.
1202 let clause = builder.start_case_clause();
1203 let mut generator = PatternGenerator::new(self);
1204 generator.pattern(builder, pattern);
1205 let clause = builder.end_clause_pattern(clause);
1206 let clause = builder.end_clause_guards(clause);
1207 let variables_to_add_later = generator.variables_to_add_later;
1208 self.pattern_assignments(builder, variables_to_add_later);
1209 self.statement_sequence(builder, following_statements);
1210 builder.end_clause_body(clause);
1211 } else {
1212 // If there's no statements following the let assert, that means
1213 // that it's the last statement in the block and we need to return
1214 // the value being matched on.
1215 // It will look something like this:
1216 //
1217 // ```erl
1218 // case MatchedValue of
1219 // [_, A | _] = _value -> _value;
1220 // % ^^^^^^ We bind the pattern to a variable
1221 // % and return it.
1222 // _ -> erlang:error(...)
1223 // end
1224 // ```
1225 let clause = builder.start_case_clause();
1226 let matched_value_name = self.new_generated_variable();
1227 builder.match_pattern();
1228 let mut generator = PatternGenerator::new(self);
1229 generator.pattern(builder, pattern);
1230 builder.variable_pattern(&matched_value_name);
1231
1232 let clause = builder.end_clause_pattern(clause);
1233 let clause = builder.end_clause_guards(clause);
1234 builder.variable(&matched_value_name);
1235 builder.end_clause_body(clause);
1236 }
1237
1238 // This is the catch all branch to throw an error otherwise.
1239 let clause = builder.start_case_clause();
1240 let value_name = self.new_generated_variable();
1241 builder.variable_pattern(&value_name);
1242 let clause = builder.end_clause_pattern(clause);
1243 let clause = builder.end_clause_guards(clause);
1244 let error =
1245 self.start_runtime_error(builder, RuntimeErrorKind::LetAssert, location, message);
1246
1247 // We want to add some additional fields to the error map:
1248 builder.map_field();
1249 builder.atom("value");
1250 builder.variable(&value_name);
1251
1252 builder.map_field();
1253 builder.atom("start");
1254 builder.int(location.start.into());
1255
1256 builder.map_field();
1257 builder.atom("end");
1258 builder.int(value.location().end.into());
1259
1260 builder.map_field();
1261 builder.atom("pattern_start");
1262 builder.int(pattern.location().start.into());
1263
1264 builder.map_field();
1265 builder.atom("pattern_end");
1266 builder.int(pattern.location().end.into());
1267
1268 self.end_runtime_error(builder, error);
1269 builder.end_clause_body(clause);
1270
1271 builder.end_case(case);
1272 }
1273
1274 fn pipeline<Output>(
1275 &mut self,
1276 builder: &mut impl ErlangBuilder<Output>,
1277 first_value: &'a TypedPipelineAssignment,
1278 assignments: &'a [(TypedPipelineAssignment, PipelineAssignmentKind)],
1279 finally: &'a TypedExpr,
1280 ) {
1281 // A pipeline is desugared as a sequence of assignments:
1282 //
1283 // ```erl
1284 // Step1 = fun()
1285 // Step2 = fun(Step1)
1286 // Step3 = fun(Step2)
1287 // % ...
1288 // ```
1289 //
1290 // So we need to keep around the name the prevopis pipeline step had
1291 // to pass it as an argument to the following call. This is what this
1292 // variable is for.
1293 let mut previous_step_variable_name: Option<EcoString> = None;
1294 for assignment in std::iter::once(first_value)
1295 .chain(assignments.iter().map(|(assignment, _kind)| assignment))
1296 {
1297 // A pipeline step always ends up assigned to a variable.
1298 // So we start by generating `_pipe = ...`, followed by the
1299 // expression.
1300 builder.match_operator();
1301 let name = self.new_erlang_variable(&assignment.name, assignment.location);
1302 builder.variable_pattern(&name);
1303
1304 // In case of a pipe we need to manually pass the previous step to
1305 // echo as an argument.
1306 if let TypedExpr::Echo {
1307 expression: None,
1308 message,
1309 location,
1310 ..
1311 } = assignment.value.as_ref()
1312 {
1313 self.echo(
1314 builder,
1315 *location,
1316 message.as_deref(),
1317 EchoPrintedValue::PipeStep {
1318 name: previous_step_variable_name
1319 .to_owned()
1320 .expect("echo with no previous step in a pipe"),
1321 },
1322 );
1323 } else {
1324 self.maybe_block_expr(builder, &assignment.value);
1325 previous_step_variable_name = Some(name);
1326 }
1327 }
1328
1329 // We also need to do the same thing for the final step of the pipeline.
1330 // It's slightly different compared to the other ones so we have to do
1331 // that separately.
1332 if let TypedExpr::Echo {
1333 expression: None,
1334 message,
1335 location,
1336 ..
1337 } = finally
1338 {
1339 self.echo(
1340 builder,
1341 *location,
1342 message.as_deref(),
1343 EchoPrintedValue::PipeStep {
1344 name: previous_step_variable_name
1345 .expect("echo with no previous step in a pipe"),
1346 },
1347 );
1348 } else {
1349 self.expression(builder, finally);
1350 }
1351 }
1352
1353 fn assert<Output>(
1354 &mut self,
1355 builder: &mut impl ErlangBuilder<Output>,
1356 assert: &'a TypedAssert,
1357 ) {
1358 let Assert {
1359 value,
1360 location,
1361 message,
1362 } = assert;
1363
1364 match value {
1365 // We're asserting on a binary operator. We want to show the result
1366 // of each side in the error that is produced.
1367 // So we will bind the two sides of the operator to variables and
1368 // shove them in the error map too!
1369 TypedExpr::BinOp {
1370 operator,
1371 left,
1372 right,
1373 ..
1374 } => {
1375 let erlang_operator = match operator {
1376 // Writing asserts on binops requires some extra care, check
1377 // out their docs!
1378 BinOp::And => {
1379 return self.assert_and(builder, left, right, message.as_ref(), *location);
1380 }
1381 BinOp::Or => {
1382 return self.assert_or(builder, left, right, message.as_ref(), *location);
1383 }
1384
1385 BinOp::Eq => "=:=",
1386 BinOp::NotEq => "/=",
1387 BinOp::LtInt | BinOp::LtFloat => "<",
1388 BinOp::LtEqInt | BinOp::LtEqFloat => "=<",
1389 BinOp::GtInt | BinOp::GtFloat => ">",
1390 BinOp::GtEqInt | BinOp::GtEqFloat => ">=",
1391
1392 BinOp::AddInt
1393 | BinOp::AddFloat
1394 | BinOp::SubInt
1395 | BinOp::SubFloat
1396 | BinOp::MultInt
1397 | BinOp::MultFloat
1398 | BinOp::DivInt
1399 | BinOp::DivFloat
1400 | BinOp::RemainderInt
1401 | BinOp::Concatenate => {
1402 panic!("Non-boolean operators cannot appear here in well-typed code")
1403 }
1404 };
1405
1406 // If the left or right hand side are not simple variables we'll
1407 // need to first assign those to generated variables and keep
1408 // track of those names.
1409 let left = if !left.is_var() {
1410 let name = self.new_generated_variable();
1411 builder.match_operator();
1412 builder.variable_pattern(&name);
1413 self.maybe_block_expr(builder, left);
1414 AssertionExpression::from_generated_variable(name, left)
1415 } else {
1416 AssertionExpression::from_expression(left)
1417 };
1418
1419 let right = if !right.is_var() {
1420 let name = self.new_generated_variable();
1421 builder.match_operator();
1422 builder.variable_pattern(&name);
1423 self.maybe_block_expr(builder, right);
1424 AssertionExpression::from_generated_variable(name, right)
1425 } else {
1426 AssertionExpression::from_expression(right)
1427 };
1428
1429 let case = builder.start_case();
1430
1431 // Then we need to apply the operator. If any of the two sides
1432 // has been bound to a variable we can use that name directly!
1433 builder.binary_operator(erlang_operator);
1434 self.runtime_value(builder, &left);
1435 self.runtime_value(builder, &right);
1436 let case = builder.end_case_subject(case);
1437
1438 // If the operator evaluates to true the assertion succeeded.
1439 // We can just return nil.
1440 let clause = builder.start_case_clause();
1441 builder.atom_pattern("true");
1442 let clause = builder.end_clause_pattern(clause);
1443 let clause = builder.end_clause_guards(clause);
1444 builder.atom("nil");
1445 builder.end_clause_body(clause);
1446
1447 // Otherwise we want to throw a runtime error!
1448 let clause = builder.start_case_clause();
1449 builder.atom_pattern("false");
1450 let clause = builder.end_clause_pattern(clause);
1451 let clause = builder.end_clause_guards(clause);
1452 self.assert_binary_operator_error(
1453 builder,
1454 *operator,
1455 left,
1456 right,
1457 message.as_ref(),
1458 *location,
1459 );
1460 builder.end_clause_body(clause);
1461
1462 builder.end_case(case);
1463 }
1464
1465 TypedExpr::Call { fun, arguments, .. } => {
1466 // When asserting on a call, we want to include the values of
1467 // each argument in the assertion error in case of failure.
1468 // This means we first have to evaluate each argument and bind
1469 // it to a variable so that we can later reference them from the
1470 // error message without evaluating each argument twice!
1471 let mut call_arguments = Vec::with_capacity(arguments.len());
1472 for argument in arguments {
1473 let argument = if !argument.value.is_var() {
1474 let name = self.new_generated_variable();
1475 builder.match_operator();
1476 builder.variable_pattern(&name);
1477 self.maybe_block_expr(builder, &argument.value);
1478 AssertionExpression::from_generated_variable(name, &argument.value)
1479 } else {
1480 AssertionExpression::from_expression(&argument.value)
1481 };
1482 call_arguments.push(argument);
1483 }
1484
1485 let case = builder.start_case();
1486 self.call_in_assert(builder, fun, &call_arguments);
1487 let case = builder.end_case_subject(case);
1488
1489 // If the operator evaluates to true the assertion succeeded.
1490 // We can just return nil.
1491 let clause = builder.start_case_clause();
1492 builder.atom_pattern("true");
1493 let clause = builder.end_clause_pattern(clause);
1494 let clause = builder.end_clause_guards(clause);
1495 builder.atom("nil");
1496 builder.end_clause_body(clause);
1497
1498 // Otherwise we want to throw a runtime error!
1499 let clause = builder.start_case_clause();
1500 builder.atom_pattern("false");
1501 let clause = builder.end_clause_pattern(clause);
1502 let clause = builder.end_clause_guards(clause);
1503 self.assert_call_error(
1504 builder,
1505 value,
1506 &call_arguments,
1507 message.as_ref(),
1508 *location,
1509 );
1510 builder.end_clause_body(clause);
1511
1512 builder.end_case(case);
1513 }
1514
1515 TypedExpr::Int { .. }
1516 | TypedExpr::Float { .. }
1517 | TypedExpr::String { .. }
1518 | TypedExpr::Block { .. }
1519 | TypedExpr::Pipeline { .. }
1520 | TypedExpr::Var { .. }
1521 | TypedExpr::Fn { .. }
1522 | TypedExpr::List { .. }
1523 | TypedExpr::Case { .. }
1524 | TypedExpr::RecordAccess { .. }
1525 | TypedExpr::PositionalAccess { .. }
1526 | TypedExpr::ModuleSelect { .. }
1527 | TypedExpr::Tuple { .. }
1528 | TypedExpr::TupleIndex { .. }
1529 | TypedExpr::Todo { .. }
1530 | TypedExpr::Panic { .. }
1531 | TypedExpr::Echo { .. }
1532 | TypedExpr::BitArray { .. }
1533 | TypedExpr::RecordUpdate { .. }
1534 | TypedExpr::NegateBool { .. }
1535 | TypedExpr::NegateInt { .. }
1536 | TypedExpr::Invalid { .. } => {
1537 let case = builder.start_case();
1538 self.maybe_block_expr(builder, value);
1539 let case = builder.end_case_subject(case);
1540
1541 // If the expression evaluates to true the assertion succeeded.
1542 // We can just return nil.
1543 let clause = builder.start_case_clause();
1544 builder.atom_pattern("true");
1545 let clause = builder.end_clause_pattern(clause);
1546 let clause = builder.end_clause_guards(clause);
1547 builder.atom("nil");
1548 builder.end_clause_body(clause);
1549
1550 // Otherwise we want to throw a runtime error!
1551 let clause = builder.start_case_clause();
1552 builder.atom_pattern("false");
1553 let clause = builder.end_clause_pattern(clause);
1554 let clause = builder.end_clause_guards(clause);
1555 self.assert_expression_error(
1556 builder,
1557 AssertionExpression::from_expression(value).evaluated_to_bool(false),
1558 message.as_ref(),
1559 *location,
1560 );
1561 builder.end_clause_body(clause);
1562
1563 builder.end_case(case);
1564 }
1565 }
1566 }
1567
1568 /// In Gleam, the `&&` operator is short-circuiting, meaning that we can't
1569 /// pre-evaluate both sides of it, and use them in the exception that is
1570 /// thrown.
1571 /// Instead, we need to implement this short-circuiting logic ourself.
1572 ///
1573 /// If we short-circuit, we must leave the second expression unevaluated,
1574 /// and signal that using the `unevaluated` variant, as detailed in the
1575 /// exception format. For the first expression, we know it must be `false`,
1576 /// otherwise we would have continued by evaluating the second expression.
1577 ///
1578 /// Similarly, if we do evaluate the second expression and fail, we know
1579 /// that the first expression must have evaluated to `true`, and the second
1580 /// to `false`. This way, we avoid needing to evaluate either expression
1581 /// twice.
1582 ///
1583 /// The generated code then looks something like this:
1584 /// ```erlang
1585 /// case expr1 of
1586 /// true -> case expr2 of
1587 /// true -> true;
1588 /// false -> <throw exception>
1589 /// end;
1590 /// false -> <throw exception>
1591 /// end
1592 /// ```
1593 ///
1594 fn assert_and<Output>(
1595 &mut self,
1596 builder: &mut impl ErlangBuilder<Output>,
1597 left: &'a TypedExpr,
1598 right: &'a TypedExpr,
1599 message: Option<&'a TypedExpr>,
1600 location: SrcSpan,
1601 ) {
1602 let case = builder.start_case();
1603 self.maybe_block_expr(builder, left);
1604 let case = builder.end_case_subject(case);
1605
1606 // In case the first expression is true, we get to evaluate the second
1607 // one as well, then we will be able to tell if the assertion failed or
1608 // not!
1609 let clause = builder.start_case_clause();
1610 builder.atom_pattern("true");
1611 let clause = builder.end_clause_pattern(clause);
1612 let clause = builder.end_clause_guards(clause);
1613 {
1614 // Now we have to match on the right hand side!
1615 let case = builder.start_case();
1616 self.maybe_block_expr(builder, right);
1617 let case = builder.end_case_subject(case);
1618
1619 // If it's true the assertion succeded! We can return `nil`.
1620 let clause = builder.start_case_clause();
1621 builder.atom_pattern("true");
1622 let clause = builder.end_clause_pattern(clause);
1623 let clause = builder.end_clause_guards(clause);
1624 builder.atom("nil");
1625 builder.end_clause_body(clause);
1626
1627 // If it's false the assertion failed! The left hand side was true
1628 // but this one evaluated to false :(
1629 let clause = builder.start_case_clause();
1630 builder.atom_pattern("false");
1631 let clause = builder.end_clause_pattern(clause);
1632 let clause = builder.end_clause_guards(clause);
1633 self.assert_binary_operator_error(
1634 builder,
1635 BinOp::And,
1636 AssertionExpression::from_expression(left).evaluated_to_bool(true),
1637 AssertionExpression::from_expression(right).evaluated_to_bool(false),
1638 message,
1639 location,
1640 );
1641 builder.end_clause_body(clause);
1642 builder.end_case(case);
1643 }
1644 builder.end_clause_body(clause);
1645
1646 // In case the first expression is false, we want to fail fast. We are
1647 // short circuiting without evaluating the right hand side! This side
1648 // just build an error.
1649 let clause = builder.start_case_clause();
1650 builder.atom_pattern("false");
1651 let clause = builder.end_clause_pattern(clause);
1652 let clause = builder.end_clause_guards(clause);
1653 self.assert_binary_operator_error(
1654 builder,
1655 BinOp::And,
1656 AssertionExpression::from_expression(left).evaluated_to_bool(false),
1657 AssertionExpression::from_expression(right).was_unevaluated(),
1658 message,
1659 location,
1660 );
1661 builder.end_clause_body(clause);
1662
1663 builder.end_case(case);
1664 }
1665
1666 /// Similar to `&&`, `||` is also short-circuiting in Gleam. However, if `||`
1667 /// short-circuits, that's because the first expression evaluated to `true`,
1668 /// meaning the whole assertion succeeds. This allows us to directly use Erlang's
1669 /// `orelse` operator as the subject of the `case` expression.
1670 ///
1671 /// The only difference is that due to the nature of `||`, if the assertion fails,
1672 /// we know that both sides must have evaluated to `false`, so we don't
1673 /// need to store the values of them in variables beforehand.
1674 fn assert_or<Output>(
1675 &mut self,
1676 builder: &mut impl ErlangBuilder<Output>,
1677 left: &'a TypedExpr,
1678 right: &'a TypedExpr,
1679 message: Option<&'a TypedExpr>,
1680 location: SrcSpan,
1681 ) {
1682 let case = builder.start_case();
1683 builder.binary_operator("orelse");
1684 self.maybe_block_expr(builder, left);
1685 self.maybe_block_expr(builder, right);
1686 let case = builder.end_case_subject(case);
1687
1688 // If the result is true, then the assertion succeeded, we can return
1689 // nil.
1690 let clause = builder.start_case_clause();
1691 builder.atom_pattern("true");
1692 let clause = builder.end_clause_pattern(clause);
1693 let clause = builder.end_clause_guards(clause);
1694 builder.atom("nil");
1695 builder.end_clause_body(clause);
1696
1697 // But if it fails we know that both sides of the assertion resulted in
1698 // a false value. In that case we throw an error.
1699
1700 let clause = builder.start_case_clause();
1701 builder.atom_pattern("false");
1702 let clause = builder.end_clause_pattern(clause);
1703 let clause = builder.end_clause_guards(clause);
1704 self.assert_binary_operator_error(
1705 builder,
1706 BinOp::Or,
1707 AssertionExpression::from_expression(left).evaluated_to_bool(false),
1708 AssertionExpression::from_expression(right).evaluated_to_bool(false),
1709 message,
1710 location,
1711 );
1712 builder.end_clause_body(clause);
1713
1714 builder.end_case(case);
1715 }
1716
1717 /// This generates the code that throws a runtime error whan an `assert`
1718 /// that is checking the result of a binary operator fails.
1719 fn assert_binary_operator_error<Output>(
1720 &mut self,
1721 builder: &mut impl ErlangBuilder<Output>,
1722 operator: BinOp,
1723 left: AssertionExpression<'a>,
1724 right: AssertionExpression<'a>,
1725 message: Option<&'a TypedExpr>,
1726 location: SrcSpan,
1727 ) {
1728 let error = self.start_runtime_error(builder, RuntimeErrorKind::Assert, location, message);
1729
1730 builder.map_field();
1731 builder.atom("kind");
1732 builder.atom("binary_operator");
1733
1734 builder.map_field();
1735 builder.atom("operator");
1736 builder.atom(operator.name());
1737
1738 builder.map_field();
1739 builder.atom("left");
1740 self.assertion_expression_map(builder, &left);
1741
1742 builder.map_field();
1743 builder.atom("right");
1744 self.assertion_expression_map(builder, &right);
1745
1746 builder.map_field();
1747 builder.atom("start");
1748 builder.int(location.start.into());
1749
1750 builder.map_field();
1751 builder.atom("end");
1752 builder.int(right.location.end.into());
1753
1754 builder.map_field();
1755 builder.atom("expression_start");
1756 builder.int(left.location.start.into());
1757
1758 self.end_runtime_error(builder, error);
1759 }
1760
1761 /// This generates the code that throws a runtime error whan an `assert`
1762 /// that is checking the result of an arbitrary expression fails.
1763 fn assert_expression_error<Output>(
1764 &mut self,
1765 builder: &mut impl ErlangBuilder<Output>,
1766 expression: AssertionExpression<'a>,
1767 message: Option<&'a TypedExpr>,
1768 location: SrcSpan,
1769 ) {
1770 let error = self.start_runtime_error(builder, RuntimeErrorKind::Assert, location, message);
1771
1772 builder.map_field();
1773 builder.atom("kind");
1774 builder.atom("expression");
1775
1776 // If assert fails on an expression's result then we know it must have
1777 // evaluated to false!
1778 builder.map_field();
1779 builder.atom("expression");
1780 self.assertion_expression_map(builder, &expression);
1781
1782 builder.map_field();
1783 builder.atom("start");
1784 builder.int(location.start.into());
1785
1786 builder.map_field();
1787 builder.atom("end");
1788 builder.int(expression.location.end.into());
1789
1790 builder.map_field();
1791 builder.atom("expression_start");
1792 builder.int(expression.location.start.into());
1793
1794 self.end_runtime_error(builder, error);
1795 }
1796
1797 fn assert_call_error<Output>(
1798 &mut self,
1799 builder: &mut impl ErlangBuilder<Output>,
1800 call: &'a TypedExpr,
1801 arguments: &[AssertionExpression<'a>],
1802 message: Option<&'a TypedExpr>,
1803 location: SrcSpan,
1804 ) {
1805 let error = self.start_runtime_error(builder, RuntimeErrorKind::Assert, location, message);
1806
1807 builder.map_field();
1808 builder.atom("kind");
1809 builder.atom("function_call");
1810
1811 builder.map_field();
1812 builder.atom("arguments");
1813 for argument in arguments {
1814 builder.cons_list();
1815 self.assertion_expression_map(builder, argument);
1816 }
1817 builder.empty_list();
1818
1819 builder.map_field();
1820 builder.atom("start");
1821 builder.int(location.start.into());
1822
1823 builder.map_field();
1824 builder.atom("end");
1825 builder.int(call.location().end.into());
1826
1827 builder.map_field();
1828 builder.atom("expression_start");
1829 builder.int(call.location().start.into());
1830
1831 self.end_runtime_error(builder, error);
1832 }
1833
1834 /// Given an expression being asserted on. This generates the code for an
1835 /// Erlang map that describes it: with a field for its kind, its value, and
1836 /// its location in the source code.
1837 fn assertion_expression_map<Output>(
1838 &mut self,
1839 builder: &mut impl ErlangBuilder<Output>,
1840 expression: &AssertionExpression<'a>,
1841 ) {
1842 let AssertionExpression {
1843 kind,
1844 runtime_value,
1845 location,
1846 } = expression;
1847
1848 let map = builder.start_map();
1849
1850 builder.map_field();
1851 builder.atom("kind");
1852 builder.atom(match kind {
1853 AssertedExpressionKind::Literal => "literal",
1854 AssertedExpressionKind::Expression => "expression",
1855 AssertedExpressionKind::Unevaluated => "unevaluated",
1856 });
1857
1858 if runtime_value.is_some() {
1859 builder.map_field();
1860 builder.atom("value");
1861 self.runtime_value(builder, expression);
1862 }
1863
1864 builder.map_field();
1865 builder.atom("start");
1866 builder.int(location.start.into());
1867
1868 builder.map_field();
1869 builder.atom("end");
1870 builder.int(location.end.into());
1871
1872 builder.end_map(map);
1873 }
1874
1875 /// This takes a value that is in an assertion (and might have been bound
1876 /// to a variable somewhere) and produces the code that will reference
1877 /// such value.
1878 ///
1879 fn runtime_value<Output>(
1880 &mut self,
1881 builder: &mut impl ErlangBuilder<Output>,
1882 expression: &AssertionExpression<'a>,
1883 ) {
1884 match expression
1885 .runtime_value
1886 .as_ref()
1887 .expect("trying to reference unevaluated assert value")
1888 {
1889 AssertedExpressionRuntimeValue::KnownBool(true) => builder.atom("true"),
1890 AssertedExpressionRuntimeValue::KnownBool(false) => builder.atom("false"),
1891 AssertedExpressionRuntimeValue::Variable(name) => builder.variable(name),
1892 AssertedExpressionRuntimeValue::Expression(expr) => {
1893 self.maybe_block_expr(builder, expr);
1894 }
1895 }
1896 }
1897
1898 fn tuple_index<Output>(
1899 &mut self,
1900 builder: &mut impl ErlangBuilder<Output>,
1901 tuple: &'a TypedExpr,
1902 index: u64,
1903 ) {
1904 let call = builder.start_remote_call(ErlangModuleName::erlang(), "element");
1905 builder.int((index + 1).into());
1906 self.maybe_block_expr(builder, tuple);
1907 builder.end_call(call);
1908 }
1909
1910 fn var<Output>(
1911 &mut self,
1912 builder: &mut impl ErlangBuilder<Output>,
1913 name: &'a str,
1914 constructor: &'a ValueConstructor,
1915 ) {
1916 match &constructor.variant {
1917 ValueConstructorVariant::Record {
1918 name: record_name, ..
1919 } => match constructor.type_.deref() {
1920 // We have a variable referencing a record: we are either
1921 // referencing a record constructor function, or building a
1922 // record that has no fields:
1923 //
1924 // ```gleam
1925 // type Wibble {
1926 // Wibble
1927 // Wobble(Int)
1928 // }
1929 //
1930 // pub fn main() {
1931 // Wibble
1932 // //^^^^^^ Building record with no fields
1933 // Wobble
1934 // //^^^^^^ Referencing record constructor function
1935 // }
1936 // ```
1937 Type::Named { .. } | Type::Var { .. } | Type::Tuple { .. } => {
1938 builder.atom(&to_snake_case(record_name));
1939 }
1940 Type::Fn { arguments, .. } => {
1941 self.record_builder_anonymous_function(builder, record_name, arguments.len());
1942 }
1943 },
1944
1945 ValueConstructorVariant::LocalVariable { location, .. } => {
1946 builder.variable(&self.local_var_name(location));
1947 }
1948
1949 ValueConstructorVariant::ModuleConstant { literal, .. } => {
1950 self.inlined_constant(builder, literal);
1951 }
1952
1953 ValueConstructorVariant::ModuleFn {
1954 arity,
1955 external_erlang: Some((module, name)),
1956 ..
1957 } => {
1958 let name = escape_erlang_existing_name(name);
1959 if *module == self.module_generator.module.name {
1960 builder.function_reference(None, name, *arity);
1961 } else {
1962 builder.function_reference(Some(ErlangModuleName::new(module)), name, *arity);
1963 }
1964 }
1965
1966 ValueConstructorVariant::ModuleFn { arity, module, .. }
1967 if *module == self.module_generator.module.name =>
1968 {
1969 builder.function_reference(None, escape_erlang_existing_name(name), *arity);
1970 }
1971
1972 ValueConstructorVariant::ModuleFn {
1973 arity,
1974 module,
1975 name,
1976 ..
1977 } => builder.function_reference(
1978 Some(ErlangModuleName::new(module)),
1979 escape_erlang_existing_name(name),
1980 *arity,
1981 ),
1982 }
1983 }
1984
1985 fn call<Output>(
1986 &mut self,
1987 builder: &mut impl ErlangBuilder<Output>,
1988 fun: &'a TypedExpr,
1989 arguments: &'a [TypedCallArg],
1990 ) {
1991 match how_to_call(fun) {
1992 // If we're building a record then we want to just output a
1993 // tagged tuple, there's no function call at all!
1994 FunctionCall::BuildRecord { name } => self.build_record(builder, name, arguments),
1995 // If we're calling some module function like `io.println`, `main`,
1996 // `list.map` then we can call the function using its name (and
1997 // module name if it comes from a different module).
1998 FunctionCall::Call { module, name } => {
1999 let call = if module != self.module_generator.module.name {
2000 builder.start_remote_call(
2001 ErlangModuleName::new(module),
2002 escape_erlang_existing_name(name),
2003 )
2004 } else {
2005 let call = builder.start_call();
2006 builder.atom(escape_erlang_existing_name(name));
2007 builder.end_called_expression(call)
2008 };
2009 for argument in arguments {
2010 self.maybe_block_expr(builder, &argument.value);
2011 }
2012 builder.end_call(call);
2013 }
2014 // If we're calling anything else (like an anonymous function, or
2015 // the result of another function call) we generate its code and
2016 // call that result directly.
2017 FunctionCall::DirectCall => {
2018 let call = builder.start_call();
2019 self.maybe_block_expr(builder, fun);
2020 let call = builder.end_called_expression(call);
2021 for argument in arguments {
2022 self.maybe_block_expr(builder, &argument.value);
2023 }
2024 builder.end_call(call);
2025 }
2026 }
2027 }
2028
2029 /// This generates the code for a call that happens in an `assert`.
2030 /// For example: `assert wibble.wobble(a, b)`.
2031 ///
2032 /// This is a function separate from the regular `self.call` since the call
2033 /// arguments are not just TypedExpressions but values that might have been
2034 /// bound to variables in previous statements.
2035 /// Assert has to do it when a call is asserted so that those arguments can
2036 /// be referenced later in the error thrown at runtime!
2037 ///
2038 fn call_in_assert<Output>(
2039 &mut self,
2040 builder: &mut impl ErlangBuilder<Output>,
2041 fun: &'a TypedExpr,
2042 arguments: &[AssertionExpression<'a>],
2043 ) {
2044 match how_to_call(fun) {
2045 // What comes after `assert` has to produce a boolean, so type
2046 // checking should make it impossible to build a record here.
2047 FunctionCall::BuildRecord { .. } => {
2048 panic!("type checking should make it impossible to call a record in an assert")
2049 }
2050
2051 // If we're calling some module function like `io.println`, `main`,
2052 // `list.map` then we can call the function using its name (and
2053 // module name if it comes from a different module).
2054 FunctionCall::Call { module, name } => {
2055 let call = if module != self.module_generator.module.name {
2056 builder.start_remote_call(
2057 ErlangModuleName::new(module),
2058 escape_erlang_existing_name(name),
2059 )
2060 } else {
2061 let call = builder.start_call();
2062 builder.atom(escape_erlang_existing_name(name));
2063 builder.end_called_expression(call)
2064 };
2065 for argument in arguments {
2066 self.runtime_value(builder, argument);
2067 }
2068 builder.end_call(call);
2069 }
2070
2071 // If we're calling anything else (like an anonymous function, or
2072 // the result of another function call) we generate its code and
2073 // call that result directly.
2074 FunctionCall::DirectCall => {
2075 let call = builder.start_call();
2076 self.maybe_block_expr(builder, fun);
2077 let call = builder.end_called_expression(call);
2078 for argument in arguments {
2079 self.runtime_value(builder, argument);
2080 }
2081 builder.end_call(call);
2082 }
2083 }
2084 }
2085
2086 /// Given a Gleam record name and the arguments it's called with, this
2087 /// generates the code to build such record.
2088 /// For example: `Wibble(1, 2)` would be `record_builder("Wibble", [1, 2])`.
2089 /// It would result in a tuple like this: `{wibble, 1, 2}`.
2090 ///
2091 /// Notice how the name you have to specify is the _Gleam name_ of the
2092 /// record. This function will take care of turning it to snake case!
2093 fn build_record<Output>(
2094 &mut self,
2095 builder: &mut impl ErlangBuilder<Output>,
2096 record_name: &str,
2097 arguments: &'a [TypedCallArg],
2098 ) {
2099 if arguments.is_empty() {
2100 builder.atom(&to_snake_case(record_name));
2101 } else {
2102 let tuple = builder.start_tuple();
2103 builder.atom(&to_snake_case(record_name));
2104 for argument in arguments {
2105 self.maybe_block_expr(builder, &argument.value);
2106 }
2107 builder.end_tuple(tuple);
2108 }
2109 }
2110
2111 fn case<Output>(
2112 &mut self,
2113 builder: &mut impl ErlangBuilder<Output>,
2114 subjects: &'a [TypedExpr],
2115 clauses: &'a [TypedClause],
2116 ) {
2117 let case = builder.start_case();
2118
2119 // If there's more than a single subject we will need to wrap those in a
2120 // tuple and start matching on tuple patterns. That's because Erlang
2121 // doesn't support matching on multiple subjects like Gleam.
2122 match subjects {
2123 [subject] => self.maybe_block_expr(builder, subject),
2124 subjects => {
2125 let tuple = builder.start_tuple();
2126 for subject in subjects {
2127 self.maybe_block_expr(builder, subject);
2128 }
2129 builder.end_tuple(tuple);
2130 }
2131 }
2132 let case = builder.end_case_subject(case);
2133
2134 for clause in clauses {
2135 let taken_names_before_clause = self.taken_names.clone();
2136
2137 self.clause_branch(builder, &clause.pattern, clause);
2138
2139 // Erlang doesn't support alternative patterns so we're gonna have
2140 // to turn those into separate branches!
2141 // Since those are going to have the exact same body we don't want
2142 // it to use different updated variable names.
2143 // So they should have the same scope that existed before generating
2144 // the first clause branch.
2145 // For example:
2146 //
2147 // ```gleam
2148 // case x {
2149 // 1 | 2 -> { let a = Nil }
2150 // _ -> Nil
2151 // }
2152 // ```
2153 //
2154 // We want the generated code to look like this:
2155 //
2156 // ```erl
2157 // case x of
2158 // 1 -> A = nil;
2159 // 2 -> A = nil;
2160 // % ^ We're still using `A`, not `A@1`!
2161 // _ -> nil
2162 // end
2163 // ```
2164 //
2165 for pattern in &clause.alternative_patterns {
2166 self.taken_names = taken_names_before_clause.clone();
2167 self.clause_branch(builder, pattern, clause);
2168 }
2169 }
2170
2171 builder.end_case(case);
2172 }
2173
2174 /// Given a pattern and the branch it belongs to this generates an Erlang
2175 /// case clause for that pattern.
2176 fn clause_branch<Output>(
2177 &mut self,
2178 builder: &mut impl ErlangBuilder<Output>,
2179 patterns: &'a Vec<Pattern<Arc<Type>>>,
2180 clause: &'a Clause<TypedExpr, Arc<Type>>,
2181 ) {
2182 let clause_pattern = builder.start_case_clause();
2183
2184 // We start by generating the case clause pattern. If we're matching on
2185 // multiple subjects (and so patterns has more that a single item) those
2186 // are gonna be wrapped in a tuple pattern.
2187 // That's how we match on multiple things on the Erlang target.
2188 let mut pattern_generator = PatternGenerator::new(self);
2189 match patterns.as_slice() {
2190 [pattern] => pattern_generator.pattern(builder, pattern),
2191 patterns => {
2192 let tuple = builder.start_tuple_pattern();
2193 for pattern in patterns {
2194 pattern_generator.pattern(builder, pattern);
2195 }
2196 builder.end_tuple_pattern(tuple);
2197 }
2198 }
2199
2200 let variables_to_add_later = pattern_generator.variables_to_add_later;
2201
2202 let clause_guards = builder.end_clause_pattern(clause_pattern);
2203 if let Some(guard) = clause.guard.as_ref() {
2204 let guard_ender = builder.start_clause_guard();
2205 self.clause_guard(builder, guard, &variables_to_add_later);
2206 builder.end_clause_guard(guard_ender);
2207 }
2208
2209 // Finally we can generate the clause body. If the clause is
2210 // followed by a single block then we want it to be a statements
2211 // sequence (and not wrapped in a begin ... end block as we usually
2212 // would when generating code for a block expression).
2213 let clause_body = builder.end_clause_guards(clause_guards);
2214 self.pattern_assignments(builder, variables_to_add_later);
2215 if let TypedExpr::Block { statements, .. } = &clause.then {
2216 self.statement_sequence(builder, statements);
2217 } else {
2218 self.expression(builder, &clause.then);
2219 }
2220 builder.end_clause_body(clause_body);
2221 }
2222
2223 /// Erlang doesn't have a special constant declaration syntax; so each Gleam
2224 /// constant is simply inlined anywhere it is used.
2225 ///
2226 /// This function produces the code of a constant expression.
2227 ///
2228 fn inlined_constant<Output>(
2229 &mut self,
2230 builder: &mut impl ErlangBuilder<Output>,
2231 literal: &'a TypedConstant,
2232 ) {
2233 match literal {
2234 Constant::Int { int_value, .. } => builder.int(int_value.clone()),
2235 Constant::Float { float_value, .. } => builder.float(float_value.value()),
2236 Constant::String { value, .. } => builder.string(value),
2237 Constant::Var {
2238 name, constructor, ..
2239 } => self.var(
2240 builder,
2241 name,
2242 constructor
2243 .as_ref()
2244 .expect("This is guaranteed to hold a value."),
2245 ),
2246
2247 Constant::Tuple { elements, .. } => {
2248 let tuple = builder.start_tuple();
2249 for element in elements {
2250 self.inlined_constant(builder, element);
2251 }
2252 builder.end_tuple(tuple);
2253 }
2254
2255 Constant::List { elements, tail, .. } => {
2256 for element in elements {
2257 builder.cons_list();
2258 self.inlined_constant(builder, element);
2259 }
2260 match tail {
2261 // If there's no tail we simply add an empty list cell to
2262 // end the cons list.
2263 None => builder.empty_list(),
2264 Some(tail) => match tail.list_elements() {
2265 // If there's a tail and we don't statically know the
2266 // elements it's made of, we add it as a regular Erlang
2267 // tail and it will be `[1, 2 | Tail]`.
2268 None => self.inlined_constant(builder, tail),
2269 // But if we can tell it has some fixed amount of
2270 // constant elements, then those are inlined too!
2271 Some(list_elements) => {
2272 for element in list_elements {
2273 builder.cons_list();
2274 self.inlined_constant(builder, element);
2275 }
2276 builder.empty_list();
2277 }
2278 },
2279 }
2280 }
2281
2282 Constant::BitArray { segments, .. } => {
2283 let bit_array = builder.start_bit_array();
2284 for segment in segments {
2285 self.bit_array_constant_segment(builder, segment);
2286 }
2287 builder.end_bit_array(bit_array);
2288 }
2289
2290 Constant::Record {
2291 type_, arguments, ..
2292 } => {
2293 let tag = literal
2294 .constant_record_tag()
2295 .expect("record without inferred constructor made it to code generation");
2296
2297 match arguments {
2298 // This is a regular record call, we're building a record as
2299 // usual as a tagged tuple.
2300 Some(arguments) => {
2301 let tuple = builder.start_tuple();
2302 builder.atom(&to_snake_case(&tag));
2303 for argument in arguments {
2304 self.inlined_constant(builder, &argument.value);
2305 }
2306 builder.end_tuple(tuple);
2307 }
2308 // Otherwise we are either referencing a record constructor
2309 // function, or building a record that has no fields:
2310 //
2311 // ```gleam
2312 // type Wibble {
2313 // Wibble
2314 // Wobble(Int)
2315 // }
2316 //
2317 // const a = Wibble
2318 // // ^^^^^^ Building record with no fields
2319 // const b = Wobble
2320 // // ^^^^^^ Referencing record constructor function
2321 // ```
2322 None => match type_::collapse_links(type_.clone()).deref() {
2323 Type::Named { .. } | Type::Var { .. } | Type::Tuple { .. } => {
2324 builder.atom(&to_snake_case(&tag));
2325 }
2326 Type::Fn { arguments, .. } => {
2327 self.record_builder_anonymous_function(builder, &tag, arguments.len());
2328 }
2329 },
2330 }
2331 }
2332
2333 Constant::StringConcatenation { left, right, .. } => {
2334 self.constant_string_concatenate(builder, left, right);
2335 }
2336
2337 Constant::RecordUpdate { .. } => {
2338 panic!("record updates should not reach code generation")
2339 }
2340 Constant::Todo { .. } => panic!("todo constants should not reach code generation"),
2341 Constant::Invalid { .. } => {
2342 panic!("invalid constants should not reach code generation")
2343 }
2344 }
2345 }
2346
2347 fn bit_array_constant_segment<Output>(
2348 &mut self,
2349 builder: &mut impl ErlangBuilder<Output>,
2350 segment: &'a TypedConstantBitArraySegment,
2351 ) {
2352 builder.bit_array_segment();
2353 self.inlined_constant(builder, &segment.value);
2354 match segment.size() {
2355 Some(TypedConstant::Int { int_value, .. }) if int_value.is_negative() => {
2356 builder.int(BigInt::ZERO);
2357 }
2358 Some(size) => self.inlined_constant(builder, size),
2359 None => builder.atom("default"),
2360 }
2361 self.bit_array_segment_specifiers(builder, segment);
2362 }
2363
2364 fn bit_array_segment_specifiers<Output, Expr>(
2365 &self,
2366 builder: &mut impl ErlangBuilder<Output>,
2367 segment: &'a BitArraySegment<Expr, Arc<Type>>,
2368 ) {
2369 let options = segment.options.iter();
2370 builder.bit_array_segment_specifiers(options.filter_map(|option| match option {
2371 BitArrayOption::Utf8 { .. } | BitArrayOption::Utf8Codepoint { .. } => {
2372 Some(BitArraySegmentSpecifier::Utf8)
2373 }
2374 BitArrayOption::Utf16 { .. } | BitArrayOption::Utf16Codepoint { .. } => {
2375 Some(BitArraySegmentSpecifier::Utf16)
2376 }
2377 BitArrayOption::Utf32 { .. } | BitArrayOption::Utf32Codepoint { .. } => {
2378 Some(BitArraySegmentSpecifier::Utf32)
2379 }
2380 BitArrayOption::Int { .. } => Some(BitArraySegmentSpecifier::Integer),
2381 BitArrayOption::Float { .. } => Some(BitArraySegmentSpecifier::Float),
2382 BitArrayOption::Bytes { .. } => Some(BitArraySegmentSpecifier::Binary),
2383 BitArrayOption::Bits { .. } => Some(BitArraySegmentSpecifier::Bitstring),
2384 BitArrayOption::Signed { .. } => Some(BitArraySegmentSpecifier::Signed),
2385 BitArrayOption::Unsigned { .. } => Some(BitArraySegmentSpecifier::Unsigned),
2386 BitArrayOption::Big { .. } => Some(BitArraySegmentSpecifier::Big),
2387 BitArrayOption::Little { .. } => Some(BitArraySegmentSpecifier::Little),
2388 BitArrayOption::Native { .. } => Some(BitArraySegmentSpecifier::Native),
2389 BitArrayOption::Unit { value, .. } => Some(BitArraySegmentSpecifier::Unit(*value)),
2390 BitArrayOption::Size { .. } => None,
2391 }));
2392 }
2393
2394 fn constant_string_concatenate<Output>(
2395 &mut self,
2396 builder: &mut impl ErlangBuilder<Output>,
2397 left: &'a TypedConstant,
2398 right: &'a TypedConstant,
2399 ) {
2400 let mut items = VecDeque::new();
2401 items.push_back(left);
2402 items.push_back(right);
2403
2404 let bit_array = builder.start_bit_array();
2405 while let Some(segment) = items.pop_front() {
2406 match segment {
2407 // When concatenating constant strings we flatten out all
2408 // strings that are being concatenated: so that
2409 // `"a" <> "b" <> "c"` becomes a single bitstring like this:
2410 // `<<~"a", ~"b", ~"c">>` rather than nested bitstrings:
2411 // `<<<<~"a", ~"b">>/binary, ~"c">>`.
2412 // If we find a string concatenation we push its separate items
2413 // to be printed next!
2414 Constant::StringConcatenation { left, right, .. } => {
2415 items.push_front(right);
2416 items.push_front(left);
2417 continue;
2418 }
2419 // When concatenating constant strings we want all constant
2420 // variables to also be fully expanded, so that if we have
2421 //
2422 // ```gleam
2423 // const a = "one"
2424 // const b = a <> "two"
2425 // ```
2426 //
2427 // Any use of b will be replaced with `<<~"one", ~"two">>`.
2428 Constant::Var {
2429 constructor: Some(constructor),
2430 ..
2431 } if let ValueConstructorVariant::ModuleConstant { literal, .. } =
2432 &constructor.variant =>
2433 {
2434 items.push_front(literal);
2435 continue;
2436 }
2437
2438 Constant::Int { .. }
2439 | Constant::Float { .. }
2440 | Constant::String { .. }
2441 | Constant::Tuple { .. }
2442 | Constant::List { .. }
2443 | Constant::Record { .. }
2444 | Constant::RecordUpdate { .. }
2445 | Constant::BitArray { .. }
2446 | Constant::Var { .. }
2447 | Constant::Invalid { .. }
2448 | Constant::Todo { .. } => (),
2449 }
2450
2451 builder.bit_array_segment();
2452 self.inlined_constant(builder, segment);
2453 builder.atom("default");
2454 builder.bit_array_segment_specifiers([BitArraySegmentSpecifier::Utf8]);
2455 }
2456 builder.end_bit_array(bit_array);
2457 }
2458
2459 fn string_concatenate<Output>(
2460 &mut self,
2461 builder: &mut impl ErlangBuilder<Output>,
2462 left: &'a TypedExpr,
2463 right: &'a TypedExpr,
2464 ) {
2465 let bit_array = builder.start_bit_array();
2466 self.string_concatenate_argument(builder, left);
2467 self.string_concatenate_argument(builder, right);
2468 builder.end_bit_array(bit_array);
2469 }
2470
2471 fn string_concatenate_argument<Output>(
2472 &mut self,
2473 builder: &mut impl ErlangBuilder<Output>,
2474 value: &'a TypedExpr,
2475 ) {
2476 // String concatenation is basically building a bit array with two
2477 // elements. Anything is going to be simply added as a `/binary` segment
2478 // with one exception: if we're dealing with a literal string that needs
2479 // the `/utf8` specifier instead!
2480 // In both cases the size is alwaus automatic, so we generate the
2481 // `default` atom.
2482 builder.bit_array_segment();
2483 self.maybe_block_expr(builder, value);
2484 builder.atom("default");
2485 builder.bit_array_segment_specifiers(if produces_literal_string(value) {
2486 [BitArraySegmentSpecifier::Utf8]
2487 } else {
2488 [BitArraySegmentSpecifier::Binary]
2489 });
2490 }
2491
2492 fn binary_operator<Output>(
2493 &mut self,
2494 builder: &mut impl ErlangBuilder<Output>,
2495 name: &'a BinOp,
2496 left: &'a TypedExpr,
2497 right: &'a TypedExpr,
2498 ) {
2499 let operator = match name {
2500 BinOp::And => "andalso",
2501 BinOp::Or => "orelse",
2502 BinOp::LtInt | BinOp::LtFloat => "<",
2503 BinOp::LtEqInt | BinOp::LtEqFloat => "=<",
2504 BinOp::Eq => "=:=",
2505 BinOp::NotEq => "/=",
2506 BinOp::GtInt | BinOp::GtFloat => ">",
2507 BinOp::GtEqInt | BinOp::GtEqFloat => ">=",
2508 BinOp::AddInt | BinOp::AddFloat => "+",
2509 BinOp::SubInt | BinOp::SubFloat => "-",
2510 BinOp::MultInt | BinOp::MultFloat => "*",
2511
2512 // Division needs some extra case, in Gleam dividing by 0 results
2513 // in 0; while in Erlang that's an exception.
2514 BinOp::DivFloat => return self.float_division(builder, left, right),
2515 BinOp::DivInt => return self.int_division(builder, left, right, "div"),
2516 BinOp::RemainderInt => return self.int_division(builder, left, right, "rem"),
2517
2518 // String concatenation is not a binop at all! It's just building a
2519 // bit array.
2520 BinOp::Concatenate => return self.string_concatenate(builder, left, right),
2521 };
2522
2523 builder.binary_operator(operator);
2524 self.maybe_block_expr(builder, left);
2525 self.maybe_block_expr(builder, right);
2526 }
2527
2528 fn float_division<Output>(
2529 &mut self,
2530 builder: &mut impl ErlangBuilder<Output>,
2531 left: &'a TypedExpr,
2532 right: &'a TypedExpr,
2533 ) {
2534 match how_to_divide(left, right) {
2535 HowToDivide::ReplaceWithZero => builder.float(0.0),
2536 HowToDivide::EvaluateLeftAndReturnZero => {
2537 // We first evaluate the left hand side, and ignore its return
2538 // value, and then we return zero directly!
2539 self.maybe_block_expr(builder, left);
2540 builder.float(0.0);
2541 }
2542 HowToDivide::PlainErlangDivision => {
2543 builder.binary_operator("/");
2544 self.maybe_block_expr(builder, left);
2545 self.maybe_block_expr(builder, right);
2546 }
2547 HowToDivide::MatchOnRight {
2548 is_left_hand_side_pure,
2549 } => {
2550 // We first have to evaluate the left hand side, and store its
2551 // result in a variable to use later.
2552 let left_name = if !is_left_hand_side_pure {
2553 let left_name = self.new_generated_variable();
2554 builder.match_operator();
2555 builder.variable_pattern(&left_name);
2556 self.maybe_block_expr(builder, left);
2557 Some(left_name)
2558 } else {
2559 None
2560 };
2561
2562 let case = builder.start_case();
2563 self.maybe_block_expr(builder, right);
2564 let case = builder.end_case_subject(case);
2565
2566 // +0.0 -> +0.0
2567 let clause = builder.start_case_clause();
2568 builder.float_pattern(0.0);
2569 let guards = builder.end_clause_pattern(clause);
2570 let body = builder.end_clause_guards(guards);
2571 builder.float(0.0);
2572 builder.end_clause_body(body);
2573
2574 // -0.0 -> -0.0
2575 let clause = builder.start_case_clause();
2576 builder.float_pattern(-0.0);
2577 let guards = builder.end_clause_pattern(clause);
2578 let body = builder.end_clause_guards(guards);
2579 builder.float(-0.0);
2580 builder.end_clause_body(body);
2581
2582 // _value -> left / _value
2583 let denominator = self.new_generated_variable();
2584 let clause = builder.start_case_clause();
2585 builder.variable_pattern(&denominator);
2586 let guards = builder.end_clause_pattern(clause);
2587 let body = builder.end_clause_guards(guards);
2588 builder.binary_operator("/");
2589 // If we had bound the left hand side to a variabe we just
2590 // reference it, otherwise we will generate the code for the
2591 // numerator.
2592 if let Some(left_name) = left_name {
2593 builder.variable(&left_name);
2594 } else {
2595 self.maybe_block_expr(builder, left);
2596 }
2597 builder.variable(&denominator);
2598 builder.end_clause_body(body);
2599
2600 builder.end_case(case);
2601 }
2602 }
2603 }
2604
2605 fn int_division<Output>(
2606 &mut self,
2607 builder: &mut impl ErlangBuilder<Output>,
2608 left: &'a TypedExpr,
2609 right: &'a TypedExpr,
2610 op: &'static str,
2611 ) {
2612 match how_to_divide(left, right) {
2613 HowToDivide::ReplaceWithZero => builder.int(BigInt::ZERO),
2614 HowToDivide::EvaluateLeftAndReturnZero => {
2615 // We first evaluate the left hand side, and ignore its return
2616 // value, and then we return zero directly!
2617 self.maybe_block_expr(builder, left);
2618 builder.int(BigInt::ZERO);
2619 }
2620 HowToDivide::PlainErlangDivision => {
2621 builder.binary_operator(op);
2622 self.maybe_block_expr(builder, left);
2623 self.maybe_block_expr(builder, right);
2624 }
2625 HowToDivide::MatchOnRight {
2626 is_left_hand_side_pure,
2627 } => {
2628 // If the left hand side is not a pure expression we will have
2629 // to evaluate it before the right hand side of the expression.
2630 // So we assign it to a generated variable that we will then
2631 // reference in the case expression's body.
2632 // It will look something like this:
2633 //
2634 // ```erl
2635 // _value = <left_hand_side>,
2636 // case <right_hand_side> of
2637 // 0 -> 0;
2638 // _value@1 -> _value div _value@1
2639 // end
2640 // ```
2641 //
2642 let left_name = if !is_left_hand_side_pure {
2643 let left_name = self.new_generated_variable();
2644 builder.match_operator();
2645 builder.variable_pattern(&left_name);
2646 self.maybe_block_expr(builder, left);
2647 Some(left_name)
2648 } else {
2649 None
2650 };
2651
2652 let case = builder.start_case();
2653 self.maybe_block_expr(builder, right);
2654 let case = builder.end_case_subject(case);
2655
2656 // 0 -> 0
2657 let clause = builder.start_case_clause();
2658 builder.int_pattern(BigInt::ZERO);
2659 let guards = builder.end_clause_pattern(clause);
2660 let body = builder.end_clause_guards(guards);
2661 builder.int(BigInt::ZERO);
2662 builder.end_clause_body(body);
2663
2664 // _value -> left div _value
2665 let denominator = self.new_generated_variable();
2666 let clause = builder.start_case_clause();
2667 builder.variable_pattern(&denominator);
2668 let guards = builder.end_clause_pattern(clause);
2669 let body = builder.end_clause_guards(guards);
2670 builder.binary_operator(op);
2671 // If we had bound the left hand side to a variabe we just
2672 // reference it, otherwise we will generate the code for the
2673 // numerator.
2674 if let Some(left_name) = left_name {
2675 builder.variable(&left_name);
2676 } else {
2677 self.maybe_block_expr(builder, left);
2678 }
2679 builder.variable(&denominator);
2680 builder.end_clause_body(body);
2681
2682 builder.end_case(case);
2683 }
2684 }
2685 }
2686
2687 /// This is used to print segments of a bit array expression.
2688 /// Those are different enough from the constant and pattern ones that it would
2689 /// no longer make sense to try and adapt the `bit_array_segment` generic
2690 /// function to work with the three of them.
2691 /// So you should use this one for printing expression segments, and the generic
2692 /// `bit_array_segment` function for constant and pattern segments instead.
2693 ///
2694 fn bit_array_expression_segment<Output>(
2695 &mut self,
2696 builder: &mut impl ErlangBuilder<Output>,
2697 segment: &'a TypedExprBitArraySegment,
2698 ) {
2699 // Literal strings can have the `utf8`, `utf16`, or `utf32` options just
2700 // fine, and that would be no issue on the Erlang side:
2701 //
2702 // ```erl
2703 // <<"wibble"/utf8>>
2704 // <<"wibble"/utf16>>
2705 // <<"wibble"/utf32>>
2706 // ```
2707 //
2708 // However there's issues when we try and use those options with _variables_
2709 // with the string type. That will result in errors on the Erlang target:
2710 //
2711 // ```erl
2712 // % These are all runtime errors!!
2713 // <<SomeString/utf8>>
2714 // <<SomeString/utf16>>
2715 // <<SomeString/utf32>>
2716 // ```
2717 //
2718 // In Gleam we support those options for all string values, not just
2719 // literals. So we need to do something about them:
2720 //
2721 // - `utf8`: strings are already `utf8` binaries in Gleam, so if we have a
2722 // string value with that option we can put it in the bit array like any
2723 // other binary value:
2724 // ```gleam
2725 // <<some_string:utf8>>
2726 // // becomes <<SomeString/binary>>
2727 // ```
2728 // - `utf16` and `utf32`: these are a bit tricker since they will require
2729 // some conversion (which is what we also do on the JavaScript target!).
2730 // So in this case we need to use the `unicode:characters_to_binary`
2731 // function that will return a binary value we can then put in the bit
2732 // array:
2733 // ```gleam
2734 // <<some_string:utf16-little>>
2735 // // becomes
2736 // // <<(unicode:characters_to_binary(
2737 // // SomeString,
2738 // // utf8, the current encoding
2739 // // {utf16, little}) the encoding we want
2740 // // )/binary>>
2741 // ```
2742 //
2743 if segment.type_.is_string()
2744 && !segment.value.is_literal_string()
2745 && let Some(encoding) = expression_segment_string_encoding(segment)
2746 {
2747 let (size, endiannes) = match encoding {
2748 ExpressionSegmentStringEncoding::Utf16 { endiannes } => (16, endiannes),
2749 ExpressionSegmentStringEncoding::Utf32 { endiannes } => (32, endiannes),
2750 ExpressionSegmentStringEncoding::Utf8 => {
2751 // Gleam strings are utf8 encoded binaries, so we just need
2752 // to add the binary option and we can call it a day.
2753 builder.bit_array_segment();
2754 self.maybe_block_expr(builder, &segment.value);
2755 builder.atom("default");
2756 builder.bit_array_segment_specifiers([BitArraySegmentSpecifier::Binary]);
2757 return;
2758 }
2759 };
2760
2761 builder.bit_array_segment();
2762
2763 // For utf16 and utf32 we need an explicit conversion using erlang's
2764 // `unicode:characters_to_binary`. The segment value will be
2765 // something like this:
2766 // ```erl
2767 // unicode:characters_to_binary(<segment_value>, utf8, {utf16, big})
2768 // ```
2769 let call =
2770 builder.start_remote_call(ErlangModuleName::unicode(), "characters_to_binary");
2771 {
2772 self.maybe_block_expr(builder, &segment.value);
2773 builder.atom("utf8");
2774 let tuple = builder.start_tuple();
2775 builder.atom(&format!("utf{size}"));
2776 match endiannes {
2777 Endianness::Big => builder.atom("big"),
2778 Endianness::Little => builder.atom("little"),
2779 }
2780 builder.end_tuple(tuple);
2781 }
2782 builder.end_call(call);
2783
2784 builder.atom("default");
2785 builder.bit_array_segment_specifiers([BitArraySegmentSpecifier::Binary]);
2786 } else {
2787 // If the bit array segment doesn't need any special handling we use the
2788 // regular printing functions to format its value and options.
2789 builder.bit_array_segment();
2790 self.maybe_block_expr(builder, &segment.value);
2791 self.bit_array_expression_segment_size(builder, segment);
2792 self.bit_array_segment_specifiers(builder, segment);
2793 }
2794 }
2795
2796 /// This generates the code that will produce the size expression of a bit
2797 /// array segment.
2798 ///
2799 /// Make sure to only call this when you're expected to generate a bit array
2800 /// size!
2801 fn bit_array_expression_segment_size<Output>(
2802 &mut self,
2803 builder: &mut impl ErlangBuilder<Output>,
2804 segment: &'a TypedExprBitArraySegment,
2805 ) {
2806 let Some(size) = segment.size() else {
2807 builder.atom("default");
2808 return;
2809 };
2810
2811 // Sizes need some care: in Erlang, having a negative segment size
2812 // results in a runtime error. We can't do that in Gleam! So any
2813 // negative value must be turned to zero instead:
2814 if let TypedExpr::Int { int_value, .. } = &size {
2815 if int_value.is_negative() {
2816 builder.int(BigInt::ZERO);
2817 } else {
2818 builder.int(int_value.clone());
2819 }
2820 } else {
2821 let call = builder.start_remote_call(ErlangModuleName::erlang(), "max");
2822 builder.int(BigInt::ZERO);
2823 self.maybe_block_expr(builder, size);
2824 builder.end_call(call);
2825 }
2826 }
2827
2828 fn clause_guard<Output>(
2829 &mut self,
2830 builder: &mut impl ErlangBuilder<Output>,
2831 guard: &'a TypedClauseGuard,
2832 assignments: &HashMap<EcoString, AliasedLiteral>,
2833 ) {
2834 match guard {
2835 ClauseGuard::Invalid { .. } => unreachable!("invalid guard made it to code generation"),
2836
2837 ClauseGuard::ModuleSelect { literal, .. } => self.inlined_constant(builder, literal),
2838 ClauseGuard::Constant(constant) => self.inlined_constant(builder, constant),
2839
2840 ClauseGuard::Block { value, .. } => self.clause_guard(builder, value, assignments),
2841
2842 ClauseGuard::TupleIndex { tuple, index, .. } => {
2843 self.clause_guard_tuple_index(builder, tuple, *index);
2844 }
2845
2846 ClauseGuard::FieldAccess {
2847 container, index, ..
2848 } => self.clause_guard_tuple_index(
2849 builder,
2850 container,
2851 index.expect("Unable to find index") + 1,
2852 ),
2853 ClauseGuard::Not { expression, .. } => {
2854 builder.unary_operator("not");
2855 self.clause_guard(builder, expression, assignments);
2856 }
2857
2858 ClauseGuard::BinaryOperator {
2859 operator,
2860 left,
2861 right,
2862 ..
2863 } => {
2864 let operator = match operator {
2865 BinOp::Or => "orelse",
2866 BinOp::And => "andalso",
2867 BinOp::Eq => "=:=",
2868 BinOp::NotEq => "=/=",
2869 BinOp::GtInt | BinOp::GtFloat => ">",
2870 BinOp::GtEqInt | BinOp::GtEqFloat => ">=",
2871 BinOp::LtInt | BinOp::LtFloat => "<",
2872 BinOp::LtEqInt | BinOp::LtEqFloat => "=<",
2873 BinOp::AddInt | BinOp::AddFloat => "+",
2874 BinOp::SubInt | BinOp::SubFloat => "-",
2875 BinOp::MultInt | BinOp::MultFloat => "*",
2876 BinOp::DivFloat => "/",
2877 BinOp::DivInt => "div",
2878 BinOp::RemainderInt => "rem",
2879 BinOp::Concatenate => {
2880 return self.clause_guard_string_concatenate(
2881 builder,
2882 left,
2883 right,
2884 assignments,
2885 );
2886 }
2887 };
2888
2889 builder.binary_operator(operator);
2890 self.clause_guard(builder, left, assignments);
2891 self.clause_guard(builder, right, assignments);
2892 }
2893
2894 // Only local variables are supported and the typer ensures that all
2895 // ClauseGuard::Vars are local variables
2896 ClauseGuard::Var {
2897 name,
2898 definition_location,
2899 ..
2900 } => {
2901 // If we're referencing a variable introduced by an alias pattern
2902 // we need to replace it with its actual literal value: in the
2903 // generated code the variable is only defined later, so just
2904 // referencing its name would result in an error.
2905 match assignments.get(name) {
2906 Some(AliasedLiteral::String { value, .. }) => builder.string(value),
2907 Some(AliasedLiteral::Int { value, .. }) => builder.int(value.clone()),
2908 Some(AliasedLiteral::Float { value, .. }) => builder.float(value.value()),
2909 None => {
2910 builder.variable(&self.local_var_name(definition_location));
2911 }
2912 }
2913 }
2914 }
2915 }
2916
2917 fn clause_guard_tuple_index<Output>(
2918 &mut self,
2919 builder: &mut impl ErlangBuilder<Output>,
2920 tuple: &'a TypedClauseGuard,
2921 index: u64,
2922 ) {
2923 let call = builder.start_remote_call(ErlangModuleName::erlang(), "element");
2924 builder.int((index + 1).into());
2925 self.clause_guard(builder, tuple, &HashMap::new());
2926 builder.end_call(call);
2927 }
2928
2929 fn clause_guard_string_concatenate<Output>(
2930 &mut self,
2931 builder: &mut impl ErlangBuilder<Output>,
2932 left: &'a TypedClauseGuard,
2933 right: &'a TypedClauseGuard,
2934 assignments: &HashMap<EcoString, AliasedLiteral>,
2935 ) {
2936 let bit_array = builder.start_bit_array();
2937 self.clause_guard_string_concatenate_argument(builder, left, assignments);
2938 self.clause_guard_string_concatenate_argument(builder, right, assignments);
2939 builder.end_bit_array(bit_array);
2940 }
2941
2942 fn clause_guard_string_concatenate_argument<Output>(
2943 &mut self,
2944 builder: &mut impl ErlangBuilder<Output>,
2945 guard: &'a TypedClauseGuard,
2946 assignments: &HashMap<EcoString, AliasedLiteral>,
2947 ) {
2948 // String concatenation is basically building a bit array with two
2949 // elements. Anything is going to be simply added as a `/binary` segment
2950 // with one exception: if we're dealing with a literal string that needs
2951 // the `/utf8` specifier instead!
2952 // In both cases the size is alwaus automatic, so we generate the
2953 // `default` atom.
2954 builder.bit_array_segment();
2955 self.clause_guard(builder, guard, assignments);
2956 builder.atom("default");
2957 builder.bit_array_segment_specifiers(if guard_produces_literal_string(guard) {
2958 [BitArraySegmentSpecifier::Utf8]
2959 } else {
2960 [BitArraySegmentSpecifier::Binary]
2961 });
2962 }
2963
2964 /// Given a record name and the number of arguments it accepts, this outputs
2965 /// the code to generate an anonymous function that builds that record.
2966 ///
2967 /// For example, given:
2968 ///
2969 /// ```gleam
2970 /// pub type Wibble {
2971 /// Wibble(Int, String)
2972 /// }
2973 ///
2974 /// pub fn main() {
2975 /// Wibble
2976 /// //^^^^^^ This has to return the builder function!
2977 /// }
2978 /// ```
2979 ///
2980 /// We will produce the following Erlang code:
2981 ///
2982 /// ```erl
2983 /// main() ->
2984 /// fun(_value, _value@1) ->
2985 /// {wibble, _value, _value@1}
2986 /// end.
2987 /// ```
2988 ///
2989 fn record_builder_anonymous_function<Output>(
2990 &mut self,
2991 builder: &mut impl ErlangBuilder<Output>,
2992 record_name: &str,
2993 arguments: usize,
2994 ) {
2995 let arguments = (0..arguments)
2996 .map(|_| self.new_generated_variable())
2997 .collect_vec();
2998 let function = builder.start_anonymous_function(&arguments);
2999
3000 if arguments.is_empty() {
3001 builder.atom(&to_snake_case(record_name));
3002 } else {
3003 let tuple = builder.start_tuple();
3004 builder.atom(&to_snake_case(record_name));
3005 for argument in arguments {
3006 builder.variable(&argument);
3007 }
3008 builder.end_tuple(tuple);
3009 }
3010
3011 builder.end_function(function);
3012 }
3013
3014 /// After generating a pattern we might have to generate additional variable
3015 /// bindings in the body following a clause pattern.
3016 /// This adds those variable to the current body.
3017 fn pattern_assignments<Output>(
3018 &mut self,
3019 builder: &mut impl ErlangBuilder<Output>,
3020 variables_to_add_later: HashMap<EcoString, AliasedLiteral>,
3021 ) {
3022 let variables_to_add_later = variables_to_add_later
3023 .into_iter()
3024 .sorted_by(|(one, _), (other, _)| one.cmp(other));
3025
3026 for (gleam_name, value) in variables_to_add_later {
3027 builder.match_operator();
3028 match value {
3029 AliasedLiteral::String { location, value } => {
3030 builder.variable_pattern(&self.new_erlang_variable(&gleam_name, location));
3031 builder.string(&value);
3032 }
3033 AliasedLiteral::Float { location, value } => {
3034 builder.variable_pattern(&self.new_erlang_variable(&gleam_name, location));
3035 builder.float(value.value());
3036 }
3037 AliasedLiteral::Int { location, value } => {
3038 builder.variable_pattern(&self.new_erlang_variable(&gleam_name, location));
3039 builder.int(value.clone());
3040 }
3041 }
3042 }
3043 }
3044}
3045
3046pub fn records(module: &TypedModule) -> Vec<(&str, String)> {
3047 module
3048 .definitions
3049 .custom_types
3050 .iter()
3051 .filter(|custom_type| {
3052 custom_type.publicity.is_public()
3053 && !module
3054 .unused_definition_positions
3055 .contains(&custom_type.location.start)
3056 })
3057 .flat_map(|custom_type| &custom_type.constructors)
3058 .filter(|constructor| !constructor.arguments.is_empty())
3059 .filter_map(|constructor| {
3060 constructor
3061 .arguments
3062 .iter()
3063 .map(
3064 |RecordConstructorArg {
3065 label,
3066 ast: _,
3067 location: _,
3068 type_,
3069 ..
3070 }| {
3071 label
3072 .as_ref()
3073 .map(|(_, label)| (label.as_str(), type_.clone()))
3074 },
3075 )
3076 .collect::<Option<Vec<_>>>()
3077 .map(|fields| (constructor.name.as_str(), fields))
3078 })
3079 .map(|(name, fields)| (name, record_definition(name, &fields)))
3080 .collect()
3081}
3082
3083/// Given an expression, this tells us how we should be calling it as a
3084/// function in the generated erlang code.
3085fn how_to_call<'a>(function: &'a TypedExpr) -> FunctionCall<'a> {
3086 match function {
3087 // This is a record constructor from the current module.
3088 // For example:
3089 //
3090 // ```gleam
3091 // pub type Wibble { Wibble(Int) }
3092 // pub fn main() {
3093 // Wibble(1)
3094 // //^^^^^^^^^ This!
3095 // }
3096 // ```
3097 //
3098 // On the Erlang side we have to build a tagged tuple
3099 //
3100 TypedExpr::ModuleSelect {
3101 constructor: ModuleValueConstructor::Record { name, .. },
3102 ..
3103 } => FunctionCall::BuildRecord { name },
3104
3105 // Notice how whenever we have a function that has an erlang
3106 // external definition we will always directly call that and not go
3107 // through the Gleam function. For example:
3108 //
3109 // ```gleam
3110 // pub fn main() {
3111 // format("hello", [])
3112 // }
3113 //
3114 // @external(erlang, "io", "format")
3115 // fn format(string: String, args: List(String)) -> Nil
3116 // ```
3117 //
3118 // Will result in:
3119 //
3120 // ```erl
3121 // main() ->
3122 // io:format(~"hello", []).
3123 // ```
3124 //
3125 // This enables the Erlang compiler to further optimise those calls.
3126 //
3127 TypedExpr::ModuleSelect {
3128 constructor:
3129 ModuleValueConstructor::Fn {
3130 external_erlang: Some((module, name)),
3131 ..
3132 }
3133 | ModuleValueConstructor::Fn { module, name, .. },
3134 ..
3135 } => FunctionCall::Call { module, name },
3136
3137 // We're calling a variable as a function.
3138 TypedExpr::Var { constructor, .. } => match &constructor.variant {
3139 // The variable is the constructor for a record.
3140 // That's a tagged tuple.
3141 ValueConstructorVariant::Record { name, .. } => FunctionCall::BuildRecord { name },
3142 // The variable is a module function, we can call that as usual
3143 // just like we did for `TypedExpr::ModuleSelect`.
3144 ValueConstructorVariant::ModuleFn {
3145 external_erlang: Some((module, name)),
3146 ..
3147 }
3148 | ValueConstructorVariant::ModuleFn { module, name, .. } => {
3149 FunctionCall::Call { module, name }
3150 }
3151 // The variable is a variable defined inside the function, we
3152 // can call it directly:
3153 //
3154 // ```erl
3155 // SomeVariable = fun() -> ... end,
3156 // SomeVariable()
3157 // ```
3158 ValueConstructorVariant::LocalVariable { .. } => FunctionCall::DirectCall,
3159 // The variable is a module constant, if it refers to a module
3160 // function we want to call it directly.
3161 ValueConstructorVariant::ModuleConstant { literal, .. } => {
3162 if let Constant::Var {
3163 constructor: Some(constructor),
3164 ..
3165 } = literal
3166 && let ValueConstructorVariant::ModuleFn {
3167 external_erlang: Some((module, name)),
3168 ..
3169 }
3170 | ValueConstructorVariant::ModuleFn { module, name, .. } =
3171 &constructor.variant
3172 {
3173 FunctionCall::Call { module, name }
3174 } else {
3175 FunctionCall::DirectCall
3176 }
3177 }
3178 },
3179
3180 TypedExpr::Fn { .. }
3181 | TypedExpr::Call { .. }
3182 | TypedExpr::Todo { .. }
3183 | TypedExpr::Panic { .. }
3184 | TypedExpr::RecordAccess { .. }
3185 | TypedExpr::TupleIndex { .. }
3186 | TypedExpr::Int { .. }
3187 | TypedExpr::Float { .. }
3188 | TypedExpr::String { .. }
3189 | TypedExpr::Block { .. }
3190 | TypedExpr::Pipeline { .. }
3191 | TypedExpr::List { .. }
3192 | TypedExpr::BinOp { .. }
3193 | TypedExpr::Case { .. }
3194 | TypedExpr::PositionalAccess { .. }
3195 | TypedExpr::ModuleSelect { .. }
3196 | TypedExpr::Tuple { .. }
3197 | TypedExpr::Echo { .. }
3198 | TypedExpr::BitArray { .. }
3199 | TypedExpr::RecordUpdate { .. }
3200 | TypedExpr::NegateBool { .. }
3201 | TypedExpr::NegateInt { .. }
3202 | TypedExpr::Invalid { .. } => FunctionCall::DirectCall,
3203 }
3204}
3205
3206/// This represents the different ways a Gleam division could be turned into an
3207/// Erlang division.
3208/// In Gleam dividing by zero results in a 0, not in an exception. This means
3209/// we can't always just use the plain Erlang division operator.
3210enum HowToDivide {
3211 /// Means we can divide two expression by just doing `One / Other`.
3212 PlainErlangDivision,
3213
3214 /// The entire division can be safely replaced with a literal `0`.
3215 ReplaceWithZero,
3216
3217 /// This means the left hand side could have side effects, but then we're
3218 /// dividing it by zero, so we can just ignore its result and directly
3219 /// return 0.
3220 EvaluateLeftAndReturnZero,
3221
3222 /// This means we have to pattern match on the right hand side to make sure
3223 /// that it is not zero, otherwise we'll have to return zero, or the
3224 /// division operation would result in an exception.
3225 ///
3226 /// It will look something like this:
3227 ///
3228 /// ```erl
3229 /// case right_hand_side of
3230 /// 0 -> 0;
3231 /// _denominator -> left_hand_side / _denominator
3232 /// ```
3233 ///
3234 MatchOnRight { is_left_hand_side_pure: bool },
3235}
3236
3237fn how_to_divide(left: &TypedExpr, right: &TypedExpr) -> HowToDivide {
3238 if right.is_non_zero_compile_time_number() {
3239 // Right can't be zero, so it's safe to just divide!
3240 HowToDivide::PlainErlangDivision
3241 } else if left.is_pure_value_constructor() {
3242 if right.is_zero_compile_time_number() {
3243 // Left has no side effects and `right` is `0`, so we can just
3244 // replace the result with zero!
3245 HowToDivide::ReplaceWithZero
3246 } else {
3247 // Left has no side effects, but `right` could still be zero at
3248 // runtime, we need to match on it.
3249 HowToDivide::MatchOnRight {
3250 is_left_hand_side_pure: true,
3251 }
3252 }
3253 } else {
3254 // Left can have side effects, but the right hand side is zero!
3255 // In that case we have to evaluate `left`, but then we can directly
3256 // return 0.
3257 if right.is_zero_compile_time_number() {
3258 HowToDivide::EvaluateLeftAndReturnZero
3259 } else {
3260 // Otherwise we'll have to make sure things are evaluated in the
3261 // correct order.
3262 HowToDivide::MatchOnRight {
3263 is_left_hand_side_pure: false,
3264 }
3265 }
3266 }
3267}
3268
3269pub fn record_definition(record_name: &str, fields: &[(&str, Arc<Type>)]) -> String {
3270 let mut builder = ErlangSourceBuilder::new(None);
3271
3272 let attribute = builder.start_record_attribute(&to_snake_case(record_name));
3273
3274 let type_printer = TypeGenerator::new("").var_as_any();
3275 for (field_name, field_type) in fields {
3276 builder.record_field();
3277 builder.atom(field_name);
3278 type_printer.type_(&mut builder, field_type);
3279 }
3280
3281 builder.end_record_attribute(attribute);
3282 builder.into_output()
3283}
3284
3285pub fn module<'a>(
3286 module: &'a TypedModule,
3287 line_numbers: &'a LineNumbers,
3288 root: &'a Utf8Path,
3289) -> String {
3290 let mut generator = Generator::new(module, line_numbers, root);
3291 let mut builder = ErlangSourceBuilder::new(Some(ErlangModuleName::new(&module.name)));
3292 generator.module_document(&mut builder);
3293
3294 let mut output = builder.into_output();
3295 if generator.echo_used {
3296 output.push_str(std::include_str!("../templates/echo.erl"));
3297 }
3298 output
3299}
3300
3301/// If the given function should be exported from the current Erlang module then
3302/// this function will return its name and arity to be used when exporting it.
3303/// For example: `pub fn wibble(a, b)` will produce `Some(("wibble", 2))`, so
3304/// we can export `wibble/2`.
3305fn function_export<'a>(
3306 function: &'a TypedFunction,
3307 overridden_publicity: &im::HashSet<EcoString>,
3308) -> Option<(&'a str, usize)> {
3309 let (_, name) = function
3310 .name
3311 .as_ref()
3312 .expect("module function with no name");
3313
3314 // If the function is not implemented for this target, don't attempt to
3315 // export it.
3316 if !function.implementations.supports(Target::Erlang) {
3317 return None;
3318 }
3319
3320 // If the function is not importable and it's publicity has not been
3321 // overridden, don't attempt to export it.
3322 if !function.publicity.is_importable() && !overridden_publicity.contains(name) {
3323 return None;
3324 }
3325
3326 let name = escape_erlang_existing_name(name);
3327 Some((name, function.arguments.len()))
3328}
3329
3330/// Given a custom type this returns the name it should be used to export it and
3331/// its arity. For example: `pub type Wibble(a, b)` will produce `("wibble", 2)`,
3332/// so we can export `wibble/2`.
3333fn type_export(custom_type: &TypedCustomType) -> (EcoString, usize) {
3334 let name = erl_safe_type_name(to_snake_case(&custom_type.name));
3335 let arity = custom_type.typed_parameters.len();
3336 (name, arity)
3337}
3338
3339/// This returns true if the given expression is going to be compiled to a
3340/// single literal Erlang string.
3341/// This is not true just for literal Gleam strings like `"abc"`, but also
3342/// variables referencing string constants (as those are inlined)
3343fn produces_literal_string(value: &TypedExpr) -> bool {
3344 match value {
3345 TypedExpr::String { .. }
3346 // Constants are inlined on the Erlang target, so we need to check if
3347 // those are literal strings too!
3348 | TypedExpr::ModuleSelect {
3349 constructor:
3350 ModuleValueConstructor::Constant {
3351 literal: Constant::String { .. },
3352 ..
3353 },
3354 ..
3355 }
3356 | TypedExpr::Var {
3357 constructor:
3358 ValueConstructor {
3359 variant:
3360 ValueConstructorVariant::ModuleConstant {
3361 literal: Constant::String { .. },
3362 ..
3363 },
3364 ..
3365 },
3366 ..
3367 } => true,
3368
3369 TypedExpr::Int { .. }
3370 | TypedExpr::Var { .. }
3371 | TypedExpr::Float { .. }
3372 | TypedExpr::Block { .. }
3373 | TypedExpr::Pipeline { .. }
3374 | TypedExpr::Fn { .. }
3375 | TypedExpr::List { .. }
3376 | TypedExpr::Call { .. }
3377 | TypedExpr::BinOp { .. }
3378 | TypedExpr::Case { .. }
3379 | TypedExpr::RecordAccess { .. }
3380 | TypedExpr::PositionalAccess { .. }
3381 | TypedExpr::ModuleSelect { .. }
3382 | TypedExpr::Tuple { .. }
3383 | TypedExpr::TupleIndex { .. }
3384 | TypedExpr::Todo { .. }
3385 | TypedExpr::Panic { .. }
3386 | TypedExpr::Echo { .. }
3387 | TypedExpr::BitArray { .. }
3388 | TypedExpr::RecordUpdate { .. }
3389 | TypedExpr::NegateBool { .. }
3390 | TypedExpr::NegateInt { .. }
3391 | TypedExpr::Invalid { .. } => false,
3392 }
3393}
3394
3395/// This returns true if the given expression is going to be compiled to a
3396/// single literal Erlang string.
3397/// This is not true just for literal Gleam strings like `"abc"`, but also
3398/// variables referencing string constants (as those are inlined)
3399fn guard_produces_literal_string(guard: &ClauseGuard<Arc<Type>>) -> bool {
3400 match guard {
3401 ClauseGuard::Block { value, .. } => guard_produces_literal_string(value),
3402
3403 ClauseGuard::ModuleSelect {
3404 literal: Constant::String { .. },
3405 ..
3406 }
3407 | ClauseGuard::Constant(Constant::String { .. }) => true,
3408
3409 ClauseGuard::BinaryOperator { .. }
3410 | ClauseGuard::Constant(..)
3411 | ClauseGuard::ModuleSelect { .. }
3412 | ClauseGuard::Not { .. }
3413 | ClauseGuard::Var { .. }
3414 | ClauseGuard::TupleIndex { .. }
3415 | ClauseGuard::FieldAccess { .. }
3416 | ClauseGuard::Invalid { .. } => false,
3417 }
3418}
3419
3420static ATOM_PATTERN: OnceLock<Regex> = OnceLock::new();
3421
3422fn atom_pattern() -> &'static Regex {
3423 ATOM_PATTERN.get_or_init(|| Regex::new(r"^[a-z][a-z0-9_@]*$").expect("atom RE regex"))
3424}
3425
3426pub fn escape_atom_string(value: EcoString) -> EcoString {
3427 if is_erlang_reserved_word(&value) {
3428 // Escape because of keyword collision
3429 eco_format!("'{value}'")
3430 } else if atom_pattern().is_match(&value) {
3431 value
3432 } else {
3433 // Escape because of characters contained
3434 eco_format!("'{value}'")
3435 }
3436}
3437
3438enum ExpressionSegmentStringEncoding {
3439 Utf8,
3440 Utf16 { endiannes: Endianness },
3441 Utf32 { endiannes: Endianness },
3442}
3443
3444fn expression_segment_string_encoding(
3445 segment: &TypedExprBitArraySegment,
3446) -> Option<ExpressionSegmentStringEncoding> {
3447 let endiannes = segment.endianness();
3448 segment.options.iter().find_map(|option| match option {
3449 BitArrayOption::Utf8 { .. } => Some(ExpressionSegmentStringEncoding::Utf8),
3450 BitArrayOption::Utf16 { .. } => Some(ExpressionSegmentStringEncoding::Utf16 { endiannes }),
3451 BitArrayOption::Utf32 { .. } => Some(ExpressionSegmentStringEncoding::Utf32 { endiannes }),
3452
3453 BitArrayOption::Bytes { .. }
3454 | BitArrayOption::Int { .. }
3455 | BitArrayOption::Float { .. }
3456 | BitArrayOption::Bits { .. }
3457 | BitArrayOption::Utf8Codepoint { .. }
3458 | BitArrayOption::Utf16Codepoint { .. }
3459 | BitArrayOption::Utf32Codepoint { .. }
3460 | BitArrayOption::Signed { .. }
3461 | BitArrayOption::Unsigned { .. }
3462 | BitArrayOption::Big { .. }
3463 | BitArrayOption::Little { .. }
3464 | BitArrayOption::Native { .. }
3465 | BitArrayOption::Size { .. }
3466 | BitArrayOption::Unit { .. } => None,
3467 })
3468}
3469
3470fn needs_begin_end_wrapping(expression: &TypedExpr) -> bool {
3471 match expression {
3472 // Record updates are 1 expression if there's no assignment, multiple
3473 // otherwise.
3474 TypedExpr::RecordUpdate {
3475 updated_record_assigned_name,
3476 ..
3477 } => updated_record_assigned_name.is_some(),
3478
3479 // Pipelines are always multiple assignments.
3480 TypedExpr::Pipeline { .. } => true,
3481
3482 // Binary operations that require division might have to be turned into
3483 // multiple statements!
3484 TypedExpr::BinOp {
3485 operator: BinOp::DivFloat | BinOp::DivInt | BinOp::RemainderInt,
3486 left,
3487 right,
3488 ..
3489 } => match how_to_divide(left, right) {
3490 // In these cases we'll have to generate two statements: a variable
3491 // assignment for the left hand side, and one to return the value!
3492 HowToDivide::MatchOnRight {
3493 is_left_hand_side_pure: false,
3494 }
3495 | HowToDivide::EvaluateLeftAndReturnZero => true,
3496 // Here we just generate a single statement.
3497 HowToDivide::PlainErlangDivision
3498 | HowToDivide::ReplaceWithZero
3499 | HowToDivide::MatchOnRight {
3500 is_left_hand_side_pure: true,
3501 } => false,
3502 },
3503
3504 TypedExpr::Int { .. }
3505 | TypedExpr::Float { .. }
3506 | TypedExpr::String { .. }
3507 | TypedExpr::Var { .. }
3508 | TypedExpr::Fn { .. }
3509 | TypedExpr::List { .. }
3510 | TypedExpr::Call { .. }
3511 | TypedExpr::BinOp { .. }
3512 | TypedExpr::Case { .. }
3513 | TypedExpr::RecordAccess { .. }
3514 | TypedExpr::PositionalAccess { .. }
3515 | TypedExpr::Block { .. }
3516 | TypedExpr::ModuleSelect { .. }
3517 | TypedExpr::Tuple { .. }
3518 | TypedExpr::TupleIndex { .. }
3519 | TypedExpr::Todo { .. }
3520 | TypedExpr::Echo { .. }
3521 | TypedExpr::Panic { .. }
3522 | TypedExpr::BitArray { .. }
3523 | TypedExpr::NegateBool { .. }
3524 | TypedExpr::NegateInt { .. }
3525 | TypedExpr::Invalid { .. } => false,
3526 }
3527}
3528
3529/// This represents an expression that appears in an expression, either because
3530/// it is part of some larger expression (like a binop: `assert a && b`, or a
3531/// call `assert wibble(wobble)`), or because it is being matched against
3532/// directly (like `assert wibble`).
3533struct AssertionExpression<'a> {
3534 /// This tells us the kind of expression we're dealing with: wether that's a
3535 /// literal, an expression that can't be known at compile time, or if it
3536 /// hasn't been evaluated at all!
3537 kind: AssertedExpressionKind,
3538 /// If the expression has been evaluated, this is going to tell us how we
3539 /// can reference its value.
3540 runtime_value: Option<AssertedExpressionRuntimeValue<'a>>,
3541 /// This is the location pointing to where in the Gleam source code this
3542 /// expression comes from.
3543 location: SrcSpan,
3544}
3545
3546impl<'a> AssertionExpression<'a> {
3547 fn from_expression(expression: &'a TypedExpr) -> Self {
3548 Self {
3549 runtime_value: Some(AssertedExpressionRuntimeValue::Expression(expression)),
3550 kind: if expression.is_literal() {
3551 AssertedExpressionKind::Literal
3552 } else {
3553 AssertedExpressionKind::Expression
3554 },
3555 location: expression.location(),
3556 }
3557 }
3558
3559 fn was_unevaluated(mut self) -> Self {
3560 self.kind = AssertedExpressionKind::Unevaluated;
3561 self.runtime_value = None;
3562 self
3563 }
3564
3565 fn evaluated_to_bool(mut self, result: bool) -> Self {
3566 self.runtime_value = Some(AssertedExpressionRuntimeValue::KnownBool(result));
3567 self
3568 }
3569
3570 fn from_generated_variable(name: EcoString, original_expression: &'a TypedExpr) -> Self {
3571 Self {
3572 runtime_value: Some(AssertedExpressionRuntimeValue::Variable(name)),
3573 kind: if original_expression.is_literal() {
3574 AssertedExpressionKind::Literal
3575 } else {
3576 AssertedExpressionKind::Expression
3577 },
3578 location: original_expression.location(),
3579 }
3580 }
3581}
3582
3583/// This describes the kind of expression we're asserting against.
3584///
3585#[derive(Debug, Clone, Copy)]
3586enum AssertedExpressionKind {
3587 /// The expression being asserted against is a literal value. For example:
3588 ///
3589 /// ```gleam
3590 /// assert True && wibble
3591 /// // ^^^^ This is a literal value.
3592 /// ```
3593 ///
3594 Literal,
3595 /// The expression being asserted against is anything but a literal, well
3596 /// known, value.
3597 /// For example:
3598 ///
3599 /// ```gleam
3600 /// assert True && wibble
3601 /// // ^^^^^^ This is an expression.
3602 /// ```
3603 ///
3604 Expression,
3605 /// The expression being asserted against has not been evaluated yet,
3606 /// because of some short circuiting behaviour.
3607 ///
3608 /// ```gleam
3609 /// assert False && something_else()
3610 /// // ^^^^^^^^^^^^^^^^ This will never be evaluated.
3611 /// ```
3612 Unevaluated,
3613}
3614
3615/// This is telling us what the runtime value of some asserted value is.
3616/// Used to produce the code for such value in runtime errors if an assert
3617/// fails.
3618///
3619/// For example:
3620///
3621/// ```gleam
3622/// assert wibble()
3623/// ```
3624///
3625/// If the assertion fails we know that `wibble()` must be `false` at runtime.
3626/// It's `AssertedExpressionRuntimeValue` would be
3627/// `AssertedExpressionRuntimeValue::Bool(false)`.
3628///
3629#[derive(Debug)]
3630enum AssertedExpressionRuntimeValue<'a> {
3631 /// We can tell that the asserted value must be a known bool.
3632 /// That's because we know wether the assertion failed (it must be false),
3633 /// or not (it must be true).
3634 KnownBool(bool),
3635 /// The asserted value was bound to a generated variable with the given
3636 /// name, and we can reference it using that name.
3637 Variable(EcoString),
3638 /// The asserted value is an expression we need to inline in the error.
3639 Expression(&'a TypedExpr),
3640}
3641
3642fn variable_name(name: &str) -> EcoString {
3643 let mut chars = name.chars();
3644 let first_char = chars.next();
3645 let first_uppercased = first_char.into_iter().flat_map(char::to_uppercase);
3646 first_uppercased.chain(chars).collect()
3647}
3648
3649/// When rendering a type variable to an erlang type spec we need all type
3650/// variables with the same id to end up with the same name in the generated
3651/// Erlang.
3652/// This function converts a usize into base 26 A-Z for this purpose.
3653fn id_to_type_var_str(id: u64) -> EcoString {
3654 if id < 26 {
3655 let mut name = EcoString::from("");
3656 name.push(char::from_u32((id % 26 + 65) as u32).expect("id_to_type_var 0"));
3657 return name;
3658 }
3659 let mut name = vec![];
3660 let mut last_char = id;
3661 while last_char >= 26 {
3662 name.push(char::from_u32((last_char % 26 + 65) as u32).expect("id_to_type_var 1"));
3663 last_char /= 26;
3664 }
3665 name.push(char::from_u32((last_char % 26 + 64) as u32).expect("id_to_type_var 2"));
3666 name.reverse();
3667 name.into_iter().collect()
3668}
3669
3670pub fn is_erlang_reserved_word(name: &str) -> bool {
3671 match name {
3672 "!" | "receive" | "bnot" | "div" | "rem" | "band" | "bor" | "bxor" | "bsl" | "bsr"
3673 | "not" | "and" | "or" | "xor" | "orelse" | "andalso" | "when" | "end" | "fun" | "try"
3674 | "catch" | "after" | "begin" | "let" | "query" | "cond" | "if" | "of" | "case"
3675 | "maybe" | "else" => true,
3676 _ => false,
3677 }
3678}
3679
3680// Includes shell_default & user_default which are looked for by the erlang shell
3681pub fn is_erlang_standard_library_module(name: &str) -> bool {
3682 match name {
3683 "array" | "base64" | "beam_lib" | "binary" | "c" | "calendar" | "dets" | "dict"
3684 | "digraph" | "digraph_utils" | "epp" | "erl_anno" | "erl_eval" | "erl_expand_records"
3685 | "erl_id_trans" | "erl_internal" | "erl_lint" | "erl_parse" | "erl_pp" | "erl_scan"
3686 | "erl_tar" | "ets" | "file_sorter" | "filelib" | "filename" | "gb_sets" | "gb_trees"
3687 | "gen_event" | "gen_fsm" | "gen_server" | "gen_statem" | "io" | "io_lib" | "lists"
3688 | "log_mf_h" | "maps" | "math" | "ms_transform" | "orddict" | "ordsets" | "pool"
3689 | "proc_lib" | "proplists" | "qlc" | "queue" | "rand" | "random" | "re" | "sets"
3690 | "shell" | "shell_default" | "shell_docs" | "slave" | "sofs" | "string" | "supervisor"
3691 | "supervisor_bridge" | "sys" | "timer" | "unicode" | "uri_string" | "user_default"
3692 | "win32reg" | "zip" => true,
3693 _ => false,
3694 }
3695}
3696
3697// Includes the functions that are autogenerated by Erlang itself
3698pub fn escape_erlang_existing_name(name: &str) -> &str {
3699 match name {
3700 "module_info" => "moduleInfo",
3701 _ => name,
3702 }
3703}
3704
3705/// A TypeVar can either be rendered as an actual type variable such as `A` or `B`,
3706/// or it can be rendered as `any()` depending on how many usages it has. If it
3707/// has only 1 usage it is an `any()` type. If it has more than 1 usage it is a
3708/// type variable. This function gathers usages for this determination.
3709///
3710/// Examples:
3711/// fn(a) -> String // `a` is `any()`
3712/// fn() -> Result(a, b) // `a` and `b` are `any()`
3713/// fn(a) -> a // `a` is a type var
3714fn collect_type_var_usages<'a>(
3715 mut ids: HashMap<u64, u64>,
3716 types: impl IntoIterator<Item = &'a Arc<Type>>,
3717) -> HashMap<u64, u64> {
3718 for type_ in types {
3719 type_var_ids(type_, &mut ids);
3720 }
3721 ids
3722}
3723
3724fn result_type_var_ids(ids: &mut HashMap<u64, u64>, arg_ok: &Type, arg_err: &Type) {
3725 let mut ok_ids = HashMap::new();
3726 type_var_ids(arg_ok, &mut ok_ids);
3727
3728 let mut err_ids = HashMap::new();
3729 type_var_ids(arg_err, &mut err_ids);
3730
3731 let mut result_counts = ok_ids;
3732 for (id, count) in err_ids {
3733 let _ = result_counts
3734 .entry(id)
3735 .and_modify(|current_count| {
3736 if *current_count < count {
3737 *current_count = count;
3738 }
3739 })
3740 .or_insert(count);
3741 }
3742 for (id, count) in result_counts {
3743 let _ = ids
3744 .entry(id)
3745 .and_modify(|current_count| {
3746 *current_count += count;
3747 })
3748 .or_insert(count);
3749 }
3750}
3751
3752fn type_var_ids(type_: &Type, ids: &mut HashMap<u64, u64>) {
3753 match type_ {
3754 Type::Var { type_ } => match type_.borrow().deref() {
3755 TypeVar::Generic { id, .. } | TypeVar::Unbound { id, .. } => {
3756 let count = ids.entry(*id).or_insert(0);
3757 *count += 1;
3758 }
3759 TypeVar::Link { type_ } => type_var_ids(type_, ids),
3760 },
3761 Type::Named {
3762 arguments,
3763 module,
3764 name,
3765 ..
3766 } => match arguments[..] {
3767 [ref arg_ok, ref arg_err] if is_prelude_module(module) && name == "Result" => {
3768 result_type_var_ids(ids, arg_ok, arg_err);
3769 }
3770 _ => {
3771 for argument in arguments {
3772 type_var_ids(argument, ids);
3773 }
3774 }
3775 },
3776 Type::Fn { arguments, return_ } => {
3777 for argument in arguments {
3778 type_var_ids(argument, ids);
3779 }
3780 type_var_ids(return_, ids);
3781 }
3782 Type::Tuple { elements } => {
3783 for element in elements {
3784 type_var_ids(element, ids);
3785 }
3786 }
3787 }
3788}
3789
3790fn erl_safe_type_name(mut name: EcoString) -> EcoString {
3791 match name.as_str() {
3792 "any"
3793 | "arity"
3794 | "atom"
3795 | "binary"
3796 | "bitstring"
3797 | "boolean"
3798 | "byte"
3799 | "char"
3800 | "dynamic"
3801 | "float"
3802 | "function"
3803 | "identifier"
3804 | "integer"
3805 | "iodata"
3806 | "iolist"
3807 | "list"
3808 | "map"
3809 | "maybe_improper_list"
3810 | "mfa"
3811 | "module"
3812 | "neg_integer"
3813 | "nil"
3814 | "no_return"
3815 | "node"
3816 | "non_neg_integer"
3817 | "none"
3818 | "nonempty_improper_list"
3819 | "nonempty_list"
3820 | "nonempty_string"
3821 | "number"
3822 | "pid"
3823 | "port"
3824 | "pos_integer"
3825 | "reference"
3826 | "string"
3827 | "term"
3828 | "timeout"
3829 | "tuple" => {
3830 name.push('_');
3831 name
3832 }
3833
3834 _ => name,
3835 }
3836}
3837
3838#[derive(Debug)]
3839struct TypeGenerator<'a> {
3840 /// If this is true, all types that are generic or unbound are going to be
3841 /// treated as `any()`.
3842 ///
3843 var_as_any: bool,
3844 /// A TypeVar can either be rendered as an actual type variable such as `A`
3845 /// or `B`, or it can be rendered as `any()` depending on how many times it
3846 /// is used.
3847 /// If it is only ever used once, it is an `any()` type.
3848 /// If it has more than 1 usage it is a regular type variable.
3849 ///
3850 /// For example:
3851 ///
3852 /// ```gleam
3853 /// fn(a) -> String // `a` is turned into `any()`
3854 /// fn() -> Result(a, b) // `a` and `b` are turned into `any()`
3855 /// fn(a) -> a // `a` is a type var
3856 /// ```
3857 ///
3858 /// If present, this is a map telling us from generic variable id, to number
3859 /// of times that variable is used.
3860 ///
3861 var_usages: Option<&'a HashMap<u64, u64>>,
3862 current_module: &'a str,
3863}
3864
3865impl<'a> TypeGenerator<'a> {
3866 fn new(current_module: &'a str) -> Self {
3867 Self {
3868 current_module,
3869 var_usages: None,
3870 var_as_any: false,
3871 }
3872 }
3873
3874 /// Records the how type variables are used in order to correctly print
3875 /// the type
3876 pub fn with_var_usages(mut self, var_usages: &'a HashMap<u64, u64>) -> Self {
3877 self.var_usages = Some(var_usages);
3878 self
3879 }
3880
3881 /// Print any type variable as `any()` rather than a generic type (like `A`,
3882 /// `B`, ...).
3883 fn var_as_any(mut self) -> Self {
3884 self.var_as_any = true;
3885 self
3886 }
3887
3888 pub fn type_<Output>(&self, builder: &mut impl ErlangBuilder<Output>, type_: &Type) {
3889 match type_ {
3890 Type::Var { type_ } => self.type_variable(builder, &type_.borrow()),
3891 Type::Named {
3892 name,
3893 module,
3894 arguments,
3895 ..
3896 } if is_prelude_module(module) => self.prelude_type(builder, name, arguments),
3897 Type::Named {
3898 name,
3899 module,
3900 arguments,
3901 ..
3902 } => self.named_type(builder, module.into(), name, arguments),
3903 Type::Fn { arguments, return_ } => {
3904 let function_type = builder.start_function_type();
3905 for argument in arguments {
3906 self.type_(builder, argument);
3907 }
3908 let function_type = builder.end_function_type_arguments(function_type);
3909 self.type_(builder, return_);
3910 builder.end_function_type(function_type);
3911 }
3912 Type::Tuple { elements } => {
3913 let tuple = builder.start_tuple_type();
3914 for element in elements {
3915 self.type_(builder, element);
3916 }
3917 builder.end_tuple_type(tuple);
3918 }
3919 }
3920 }
3921
3922 fn type_variable<Output>(&self, builder: &mut impl ErlangBuilder<Output>, type_: &TypeVar) {
3923 match type_ {
3924 TypeVar::Link { type_ } => self.type_(builder, type_),
3925 TypeVar::Generic { id, .. } | TypeVar::Unbound { id, .. } => {
3926 if self.var_as_any || self.type_variable_is_used_exactly_once(*id) {
3927 let any = builder.start_named_type("any");
3928 builder.end_named_type(any);
3929 } else {
3930 builder.type_variable(&id_to_type_var_str(*id));
3931 }
3932 }
3933 }
3934 }
3935
3936 /// Given a type variable id, this returns true if the `var_usages` field
3937 /// is set and the variable is used exactly once.
3938 ///
3939 #[must_use]
3940 fn type_variable_is_used_exactly_once(&self, id: u64) -> bool {
3941 match self.var_usages {
3942 Some(usages) => usages.get(&id) == Some(&1),
3943 None => false,
3944 }
3945 }
3946
3947 fn prelude_type<Output>(
3948 &self,
3949 builder: &mut impl ErlangBuilder<Output>,
3950 name: &str,
3951 arguments: &[Arc<Type>],
3952 ) {
3953 match name {
3954 "Nil" => builder.literal_atom_type("nil"),
3955 "Int" | "UtfCodepoint" => {
3956 let integer = builder.start_named_type("integer");
3957 builder.end_named_type(integer);
3958 }
3959 "String" => {
3960 let string = builder.start_named_type("binary");
3961 builder.end_named_type(string);
3962 }
3963 "Bool" => {
3964 let boolean = builder.start_named_type("boolean");
3965 builder.end_named_type(boolean);
3966 }
3967 "Float" => {
3968 let float = builder.start_named_type("float");
3969 builder.end_named_type(float);
3970 }
3971 "BitArray" => {
3972 let bitstring = builder.start_named_type("bitstring");
3973 builder.end_named_type(bitstring);
3974 }
3975 "List" => {
3976 let list = builder.start_named_type("list");
3977 let list_item = arguments
3978 .first()
3979 .expect("prelude type list with no argument");
3980 self.type_(builder, list_item);
3981 builder.end_named_type(list);
3982 }
3983 "Result" => {
3984 let [ok_type, error_type] = arguments else {
3985 panic!("result type with no ok and err types")
3986 };
3987
3988 let result = builder.start_union_type();
3989
3990 let ok = builder.start_tuple_type();
3991 builder.literal_atom_type("ok");
3992 self.type_(builder, ok_type);
3993 builder.end_tuple_type(ok);
3994
3995 let error = builder.start_tuple_type();
3996 builder.literal_atom_type("error");
3997 self.type_(builder, error_type);
3998 builder.end_tuple_type(error);
3999
4000 builder.end_union_type(result);
4001 }
4002
4003 // Getting here should mean we either forgot a built-in type or there is a
4004 // compiler error
4005 name => panic!("{name} is not a prelude type."),
4006 }
4007 }
4008
4009 fn named_type<Output>(
4010 &self,
4011 builder: &mut impl ErlangBuilder<Output>,
4012 module: EcoString,
4013 name: &str,
4014 arguments: &[Arc<Type>],
4015 ) {
4016 let name = erl_safe_type_name(to_snake_case(name));
4017 if self.current_module == module {
4018 let type_ = builder.start_named_type(&name);
4019 for argument in arguments {
4020 self.type_(builder, argument);
4021 }
4022 builder.end_named_type(type_);
4023 } else {
4024 let type_ = builder.start_remote_named_type(ErlangModuleName::new(&module), &name);
4025 for argument in arguments {
4026 self.type_(builder, argument);
4027 }
4028 builder.end_remote_named_type(type_);
4029 };
4030 }
4031}
4032
4033fn find_private_functions_referenced_in_importable_constants(
4034 module: &TypedModule,
4035) -> im::HashSet<EcoString> {
4036 let mut overridden_publicity = im::HashSet::new();
4037
4038 for constant in &module.definitions.constants {
4039 if constant.publicity.is_importable() {
4040 find_referenced_private_functions(&constant.value, &mut overridden_publicity);
4041 }
4042 }
4043 overridden_publicity
4044}
4045
4046fn find_referenced_private_functions(
4047 constant: &TypedConstant,
4048 already_found: &mut im::HashSet<EcoString>,
4049) {
4050 match constant {
4051 Constant::Todo { .. } => panic!("todo constants should not reach code generation"),
4052 Constant::Invalid { .. } => panic!("invalid constants should not reach code generation"),
4053 Constant::RecordUpdate { .. } => {
4054 panic!("record updates should not reach code generation")
4055 }
4056
4057 Constant::Int { .. }
4058 | Constant::Float { .. }
4059 | Constant::String { .. }
4060 | Constant::BitArray { .. } => (),
4061
4062 TypedConstant::Var {
4063 name, constructor, ..
4064 } => {
4065 if let Some(ValueConstructor { type_, .. }) = constructor.as_deref()
4066 && let Type::Fn { .. } = **type_
4067 {
4068 let _ = already_found.insert(name.clone());
4069 }
4070 }
4071
4072 TypedConstant::Record { arguments, .. } => arguments
4073 .iter()
4074 .flatten()
4075 .for_each(|argument| find_referenced_private_functions(&argument.value, already_found)),
4076
4077 TypedConstant::StringConcatenation { left, right, .. } => {
4078 find_referenced_private_functions(left, already_found);
4079 find_referenced_private_functions(right, already_found);
4080 }
4081
4082 Constant::Tuple { elements, .. } => elements
4083 .iter()
4084 .for_each(|element| find_referenced_private_functions(element, already_found)),
4085
4086 Constant::List { elements, tail, .. } => {
4087 elements
4088 .iter()
4089 .for_each(|element| find_referenced_private_functions(element, already_found));
4090
4091 if let Some(tail) = tail {
4092 find_referenced_private_functions(tail, already_found);
4093 }
4094 }
4095 }
4096}