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