Fork of daniellemaywood.uk/gleam — Wasm codegen work
80 kB
2256 lines
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2018 The Gleam contributors
3
4mod imports;
5pub mod name;
6
7#[cfg(test)]
8mod tests;
9
10use crate::{
11 GLEAM_CORE_PACKAGE_NAME, STDLIB_PACKAGE_NAME,
12 ast::{
13 self, Arg, BitArrayOption, CustomType, DefinitionLocation, Function, GroupedDefinitions,
14 Import, ModuleConstant, Publicity, RecordConstructor, RecordConstructorArg, SrcSpan,
15 Statement, TypeAlias, TypeAst, TypeAstConstructor, TypeAstFn, TypeAstHole, TypeAstTuple,
16 TypeAstVar, TypedCustomType, TypedDefinitions, TypedExpr, TypedFunction, TypedImport,
17 TypedModule, TypedModuleConstant, TypedTypeAlias, UntypedArg, UntypedCustomType,
18 UntypedFunction, UntypedImport, UntypedModule, UntypedModuleConstant, UntypedStatement,
19 UntypedTypeAlias,
20 },
21 build::{Origin, Outcome, Target},
22 call_graph::{CallGraphNode, into_dependency_order},
23 config::PackageConfig,
24 dep_tree,
25 inline::{self, InlinableFunction},
26 line_numbers::LineNumbers,
27 parse::SpannedString,
28 reference::{EntityKind, ReferenceKind},
29 type_::{
30 self, AccessorsMap, Deprecation, FieldMap, ModuleInterface, Opaque, PatternConstructor,
31 RecordAccessor, References, Type, TypeAliasConstructor, TypeConstructor,
32 TypeValueConstructor, TypeValueConstructorField, TypeVariantConstructors, ValueConstructor,
33 ValueConstructorVariant, Warning,
34 environment::*,
35 error::{Error, FeatureKind, MissingAnnotation, Named, Problems, convert_unify_error},
36 expression::{ExprTyper, FunctionDefinition, Implementations, Purity},
37 fields::FieldMapBuilder,
38 hydrator::Hydrator,
39 prelude::*,
40 },
41 uid::UniqueIdGenerator,
42 warning::TypeWarningEmitter,
43};
44use camino::Utf8PathBuf;
45use ecow::{EcoString, eco_format};
46use hexpm::version::Version;
47use itertools::Itertools;
48use name::{check_argument_names, check_name_case};
49use regex::Regex;
50use std::{
51 collections::{HashMap, HashSet},
52 ops::Deref,
53 sync::{Arc, OnceLock},
54};
55use vec1::Vec1;
56
57use self::imports::Importer;
58
59static EXTERNAL_MODULE_PATTERN: OnceLock<Regex> = OnceLock::new();
60static EXTERNAL_FUNCTION_PATTERN: OnceLock<Regex> = OnceLock::new();
61
62#[derive(Debug, Clone, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
63pub enum Inferred<T> {
64 Known(T),
65 #[default]
66 Unknown,
67}
68
69impl<T> Inferred<T> {
70 pub fn expect(self, message: &str) -> T {
71 match self {
72 Inferred::Known(value) => Some(value),
73 Inferred::Unknown => None,
74 }
75 .expect(message)
76 }
77
78 pub fn expect_ref(&self, message: &str) -> &T {
79 match self {
80 Inferred::Known(value) => Some(value),
81 Inferred::Unknown => None,
82 }
83 .expect(message)
84 }
85}
86
87impl Inferred<PatternConstructor> {
88 pub fn definition_location(&self) -> Option<DefinitionLocation> {
89 match self {
90 Inferred::Known(value) => value.definition_location(),
91 Inferred::Unknown => None,
92 }
93 }
94
95 pub fn get_documentation(&self) -> Option<&str> {
96 match self {
97 Inferred::Known(value) => value.get_documentation(),
98 Inferred::Unknown => None,
99 }
100 }
101
102 pub fn field_map(&self) -> Option<&FieldMap> {
103 match self {
104 Inferred::Known(value) => value.field_map.as_ref(),
105 Inferred::Unknown => None,
106 }
107 }
108}
109
110/// How the compiler should treat target support.
111#[derive(Clone, Copy, Debug, PartialEq, Eq)]
112pub enum TargetSupport {
113 /// Target support is enfored, meaning if a function is found to not have an implementation for
114 /// the current target then an error is emitted and compilation halts.
115 ///
116 /// This is used when compiling the root package, with the exception of when using
117 /// `gleam run --module $module` to run a module from a dependency package, in which case we do
118 /// not want to error as the root package code isn't going to be run.
119 Enforced,
120 /// Target support is enfored, meaning if a function is found to not have an implementation for
121 /// the current target it will continue onwards and not generate any code for this function.
122 ///
123 /// This is used when compiling dependencies.
124 NotEnforced,
125}
126
127impl TargetSupport {
128 /// Returns `true` if the target support is [`Enforced`].
129 ///
130 /// [`Enforced`]: TargetSupport::Enforced
131 #[must_use]
132 pub fn is_enforced(&self) -> bool {
133 match self {
134 Self::Enforced => true,
135 Self::NotEnforced => false,
136 }
137 }
138}
139
140impl<T> From<Error> for Outcome<T, Vec1<Error>> {
141 fn from(error: Error) -> Self {
142 Outcome::TotalFailure(Vec1::new(error))
143 }
144}
145
146/// This struct is used to take the data required for analysis. It is used to
147/// construct the private ModuleAnalyzer which has this data plus any
148/// internal state.
149///
150#[derive(Debug)]
151pub struct ModuleAnalyzerConstructor<'a, A> {
152 pub target: Target,
153 pub ids: &'a UniqueIdGenerator,
154 pub origin: Origin,
155 pub importable_modules: &'a im::HashMap<EcoString, ModuleInterface>,
156 pub warnings: &'a TypeWarningEmitter,
157 pub direct_dependencies: &'a HashMap<EcoString, A>,
158 pub dev_dependencies: &'a HashSet<EcoString>,
159 pub target_support: TargetSupport,
160 pub package_config: &'a PackageConfig,
161}
162
163impl<A> ModuleAnalyzerConstructor<'_, A> {
164 /// Crawl the AST, annotating each node with the inferred type or
165 /// returning an error.
166 ///
167 pub fn infer_module(
168 self,
169 module: UntypedModule,
170 line_numbers: LineNumbers,
171 src_path: Utf8PathBuf,
172 ) -> Outcome<TypedModule, Vec1<Error>> {
173 ModuleAnalyzer {
174 target: self.target,
175 ids: self.ids,
176 origin: self.origin,
177 importable_modules: self.importable_modules,
178 warnings: self.warnings,
179 direct_dependencies: self.direct_dependencies,
180 dev_dependencies: self.dev_dependencies,
181 target_support: self.target_support,
182 package_config: self.package_config,
183 line_numbers,
184 src_path,
185 problems: Problems::new(),
186 value_names: HashMap::with_capacity(module.definitions.len()),
187 hydrators: HashMap::with_capacity(module.definitions.len()),
188 module_name: module.name.clone(),
189 inline_functions: HashMap::new(),
190 minimum_required_version: Version::new(0, 1, 0),
191 }
192 .infer_module(module)
193 }
194}
195
196struct ModuleAnalyzer<'a, A> {
197 target: Target,
198 ids: &'a UniqueIdGenerator,
199 origin: Origin,
200 importable_modules: &'a im::HashMap<EcoString, ModuleInterface>,
201 warnings: &'a TypeWarningEmitter,
202 direct_dependencies: &'a HashMap<EcoString, A>,
203 dev_dependencies: &'a HashSet<EcoString>,
204 target_support: TargetSupport,
205 package_config: &'a PackageConfig,
206 line_numbers: LineNumbers,
207 src_path: Utf8PathBuf,
208 problems: Problems,
209 value_names: HashMap<EcoString, SrcSpan>,
210 hydrators: HashMap<EcoString, Hydrator>,
211 module_name: EcoString,
212
213 inline_functions: HashMap<EcoString, InlinableFunction>,
214
215 /// The minimum Gleam version required to compile the analysed module.
216 minimum_required_version: Version,
217}
218
219impl<'a, A> ModuleAnalyzer<'a, A> {
220 pub fn infer_module(mut self, mut module: UntypedModule) -> Outcome<TypedModule, Vec1<Error>> {
221 if let Err(error) = validate_module_name(&self.module_name) {
222 return self.all_errors(error);
223 }
224
225 let documentation = std::mem::take(&mut module.documentation);
226 let env = EnvironmentArguments {
227 ids: self.ids.clone(),
228 current_package: self.package_config.name.clone(),
229 gleam_version: self
230 .package_config
231 .gleam_version
232 .clone()
233 .map(|version| version.into()),
234 current_module: self.module_name.clone(),
235 target: self.target,
236 importable_modules: self.importable_modules,
237 target_support: self.target_support,
238 current_origin: self.origin,
239 dev_dependencies: self.dev_dependencies,
240 }
241 .build();
242
243 let definitions = GroupedDefinitions::new(module.into_iter_definitions(self.target));
244
245 // Register any modules, types, and values being imported
246 // We process imports first so that anything imported can be referenced
247 // anywhere in the module.
248 let mut env = Importer::run(self.origin, env, &definitions.imports, &mut self.problems);
249
250 // Register types so they can be used in constructors and functions
251 // earlier in the module.
252 for type_ in &definitions.custom_types {
253 if let Err(error) = self.register_types_from_custom_type(type_, &mut env) {
254 return self.all_errors(error);
255 }
256 }
257
258 let sorted_aliases = match sorted_type_aliases(&definitions.type_aliases) {
259 Ok(sorted_aliases) => sorted_aliases,
260 Err(error) => return self.all_errors(error),
261 };
262 for type_alias in sorted_aliases {
263 self.register_type_alias(type_alias, &mut env);
264 }
265
266 for function in &definitions.functions {
267 self.register_value_from_function(function, &mut env);
268 }
269
270 // Infer the types of each statement in the module
271 let typed_imports = definitions
272 .imports
273 .into_iter()
274 .filter_map(|import| self.analyse_import(import, &env))
275 .collect_vec();
276
277 let typed_custom_types = definitions
278 .custom_types
279 .into_iter()
280 .filter_map(|custom_type| self.analyse_custom_type(custom_type, &mut env))
281 .collect_vec();
282
283 let typed_type_aliases = definitions
284 .type_aliases
285 .into_iter()
286 .map(|type_alias| analyse_type_alias(type_alias, &mut env))
287 .collect_vec();
288
289 // Sort functions and constants into dependency order for inference.
290 // Definitions that do not depend on other definitions are inferred
291 // first, then ones that depend on those, etc.
292 let mut typed_functions = Vec::with_capacity(definitions.functions.len());
293 let mut typed_constants = Vec::with_capacity(definitions.constants.len());
294 let definition_groups =
295 match into_dependency_order(definitions.functions, definitions.constants) {
296 Ok(definition_groups) => definition_groups,
297 Err(error) => return self.all_errors(error),
298 };
299
300 let mut working_constants = vec![];
301 let mut working_functions = vec![];
302 for group in definition_groups {
303 // A group may have multiple functions and constants that depend on
304 // each other by mutual reference.
305 for definition in group {
306 match definition {
307 CallGraphNode::Function(function) => {
308 working_functions.push(self.infer_function(function, &mut env));
309 }
310 CallGraphNode::ModuleConstant(constant) => {
311 working_constants.push(self.infer_module_constant(constant, &mut env));
312 }
313 }
314 }
315
316 // Now that the entire group has been inferred, generalise their types.
317 for inferred_constant in working_constants.drain(..) {
318 typed_constants.push(generalise_module_constant(
319 inferred_constant,
320 &mut env,
321 &self.module_name,
322 ));
323 }
324 for inferred_function in working_functions.drain(..) {
325 typed_functions.push(generalise_function(
326 inferred_function,
327 &mut env,
328 &self.module_name,
329 ));
330 }
331 }
332
333 let typed_definitions = TypedDefinitions {
334 imports: typed_imports,
335 constants: typed_constants,
336 custom_types: typed_custom_types,
337 type_aliases: typed_type_aliases,
338 functions: typed_functions,
339 };
340
341 // Generate warnings for unused items
342 let unused_definition_positions = env.handle_unused(&mut self.problems);
343
344 // Remove imported types and values to create the public interface
345 // Private types and values are retained so they can be used in the language
346 // server, but are filtered out when type checking to prevent using private
347 // items.
348 env.module_types
349 .retain(|_, info| info.module == self.module_name);
350
351 // Ensure no exported values have private types in their type signature
352 for value in env.module_values.values() {
353 self.check_for_type_leaks(value);
354 }
355
356 // Resolve deferred type variable aliases now that all unification is
357 // done and link chains are stable.
358 env.resolve_deferred_type_variable_aliases();
359
360 let Environment {
361 module_types: types,
362 module_types_constructors: types_constructors,
363 module_values: values,
364 accessors,
365 names: type_names,
366 module_type_aliases: type_aliases,
367 echo_found,
368 ..
369 } = env;
370
371 let is_internal = self
372 .package_config
373 .is_internal_module(self.module_name.as_str());
374
375 // We sort warnings and errors to ensure they are emitted in a
376 // deterministic order, making them easier to test and debug, and to
377 // make the output predictable.
378 self.problems.sort();
379
380 let warnings = self.problems.take_warnings();
381 for warning in &warnings {
382 // TODO: remove this clone
383 self.warnings.emit(warning.clone());
384 }
385
386 let module = ast::Module {
387 documentation: documentation.clone(),
388 name: self.module_name.clone(),
389 definitions: typed_definitions,
390 names: type_names,
391 unused_definition_positions,
392 type_info: ModuleInterface {
393 name: self.module_name,
394 types,
395 types_value_constructors: types_constructors,
396 values,
397 accessors,
398 origin: self.origin,
399 package: self.package_config.name.clone(),
400 is_internal,
401 line_numbers: self.line_numbers,
402 src_path: self.src_path,
403 warnings,
404 minimum_required_version: self.minimum_required_version,
405 type_aliases,
406 documentation,
407 contains_echo: echo_found,
408 references: References {
409 imported_modules: env
410 .imported_modules
411 .values()
412 .map(|(_location, module)| module.name.clone())
413 .collect(),
414 value_references: env.references.value_references,
415 type_references: env.references.type_references,
416 module_references: env.references.module_references,
417 label_references: env.references.label_references,
418 label_definitions: env.references.label_definitions,
419 },
420 inline_functions: self.inline_functions,
421 },
422 };
423
424 match Vec1::try_from_vec(self.problems.take_errors()) {
425 Err(_) => Outcome::Ok(module),
426 Ok(errors) => Outcome::PartialFailure(module, errors),
427 }
428 }
429
430 fn all_errors<T>(&mut self, error: Error) -> Outcome<T, Vec1<Error>> {
431 Outcome::TotalFailure(Vec1::from_vec_push(self.problems.take_errors(), error))
432 }
433
434 fn infer_module_constant(
435 &mut self,
436 c: UntypedModuleConstant,
437 environment: &mut Environment<'_>,
438 ) -> TypedModuleConstant {
439 let ModuleConstant {
440 documentation: doc,
441 location,
442 name,
443 name_location,
444 annotation,
445 publicity,
446 value,
447 deprecation,
448 ..
449 } = c;
450 self.check_name_case(name_location, &name, Named::Constant);
451 // If the constant's name matches an unqualified import, emit a warning:
452 self.check_shadow_import(&name, c.location, environment);
453
454 environment.references.begin_constant();
455
456 let definition = FunctionDefinition {
457 has_body: true,
458 has_erlang_external: false,
459 has_javascript_external: false,
460 has_wasm_external: false,
461 };
462 let mut expr_typer = ExprTyper::new(environment, definition, &mut self.problems);
463 let typed_expr = expr_typer.infer_const(&annotation, *value);
464 let type_ = typed_expr.type_();
465 let implementations = expr_typer.implementations;
466
467 let minimum_required_version = expr_typer.minimum_required_version;
468 if minimum_required_version > self.minimum_required_version {
469 self.minimum_required_version = minimum_required_version;
470 }
471
472 match publicity {
473 Publicity::Private
474 | Publicity::Public
475 | Publicity::Internal {
476 attribute_location: None,
477 } => (),
478
479 Publicity::Internal {
480 attribute_location: Some(location),
481 } => self.track_feature_usage(FeatureKind::InternalAnnotation, location),
482 }
483
484 let variant = ValueConstructor {
485 publicity,
486 deprecation: deprecation.clone(),
487 variant: ValueConstructorVariant::ModuleConstant {
488 documentation: doc.as_ref().map(|(_, doc)| doc.clone()),
489 location,
490 literal: typed_expr.clone(),
491 module: self.module_name.clone(),
492 name: name.clone(),
493 implementations,
494 },
495 type_: type_.clone(),
496 };
497
498 environment.insert_variable(
499 name.clone(),
500 variant.variant.clone(),
501 type_.clone(),
502 publicity,
503 Deprecation::NotDeprecated,
504 );
505 environment.insert_module_value(name.clone(), variant);
506
507 environment
508 .references
509 .register_constant(name.clone(), location, publicity);
510
511 environment.references.register_value_reference(
512 environment.current_module.clone(),
513 name.clone(),
514 &name,
515 name_location,
516 ReferenceKind::Definition,
517 );
518
519 ModuleConstant {
520 documentation: doc,
521 location,
522 name,
523 name_location,
524 annotation,
525 publicity,
526 value: Box::new(typed_expr),
527 type_,
528 deprecation,
529 implementations,
530 }
531 }
532
533 // TODO: Extract this into a class of its own! Or perhaps it just wants some
534 // helper methods extracted. There's a whole bunch of state in this one
535 // function, and it does a handful of things.
536 fn infer_function(
537 &mut self,
538 f: UntypedFunction,
539 environment: &mut Environment<'_>,
540 ) -> TypedFunction {
541 let Function {
542 documentation: doc,
543 location,
544 name,
545 publicity,
546 arguments,
547 body,
548 body_start,
549 return_annotation,
550 end_position: end_location,
551 deprecation,
552 external_erlang,
553 external_javascript,
554 external_wasm,
555 return_type: (),
556 implementations: _,
557 purity: _,
558 } = f;
559 let (name_location, name) = name.expect("Function in a definition must be named");
560 let target = environment.target;
561 let body_location = body
562 .last()
563 .map(|statement| statement.location())
564 .unwrap_or(location);
565 let preregistered_fn = environment
566 .get_variable(&name)
567 .expect("Could not find preregistered type for function");
568 let field_map = preregistered_fn.field_map().cloned();
569 let preregistered_type = preregistered_fn.type_.clone();
570 let (prereg_arguments_types, prereg_return_type) = preregistered_type
571 .fn_types()
572 .expect("Preregistered type for fn was not a fn");
573
574 // Ensure that folks are not writing inline JavaScript expressions as
575 // the implementation for JS externals.
576 self.assert_valid_javascript_external(&name, external_javascript.as_ref(), location);
577
578 // Find the external implementation for the current target, if one has been given.
579 let external = target_function_implementation(
580 target,
581 &external_erlang,
582 &external_javascript,
583 &external_wasm,
584 );
585
586 // The function must have at least one implementation somewhere.
587 let has_implementation = self.ensure_function_has_an_implementation(
588 &body,
589 &external_erlang,
590 &external_javascript,
591 &external_wasm,
592 location,
593 );
594
595 if external.is_some() {
596 // There was an external implementation, so type annotations are
597 // mandatory as the Gleam implementation may be absent, and because we
598 // think you should always specify types for external functions for
599 // clarity + to avoid accidental mistakes.
600 self.ensure_annotations_present(&arguments, return_annotation.as_ref(), location);
601 }
602
603 let has_body = !body.is_empty();
604 let definition = FunctionDefinition {
605 has_body,
606 has_erlang_external: external_erlang.is_some(),
607 has_javascript_external: external_javascript.is_some(),
608 has_wasm_external: external_wasm.is_some(),
609 };
610
611 // We have already registered the function in the `register_value_from_function`
612 // method, but here we must set this as the current function again, so that anything
613 // we reference in the body of it can be tracked properly in the call graph.
614 environment.references.set_current_node(name.clone());
615
616 let mut typed_arguments = Vec::with_capacity(arguments.len());
617
618 // Infer the type using the preregistered args + return types as a starting point
619 let result = environment.in_new_scope(&mut self.problems, |environment, problems| {
620 for (argument, type_) in arguments.into_iter().zip(&prereg_arguments_types) {
621 let argument = argument.set_type(type_.clone());
622
623 // We track which arguments are discarded so we can provide nice
624 // error messages when someone
625 match &argument.names {
626 ast::ArgNames::Named { .. } | ast::ArgNames::NamedLabelled { .. } => (),
627 ast::ArgNames::Discard { name, location }
628 | ast::ArgNames::LabelledDiscard {
629 name,
630 name_location: location,
631 ..
632 } => {
633 let _ = environment.discarded_names.insert(name.clone(), *location);
634 }
635 }
636
637 typed_arguments.push(argument);
638 }
639
640 let mut expr_typer = ExprTyper::new(environment, definition, problems);
641 expr_typer.hydrator = self
642 .hydrators
643 .remove(&name)
644 .expect("Could not find hydrator for fn");
645
646 let (arguments, body) = expr_typer.infer_fn_with_known_types(
647 Some(name.clone()),
648 typed_arguments.clone(),
649 body,
650 Some(prereg_return_type.clone()),
651 )?;
652 let arguments_types = arguments.iter().map(|a| a.type_.clone()).collect();
653 let return_type = body
654 .last()
655 .map_or(prereg_return_type.clone(), |last| last.type_());
656
657 // `dict.do_fold` is a bit special: since it belongs to the stdlib
658 // it is considered pure by default.
659 // However, since it ends up calling its function argument its
660 // purity should actually be `Impure`. We need to special case it
661 // and set the value ourselves.
662 //
663 // You might wonder why `do_fold` needs this but other similar
664 // functions like `list.each` don't need this special handling.
665 // The key difference is `list.each` calls the higher order function
666 // in its gleam body:
667 //
668 // ```gleam
669 // fn each(list, fun) {
670 // case list {
671 // [] -> Nil
672 // [first, ..rest] -> {
673 // fun(first)
674 // // ^^^ Here we're calling `fun`. It's happening in Gleam so
675 // // the compiler can see this and understand that `each`
676 // // is impure.
677 // // You might argue the purity actually depends on the
678 // // purity of `fun` itself. That's true! But it's a
679 // // separate known problem. For the time being we always
680 // // assume a function argument is impure.
681 // each(rest, fun)
682 // }
683 // }
684 // }
685 // ```
686 //
687 // But since `do_fold` is an external the compiler can't know what
688 // is going on with its function argument and keeps thinking it must
689 // be pure
690 //
691 // ```gleam
692 // @external(erlang, "", "")
693 // fn do_fold(dict: Dict(k, v), fun: fn(k, v) -> a) -> Nil
694 // ```
695 //
696 let purity = if expr_typer.environment.current_package == STDLIB_PACKAGE_NAME
697 && expr_typer.environment.current_module == "gleam/dict"
698 && name == "do_fold"
699 {
700 Purity::Impure
701 } else {
702 expr_typer.purity
703 };
704
705 let type_ = fn_(arguments_types, return_type);
706 Ok((
707 type_,
708 body,
709 expr_typer.implementations,
710 expr_typer.minimum_required_version,
711 purity,
712 ))
713 });
714
715 // If we could not successfully infer the type etc information of the
716 // function then register the error and continue anaylsis using the best
717 // information that we have, so we can still learn about the rest of the
718 // module.
719 let (type_, body, implementations, required_version, purity) = match result {
720 Ok((type_, body, implementations, required_version, purity)) => {
721 (type_, body, implementations, required_version, purity)
722 }
723 Err(error) => {
724 self.problems.error(error);
725 let type_ = preregistered_type.clone();
726 let body = vec![Statement::Expression(TypedExpr::Invalid {
727 type_: prereg_return_type.clone(),
728 location: SrcSpan {
729 start: body_location.end,
730 end: body_location.end,
731 },
732 extra_information: None,
733 })];
734 let implementations = Implementations::supporting_all();
735 (
736 type_,
737 body,
738 implementations,
739 Version::new(1, 0, 0),
740 Purity::Impure,
741 )
742 }
743 };
744
745 if required_version > self.minimum_required_version {
746 self.minimum_required_version = required_version;
747 }
748
749 match publicity {
750 Publicity::Private
751 | Publicity::Public
752 | Publicity::Internal {
753 attribute_location: None,
754 } => (),
755
756 Publicity::Internal {
757 attribute_location: Some(location),
758 } => self.track_feature_usage(FeatureKind::InternalAnnotation, location),
759 }
760
761 if let Some((module, _, location)) = &external_javascript
762 && module.contains('@')
763 {
764 self.track_feature_usage(FeatureKind::AtInJavascriptModules, *location);
765 }
766
767 // Assert that the inferred type matches the type of any recursive call
768 if let Err(error) = unify(preregistered_type.clone(), type_) {
769 self.problems.error(convert_unify_error(error, location));
770 }
771
772 // Ensure that the current target has an implementation for the function.
773 // This is done at the expression level while inferring the function body, but we do it again
774 // here as externally implemented functions may not have a Gleam body.
775 //
776 // We don't emit this error if there is no implementation, as this would
777 // have already emitted an error above.
778 if has_implementation
779 && publicity.is_importable()
780 && environment.target_support.is_enforced()
781 && !implementations.supports(target)
782 // We don't emit this error if there is a body
783 // since this would be caught at the statement level
784 && !has_body
785 {
786 self.problems.error(Error::UnsupportedPublicFunctionTarget {
787 name: name.clone(),
788 target,
789 location,
790 });
791 }
792
793 let variant = ValueConstructorVariant::ModuleFn {
794 documentation: doc.as_ref().map(|(_, doc)| doc.clone()),
795 name: name.clone(),
796 external_erlang: external_erlang
797 .as_ref()
798 .map(|(m, f, _)| (m.clone(), f.clone())),
799 external_javascript: external_javascript
800 .as_ref()
801 .map(|(m, f, _)| (m.clone(), f.clone())),
802 external_wasm: external_wasm
803 .as_ref()
804 .map(|(m, f, _)| (m.clone(), f.clone())),
805 field_map,
806 module: environment.current_module.clone(),
807 arity: typed_arguments.len(),
808 location,
809 implementations,
810 purity,
811 };
812
813 environment.insert_variable(
814 name.clone(),
815 variant,
816 preregistered_type.clone(),
817 publicity,
818 deprecation.clone(),
819 );
820
821 environment.references.register_value_reference(
822 environment.current_module.clone(),
823 name.clone(),
824 &name,
825 name_location,
826 ReferenceKind::Definition,
827 );
828
829 let function = Function {
830 documentation: doc,
831 location,
832 name: Some((name_location, name.clone())),
833 publicity,
834 deprecation,
835 arguments: typed_arguments,
836 body_start,
837 end_position: end_location,
838 return_annotation,
839 return_type: preregistered_type
840 .return_type()
841 .expect("Could not find return type for fn"),
842 body,
843 external_erlang,
844 external_javascript,
845 external_wasm,
846 implementations,
847 purity,
848 };
849
850 if let Some(inline_function) = inline::function_to_inlinable(
851 &environment.current_package,
852 &environment.current_module,
853 &function,
854 ) {
855 _ = self.inline_functions.insert(name, inline_function);
856 }
857
858 function
859 }
860
861 fn assert_valid_javascript_external(
862 &mut self,
863 function_name: &EcoString,
864 external_javascript: Option<&(EcoString, EcoString, SrcSpan)>,
865 location: SrcSpan,
866 ) {
867 use regex::Regex;
868
869 let (module, function) = match external_javascript {
870 None => return,
871 Some((module, function, _location)) => (module, function),
872 };
873 if !EXTERNAL_MODULE_PATTERN
874 .get_or_init(|| Regex::new("^[@a-zA-Z0-9\\./:_-]+$").expect("regex"))
875 .is_match(module)
876 {
877 self.problems.error(Error::InvalidExternalJavascriptModule {
878 location,
879 module: module.clone(),
880 name: function_name.clone(),
881 });
882 }
883 if !EXTERNAL_FUNCTION_PATTERN
884 .get_or_init(|| Regex::new("^[a-zA-Z_][a-zA-Z0-9_]*$").expect("regex"))
885 .is_match(function)
886 {
887 self.problems
888 .error(Error::InvalidExternalJavascriptFunction {
889 location,
890 function: function.clone(),
891 name: function_name.clone(),
892 });
893 }
894 }
895
896 fn ensure_annotations_present(
897 &mut self,
898 arguments: &[UntypedArg],
899 return_annotation: Option<&TypeAst>,
900 location: SrcSpan,
901 ) {
902 for arg in arguments {
903 if arg.annotation.is_none() {
904 self.problems.error(Error::ExternalMissingAnnotation {
905 location: arg.location,
906 kind: MissingAnnotation::Parameter,
907 });
908 }
909 }
910 if return_annotation.is_none() {
911 self.problems.error(Error::ExternalMissingAnnotation {
912 location,
913 kind: MissingAnnotation::Return,
914 });
915 }
916 }
917
918 fn ensure_function_has_an_implementation(
919 &mut self,
920 body: &[UntypedStatement],
921 external_erlang: &Option<(EcoString, EcoString, SrcSpan)>,
922 external_javascript: &Option<(EcoString, EcoString, SrcSpan)>,
923 external_wasm: &Option<(EcoString, EcoString, SrcSpan)>,
924 location: SrcSpan,
925 ) -> bool {
926 let has_external = external_erlang.is_some()
927 || external_javascript.is_some()
928 || external_wasm.is_some();
929 if !has_external && body.is_empty() {
930 self.problems.error(Error::NoImplementation { location });
931 false
932 } else {
933 true
934 }
935 }
936
937 fn analyse_import(
938 &mut self,
939 i: UntypedImport,
940 environment: &Environment<'_>,
941 ) -> Option<TypedImport> {
942 let Import {
943 documentation,
944 location,
945 module_location,
946 module,
947 as_name,
948 unqualified_values,
949 unqualified_types,
950 ..
951 } = i;
952 // Find imported module
953 let Some(module_info) = environment.importable_modules.get(&module) else {
954 // Here the module being imported doesn't exist. We don't emit an
955 // error here as the `Importer` that was run earlier will have
956 // already emitted an error for this.
957 return None;
958 };
959
960 // Modules should belong to a package that is a direct dependency of the
961 // current package to be imported.
962 // Upgrade this to an error in future.
963 if module_info.package != GLEAM_CORE_PACKAGE_NAME
964 && module_info.package != self.package_config.name
965 && !self.direct_dependencies.contains_key(&module_info.package)
966 {
967 self.warnings.emit(Warning::TransitiveDependencyImported {
968 location,
969 module: module_info.name.clone(),
970 package: module_info.package.clone(),
971 });
972 }
973
974 Some(Import {
975 documentation,
976 location,
977 module_location,
978 module,
979 as_name,
980 unqualified_values,
981 unqualified_types,
982 package: module_info.package.clone(),
983 })
984 }
985
986 fn analyse_custom_type(
987 &mut self,
988 t: UntypedCustomType,
989 environment: &mut Environment<'_>,
990 ) -> Option<TypedCustomType> {
991 match self.do_analyse_custom_type(t, environment) {
992 Ok(custom_type) => Some(custom_type),
993 Err(error) => {
994 self.problems.error(error);
995 None
996 }
997 }
998 }
999
1000 // TODO: split this into a new class.
1001 fn do_analyse_custom_type(
1002 &mut self,
1003 t: UntypedCustomType,
1004 environment: &mut Environment<'_>,
1005 ) -> Result<TypedCustomType, Error> {
1006 self.register_values_from_custom_type(
1007 &t,
1008 environment,
1009 &t.parameters.iter().map(|(_, name)| name).collect_vec(),
1010 )?;
1011
1012 let CustomType {
1013 documentation: doc,
1014 location,
1015 end_position,
1016 publicity,
1017 opaque,
1018 name,
1019 name_location,
1020 parameters,
1021 constructors,
1022 deprecation,
1023 external_erlang,
1024 external_javascript,
1025 ..
1026 } = t;
1027
1028 match publicity {
1029 Publicity::Private
1030 | Publicity::Public
1031 | Publicity::Internal {
1032 attribute_location: None,
1033 } => (),
1034
1035 Publicity::Internal {
1036 attribute_location: Some(location),
1037 } => self.track_feature_usage(FeatureKind::InternalAnnotation, location),
1038 }
1039
1040 let constructors: Vec<RecordConstructor<Arc<Type>>> = constructors
1041 .into_iter()
1042 .map(
1043 |RecordConstructor {
1044 location,
1045 name_location,
1046 name,
1047 arguments,
1048 documentation,
1049 deprecation: constructor_deprecation,
1050 }| {
1051 self.check_name_case(name_location, &name, Named::CustomTypeVariant);
1052 if constructor_deprecation.is_deprecated() {
1053 self.track_feature_usage(
1054 FeatureKind::VariantWithDeprecatedAnnotation,
1055 location,
1056 );
1057 }
1058
1059 let preregistered_fn = environment
1060 .get_variable(&name)
1061 .expect("Could not find preregistered type for function");
1062 let preregistered_type = preregistered_fn.type_.clone();
1063
1064 let arguments = match preregistered_type.fn_types() {
1065 Some((arguments_types, _return_type)) => arguments
1066 .into_iter()
1067 .zip(&arguments_types)
1068 .map(|(argument, type_)| {
1069 if let Some((location, label)) = &argument.label {
1070 self.check_name_case(*location, label, Named::Label);
1071 }
1072
1073 RecordConstructorArg {
1074 label: argument.label,
1075 ast: argument.ast,
1076 location: argument.location,
1077 type_: type_.clone(),
1078 doc: argument.doc,
1079 }
1080 })
1081 .collect(),
1082 _ => {
1083 vec![]
1084 }
1085 };
1086
1087 RecordConstructor {
1088 location,
1089 name_location,
1090 name,
1091 arguments,
1092 documentation,
1093 deprecation: constructor_deprecation,
1094 }
1095 },
1096 )
1097 .collect();
1098 let typed_parameters = environment
1099 .get_type_constructor(&None, &name)
1100 .expect("Could not find preregistered type constructor")
1101 .parameters
1102 .clone();
1103
1104 // Check if all constructors are deprecated if so error.
1105 if !constructors.is_empty()
1106 && constructors
1107 .iter()
1108 .all(|record| record.deprecation.is_deprecated())
1109 {
1110 self.problems
1111 .error(Error::AllVariantsDeprecated { location });
1112 }
1113
1114 // If any constructor record/varient is deprecated while
1115 // the type is deprecated as a whole that is considered an error.
1116 if deprecation.is_deprecated()
1117 && !constructors.is_empty()
1118 && constructors
1119 .iter()
1120 .any(|record| record.deprecation.is_deprecated())
1121 {
1122 // Report error on all variants attibuted with deprecated
1123 constructors
1124 .iter()
1125 .filter(|record| record.deprecation.is_deprecated())
1126 .for_each(|record| {
1127 self.problems
1128 .error(Error::DeprecatedVariantOnDeprecatedType {
1129 location: record.location,
1130 });
1131 });
1132 }
1133
1134 if external_erlang.is_some() || external_javascript.is_some() {
1135 self.track_feature_usage(FeatureKind::ExternalCustomType, location);
1136
1137 if !constructors.is_empty() {
1138 self.problems
1139 .error(Error::ExternalTypeWithConstructors { location });
1140 }
1141 }
1142
1143 Ok(CustomType {
1144 documentation: doc,
1145 location,
1146 end_position,
1147 publicity,
1148 opaque,
1149 name,
1150 name_location,
1151 parameters,
1152 constructors,
1153 typed_parameters,
1154 deprecation,
1155 external_erlang,
1156 external_javascript,
1157 })
1158 }
1159
1160 fn register_values_from_custom_type(
1161 &mut self,
1162 t: &UntypedCustomType,
1163 environment: &mut Environment<'_>,
1164 type_parameters: &[&EcoString],
1165 ) -> Result<(), Error> {
1166 let CustomType {
1167 publicity,
1168 opaque,
1169 name,
1170 constructors,
1171 deprecation,
1172 ..
1173 } = t;
1174
1175 let mut hydrator = self
1176 .hydrators
1177 .remove(name)
1178 .expect("Could not find hydrator for register_values custom type");
1179 hydrator.disallow_new_type_variables();
1180 let type_ = environment
1181 .module_types
1182 .get(name)
1183 .expect("Type for custom type not found in register_values")
1184 .type_
1185 .clone();
1186
1187 let mut constructors_data = vec![];
1188
1189 let mut index = 0;
1190 for constructor in constructors.iter() {
1191 if let Err(error) = assert_unique_name(
1192 &mut self.value_names,
1193 &constructor.name,
1194 constructor.location,
1195 ) {
1196 self.problems.error(error);
1197 continue;
1198 }
1199
1200 // If the constructor belongs to an opaque type then it's going to be
1201 // considered as private.
1202 let value_constructor_publicity = if *opaque {
1203 Publicity::Private
1204 } else {
1205 *publicity
1206 };
1207
1208 environment.references.register_value(
1209 constructor.name.clone(),
1210 EntityKind::Constructor,
1211 constructor.location,
1212 value_constructor_publicity,
1213 );
1214
1215 environment
1216 .references
1217 .register_type_reference_in_call_graph(name.clone());
1218
1219 let mut field_map_builder = FieldMapBuilder::new(constructor.arguments.len() as u32);
1220 let mut arguments_types = Vec::with_capacity(constructor.arguments.len());
1221 let mut fields = Vec::with_capacity(constructor.arguments.len());
1222
1223 for RecordConstructorArg {
1224 label,
1225 ast,
1226 location,
1227 doc,
1228 ..
1229 } in constructor.arguments.iter()
1230 {
1231 // Build a type from the annotation AST
1232 let t = match hydrator.type_from_ast(ast, environment, &mut self.problems) {
1233 Ok(t) => t,
1234 Err(e) => {
1235 self.problems.error(e);
1236 environment.new_unbound_var()
1237 }
1238 };
1239
1240 fields.push(TypeValueConstructorField {
1241 type_: t.clone(),
1242 label: label.as_ref().map(|(_location, label)| label.clone()),
1243 documentation: doc.as_ref().map(|(_, documentation)| documentation.clone()),
1244 });
1245
1246 // Register the type for this parameter
1247 arguments_types.push(t);
1248
1249 let (label_location, label) = match label {
1250 Some((location, label)) => (*location, Some(label)),
1251 None => (*location, None),
1252 };
1253
1254 if let Some(label) = label {
1255 environment.references.register_label_definition(
1256 (environment.current_module.clone(), name.clone()),
1257 label.clone(),
1258 label_location,
1259 constructor.name.clone(),
1260 );
1261 }
1262
1263 // Register the label for this parameter
1264 if let Err(error) = field_map_builder.add(label, label_location) {
1265 self.problems.error(error);
1266 }
1267 }
1268 let field_map = field_map_builder.finish();
1269 // Insert constructor function into module scope
1270 let mut type_ = type_.deref().clone();
1271 type_.set_custom_type_variant(index as u16);
1272 let type_ = match constructor.arguments.len() {
1273 0 => Arc::new(type_),
1274 _ => fn_(arguments_types.clone(), Arc::new(type_)),
1275 };
1276 let constructor_info = ValueConstructorVariant::Record {
1277 documentation: constructor
1278 .documentation
1279 .as_ref()
1280 .map(|(_, doc)| doc.clone()),
1281 variants_count: constructors.len() as u16,
1282 name: constructor.name.clone(),
1283 arity: constructor.arguments.len() as u16,
1284 field_map: field_map.clone(),
1285 location: constructor.location,
1286 module: self.module_name.clone(),
1287 variant_index: index as u16,
1288 };
1289 index += 1;
1290
1291 // If the whole custom type is deprecated all of its varints are too.
1292 // Otherwise just the varint(s) attributed as deprecated are.
1293 let deprecate_constructor = if deprecation.is_deprecated() {
1294 deprecation
1295 } else {
1296 &constructor.deprecation
1297 };
1298
1299 environment.insert_module_value(
1300 constructor.name.clone(),
1301 ValueConstructor {
1302 publicity: value_constructor_publicity,
1303 deprecation: deprecate_constructor.clone(),
1304 type_: type_.clone(),
1305 variant: constructor_info.clone(),
1306 },
1307 );
1308
1309 environment.references.register_value_reference(
1310 environment.current_module.clone(),
1311 constructor.name.clone(),
1312 &constructor.name,
1313 constructor.name_location,
1314 ReferenceKind::Definition,
1315 );
1316
1317 constructors_data.push(TypeValueConstructor {
1318 name: constructor.name.clone(),
1319 parameters: fields,
1320 documentation: constructor
1321 .documentation
1322 .as_ref()
1323 .map(|(_, documentation)| documentation.clone()),
1324 });
1325 environment.insert_variable(
1326 constructor.name.clone(),
1327 constructor_info,
1328 type_,
1329 value_constructor_publicity,
1330 deprecate_constructor.clone(),
1331 );
1332
1333 environment.names.named_constructor_in_scope(
1334 environment.current_module.clone(),
1335 constructor.name.clone(),
1336 constructor.name.clone(),
1337 );
1338 }
1339
1340 let Accessors {
1341 shared_accessors,
1342 variant_specific_accessors,
1343 positional_accessors,
1344 } = custom_type_accessors(&constructors_data)?;
1345
1346 let map = AccessorsMap {
1347 publicity: if *opaque {
1348 Publicity::Private
1349 } else {
1350 *publicity
1351 },
1352 shared_accessors,
1353 // TODO: improve the ownership here so that we can use the
1354 // `return_type_constructor` below rather than looking it up twice.
1355 type_,
1356 variant_specific_accessors,
1357 variant_positional_accessors: positional_accessors,
1358 };
1359 environment.insert_accessors(name.clone(), map);
1360
1361 let opaque = if *opaque {
1362 Opaque::Opaque
1363 } else {
1364 Opaque::NotOpaque
1365 };
1366 // Now record the constructors for the type.
1367 environment.insert_type_to_constructors(
1368 name.clone(),
1369 TypeVariantConstructors::new(constructors_data, type_parameters, opaque, hydrator),
1370 );
1371
1372 Ok(())
1373 }
1374
1375 fn register_types_from_custom_type(
1376 &mut self,
1377 t: &UntypedCustomType,
1378 environment: &mut Environment<'a>,
1379 ) -> Result<(), Error> {
1380 let CustomType {
1381 name,
1382 name_location,
1383 publicity,
1384 parameters,
1385 location,
1386 deprecation,
1387 opaque,
1388 constructors,
1389 documentation,
1390 ..
1391 } = t;
1392 // We exit early here as we don't yet have a good way to handle the two
1393 // duplicate definitions in the later pass of the analyser which
1394 // register the constructor values for the types. The latter would end up
1395 // overwriting the former, but here in type registering we keep the
1396 // former. I think we want to really keep the former both times.
1397 // The fact we can't straightforwardly do this indicated to me that we
1398 // could improve our approach here somewhat.
1399 environment.assert_unique_type_name(name, *location)?;
1400
1401 self.check_name_case(*name_location, name, Named::Type);
1402
1403 let mut hydrator = Hydrator::new();
1404 let parameters = self.make_type_vars(parameters, &mut hydrator, environment);
1405
1406 hydrator.clear_ridgid_type_names();
1407
1408 // We check is the type comes from an internal module and restrict its
1409 // publicity.
1410 let publicity = match publicity {
1411 // It's important we only restrict the publicity of public types.
1412 Publicity::Public if self.package_config.is_internal_module(&self.module_name) => {
1413 Publicity::Internal {
1414 attribute_location: None,
1415 }
1416 }
1417 // If a type is private we don't want to make it internal just because
1418 // it comes from an internal module, so in that case the publicity is
1419 // left unchanged.
1420 Publicity::Public | Publicity::Private | Publicity::Internal { .. } => *publicity,
1421 };
1422
1423 let type_ = Arc::new(Type::Named {
1424 publicity,
1425 package: environment.current_package.clone(),
1426 module: self.module_name.to_owned(),
1427 name: name.clone(),
1428 arguments: parameters.clone(),
1429 inferred_variant: None,
1430 });
1431 let _ = self.hydrators.insert(name.clone(), hydrator);
1432 environment
1433 .insert_type_constructor(
1434 name.clone(),
1435 TypeConstructor {
1436 origin: *location,
1437 module: self.module_name.clone(),
1438 deprecation: deprecation.clone(),
1439 parameters,
1440 publicity,
1441 type_,
1442 documentation: documentation.as_ref().map(|(_, doc)| doc.clone()),
1443 },
1444 )
1445 .expect("name uniqueness checked above");
1446
1447 environment.names.named_type_in_scope(
1448 environment.current_module.clone(),
1449 name.clone(),
1450 name.clone(),
1451 );
1452
1453 environment
1454 .references
1455 .register_type(name.clone(), EntityKind::Type, *location, publicity);
1456
1457 environment.references.register_type_reference(
1458 environment.current_module.clone(),
1459 name.clone(),
1460 name,
1461 *name_location,
1462 ReferenceKind::Definition,
1463 );
1464
1465 if *opaque && constructors.is_empty() {
1466 self.problems.warning(Warning::OpaqueExternalType {
1467 location: *location,
1468 });
1469 }
1470
1471 if *opaque && publicity.is_private() {
1472 self.problems.error(Error::PrivateOpaqueType {
1473 location: SrcSpan {
1474 start: location.start,
1475 end: location.start + 6,
1476 },
1477 });
1478 }
1479
1480 Ok(())
1481 }
1482
1483 fn register_type_alias(&mut self, t: &UntypedTypeAlias, environment: &mut Environment<'_>) {
1484 let TypeAlias {
1485 location,
1486 publicity,
1487 parameters: arguments,
1488 alias: name,
1489 name_location,
1490 type_ast: resolved_type,
1491 deprecation,
1492 type_: _,
1493 documentation,
1494 } = t;
1495
1496 // A type alias must not have the same name as any other type in the module.
1497 if let Err(error) = environment.assert_unique_type_name(name, *location) {
1498 self.problems.error(error);
1499 // A type already exists with the name so we cannot continue and
1500 // register this new type with the same name.
1501 return;
1502 }
1503
1504 self.check_name_case(*name_location, name, Named::TypeAlias);
1505
1506 environment
1507 .references
1508 .register_type(name.clone(), EntityKind::Type, *location, *publicity);
1509
1510 // Use the hydrator to convert the AST into a type, erroring if the AST was invalid
1511 // in some fashion.
1512 let mut hydrator = Hydrator::new();
1513 let parameters = self.make_type_vars(arguments, &mut hydrator, environment);
1514 let arity = parameters.len();
1515 let tryblock = || {
1516 hydrator.disallow_new_type_variables();
1517 let type_ = hydrator.type_from_ast(resolved_type, environment, &mut self.problems)?;
1518
1519 environment
1520 .names
1521 .type_in_scope(name.clone(), type_.as_ref(), ¶meters);
1522
1523 // Insert the alias so that it can be used by other code.
1524 environment.insert_type_constructor(
1525 name.clone(),
1526 TypeConstructor {
1527 origin: *location,
1528 module: self.module_name.clone(),
1529 parameters: parameters.clone(),
1530 type_: type_.clone(),
1531 deprecation: deprecation.clone(),
1532 publicity: *publicity,
1533 documentation: documentation.as_ref().map(|(_, doc)| doc.clone()),
1534 },
1535 )?;
1536
1537 let alias = TypeAliasConstructor {
1538 origin: *location,
1539 module: self.module_name.clone(),
1540 type_,
1541 publicity: *publicity,
1542 deprecation: deprecation.clone(),
1543 documentation: documentation.as_ref().map(|(_, doc)| doc.clone()),
1544 arity,
1545 parameters,
1546 };
1547
1548 environment.names.maybe_register_reexport_alias(
1549 &environment.current_package,
1550 name,
1551 &alias,
1552 );
1553
1554 environment.insert_type_alias(name.clone(), alias)?;
1555
1556 if let Some(name) = hydrator.unused_type_variables().next() {
1557 return Err(Error::UnusedTypeAliasParameter {
1558 location: *location,
1559 name: name.clone(),
1560 });
1561 }
1562
1563 Ok(())
1564 };
1565 let result = tryblock();
1566 self.record_if_error(result);
1567 }
1568
1569 fn make_type_vars(
1570 &mut self,
1571 arguments: &[SpannedString],
1572 hydrator: &mut Hydrator,
1573 environment: &mut Environment<'_>,
1574 ) -> Vec<Arc<Type>> {
1575 arguments
1576 .iter()
1577 .map(|(location, name)| {
1578 self.check_name_case(*location, name, Named::TypeVariable);
1579 match hydrator.add_type_variable(name, environment) {
1580 Ok(t) => t,
1581 Err(t) => {
1582 self.problems.error(Error::DuplicateTypeParameter {
1583 location: *location,
1584 name: name.clone(),
1585 });
1586 t
1587 }
1588 }
1589 })
1590 .collect()
1591 }
1592
1593 fn record_if_error(&mut self, result: Result<(), Error>) {
1594 if let Err(error) = result {
1595 self.problems.error(error);
1596 }
1597 }
1598
1599 fn register_value_from_function(
1600 &mut self,
1601 f: &UntypedFunction,
1602 environment: &mut Environment<'_>,
1603 ) {
1604 let Function {
1605 name,
1606 arguments,
1607 location,
1608 return_annotation,
1609 publicity,
1610 documentation,
1611 external_erlang,
1612 external_javascript,
1613 external_wasm,
1614 deprecation,
1615 end_position: _,
1616 body: _,
1617 body_start: _,
1618 return_type: _,
1619 implementations,
1620 purity,
1621 } = f;
1622 let (name_location, name) = name.as_ref().expect("A module's function must be named");
1623
1624 self.check_name_case(*name_location, name, Named::Function);
1625 // If the function's name matches an unqualified import, emit a warning:
1626 self.check_shadow_import(name, f.location, environment);
1627
1628 environment.references.register_value(
1629 name.clone(),
1630 EntityKind::Function,
1631 *location,
1632 *publicity,
1633 );
1634
1635 let mut builder = FieldMapBuilder::new(arguments.len() as u32);
1636 for Arg {
1637 names, location, ..
1638 } in arguments.iter()
1639 {
1640 check_argument_names(names, &mut self.problems);
1641
1642 if let Err(error) = builder.add(names.get_label(), *location) {
1643 self.problems.error(error);
1644 }
1645 }
1646 let field_map = builder.finish();
1647 let mut hydrator = Hydrator::new();
1648
1649 // When external implementations are present then the type annotations
1650 // must be given in full, so we disallow holes in the annotations.
1651 hydrator.permit_holes(external_erlang.is_none() && external_javascript.is_none());
1652
1653 let arguments_types = arguments
1654 .iter()
1655 .map(|argument| {
1656 match hydrator.type_from_option_ast(
1657 &argument.annotation,
1658 environment,
1659 &mut self.problems,
1660 ) {
1661 Ok(type_) => type_,
1662 Err(error) => {
1663 self.problems.error(error);
1664 environment.new_unbound_var()
1665 }
1666 }
1667 })
1668 .collect();
1669
1670 let return_type =
1671 match hydrator.type_from_option_ast(return_annotation, environment, &mut self.problems)
1672 {
1673 Ok(type_) => type_,
1674 Err(error) => {
1675 self.problems.error(error);
1676 environment.new_unbound_var()
1677 }
1678 };
1679
1680 let type_ = fn_(arguments_types, return_type);
1681 let _ = self.hydrators.insert(name.clone(), hydrator);
1682
1683 let variant = ValueConstructorVariant::ModuleFn {
1684 documentation: documentation.as_ref().map(|(_, doc)| doc.clone()),
1685 name: name.clone(),
1686 field_map,
1687 external_erlang: external_erlang
1688 .as_ref()
1689 .map(|(m, f, _)| (m.clone(), f.clone())),
1690 external_javascript: external_javascript
1691 .as_ref()
1692 .map(|(m, f, _)| (m.clone(), f.clone())),
1693 external_wasm: external_wasm
1694 .as_ref()
1695 .map(|(m, f, _)| (m.clone(), f.clone())),
1696 module: environment.current_module.clone(),
1697 arity: arguments.len(),
1698 location: *location,
1699 implementations: *implementations,
1700 purity: *purity,
1701 };
1702 environment.insert_variable(
1703 name.clone(),
1704 variant,
1705 type_,
1706 *publicity,
1707 deprecation.clone(),
1708 );
1709 }
1710
1711 fn check_for_type_leaks(&mut self, value: &ValueConstructor) {
1712 // A private value doesn't export anything so it can't leak anything.
1713 if value.publicity.is_private() {
1714 return;
1715 }
1716
1717 // If a private or internal value references a private type
1718 if let Some(leaked) = value.type_.find_private_type() {
1719 self.problems.error(Error::PrivateTypeLeak {
1720 location: value.variant.definition_location(),
1721 leaked,
1722 });
1723 }
1724 }
1725
1726 fn check_name_case(&mut self, location: SrcSpan, name: &EcoString, kind: Named) {
1727 if let Err(error) = check_name_case(location, name, kind) {
1728 self.problems.error(error);
1729 }
1730 }
1731
1732 fn track_feature_usage(&mut self, feature_kind: FeatureKind, location: SrcSpan) {
1733 let minimum_required_version = feature_kind.required_version();
1734
1735 // Then if the required version is not in the specified version for the
1736 // range we emit a warning highlighting the usage of the feature.
1737 if let Some(gleam_version) = &self.package_config.gleam_version
1738 && let Some(lowest_allowed_version) = gleam_version.lowest_version()
1739 {
1740 // There is a version in the specified range that is lower than
1741 // the one required by this feature! This means that the
1742 // specified range is wrong and would allow someone to run a
1743 // compiler that is too old to know of this feature.
1744 if minimum_required_version > lowest_allowed_version {
1745 self.problems
1746 .warning(Warning::FeatureRequiresHigherGleamVersion {
1747 location,
1748 feature_kind,
1749 minimum_required_version: minimum_required_version.clone(),
1750 wrongfully_allowed_version: lowest_allowed_version,
1751 });
1752 }
1753 }
1754
1755 if minimum_required_version > self.minimum_required_version {
1756 self.minimum_required_version = minimum_required_version;
1757 }
1758 }
1759
1760 fn check_shadow_import(
1761 &mut self,
1762 name: &EcoString,
1763 location: SrcSpan,
1764 environment: &mut Environment<'_>,
1765 ) {
1766 if environment.unqualified_imported_names.contains_key(name) {
1767 self.problems
1768 .warning(Warning::TopLevelDefinitionShadowsImport {
1769 location,
1770 name: name.clone(),
1771 });
1772 }
1773 }
1774}
1775
1776fn validate_module_name(name: &EcoString) -> Result<(), Error> {
1777 if is_prelude_module(name) {
1778 return Err(Error::ReservedModuleName { name: name.clone() });
1779 }
1780 for segment in name.split('/') {
1781 if crate::parse::lexer::string_to_keyword(segment).is_some() {
1782 return Err(Error::KeywordInModuleName {
1783 name: name.clone(),
1784 keyword: segment.into(),
1785 });
1786 }
1787 }
1788 Ok(())
1789}
1790
1791fn target_function_implementation<'a>(
1792 target: Target,
1793 external_erlang: &'a Option<(EcoString, EcoString, SrcSpan)>,
1794 external_javascript: &'a Option<(EcoString, EcoString, SrcSpan)>,
1795 external_wasm: &'a Option<(EcoString, EcoString, SrcSpan)>,
1796) -> &'a Option<(EcoString, EcoString, SrcSpan)> {
1797 match target {
1798 Target::Erlang => external_erlang,
1799 Target::JavaScript => external_javascript,
1800 Target::Wasm => external_wasm,
1801 }
1802}
1803
1804fn analyse_type_alias(t: UntypedTypeAlias, environment: &mut Environment<'_>) -> TypedTypeAlias {
1805 let TypeAlias {
1806 documentation: doc,
1807 location,
1808 publicity,
1809 alias,
1810 name_location,
1811 parameters: arguments,
1812 type_ast: resolved_type,
1813 deprecation,
1814 ..
1815 } = t;
1816
1817 // There could be no type alias registered if it was invalid in some way.
1818 // analysis aims to be fault tolerant to get the best possible feedback for
1819 // the programmer in the language server, so the analyser gets here even
1820 // though there was previously errors.
1821 let type_ = match environment.get_type_constructor(&None, &alias) {
1822 Ok(constructor) => constructor.type_.clone(),
1823 Err(_) => environment.new_generic_var(),
1824 };
1825
1826 TypeAlias {
1827 documentation: doc,
1828 location,
1829 publicity,
1830 alias,
1831 name_location,
1832 parameters: arguments,
1833 type_ast: resolved_type,
1834 type_,
1835 deprecation,
1836 }
1837}
1838
1839pub fn infer_bit_array_option<UntypedValue, TypedValue, Typer>(
1840 segment_option: BitArrayOption<UntypedValue>,
1841 mut type_check: Typer,
1842) -> Result<BitArrayOption<TypedValue>, Error>
1843where
1844 Typer: FnMut(UntypedValue, Arc<Type>) -> Result<TypedValue, Error>,
1845{
1846 match segment_option {
1847 BitArrayOption::Size {
1848 value,
1849 location,
1850 short_form,
1851 ..
1852 } => {
1853 let value = type_check(*value, int())?;
1854 Ok(BitArrayOption::Size {
1855 location,
1856 short_form,
1857 value: Box::new(value),
1858 })
1859 }
1860
1861 BitArrayOption::Unit { location, value } => Ok(BitArrayOption::Unit { location, value }),
1862
1863 BitArrayOption::Bytes { location } => Ok(BitArrayOption::Bytes { location }),
1864 BitArrayOption::Int { location } => Ok(BitArrayOption::Int { location }),
1865 BitArrayOption::Float { location } => Ok(BitArrayOption::Float { location }),
1866 BitArrayOption::Bits { location } => Ok(BitArrayOption::Bits { location }),
1867 BitArrayOption::Utf8 { location } => Ok(BitArrayOption::Utf8 { location }),
1868 BitArrayOption::Utf16 { location } => Ok(BitArrayOption::Utf16 { location }),
1869 BitArrayOption::Utf32 { location } => Ok(BitArrayOption::Utf32 { location }),
1870 BitArrayOption::Utf8Codepoint { location } => {
1871 Ok(BitArrayOption::Utf8Codepoint { location })
1872 }
1873 BitArrayOption::Utf16Codepoint { location } => {
1874 Ok(BitArrayOption::Utf16Codepoint { location })
1875 }
1876 BitArrayOption::Utf32Codepoint { location } => {
1877 Ok(BitArrayOption::Utf32Codepoint { location })
1878 }
1879 BitArrayOption::Signed { location } => Ok(BitArrayOption::Signed { location }),
1880 BitArrayOption::Unsigned { location } => Ok(BitArrayOption::Unsigned { location }),
1881 BitArrayOption::Big { location } => Ok(BitArrayOption::Big { location }),
1882 BitArrayOption::Little { location } => Ok(BitArrayOption::Little { location }),
1883 BitArrayOption::Native { location } => Ok(BitArrayOption::Native { location }),
1884 }
1885}
1886
1887fn generalise_module_constant(
1888 constant: ModuleConstant<Arc<Type>>,
1889 environment: &mut Environment<'_>,
1890 module_name: &EcoString,
1891) -> TypedModuleConstant {
1892 let ModuleConstant {
1893 documentation: doc,
1894 location,
1895 name,
1896 name_location,
1897 annotation,
1898 publicity,
1899 value,
1900 type_,
1901 deprecation,
1902 implementations,
1903 } = constant;
1904 let type_ = type_::generalise(type_);
1905 let variant = ValueConstructorVariant::ModuleConstant {
1906 documentation: doc.as_ref().map(|(_, doc)| doc.clone()),
1907 location,
1908 literal: *value.clone(),
1909 module: module_name.clone(),
1910 implementations,
1911 name: name.clone(),
1912 };
1913 environment.insert_variable(
1914 name.clone(),
1915 variant.clone(),
1916 type_.clone(),
1917 publicity,
1918 deprecation.clone(),
1919 );
1920
1921 environment.insert_module_value(
1922 name.clone(),
1923 ValueConstructor {
1924 publicity,
1925 variant,
1926 deprecation: deprecation.clone(),
1927 type_: type_.clone(),
1928 },
1929 );
1930
1931 ModuleConstant {
1932 documentation: doc,
1933 location,
1934 name,
1935 name_location,
1936 annotation,
1937 publicity,
1938 value,
1939 type_,
1940 deprecation,
1941 implementations,
1942 }
1943}
1944
1945fn generalise_function(
1946 function: TypedFunction,
1947 environment: &mut Environment<'_>,
1948 module_name: &EcoString,
1949) -> TypedFunction {
1950 let Function {
1951 documentation: doc,
1952 location,
1953 name,
1954 publicity,
1955 deprecation,
1956 arguments,
1957 body,
1958 return_annotation,
1959 end_position: end_location,
1960 body_start,
1961 return_type,
1962 external_erlang,
1963 external_javascript,
1964 external_wasm,
1965 implementations,
1966 purity,
1967 } = function;
1968
1969 let (name_location, name) = name.expect("Function in a definition must be named");
1970
1971 // Lookup the inferred function information
1972 let function = environment
1973 .get_variable(&name)
1974 .expect("Could not find preregistered type for function");
1975 let field_map = function.field_map().cloned();
1976 let type_ = function.type_.clone();
1977
1978 let type_ = type_::generalise(type_);
1979
1980 // Insert the function into the module's interface
1981 let variant = ValueConstructorVariant::ModuleFn {
1982 documentation: doc.as_ref().map(|(_, doc)| doc.clone()),
1983 name: name.clone(),
1984 field_map,
1985 external_erlang: external_erlang
1986 .as_ref()
1987 .map(|(m, f, _)| (m.clone(), f.clone())),
1988 external_javascript: external_javascript
1989 .as_ref()
1990 .map(|(m, f, _)| (m.clone(), f.clone())),
1991 external_wasm: external_javascript
1992 .as_ref()
1993 .map(|(m, f, _)| (m.clone(), f.clone())),
1994 module: module_name.clone(),
1995 arity: arguments.len(),
1996 location,
1997 implementations,
1998 purity,
1999 };
2000 environment.insert_variable(
2001 name.clone(),
2002 variant.clone(),
2003 type_.clone(),
2004 publicity,
2005 deprecation.clone(),
2006 );
2007 environment.insert_module_value(
2008 name.clone(),
2009 ValueConstructor {
2010 publicity,
2011 deprecation: deprecation.clone(),
2012 type_,
2013 variant,
2014 },
2015 );
2016
2017 Function {
2018 documentation: doc,
2019 location,
2020 name: Some((name_location, name)),
2021 publicity,
2022 deprecation,
2023 arguments,
2024 end_position: end_location,
2025 body_start,
2026 return_annotation,
2027 return_type,
2028 body,
2029 external_erlang,
2030 external_javascript,
2031 external_wasm,
2032 implementations,
2033 purity,
2034 }
2035}
2036
2037fn assert_unique_name(
2038 names: &mut HashMap<EcoString, SrcSpan>,
2039 name: &EcoString,
2040 location: SrcSpan,
2041) -> Result<(), Error> {
2042 match names.insert(name.clone(), location) {
2043 Some(previous_location) => Err(Error::DuplicateName {
2044 location_a: location,
2045 location_b: previous_location,
2046 name: name.clone(),
2047 }),
2048 None => Ok(()),
2049 }
2050}
2051
2052struct Accessors {
2053 shared_accessors: HashMap<EcoString, RecordAccessor>,
2054 variant_specific_accessors: Vec<HashMap<EcoString, RecordAccessor>>,
2055 positional_accessors: Vec<Vec<Arc<Type>>>,
2056}
2057
2058fn custom_type_accessors(constructors: &[TypeValueConstructor]) -> Result<Accessors, Error> {
2059 let accessors = get_compatible_record_fields(constructors);
2060
2061 let mut shared_accessors = HashMap::with_capacity(accessors.len());
2062
2063 for accessor in accessors {
2064 let _ = shared_accessors.insert(accessor.label.clone(), accessor);
2065 }
2066
2067 let mut variant_specific_accessors = Vec::with_capacity(constructors.len());
2068 let mut positional_accessors = Vec::with_capacity(constructors.len());
2069
2070 for constructor in constructors {
2071 let mut fields = HashMap::with_capacity(constructor.parameters.len());
2072 let mut positional_fields = Vec::new();
2073
2074 for (index, parameter) in constructor.parameters.iter().enumerate() {
2075 if let Some(label) = ¶meter.label {
2076 _ = fields.insert(
2077 label.clone(),
2078 RecordAccessor {
2079 index: index as u64,
2080 label: label.clone(),
2081 type_: parameter.type_.clone(),
2082 documentation: parameter.documentation.clone(),
2083 },
2084 );
2085 } else {
2086 positional_fields.push(parameter.type_.clone());
2087 }
2088 }
2089 variant_specific_accessors.push(fields);
2090 positional_accessors.push(positional_fields);
2091 }
2092
2093 Ok(Accessors {
2094 shared_accessors,
2095 variant_specific_accessors,
2096 positional_accessors,
2097 })
2098}
2099
2100/// Returns the fields that have the same label and type across all variants of
2101/// the given type.
2102fn get_compatible_record_fields(constructors: &[TypeValueConstructor]) -> Vec<RecordAccessor> {
2103 let mut compatible = vec![];
2104
2105 let first = match constructors.first() {
2106 Some(first) => first,
2107 None => return compatible,
2108 };
2109
2110 'next_argument: for (index, first_parameter) in first.parameters.iter().enumerate() {
2111 // Fields without labels do not have accessors
2112 let first_label = match first_parameter.label.as_ref() {
2113 Some(label) => label,
2114 None => continue 'next_argument,
2115 };
2116
2117 let mut documentation = if constructors.len() == 1 {
2118 // If there is only one constructor, we simply show the documentation
2119 // for the field.
2120 first_parameter.documentation.clone()
2121 } else {
2122 // If there are multiple constructors, we show the documentation of
2123 // this field for each of the variants.
2124 first_parameter
2125 .documentation
2126 .as_ref()
2127 .map(|field_documentation| {
2128 eco_format!("## {}\n\n{}", first.name, field_documentation)
2129 })
2130 };
2131
2132 // Check each variant to see if they have an field in the same position
2133 // with the same label and the same type
2134 for constructor in constructors.iter().skip(1) {
2135 // The field must exist in all variants
2136 let parameter = match constructor.parameters.get(index) {
2137 Some(argument) => argument,
2138 None => continue 'next_argument,
2139 };
2140
2141 // The labels must be the same
2142 if parameter
2143 .label
2144 .as_ref()
2145 .is_none_or(|arg_label| arg_label != first_label)
2146 {
2147 continue 'next_argument;
2148 }
2149
2150 // The types must be the same
2151 if !parameter.type_.same_as(&first_parameter.type_) {
2152 continue 'next_argument;
2153 }
2154
2155 if let Some(field_documentation) = ¶meter.documentation {
2156 let field_documentation =
2157 eco_format!("## {}\n\n{}", constructor.name, field_documentation);
2158
2159 match &mut documentation {
2160 None => {
2161 documentation = Some(field_documentation);
2162 }
2163 Some(documentation) => {
2164 documentation.push('\n');
2165 documentation.push_str(&field_documentation);
2166 }
2167 }
2168 }
2169 }
2170
2171 // The previous loop did not find any incompatible fields in the other
2172 // variants so this field is compatible across variants and we should
2173 // generate an accessor for it.
2174
2175 compatible.push(RecordAccessor {
2176 index: index as u64,
2177 label: first_label.clone(),
2178 type_: first_parameter.type_.clone(),
2179 documentation,
2180 });
2181 }
2182
2183 compatible
2184}
2185
2186/// Given a type, return a list of all the types it depends on
2187fn get_type_dependencies(type_: &TypeAst) -> Vec<EcoString> {
2188 let mut deps = Vec::with_capacity(1);
2189
2190 match type_ {
2191 TypeAst::Var(TypeAstVar { .. }) => (),
2192 TypeAst::Hole(TypeAstHole { .. }) => (),
2193 TypeAst::Constructor(TypeAstConstructor {
2194 name, arguments, ..
2195 }) => {
2196 deps.push(match name {
2197 ast::TypeAstConstructorName::Unqualified { name, .. } => name.clone(),
2198 ast::TypeAstConstructorName::Qualified {
2199 module,
2200 name: Some((name, _)),
2201 ..
2202 } => format!("{module}.{name}").into(),
2203 ast::TypeAstConstructorName::Qualified {
2204 module, name: None, ..
2205 } => format!("{module}.").into(),
2206 });
2207
2208 for arg in arguments {
2209 deps.extend(get_type_dependencies(arg));
2210 }
2211 }
2212 TypeAst::Fn(TypeAstFn {
2213 arguments, return_, ..
2214 }) => {
2215 for arg in arguments {
2216 deps.extend(get_type_dependencies(arg));
2217 }
2218 deps.extend(get_type_dependencies(return_));
2219 }
2220 TypeAst::Tuple(TypeAstTuple { elements, .. }) => {
2221 for element in elements {
2222 deps.extend(get_type_dependencies(element));
2223 }
2224 }
2225 }
2226
2227 deps
2228}
2229
2230fn sorted_type_aliases(aliases: &Vec<UntypedTypeAlias>) -> Result<Vec<&UntypedTypeAlias>, Error> {
2231 let mut deps: Vec<(EcoString, Vec<EcoString>)> = Vec::with_capacity(aliases.len());
2232
2233 for alias in aliases {
2234 deps.push((alias.alias.clone(), get_type_dependencies(&alias.type_ast)));
2235 }
2236
2237 let sorted_deps = dep_tree::toposort_deps(deps).map_err(|err| {
2238 let dep_tree::Error::Cycle(cycle) = err;
2239
2240 let last = cycle.last().expect("Cycle should not be empty");
2241 let alias = aliases
2242 .iter()
2243 .find(|alias| alias.alias == *last)
2244 .expect("Could not find alias for cycle");
2245
2246 Error::RecursiveTypeAlias {
2247 cycle,
2248 location: alias.location,
2249 }
2250 })?;
2251
2252 Ok(aliases
2253 .iter()
2254 .sorted_by_key(|alias| sorted_deps.iter().position(|x| x == &alias.alias))
2255 .collect())
2256}