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