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