Fork of daniellemaywood.uk/gleam — Wasm codegen work
79 kB
2252 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_cranelift_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_cranelift,
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_cranelift,
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 location,
592 );
593
594 if external.is_some() {
595 // There was an external implementation, so type annotations are
596 // mandatory as the Gleam implementation may be absent, and because we
597 // think you should always specify types for external functions for
598 // clarity + to avoid accidental mistakes.
599 self.ensure_annotations_present(&arguments, return_annotation.as_ref(), location);
600 }
601
602 let has_body = !body.is_empty();
603 let definition = FunctionDefinition {
604 has_body,
605 has_erlang_external: external_erlang.is_some(),
606 has_javascript_external: external_javascript.is_some(),
607 has_cranelift_external: external_cranelift.is_some(),
608 };
609
610 // We have already registered the function in the `register_value_from_function`
611 // method, but here we must set this as the current function again, so that anything
612 // we reference in the body of it can be tracked properly in the call graph.
613 environment.references.set_current_node(name.clone());
614
615 let mut typed_arguments = Vec::with_capacity(arguments.len());
616
617 // Infer the type using the preregistered args + return types as a starting point
618 let result = environment.in_new_scope(&mut self.problems, |environment, problems| {
619 for (argument, type_) in arguments.into_iter().zip(&prereg_arguments_types) {
620 let argument = argument.set_type(type_.clone());
621
622 // We track which arguments are discarded so we can provide nice
623 // error messages when someone
624 match &argument.names {
625 ast::ArgNames::Named { .. } | ast::ArgNames::NamedLabelled { .. } => (),
626 ast::ArgNames::Discard { name, location }
627 | ast::ArgNames::LabelledDiscard {
628 name,
629 name_location: location,
630 ..
631 } => {
632 let _ = environment.discarded_names.insert(name.clone(), *location);
633 }
634 }
635
636 typed_arguments.push(argument);
637 }
638
639 let mut expr_typer = ExprTyper::new(environment, definition, problems);
640 expr_typer.hydrator = self
641 .hydrators
642 .remove(&name)
643 .expect("Could not find hydrator for fn");
644
645 let (arguments, body) = expr_typer.infer_fn_with_known_types(
646 Some(name.clone()),
647 typed_arguments.clone(),
648 body,
649 Some(prereg_return_type.clone()),
650 )?;
651 let arguments_types = arguments.iter().map(|a| a.type_.clone()).collect();
652 let return_type = body
653 .last()
654 .map_or(prereg_return_type.clone(), |last| last.type_());
655
656 // `dict.do_fold` is a bit special: since it belongs to the stdlib
657 // it is considered pure by default.
658 // However, since it ends up calling its function argument its
659 // purity should actually be `Impure`. We need to special case it
660 // and set the value ourselves.
661 //
662 // You might wonder why `do_fold` needs this but other similar
663 // functions like `list.each` don't need this special handling.
664 // The key difference is `list.each` calls the higher order function
665 // in its gleam body:
666 //
667 // ```gleam
668 // fn each(list, fun) {
669 // case list {
670 // [] -> Nil
671 // [first, ..rest] -> {
672 // fun(first)
673 // // ^^^ Here we're calling `fun`. It's happening in Gleam so
674 // // the compiler can see this and understand that `each`
675 // // is impure.
676 // // You might argue the purity actually depends on the
677 // // purity of `fun` itself. That's true! But it's a
678 // // separate known problem. For the time being we always
679 // // assume a function argument is impure.
680 // each(rest, fun)
681 // }
682 // }
683 // }
684 // ```
685 //
686 // But since `do_fold` is an external the compiler can't know what
687 // is going on with its function argument and keeps thinking it must
688 // be pure
689 //
690 // ```gleam
691 // @external(erlang, "", "")
692 // fn do_fold(dict: Dict(k, v), fun: fn(k, v) -> a) -> Nil
693 // ```
694 //
695 let purity = if expr_typer.environment.current_package == STDLIB_PACKAGE_NAME
696 && expr_typer.environment.current_module == "gleam/dict"
697 && name == "do_fold"
698 {
699 Purity::Impure
700 } else {
701 expr_typer.purity
702 };
703
704 let type_ = fn_(arguments_types, return_type);
705 Ok((
706 type_,
707 body,
708 expr_typer.implementations,
709 expr_typer.minimum_required_version,
710 purity,
711 ))
712 });
713
714 // If we could not successfully infer the type etc information of the
715 // function then register the error and continue anaylsis using the best
716 // information that we have, so we can still learn about the rest of the
717 // module.
718 let (type_, body, implementations, required_version, purity) = match result {
719 Ok((type_, body, implementations, required_version, purity)) => {
720 (type_, body, implementations, required_version, purity)
721 }
722 Err(error) => {
723 self.problems.error(error);
724 let type_ = preregistered_type.clone();
725 let body = vec![Statement::Expression(TypedExpr::Invalid {
726 type_: prereg_return_type.clone(),
727 location: SrcSpan {
728 start: body_location.end,
729 end: body_location.end,
730 },
731 extra_information: None,
732 })];
733 let implementations = Implementations::supporting_all();
734 (
735 type_,
736 body,
737 implementations,
738 Version::new(1, 0, 0),
739 Purity::Impure,
740 )
741 }
742 };
743
744 if required_version > self.minimum_required_version {
745 self.minimum_required_version = required_version;
746 }
747
748 match publicity {
749 Publicity::Private
750 | Publicity::Public
751 | Publicity::Internal {
752 attribute_location: None,
753 } => (),
754
755 Publicity::Internal {
756 attribute_location: Some(location),
757 } => self.track_feature_usage(FeatureKind::InternalAnnotation, location),
758 }
759
760 if let Some((module, _, location)) = &external_javascript
761 && module.contains('@')
762 {
763 self.track_feature_usage(FeatureKind::AtInJavascriptModules, *location);
764 }
765
766 // Assert that the inferred type matches the type of any recursive call
767 if let Err(error) = unify(preregistered_type.clone(), type_) {
768 self.problems.error(convert_unify_error(error, location));
769 }
770
771 // Ensure that the current target has an implementation for the function.
772 // This is done at the expression level while inferring the function body, but we do it again
773 // here as externally implemented functions may not have a Gleam body.
774 //
775 // We don't emit this error if there is no implementation, as this would
776 // have already emitted an error above.
777 if has_implementation
778 && publicity.is_importable()
779 && environment.target_support.is_enforced()
780 && !implementations.supports(target)
781 // We don't emit this error if there is a body
782 // since this would be caught at the statement level
783 && !has_body
784 {
785 self.problems.error(Error::UnsupportedPublicFunctionTarget {
786 name: name.clone(),
787 target,
788 location,
789 });
790 }
791
792 let variant = ValueConstructorVariant::ModuleFn {
793 documentation: doc.as_ref().map(|(_, doc)| doc.clone()),
794 name: name.clone(),
795 external_erlang: external_erlang
796 .as_ref()
797 .map(|(m, f, _)| (m.clone(), f.clone())),
798 external_javascript: external_javascript
799 .as_ref()
800 .map(|(m, f, _)| (m.clone(), f.clone())),
801 external_cranelift: external_javascript
802 .as_ref()
803 .map(|(m, f, _)| (m.clone(), f.clone())),
804 field_map,
805 module: environment.current_module.clone(),
806 arity: typed_arguments.len(),
807 location,
808 implementations,
809 purity,
810 };
811
812 environment.insert_variable(
813 name.clone(),
814 variant,
815 preregistered_type.clone(),
816 publicity,
817 deprecation.clone(),
818 );
819
820 environment.references.register_value_reference(
821 environment.current_module.clone(),
822 name.clone(),
823 &name,
824 name_location,
825 ReferenceKind::Definition,
826 );
827
828 let function = Function {
829 documentation: doc,
830 location,
831 name: Some((name_location, name.clone())),
832 publicity,
833 deprecation,
834 arguments: typed_arguments,
835 body_start,
836 end_position: end_location,
837 return_annotation,
838 return_type: preregistered_type
839 .return_type()
840 .expect("Could not find return type for fn"),
841 body,
842 external_erlang,
843 external_javascript,
844 external_cranelift,
845 implementations,
846 purity,
847 };
848
849 if let Some(inline_function) = inline::function_to_inlinable(
850 &environment.current_package,
851 &environment.current_module,
852 &function,
853 ) {
854 _ = self.inline_functions.insert(name, inline_function);
855 }
856
857 function
858 }
859
860 fn assert_valid_javascript_external(
861 &mut self,
862 function_name: &EcoString,
863 external_javascript: Option<&(EcoString, EcoString, SrcSpan)>,
864 location: SrcSpan,
865 ) {
866 use regex::Regex;
867
868 let (module, function) = match external_javascript {
869 None => return,
870 Some((module, function, _location)) => (module, function),
871 };
872 if !EXTERNAL_MODULE_PATTERN
873 .get_or_init(|| Regex::new("^[@a-zA-Z0-9\\./:_-]+$").expect("regex"))
874 .is_match(module)
875 {
876 self.problems.error(Error::InvalidExternalJavascriptModule {
877 location,
878 module: module.clone(),
879 name: function_name.clone(),
880 });
881 }
882 if !EXTERNAL_FUNCTION_PATTERN
883 .get_or_init(|| Regex::new("^[a-zA-Z_][a-zA-Z0-9_]*$").expect("regex"))
884 .is_match(function)
885 {
886 self.problems
887 .error(Error::InvalidExternalJavascriptFunction {
888 location,
889 function: function.clone(),
890 name: function_name.clone(),
891 });
892 }
893 }
894
895 fn ensure_annotations_present(
896 &mut self,
897 arguments: &[UntypedArg],
898 return_annotation: Option<&TypeAst>,
899 location: SrcSpan,
900 ) {
901 for arg in arguments {
902 if arg.annotation.is_none() {
903 self.problems.error(Error::ExternalMissingAnnotation {
904 location: arg.location,
905 kind: MissingAnnotation::Parameter,
906 });
907 }
908 }
909 if return_annotation.is_none() {
910 self.problems.error(Error::ExternalMissingAnnotation {
911 location,
912 kind: MissingAnnotation::Return,
913 });
914 }
915 }
916
917 fn ensure_function_has_an_implementation(
918 &mut self,
919 body: &[UntypedStatement],
920 external_erlang: &Option<(EcoString, EcoString, SrcSpan)>,
921 external_javascript: &Option<(EcoString, EcoString, SrcSpan)>,
922 location: SrcSpan,
923 ) -> bool {
924 match (external_erlang, external_javascript) {
925 (None, None) if body.is_empty() => {
926 self.problems.error(Error::NoImplementation { location });
927 false
928 }
929 _ => true,
930 }
931 }
932
933 fn analyse_import(
934 &mut self,
935 i: UntypedImport,
936 environment: &Environment<'_>,
937 ) -> Option<TypedImport> {
938 let Import {
939 documentation,
940 location,
941 module_location,
942 module,
943 as_name,
944 unqualified_values,
945 unqualified_types,
946 ..
947 } = i;
948 // Find imported module
949 let Some(module_info) = environment.importable_modules.get(&module) else {
950 // Here the module being imported doesn't exist. We don't emit an
951 // error here as the `Importer` that was run earlier will have
952 // already emitted an error for this.
953 return None;
954 };
955
956 // Modules should belong to a package that is a direct dependency of the
957 // current package to be imported.
958 // Upgrade this to an error in future.
959 if module_info.package != GLEAM_CORE_PACKAGE_NAME
960 && module_info.package != self.package_config.name
961 && !self.direct_dependencies.contains_key(&module_info.package)
962 {
963 self.warnings.emit(Warning::TransitiveDependencyImported {
964 location,
965 module: module_info.name.clone(),
966 package: module_info.package.clone(),
967 });
968 }
969
970 Some(Import {
971 documentation,
972 location,
973 module_location,
974 module,
975 as_name,
976 unqualified_values,
977 unqualified_types,
978 package: module_info.package.clone(),
979 })
980 }
981
982 fn analyse_custom_type(
983 &mut self,
984 t: UntypedCustomType,
985 environment: &mut Environment<'_>,
986 ) -> Option<TypedCustomType> {
987 match self.do_analyse_custom_type(t, environment) {
988 Ok(custom_type) => Some(custom_type),
989 Err(error) => {
990 self.problems.error(error);
991 None
992 }
993 }
994 }
995
996 // TODO: split this into a new class.
997 fn do_analyse_custom_type(
998 &mut self,
999 t: UntypedCustomType,
1000 environment: &mut Environment<'_>,
1001 ) -> Result<TypedCustomType, Error> {
1002 self.register_values_from_custom_type(
1003 &t,
1004 environment,
1005 &t.parameters.iter().map(|(_, name)| name).collect_vec(),
1006 )?;
1007
1008 let CustomType {
1009 documentation: doc,
1010 location,
1011 end_position,
1012 publicity,
1013 opaque,
1014 name,
1015 name_location,
1016 parameters,
1017 constructors,
1018 deprecation,
1019 external_erlang,
1020 external_javascript,
1021 ..
1022 } = t;
1023
1024 match publicity {
1025 Publicity::Private
1026 | Publicity::Public
1027 | Publicity::Internal {
1028 attribute_location: None,
1029 } => (),
1030
1031 Publicity::Internal {
1032 attribute_location: Some(location),
1033 } => self.track_feature_usage(FeatureKind::InternalAnnotation, location),
1034 }
1035
1036 let constructors: Vec<RecordConstructor<Arc<Type>>> = constructors
1037 .into_iter()
1038 .map(
1039 |RecordConstructor {
1040 location,
1041 name_location,
1042 name,
1043 arguments,
1044 documentation,
1045 deprecation: constructor_deprecation,
1046 }| {
1047 self.check_name_case(name_location, &name, Named::CustomTypeVariant);
1048 if constructor_deprecation.is_deprecated() {
1049 self.track_feature_usage(
1050 FeatureKind::VariantWithDeprecatedAnnotation,
1051 location,
1052 );
1053 }
1054
1055 let preregistered_fn = environment
1056 .get_variable(&name)
1057 .expect("Could not find preregistered type for function");
1058 let preregistered_type = preregistered_fn.type_.clone();
1059
1060 let arguments = match preregistered_type.fn_types() {
1061 Some((arguments_types, _return_type)) => arguments
1062 .into_iter()
1063 .zip(&arguments_types)
1064 .map(|(argument, type_)| {
1065 if let Some((location, label)) = &argument.label {
1066 self.check_name_case(*location, label, Named::Label);
1067 }
1068
1069 RecordConstructorArg {
1070 label: argument.label,
1071 ast: argument.ast,
1072 location: argument.location,
1073 type_: type_.clone(),
1074 doc: argument.doc,
1075 }
1076 })
1077 .collect(),
1078 _ => {
1079 vec![]
1080 }
1081 };
1082
1083 RecordConstructor {
1084 location,
1085 name_location,
1086 name,
1087 arguments,
1088 documentation,
1089 deprecation: constructor_deprecation,
1090 }
1091 },
1092 )
1093 .collect();
1094 let typed_parameters = environment
1095 .get_type_constructor(&None, &name)
1096 .expect("Could not find preregistered type constructor")
1097 .parameters
1098 .clone();
1099
1100 // Check if all constructors are deprecated if so error.
1101 if !constructors.is_empty()
1102 && constructors
1103 .iter()
1104 .all(|record| record.deprecation.is_deprecated())
1105 {
1106 self.problems
1107 .error(Error::AllVariantsDeprecated { location });
1108 }
1109
1110 // If any constructor record/varient is deprecated while
1111 // the type is deprecated as a whole that is considered an error.
1112 if deprecation.is_deprecated()
1113 && !constructors.is_empty()
1114 && constructors
1115 .iter()
1116 .any(|record| record.deprecation.is_deprecated())
1117 {
1118 // Report error on all variants attibuted with deprecated
1119 constructors
1120 .iter()
1121 .filter(|record| record.deprecation.is_deprecated())
1122 .for_each(|record| {
1123 self.problems
1124 .error(Error::DeprecatedVariantOnDeprecatedType {
1125 location: record.location,
1126 });
1127 });
1128 }
1129
1130 if external_erlang.is_some() || external_javascript.is_some() {
1131 self.track_feature_usage(FeatureKind::ExternalCustomType, location);
1132
1133 if !constructors.is_empty() {
1134 self.problems
1135 .error(Error::ExternalTypeWithConstructors { location });
1136 }
1137 }
1138
1139 Ok(CustomType {
1140 documentation: doc,
1141 location,
1142 end_position,
1143 publicity,
1144 opaque,
1145 name,
1146 name_location,
1147 parameters,
1148 constructors,
1149 typed_parameters,
1150 deprecation,
1151 external_erlang,
1152 external_javascript,
1153 })
1154 }
1155
1156 fn register_values_from_custom_type(
1157 &mut self,
1158 t: &UntypedCustomType,
1159 environment: &mut Environment<'_>,
1160 type_parameters: &[&EcoString],
1161 ) -> Result<(), Error> {
1162 let CustomType {
1163 publicity,
1164 opaque,
1165 name,
1166 constructors,
1167 deprecation,
1168 ..
1169 } = t;
1170
1171 let mut hydrator = self
1172 .hydrators
1173 .remove(name)
1174 .expect("Could not find hydrator for register_values custom type");
1175 hydrator.disallow_new_type_variables();
1176 let type_ = environment
1177 .module_types
1178 .get(name)
1179 .expect("Type for custom type not found in register_values")
1180 .type_
1181 .clone();
1182
1183 let mut constructors_data = vec![];
1184
1185 let mut index = 0;
1186 for constructor in constructors.iter() {
1187 if let Err(error) = assert_unique_name(
1188 &mut self.value_names,
1189 &constructor.name,
1190 constructor.location,
1191 ) {
1192 self.problems.error(error);
1193 continue;
1194 }
1195
1196 // If the constructor belongs to an opaque type then it's going to be
1197 // considered as private.
1198 let value_constructor_publicity = if *opaque {
1199 Publicity::Private
1200 } else {
1201 *publicity
1202 };
1203
1204 environment.references.register_value(
1205 constructor.name.clone(),
1206 EntityKind::Constructor,
1207 constructor.location,
1208 value_constructor_publicity,
1209 );
1210
1211 environment
1212 .references
1213 .register_type_reference_in_call_graph(name.clone());
1214
1215 let mut field_map_builder = FieldMapBuilder::new(constructor.arguments.len() as u32);
1216 let mut arguments_types = Vec::with_capacity(constructor.arguments.len());
1217 let mut fields = Vec::with_capacity(constructor.arguments.len());
1218
1219 for RecordConstructorArg {
1220 label,
1221 ast,
1222 location,
1223 doc,
1224 ..
1225 } in constructor.arguments.iter()
1226 {
1227 // Build a type from the annotation AST
1228 let t = match hydrator.type_from_ast(ast, environment, &mut self.problems) {
1229 Ok(t) => t,
1230 Err(e) => {
1231 self.problems.error(e);
1232 environment.new_unbound_var()
1233 }
1234 };
1235
1236 fields.push(TypeValueConstructorField {
1237 type_: t.clone(),
1238 label: label.as_ref().map(|(_location, label)| label.clone()),
1239 documentation: doc.as_ref().map(|(_, documentation)| documentation.clone()),
1240 });
1241
1242 // Register the type for this parameter
1243 arguments_types.push(t);
1244
1245 let (label_location, label) = match label {
1246 Some((location, label)) => (*location, Some(label)),
1247 None => (*location, None),
1248 };
1249
1250 if let Some(label) = label {
1251 environment.references.register_label_definition(
1252 (environment.current_module.clone(), name.clone()),
1253 label.clone(),
1254 label_location,
1255 constructor.name.clone(),
1256 );
1257 }
1258
1259 // Register the label for this parameter
1260 if let Err(error) = field_map_builder.add(label, label_location) {
1261 self.problems.error(error);
1262 }
1263 }
1264 let field_map = field_map_builder.finish();
1265 // Insert constructor function into module scope
1266 let mut type_ = type_.deref().clone();
1267 type_.set_custom_type_variant(index as u16);
1268 let type_ = match constructor.arguments.len() {
1269 0 => Arc::new(type_),
1270 _ => fn_(arguments_types.clone(), Arc::new(type_)),
1271 };
1272 let constructor_info = ValueConstructorVariant::Record {
1273 documentation: constructor
1274 .documentation
1275 .as_ref()
1276 .map(|(_, doc)| doc.clone()),
1277 variants_count: constructors.len() as u16,
1278 name: constructor.name.clone(),
1279 arity: constructor.arguments.len() as u16,
1280 field_map: field_map.clone(),
1281 location: constructor.location,
1282 module: self.module_name.clone(),
1283 variant_index: index as u16,
1284 };
1285 index += 1;
1286
1287 // If the whole custom type is deprecated all of its varints are too.
1288 // Otherwise just the varint(s) attributed as deprecated are.
1289 let deprecate_constructor = if deprecation.is_deprecated() {
1290 deprecation
1291 } else {
1292 &constructor.deprecation
1293 };
1294
1295 environment.insert_module_value(
1296 constructor.name.clone(),
1297 ValueConstructor {
1298 publicity: value_constructor_publicity,
1299 deprecation: deprecate_constructor.clone(),
1300 type_: type_.clone(),
1301 variant: constructor_info.clone(),
1302 },
1303 );
1304
1305 environment.references.register_value_reference(
1306 environment.current_module.clone(),
1307 constructor.name.clone(),
1308 &constructor.name,
1309 constructor.name_location,
1310 ReferenceKind::Definition,
1311 );
1312
1313 constructors_data.push(TypeValueConstructor {
1314 name: constructor.name.clone(),
1315 parameters: fields,
1316 documentation: constructor
1317 .documentation
1318 .as_ref()
1319 .map(|(_, documentation)| documentation.clone()),
1320 });
1321 environment.insert_variable(
1322 constructor.name.clone(),
1323 constructor_info,
1324 type_,
1325 value_constructor_publicity,
1326 deprecate_constructor.clone(),
1327 );
1328
1329 environment.names.named_constructor_in_scope(
1330 environment.current_module.clone(),
1331 constructor.name.clone(),
1332 constructor.name.clone(),
1333 );
1334 }
1335
1336 let Accessors {
1337 shared_accessors,
1338 variant_specific_accessors,
1339 positional_accessors,
1340 } = custom_type_accessors(&constructors_data)?;
1341
1342 let map = AccessorsMap {
1343 publicity: if *opaque {
1344 Publicity::Private
1345 } else {
1346 *publicity
1347 },
1348 shared_accessors,
1349 // TODO: improve the ownership here so that we can use the
1350 // `return_type_constructor` below rather than looking it up twice.
1351 type_,
1352 variant_specific_accessors,
1353 variant_positional_accessors: positional_accessors,
1354 };
1355 environment.insert_accessors(name.clone(), map);
1356
1357 let opaque = if *opaque {
1358 Opaque::Opaque
1359 } else {
1360 Opaque::NotOpaque
1361 };
1362 // Now record the constructors for the type.
1363 environment.insert_type_to_constructors(
1364 name.clone(),
1365 TypeVariantConstructors::new(constructors_data, type_parameters, opaque, hydrator),
1366 );
1367
1368 Ok(())
1369 }
1370
1371 fn register_types_from_custom_type(
1372 &mut self,
1373 t: &UntypedCustomType,
1374 environment: &mut Environment<'a>,
1375 ) -> Result<(), Error> {
1376 let CustomType {
1377 name,
1378 name_location,
1379 publicity,
1380 parameters,
1381 location,
1382 deprecation,
1383 opaque,
1384 constructors,
1385 documentation,
1386 ..
1387 } = t;
1388 // We exit early here as we don't yet have a good way to handle the two
1389 // duplicate definitions in the later pass of the analyser which
1390 // register the constructor values for the types. The latter would end up
1391 // overwriting the former, but here in type registering we keep the
1392 // former. I think we want to really keep the former both times.
1393 // The fact we can't straightforwardly do this indicated to me that we
1394 // could improve our approach here somewhat.
1395 environment.assert_unique_type_name(name, *location)?;
1396
1397 self.check_name_case(*name_location, name, Named::Type);
1398
1399 let mut hydrator = Hydrator::new();
1400 let parameters = self.make_type_vars(parameters, &mut hydrator, environment);
1401
1402 hydrator.clear_ridgid_type_names();
1403
1404 // We check is the type comes from an internal module and restrict its
1405 // publicity.
1406 let publicity = match publicity {
1407 // It's important we only restrict the publicity of public types.
1408 Publicity::Public if self.package_config.is_internal_module(&self.module_name) => {
1409 Publicity::Internal {
1410 attribute_location: None,
1411 }
1412 }
1413 // If a type is private we don't want to make it internal just because
1414 // it comes from an internal module, so in that case the publicity is
1415 // left unchanged.
1416 Publicity::Public | Publicity::Private | Publicity::Internal { .. } => *publicity,
1417 };
1418
1419 let type_ = Arc::new(Type::Named {
1420 publicity,
1421 package: environment.current_package.clone(),
1422 module: self.module_name.to_owned(),
1423 name: name.clone(),
1424 arguments: parameters.clone(),
1425 inferred_variant: None,
1426 });
1427 let _ = self.hydrators.insert(name.clone(), hydrator);
1428 environment
1429 .insert_type_constructor(
1430 name.clone(),
1431 TypeConstructor {
1432 origin: *location,
1433 module: self.module_name.clone(),
1434 deprecation: deprecation.clone(),
1435 parameters,
1436 publicity,
1437 type_,
1438 documentation: documentation.as_ref().map(|(_, doc)| doc.clone()),
1439 },
1440 )
1441 .expect("name uniqueness checked above");
1442
1443 environment.names.named_type_in_scope(
1444 environment.current_module.clone(),
1445 name.clone(),
1446 name.clone(),
1447 );
1448
1449 environment
1450 .references
1451 .register_type(name.clone(), EntityKind::Type, *location, publicity);
1452
1453 environment.references.register_type_reference(
1454 environment.current_module.clone(),
1455 name.clone(),
1456 name,
1457 *name_location,
1458 ReferenceKind::Definition,
1459 );
1460
1461 if *opaque && constructors.is_empty() {
1462 self.problems.warning(Warning::OpaqueExternalType {
1463 location: *location,
1464 });
1465 }
1466
1467 if *opaque && publicity.is_private() {
1468 self.problems.error(Error::PrivateOpaqueType {
1469 location: SrcSpan {
1470 start: location.start,
1471 end: location.start + 6,
1472 },
1473 });
1474 }
1475
1476 Ok(())
1477 }
1478
1479 fn register_type_alias(&mut self, t: &UntypedTypeAlias, environment: &mut Environment<'_>) {
1480 let TypeAlias {
1481 location,
1482 publicity,
1483 parameters: arguments,
1484 alias: name,
1485 name_location,
1486 type_ast: resolved_type,
1487 deprecation,
1488 type_: _,
1489 documentation,
1490 } = t;
1491
1492 // A type alias must not have the same name as any other type in the module.
1493 if let Err(error) = environment.assert_unique_type_name(name, *location) {
1494 self.problems.error(error);
1495 // A type already exists with the name so we cannot continue and
1496 // register this new type with the same name.
1497 return;
1498 }
1499
1500 self.check_name_case(*name_location, name, Named::TypeAlias);
1501
1502 environment
1503 .references
1504 .register_type(name.clone(), EntityKind::Type, *location, *publicity);
1505
1506 // Use the hydrator to convert the AST into a type, erroring if the AST was invalid
1507 // in some fashion.
1508 let mut hydrator = Hydrator::new();
1509 let parameters = self.make_type_vars(arguments, &mut hydrator, environment);
1510 let arity = parameters.len();
1511 let tryblock = || {
1512 hydrator.disallow_new_type_variables();
1513 let type_ = hydrator.type_from_ast(resolved_type, environment, &mut self.problems)?;
1514
1515 environment
1516 .names
1517 .type_in_scope(name.clone(), type_.as_ref(), ¶meters);
1518
1519 // Insert the alias so that it can be used by other code.
1520 environment.insert_type_constructor(
1521 name.clone(),
1522 TypeConstructor {
1523 origin: *location,
1524 module: self.module_name.clone(),
1525 parameters: parameters.clone(),
1526 type_: type_.clone(),
1527 deprecation: deprecation.clone(),
1528 publicity: *publicity,
1529 documentation: documentation.as_ref().map(|(_, doc)| doc.clone()),
1530 },
1531 )?;
1532
1533 let alias = TypeAliasConstructor {
1534 origin: *location,
1535 module: self.module_name.clone(),
1536 type_,
1537 publicity: *publicity,
1538 deprecation: deprecation.clone(),
1539 documentation: documentation.as_ref().map(|(_, doc)| doc.clone()),
1540 arity,
1541 parameters,
1542 };
1543
1544 environment.names.maybe_register_reexport_alias(
1545 &environment.current_package,
1546 name,
1547 &alias,
1548 );
1549
1550 environment.insert_type_alias(name.clone(), alias)?;
1551
1552 if let Some(name) = hydrator.unused_type_variables().next() {
1553 return Err(Error::UnusedTypeAliasParameter {
1554 location: *location,
1555 name: name.clone(),
1556 });
1557 }
1558
1559 Ok(())
1560 };
1561 let result = tryblock();
1562 self.record_if_error(result);
1563 }
1564
1565 fn make_type_vars(
1566 &mut self,
1567 arguments: &[SpannedString],
1568 hydrator: &mut Hydrator,
1569 environment: &mut Environment<'_>,
1570 ) -> Vec<Arc<Type>> {
1571 arguments
1572 .iter()
1573 .map(|(location, name)| {
1574 self.check_name_case(*location, name, Named::TypeVariable);
1575 match hydrator.add_type_variable(name, environment) {
1576 Ok(t) => t,
1577 Err(t) => {
1578 self.problems.error(Error::DuplicateTypeParameter {
1579 location: *location,
1580 name: name.clone(),
1581 });
1582 t
1583 }
1584 }
1585 })
1586 .collect()
1587 }
1588
1589 fn record_if_error(&mut self, result: Result<(), Error>) {
1590 if let Err(error) = result {
1591 self.problems.error(error);
1592 }
1593 }
1594
1595 fn register_value_from_function(
1596 &mut self,
1597 f: &UntypedFunction,
1598 environment: &mut Environment<'_>,
1599 ) {
1600 let Function {
1601 name,
1602 arguments,
1603 location,
1604 return_annotation,
1605 publicity,
1606 documentation,
1607 external_erlang,
1608 external_javascript,
1609 external_cranelift,
1610 deprecation,
1611 end_position: _,
1612 body: _,
1613 body_start: _,
1614 return_type: _,
1615 implementations,
1616 purity,
1617 } = f;
1618 let (name_location, name) = name.as_ref().expect("A module's function must be named");
1619
1620 self.check_name_case(*name_location, name, Named::Function);
1621 // If the function's name matches an unqualified import, emit a warning:
1622 self.check_shadow_import(name, f.location, environment);
1623
1624 environment.references.register_value(
1625 name.clone(),
1626 EntityKind::Function,
1627 *location,
1628 *publicity,
1629 );
1630
1631 let mut builder = FieldMapBuilder::new(arguments.len() as u32);
1632 for Arg {
1633 names, location, ..
1634 } in arguments.iter()
1635 {
1636 check_argument_names(names, &mut self.problems);
1637
1638 if let Err(error) = builder.add(names.get_label(), *location) {
1639 self.problems.error(error);
1640 }
1641 }
1642 let field_map = builder.finish();
1643 let mut hydrator = Hydrator::new();
1644
1645 // When external implementations are present then the type annotations
1646 // must be given in full, so we disallow holes in the annotations.
1647 hydrator.permit_holes(external_erlang.is_none() && external_javascript.is_none());
1648
1649 let arguments_types = arguments
1650 .iter()
1651 .map(|argument| {
1652 match hydrator.type_from_option_ast(
1653 &argument.annotation,
1654 environment,
1655 &mut self.problems,
1656 ) {
1657 Ok(type_) => type_,
1658 Err(error) => {
1659 self.problems.error(error);
1660 environment.new_unbound_var()
1661 }
1662 }
1663 })
1664 .collect();
1665
1666 let return_type =
1667 match hydrator.type_from_option_ast(return_annotation, environment, &mut self.problems)
1668 {
1669 Ok(type_) => type_,
1670 Err(error) => {
1671 self.problems.error(error);
1672 environment.new_unbound_var()
1673 }
1674 };
1675
1676 let type_ = fn_(arguments_types, return_type);
1677 let _ = self.hydrators.insert(name.clone(), hydrator);
1678
1679 let variant = ValueConstructorVariant::ModuleFn {
1680 documentation: documentation.as_ref().map(|(_, doc)| doc.clone()),
1681 name: name.clone(),
1682 field_map,
1683 external_erlang: external_erlang
1684 .as_ref()
1685 .map(|(m, f, _)| (m.clone(), f.clone())),
1686 external_javascript: external_javascript
1687 .as_ref()
1688 .map(|(m, f, _)| (m.clone(), f.clone())),
1689 external_cranelift: external_cranelift
1690 .as_ref()
1691 .map(|(m, f, _)| (m.clone(), f.clone())),
1692 module: environment.current_module.clone(),
1693 arity: arguments.len(),
1694 location: *location,
1695 implementations: *implementations,
1696 purity: *purity,
1697 };
1698 environment.insert_variable(
1699 name.clone(),
1700 variant,
1701 type_,
1702 *publicity,
1703 deprecation.clone(),
1704 );
1705 }
1706
1707 fn check_for_type_leaks(&mut self, value: &ValueConstructor) {
1708 // A private value doesn't export anything so it can't leak anything.
1709 if value.publicity.is_private() {
1710 return;
1711 }
1712
1713 // If a private or internal value references a private type
1714 if let Some(leaked) = value.type_.find_private_type() {
1715 self.problems.error(Error::PrivateTypeLeak {
1716 location: value.variant.definition_location(),
1717 leaked,
1718 });
1719 }
1720 }
1721
1722 fn check_name_case(&mut self, location: SrcSpan, name: &EcoString, kind: Named) {
1723 if let Err(error) = check_name_case(location, name, kind) {
1724 self.problems.error(error);
1725 }
1726 }
1727
1728 fn track_feature_usage(&mut self, feature_kind: FeatureKind, location: SrcSpan) {
1729 let minimum_required_version = feature_kind.required_version();
1730
1731 // Then if the required version is not in the specified version for the
1732 // range we emit a warning highlighting the usage of the feature.
1733 if let Some(gleam_version) = &self.package_config.gleam_version
1734 && let Some(lowest_allowed_version) = gleam_version.lowest_version()
1735 {
1736 // There is a version in the specified range that is lower than
1737 // the one required by this feature! This means that the
1738 // specified range is wrong and would allow someone to run a
1739 // compiler that is too old to know of this feature.
1740 if minimum_required_version > lowest_allowed_version {
1741 self.problems
1742 .warning(Warning::FeatureRequiresHigherGleamVersion {
1743 location,
1744 feature_kind,
1745 minimum_required_version: minimum_required_version.clone(),
1746 wrongfully_allowed_version: lowest_allowed_version,
1747 });
1748 }
1749 }
1750
1751 if minimum_required_version > self.minimum_required_version {
1752 self.minimum_required_version = minimum_required_version;
1753 }
1754 }
1755
1756 fn check_shadow_import(
1757 &mut self,
1758 name: &EcoString,
1759 location: SrcSpan,
1760 environment: &mut Environment<'_>,
1761 ) {
1762 if environment.unqualified_imported_names.contains_key(name) {
1763 self.problems
1764 .warning(Warning::TopLevelDefinitionShadowsImport {
1765 location,
1766 name: name.clone(),
1767 });
1768 }
1769 }
1770}
1771
1772fn validate_module_name(name: &EcoString) -> Result<(), Error> {
1773 if is_prelude_module(name) {
1774 return Err(Error::ReservedModuleName { name: name.clone() });
1775 }
1776 for segment in name.split('/') {
1777 if crate::parse::lexer::string_to_keyword(segment).is_some() {
1778 return Err(Error::KeywordInModuleName {
1779 name: name.clone(),
1780 keyword: segment.into(),
1781 });
1782 }
1783 }
1784 Ok(())
1785}
1786
1787fn target_function_implementation<'a>(
1788 target: Target,
1789 external_erlang: &'a Option<(EcoString, EcoString, SrcSpan)>,
1790 external_javascript: &'a Option<(EcoString, EcoString, SrcSpan)>,
1791 external_cranelift: &'a Option<(EcoString, EcoString, SrcSpan)>,
1792) -> &'a Option<(EcoString, EcoString, SrcSpan)> {
1793 match target {
1794 Target::Erlang => external_erlang,
1795 Target::JavaScript => external_javascript,
1796 Target::Cranelift => external_cranelift,
1797 }
1798}
1799
1800fn analyse_type_alias(t: UntypedTypeAlias, environment: &mut Environment<'_>) -> TypedTypeAlias {
1801 let TypeAlias {
1802 documentation: doc,
1803 location,
1804 publicity,
1805 alias,
1806 name_location,
1807 parameters: arguments,
1808 type_ast: resolved_type,
1809 deprecation,
1810 ..
1811 } = t;
1812
1813 // There could be no type alias registered if it was invalid in some way.
1814 // analysis aims to be fault tolerant to get the best possible feedback for
1815 // the programmer in the language server, so the analyser gets here even
1816 // though there was previously errors.
1817 let type_ = match environment.get_type_constructor(&None, &alias) {
1818 Ok(constructor) => constructor.type_.clone(),
1819 Err(_) => environment.new_generic_var(),
1820 };
1821
1822 TypeAlias {
1823 documentation: doc,
1824 location,
1825 publicity,
1826 alias,
1827 name_location,
1828 parameters: arguments,
1829 type_ast: resolved_type,
1830 type_,
1831 deprecation,
1832 }
1833}
1834
1835pub fn infer_bit_array_option<UntypedValue, TypedValue, Typer>(
1836 segment_option: BitArrayOption<UntypedValue>,
1837 mut type_check: Typer,
1838) -> Result<BitArrayOption<TypedValue>, Error>
1839where
1840 Typer: FnMut(UntypedValue, Arc<Type>) -> Result<TypedValue, Error>,
1841{
1842 match segment_option {
1843 BitArrayOption::Size {
1844 value,
1845 location,
1846 short_form,
1847 ..
1848 } => {
1849 let value = type_check(*value, int())?;
1850 Ok(BitArrayOption::Size {
1851 location,
1852 short_form,
1853 value: Box::new(value),
1854 })
1855 }
1856
1857 BitArrayOption::Unit { location, value } => Ok(BitArrayOption::Unit { location, value }),
1858
1859 BitArrayOption::Bytes { location } => Ok(BitArrayOption::Bytes { location }),
1860 BitArrayOption::Int { location } => Ok(BitArrayOption::Int { location }),
1861 BitArrayOption::Float { location } => Ok(BitArrayOption::Float { location }),
1862 BitArrayOption::Bits { location } => Ok(BitArrayOption::Bits { location }),
1863 BitArrayOption::Utf8 { location } => Ok(BitArrayOption::Utf8 { location }),
1864 BitArrayOption::Utf16 { location } => Ok(BitArrayOption::Utf16 { location }),
1865 BitArrayOption::Utf32 { location } => Ok(BitArrayOption::Utf32 { location }),
1866 BitArrayOption::Utf8Codepoint { location } => {
1867 Ok(BitArrayOption::Utf8Codepoint { location })
1868 }
1869 BitArrayOption::Utf16Codepoint { location } => {
1870 Ok(BitArrayOption::Utf16Codepoint { location })
1871 }
1872 BitArrayOption::Utf32Codepoint { location } => {
1873 Ok(BitArrayOption::Utf32Codepoint { location })
1874 }
1875 BitArrayOption::Signed { location } => Ok(BitArrayOption::Signed { location }),
1876 BitArrayOption::Unsigned { location } => Ok(BitArrayOption::Unsigned { location }),
1877 BitArrayOption::Big { location } => Ok(BitArrayOption::Big { location }),
1878 BitArrayOption::Little { location } => Ok(BitArrayOption::Little { location }),
1879 BitArrayOption::Native { location } => Ok(BitArrayOption::Native { location }),
1880 }
1881}
1882
1883fn generalise_module_constant(
1884 constant: ModuleConstant<Arc<Type>>,
1885 environment: &mut Environment<'_>,
1886 module_name: &EcoString,
1887) -> TypedModuleConstant {
1888 let ModuleConstant {
1889 documentation: doc,
1890 location,
1891 name,
1892 name_location,
1893 annotation,
1894 publicity,
1895 value,
1896 type_,
1897 deprecation,
1898 implementations,
1899 } = constant;
1900 let type_ = type_::generalise(type_);
1901 let variant = ValueConstructorVariant::ModuleConstant {
1902 documentation: doc.as_ref().map(|(_, doc)| doc.clone()),
1903 location,
1904 literal: *value.clone(),
1905 module: module_name.clone(),
1906 implementations,
1907 name: name.clone(),
1908 };
1909 environment.insert_variable(
1910 name.clone(),
1911 variant.clone(),
1912 type_.clone(),
1913 publicity,
1914 deprecation.clone(),
1915 );
1916
1917 environment.insert_module_value(
1918 name.clone(),
1919 ValueConstructor {
1920 publicity,
1921 variant,
1922 deprecation: deprecation.clone(),
1923 type_: type_.clone(),
1924 },
1925 );
1926
1927 ModuleConstant {
1928 documentation: doc,
1929 location,
1930 name,
1931 name_location,
1932 annotation,
1933 publicity,
1934 value,
1935 type_,
1936 deprecation,
1937 implementations,
1938 }
1939}
1940
1941fn generalise_function(
1942 function: TypedFunction,
1943 environment: &mut Environment<'_>,
1944 module_name: &EcoString,
1945) -> TypedFunction {
1946 let Function {
1947 documentation: doc,
1948 location,
1949 name,
1950 publicity,
1951 deprecation,
1952 arguments,
1953 body,
1954 return_annotation,
1955 end_position: end_location,
1956 body_start,
1957 return_type,
1958 external_erlang,
1959 external_javascript,
1960 external_cranelift,
1961 implementations,
1962 purity,
1963 } = function;
1964
1965 let (name_location, name) = name.expect("Function in a definition must be named");
1966
1967 // Lookup the inferred function information
1968 let function = environment
1969 .get_variable(&name)
1970 .expect("Could not find preregistered type for function");
1971 let field_map = function.field_map().cloned();
1972 let type_ = function.type_.clone();
1973
1974 let type_ = type_::generalise(type_);
1975
1976 // Insert the function into the module's interface
1977 let variant = ValueConstructorVariant::ModuleFn {
1978 documentation: doc.as_ref().map(|(_, doc)| doc.clone()),
1979 name: name.clone(),
1980 field_map,
1981 external_erlang: external_erlang
1982 .as_ref()
1983 .map(|(m, f, _)| (m.clone(), f.clone())),
1984 external_javascript: external_javascript
1985 .as_ref()
1986 .map(|(m, f, _)| (m.clone(), f.clone())),
1987 external_cranelift: external_javascript
1988 .as_ref()
1989 .map(|(m, f, _)| (m.clone(), f.clone())),
1990 module: module_name.clone(),
1991 arity: arguments.len(),
1992 location,
1993 implementations,
1994 purity,
1995 };
1996 environment.insert_variable(
1997 name.clone(),
1998 variant.clone(),
1999 type_.clone(),
2000 publicity,
2001 deprecation.clone(),
2002 );
2003 environment.insert_module_value(
2004 name.clone(),
2005 ValueConstructor {
2006 publicity,
2007 deprecation: deprecation.clone(),
2008 type_,
2009 variant,
2010 },
2011 );
2012
2013 Function {
2014 documentation: doc,
2015 location,
2016 name: Some((name_location, name)),
2017 publicity,
2018 deprecation,
2019 arguments,
2020 end_position: end_location,
2021 body_start,
2022 return_annotation,
2023 return_type,
2024 body,
2025 external_erlang,
2026 external_javascript,
2027 external_cranelift,
2028 implementations,
2029 purity,
2030 }
2031}
2032
2033fn assert_unique_name(
2034 names: &mut HashMap<EcoString, SrcSpan>,
2035 name: &EcoString,
2036 location: SrcSpan,
2037) -> Result<(), Error> {
2038 match names.insert(name.clone(), location) {
2039 Some(previous_location) => Err(Error::DuplicateName {
2040 location_a: location,
2041 location_b: previous_location,
2042 name: name.clone(),
2043 }),
2044 None => Ok(()),
2045 }
2046}
2047
2048struct Accessors {
2049 shared_accessors: HashMap<EcoString, RecordAccessor>,
2050 variant_specific_accessors: Vec<HashMap<EcoString, RecordAccessor>>,
2051 positional_accessors: Vec<Vec<Arc<Type>>>,
2052}
2053
2054fn custom_type_accessors(constructors: &[TypeValueConstructor]) -> Result<Accessors, Error> {
2055 let accessors = get_compatible_record_fields(constructors);
2056
2057 let mut shared_accessors = HashMap::with_capacity(accessors.len());
2058
2059 for accessor in accessors {
2060 let _ = shared_accessors.insert(accessor.label.clone(), accessor);
2061 }
2062
2063 let mut variant_specific_accessors = Vec::with_capacity(constructors.len());
2064 let mut positional_accessors = Vec::with_capacity(constructors.len());
2065
2066 for constructor in constructors {
2067 let mut fields = HashMap::with_capacity(constructor.parameters.len());
2068 let mut positional_fields = Vec::new();
2069
2070 for (index, parameter) in constructor.parameters.iter().enumerate() {
2071 if let Some(label) = ¶meter.label {
2072 _ = fields.insert(
2073 label.clone(),
2074 RecordAccessor {
2075 index: index as u64,
2076 label: label.clone(),
2077 type_: parameter.type_.clone(),
2078 documentation: parameter.documentation.clone(),
2079 },
2080 );
2081 } else {
2082 positional_fields.push(parameter.type_.clone());
2083 }
2084 }
2085 variant_specific_accessors.push(fields);
2086 positional_accessors.push(positional_fields);
2087 }
2088
2089 Ok(Accessors {
2090 shared_accessors,
2091 variant_specific_accessors,
2092 positional_accessors,
2093 })
2094}
2095
2096/// Returns the fields that have the same label and type across all variants of
2097/// the given type.
2098fn get_compatible_record_fields(constructors: &[TypeValueConstructor]) -> Vec<RecordAccessor> {
2099 let mut compatible = vec![];
2100
2101 let first = match constructors.first() {
2102 Some(first) => first,
2103 None => return compatible,
2104 };
2105
2106 'next_argument: for (index, first_parameter) in first.parameters.iter().enumerate() {
2107 // Fields without labels do not have accessors
2108 let first_label = match first_parameter.label.as_ref() {
2109 Some(label) => label,
2110 None => continue 'next_argument,
2111 };
2112
2113 let mut documentation = if constructors.len() == 1 {
2114 // If there is only one constructor, we simply show the documentation
2115 // for the field.
2116 first_parameter.documentation.clone()
2117 } else {
2118 // If there are multiple constructors, we show the documentation of
2119 // this field for each of the variants.
2120 first_parameter
2121 .documentation
2122 .as_ref()
2123 .map(|field_documentation| {
2124 eco_format!("## {}\n\n{}", first.name, field_documentation)
2125 })
2126 };
2127
2128 // Check each variant to see if they have an field in the same position
2129 // with the same label and the same type
2130 for constructor in constructors.iter().skip(1) {
2131 // The field must exist in all variants
2132 let parameter = match constructor.parameters.get(index) {
2133 Some(argument) => argument,
2134 None => continue 'next_argument,
2135 };
2136
2137 // The labels must be the same
2138 if parameter
2139 .label
2140 .as_ref()
2141 .is_none_or(|arg_label| arg_label != first_label)
2142 {
2143 continue 'next_argument;
2144 }
2145
2146 // The types must be the same
2147 if !parameter.type_.same_as(&first_parameter.type_) {
2148 continue 'next_argument;
2149 }
2150
2151 if let Some(field_documentation) = ¶meter.documentation {
2152 let field_documentation =
2153 eco_format!("## {}\n\n{}", constructor.name, field_documentation);
2154
2155 match &mut documentation {
2156 None => {
2157 documentation = Some(field_documentation);
2158 }
2159 Some(documentation) => {
2160 documentation.push('\n');
2161 documentation.push_str(&field_documentation);
2162 }
2163 }
2164 }
2165 }
2166
2167 // The previous loop did not find any incompatible fields in the other
2168 // variants so this field is compatible across variants and we should
2169 // generate an accessor for it.
2170
2171 compatible.push(RecordAccessor {
2172 index: index as u64,
2173 label: first_label.clone(),
2174 type_: first_parameter.type_.clone(),
2175 documentation,
2176 });
2177 }
2178
2179 compatible
2180}
2181
2182/// Given a type, return a list of all the types it depends on
2183fn get_type_dependencies(type_: &TypeAst) -> Vec<EcoString> {
2184 let mut deps = Vec::with_capacity(1);
2185
2186 match type_ {
2187 TypeAst::Var(TypeAstVar { .. }) => (),
2188 TypeAst::Hole(TypeAstHole { .. }) => (),
2189 TypeAst::Constructor(TypeAstConstructor {
2190 name, arguments, ..
2191 }) => {
2192 deps.push(match name {
2193 ast::TypeAstConstructorName::Unqualified { name, .. } => name.clone(),
2194 ast::TypeAstConstructorName::Qualified {
2195 module,
2196 name: Some((name, _)),
2197 ..
2198 } => format!("{module}.{name}").into(),
2199 ast::TypeAstConstructorName::Qualified {
2200 module, name: None, ..
2201 } => format!("{module}.").into(),
2202 });
2203
2204 for arg in arguments {
2205 deps.extend(get_type_dependencies(arg));
2206 }
2207 }
2208 TypeAst::Fn(TypeAstFn {
2209 arguments, return_, ..
2210 }) => {
2211 for arg in arguments {
2212 deps.extend(get_type_dependencies(arg));
2213 }
2214 deps.extend(get_type_dependencies(return_));
2215 }
2216 TypeAst::Tuple(TypeAstTuple { elements, .. }) => {
2217 for element in elements {
2218 deps.extend(get_type_dependencies(element));
2219 }
2220 }
2221 }
2222
2223 deps
2224}
2225
2226fn sorted_type_aliases(aliases: &Vec<UntypedTypeAlias>) -> Result<Vec<&UntypedTypeAlias>, Error> {
2227 let mut deps: Vec<(EcoString, Vec<EcoString>)> = Vec::with_capacity(aliases.len());
2228
2229 for alias in aliases {
2230 deps.push((alias.alias.clone(), get_type_dependencies(&alias.type_ast)));
2231 }
2232
2233 let sorted_deps = dep_tree::toposort_deps(deps).map_err(|err| {
2234 let dep_tree::Error::Cycle(cycle) = err;
2235
2236 let last = cycle.last().expect("Cycle should not be empty");
2237 let alias = aliases
2238 .iter()
2239 .find(|alias| alias.alias == *last)
2240 .expect("Could not find alias for cycle");
2241
2242 Error::RecursiveTypeAlias {
2243 cycle,
2244 location: alias.location,
2245 }
2246 })?;
2247
2248 Ok(aliases
2249 .iter()
2250 .sorted_by_key(|alias| sorted_deps.iter().position(|x| x == &alias.alias))
2251 .collect())
2252}