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