Skip to main content

lisette_semantics/checker/registration/
mod.rs

1mod builtins;
2mod convert;
3mod display;
4mod iterate;
5mod methods;
6mod types;
7
8use std::path::PathBuf;
9
10use rustc_hash::FxHashMap as HashMap;
11
12use deps::TypedefLocator;
13use syntax::ast::{
14    Annotation, Attribute, AttributeArg, Binding, EnumVariant, Expression, Generic, Span,
15    StructKind, Visibility as SyntacticVisibility,
16};
17use syntax::program::{Definition, DefinitionBody, File, FileImport, Visibility};
18use syntax::types::{Symbol, Type};
19
20use super::{FileContextKind, TaskState};
21use crate::store::Store;
22
23pub(crate) fn extract_package_directive(source: &str) -> Option<String> {
24    for line in source.lines().take(10) {
25        let line = line.trim_start();
26        if let Some(rest) = line.strip_prefix("// Package:") {
27            let name = rest.trim();
28            if !name.is_empty() {
29                return Some(name.to_string());
30            }
31        }
32        if !line.starts_with("//") && !line.is_empty() {
33            break;
34        }
35    }
36    None
37}
38
39pub(super) fn extract_go_name(attributes: &[Attribute]) -> Option<String> {
40    attributes
41        .iter()
42        .filter(|a| a.name == "go")
43        .flat_map(|a| a.args.iter())
44        .find_map(|arg| {
45            if let AttributeArg::String(name) = arg {
46                Some(name.clone())
47            } else {
48                None
49            }
50        })
51}
52
53pub(super) fn has_display_attribute(attributes: &[Attribute]) -> bool {
54    attributes.iter().any(|a| a.name == "display")
55}
56
57pub(super) fn has_closed_domain_attribute(attributes: &[Attribute]) -> bool {
58    extract_attribute_flags(attributes, "go")
59        .iter()
60        .any(|flag| flag == "closed_domain")
61}
62
63fn canonical_const_literal(expression: &Expression) -> Option<syntax::ast::Literal> {
64    use syntax::ast::{Literal, UnaryOperator};
65    match expression.unwrap_parens() {
66        Expression::Literal { literal, .. } => match literal {
67            Literal::Integer { value, .. } => Some(Literal::Integer {
68                value: *value,
69                text: None,
70            }),
71            Literal::Float { value, .. } => Some(Literal::Float {
72                value: *value,
73                text: None,
74            }),
75            Literal::Boolean(b) => Some(Literal::Boolean(*b)),
76            Literal::String { value, .. } => Some(Literal::String {
77                value: value.clone(),
78                raw: false,
79            }),
80            Literal::Char(c) => Some(Literal::Char(c.clone())),
81            _ => None,
82        },
83        Expression::Unary {
84            operator: UnaryOperator::Negative,
85            expression,
86            ..
87        } => match canonical_const_literal(expression)? {
88            Literal::Integer { value, .. } => Some(Literal::Integer {
89                value: value.wrapping_neg(),
90                text: None,
91            }),
92            Literal::Float { value, .. } => Some(Literal::Float {
93                value: -value,
94                text: None,
95            }),
96            _ => None,
97        },
98        _ => None,
99    }
100}
101
102pub(super) fn extract_attribute_flags(attributes: &[Attribute], name: &str) -> Vec<String> {
103    attributes
104        .iter()
105        .filter(|a| a.name == name)
106        .flat_map(|a| {
107            a.args.iter().filter_map(|arg| {
108                if let AttributeArg::Flag(name) = arg {
109                    Some(name.clone())
110                } else {
111                    None
112                }
113            })
114        })
115        .collect()
116}
117
118impl TaskState<'_> {
119    fn definition_exists(&self, store: &Store, qualified_name: &str) -> bool {
120        self.current_module(store)
121            .definitions
122            .contains_key(qualified_name)
123    }
124
125    fn type_definition_exists(&self, store: &Store, qualified_name: &str) -> bool {
126        self.current_module(store)
127            .definitions
128            .get(qualified_name)
129            .is_some_and(|d| {
130                matches!(
131                    d.body,
132                    DefinitionBody::Struct { .. }
133                        | DefinitionBody::Enum { .. }
134                        | DefinitionBody::Interface { .. }
135                        | DefinitionBody::TypeAlias { .. }
136                )
137            })
138    }
139
140    pub fn register_module(&mut self, store: &mut Store, id: &str) {
141        let type_name_entries =
142            self.with_module_cursor(id, |this| this.collect_module_type_name_entries(store, id));
143        self.insert_type_name_entries(store, id, type_name_entries);
144
145        let file_data = self.module_file_data(store, id);
146
147        for (file_id, imports) in &file_data {
148            self.register_file_type_definitions(store, id, *file_id, imports);
149        }
150
151        for (file_id, imports) in &file_data {
152            self.register_file_values(store, id, *file_id, imports);
153        }
154
155        self.register_module_iterate(store, id);
156        self.register_module_display(store, id);
157
158        let module = store.get_module(id).expect("module must exist");
159        let ufcs_entries = crate::call_classification::compute_module_ufcs(module, id);
160        self.ufcs_methods.extend(ufcs_entries);
161    }
162
163    /// Register a Go module (stdlib or third-party). Unlike regular modules,
164    /// Go modules export everything as public and do not put their own module
165    /// in scope (no self-references like `MyModule.Type`). `cache_path` is the
166    /// on-disk typedef location, or `None` for embedded stdlib typedefs.
167    pub fn parse_and_register_go_module(
168        &mut self,
169        store: &mut Store,
170        module_id: &str,
171        source: &str,
172        cache_path: Option<PathBuf>,
173        locator: &TypedefLocator,
174    ) {
175        if store.is_visited(module_id) {
176            return;
177        }
178
179        store.mark_visited(module_id);
180        store.add_module(module_id);
181
182        if let Some(pkg_name) = extract_package_directive(source)
183            && module_id.rsplit('/').next() != Some(pkg_name.as_str())
184        {
185            store
186                .go_package_names
187                .insert(module_id.to_string(), pkg_name);
188        }
189
190        let file_id = store.new_file_id();
191        let filename = format!("{}.d.lis", module_id.replace('/', "_"));
192
193        let build_result = syntax::build_ast(source, file_id);
194        if build_result.failed() {
195            for error in &build_result.errors {
196                eprintln!("bindgen: error parsing {}: {:?}", filename, error);
197            }
198        }
199
200        let file = File {
201            id: file_id,
202            module_id: module_id.to_string(),
203            name: filename.clone(),
204            display_path: filename,
205            source: source.to_string(),
206            items: build_result.ast,
207        };
208
209        let imports = file.imports();
210
211        for import in &imports {
212            if let Some(go_pkg) = import.name.strip_prefix("go:") {
213                if matches!(import.alias, Some(syntax::ast::ImportAlias::Blank(_))) {
214                    continue;
215                }
216
217                let import_module_id = format!("go:{}", go_pkg);
218
219                if store.is_visited(&import_module_id) {
220                    continue;
221                }
222
223                match locator.find_typedef_content(go_pkg) {
224                    deps::TypedefLocatorResult::Found { content, origin } => {
225                        self.parse_and_register_go_module(
226                            store,
227                            &import_module_id,
228                            content.as_ref(),
229                            origin.into_cache_path(),
230                            locator,
231                        );
232                    }
233                    other => {
234                        crate::diagnostics::emit_for_locator_result(
235                            &other,
236                            &import.name,
237                            go_pkg,
238                            Some(import.name_span),
239                            locator.target(),
240                            false,
241                            self.sink,
242                        );
243                    }
244                }
245            }
246        }
247
248        if let Some(path) = cache_path {
249            store.typedef_paths.insert(file_id, path);
250        }
251        store.store_file(module_id, file);
252
253        self.with_file_context_mut(
254            store,
255            module_id,
256            file_id,
257            &imports,
258            FileContextKind::ImportedTypedef,
259            |this, store| {
260                let items = std::mem::take(
261                    &mut store
262                        .get_file_mut(file_id)
263                        .expect("file must exist after store_file")
264                        .items,
265                );
266                this.register_types_and_values(store, &items, &Visibility::Public);
267            },
268        );
269    }
270
271    fn collect_module_type_name_entries(
272        &self,
273        store: &Store,
274        module_id: &str,
275    ) -> Vec<(Symbol, Definition)> {
276        let module = store
277            .get_module(module_id)
278            .expect("module must exist for declaration");
279        let mut entries = Vec::new();
280        for file in module.files.values() {
281            entries.extend(self.collect_type_name_entries(
282                &file.items,
283                &Visibility::Private,
284                false,
285            ));
286        }
287        for file in module.all_typedefs() {
288            entries.extend(self.collect_type_name_entries(&file.items, &Visibility::Private, true));
289        }
290        entries
291    }
292
293    fn insert_type_name_entries(
294        &mut self,
295        store: &mut Store,
296        module_id: &str,
297        type_name_entries: Vec<(Symbol, Definition)>,
298    ) {
299        let module = store
300            .get_module_mut(module_id)
301            .expect("module must exist for declaration");
302        for (qualified_name, definition) in type_name_entries {
303            module
304                .definitions
305                .entry(qualified_name)
306                .or_insert(definition);
307        }
308    }
309
310    fn module_file_data(&self, store: &Store, module_id: &str) -> Vec<(u32, Vec<FileImport>)> {
311        let module = store
312            .get_module(module_id)
313            .expect("module must exist for declaration");
314        module
315            .files
316            .iter()
317            .chain(module.typedefs.iter())
318            .map(|(file_id, f)| (*file_id, f.imports()))
319            .collect()
320    }
321
322    fn register_file_type_definitions(
323        &mut self,
324        store: &mut Store,
325        module_id: &str,
326        file_id: u32,
327        imports: &[FileImport],
328    ) {
329        self.with_file_context_mut(
330            store,
331            module_id,
332            file_id,
333            imports,
334            FileContextKind::Standard,
335            |this, store| {
336                let items = std::mem::take(
337                    &mut store
338                        .get_file_mut(file_id)
339                        .expect("file must exist for registration")
340                        .items,
341                );
342
343                this.register_type_definitions(store, &items);
344
345                store
346                    .get_file_mut(file_id)
347                    .expect("file must exist after registration")
348                    .items = items;
349            },
350        );
351    }
352
353    fn register_file_values(
354        &mut self,
355        store: &mut Store,
356        module_id: &str,
357        file_id: u32,
358        imports: &[FileImport],
359    ) {
360        self.with_file_context_mut(
361            store,
362            module_id,
363            file_id,
364            imports,
365            FileContextKind::Standard,
366            |this, store| {
367                let items = std::mem::take(
368                    &mut store
369                        .get_file_mut(file_id)
370                        .expect("file must exist for registration")
371                        .items,
372                );
373
374                this.register_impl_blocks(store, &items);
375                this.register_values(store, &items, &Visibility::Private);
376
377                store
378                    .get_file_mut(file_id)
379                    .expect("file must exist after registration")
380                    .items = items;
381            },
382        );
383    }
384
385    pub fn register_types_and_values(
386        &mut self,
387        store: &mut Store,
388        items: &[Expression],
389        visibility: &Visibility,
390    ) {
391        self.register_type_names(store, items, visibility);
392        self.register_type_definitions(store, items);
393        self.register_impl_blocks(store, items);
394        self.register_values(store, items, visibility);
395        self.register_iterate(store, items);
396        self.register_display(store, items);
397    }
398
399    pub fn register_type_names(
400        &mut self,
401        store: &mut Store,
402        items: &[Expression],
403        visibility: &Visibility,
404    ) {
405        let entries = self.collect_type_name_entries(items, visibility, self.is_d_lis(&*store));
406        let module = self.current_module_mut(store);
407        for (qualified_name, definition) in entries {
408            module
409                .definitions
410                .entry(qualified_name)
411                .or_insert(definition);
412        }
413    }
414
415    fn collect_type_name_entries(
416        &self,
417        items: &[Expression],
418        visibility: &Visibility,
419        is_typedef: bool,
420    ) -> Vec<(Symbol, Definition)> {
421        let mut entries = Vec::new();
422
423        for item in items {
424            let (name, generics, syntactic_visibility) = match item {
425                Expression::Enum {
426                    name,
427                    generics,
428                    visibility,
429                    ..
430                } => (name, generics, *visibility),
431                Expression::Struct {
432                    name,
433                    generics,
434                    visibility,
435                    ..
436                } => (name, generics, *visibility),
437                Expression::Interface {
438                    name,
439                    generics,
440                    visibility,
441                    ..
442                } => (name, generics, *visibility),
443                Expression::TypeAlias {
444                    name,
445                    generics,
446                    visibility,
447                    ..
448                } => (name, generics, *visibility),
449                _ => continue,
450            };
451
452            let qualified_name = self.qualify_name(name);
453            let args: Vec<Type> = generics
454                .iter()
455                .map(|g| Type::Parameter(g.name.clone()))
456                .collect();
457
458            // Canonical form for prelude-registered native types uses the
459            // dedicated Simple/Compound variants; everything else remains a
460            // nominal Constructor.
461            let canonical_ty = if self.cursor.module_id == "prelude" {
462                if let Some(simple) = syntax::types::SimpleKind::from_name(name) {
463                    debug_assert!(args.is_empty(), "simple kinds have no generics");
464                    Type::Simple(simple)
465                } else if let Some(compound) = syntax::types::CompoundKind::from_name(name) {
466                    Type::Compound {
467                        kind: compound,
468                        args,
469                    }
470                } else {
471                    Type::Nominal {
472                        id: qualified_name.clone(),
473                        params: args,
474                        underlying_ty: None,
475                    }
476                }
477            } else {
478                Type::Nominal {
479                    id: qualified_name.clone(),
480                    params: args,
481                    underlying_ty: None,
482                }
483            };
484
485            let ty = if generics.is_empty() {
486                canonical_ty
487            } else {
488                Type::Forall {
489                    vars: generics.iter().map(|g| g.name.clone()).collect(),
490                    body: Box::new(canonical_ty),
491                }
492            };
493
494            let item_visibility = match visibility {
495                Visibility::Local => Visibility::Local,
496                _ => {
497                    if syntactic_visibility == SyntacticVisibility::Public || is_typedef {
498                        Visibility::Public
499                    } else {
500                        Visibility::Private
501                    }
502                }
503            };
504
505            entries.push((
506                qualified_name,
507                Definition {
508                    visibility: item_visibility,
509                    ty,
510                    name: None,
511                    name_span: None,
512                    doc: None,
513                    body: DefinitionBody::Value {
514                        allowed_lints: vec![],
515                        go_hints: vec![],
516                        go_name: None,
517                        const_value: None,
518                    },
519                },
520            ));
521        }
522
523        entries
524    }
525
526    pub fn register_type_definitions(&mut self, store: &mut Store, items: &[Expression]) {
527        for item in items {
528            if let Expression::TypeAlias {
529                name,
530                name_span,
531                generics,
532                annotation,
533                span,
534                doc,
535                ..
536            } = item
537            {
538                self.populate_type_alias(store, name, name_span, generics, annotation, span, doc);
539            }
540        }
541
542        for item in items {
543            match item {
544                Expression::Enum {
545                    name,
546                    name_span,
547                    generics,
548                    variants,
549                    span,
550                    doc,
551                    attributes,
552                    ..
553                } => self.populate_enum(
554                    store,
555                    name,
556                    name_span,
557                    generics,
558                    variants,
559                    span,
560                    doc,
561                    has_display_attribute(attributes),
562                ),
563                Expression::Struct {
564                    name,
565                    name_span,
566                    generics,
567                    fields,
568                    kind,
569                    span,
570                    doc,
571                    attributes,
572                    ..
573                } => self.populate_struct(
574                    store,
575                    name,
576                    name_span,
577                    generics,
578                    fields,
579                    *kind,
580                    span,
581                    doc,
582                    has_display_attribute(attributes),
583                    has_closed_domain_attribute(attributes),
584                ),
585                Expression::Interface {
586                    name,
587                    name_span,
588                    generics,
589                    parents,
590                    method_signatures,
591                    span,
592                    doc,
593                    ..
594                } => self.populate_interface(
595                    store,
596                    name,
597                    name_span,
598                    generics,
599                    parents,
600                    method_signatures,
601                    span,
602                    doc,
603                ),
604                _ => (),
605            }
606        }
607    }
608
609    pub fn register_impl_blocks(&mut self, store: &mut Store, items: &[Expression]) {
610        for item in items {
611            if let Expression::ImplBlock {
612                annotation,
613                methods,
614                generics,
615                span,
616                ..
617            } = item
618            {
619                self.populate_impl_methods(store, annotation, generics, methods, span);
620            }
621        }
622    }
623
624    fn compute_item_visibility(
625        &self,
626        store: &Store,
627        syntactic: &SyntacticVisibility,
628        scope: &Visibility,
629    ) -> Visibility {
630        match scope {
631            Visibility::Local => Visibility::Local,
632            _ if *syntactic == SyntacticVisibility::Public || self.is_d_lis(store) => {
633                Visibility::Public
634            }
635            _ => Visibility::Private,
636        }
637    }
638
639    pub fn register_values(
640        &mut self,
641        store: &mut Store,
642        items: &[Expression],
643        visibility: &Visibility,
644    ) {
645        for item in items {
646            match item {
647                Expression::Function { .. } => {
648                    self.register_function_value(store, item, visibility)
649                }
650                Expression::Const { .. } => self.register_const_value(store, item, visibility),
651                Expression::VariableDeclaration { .. } => {
652                    self.register_variable_declaration(store, item, visibility)
653                }
654                Expression::Struct {
655                    kind: StructKind::Tuple,
656                    ..
657                } => self.register_tuple_struct_constructor(store, item),
658                _ => (),
659            }
660        }
661    }
662
663    fn register_function_value(
664        &mut self,
665        store: &mut Store,
666        item: &Expression,
667        visibility: &Visibility,
668    ) {
669        let Expression::Function {
670            name,
671            name_span,
672            attributes,
673            generics,
674            params,
675            return_annotation,
676            span,
677            body,
678            visibility: syntactic_visibility,
679            doc,
680            ..
681        } = item
682        else {
683            return;
684        };
685
686        if body.is_noop() && self.is_lis(&*store) {
687            self.sink
688                .push(diagnostics::infer::bodyless_function_outside_typedef(*span));
689        }
690
691        let qualified_name = self.qualify_name(name);
692
693        self.scopes.push();
694        self.put_in_scope(generics);
695
696        let fn_ty = self.extract_signature_parts(store, generics, params, return_annotation, span);
697
698        self.scopes.pop();
699
700        let item_visibility =
701            self.compute_item_visibility(&*store, syntactic_visibility, visibility);
702
703        if self.is_lis(&*store) && self.definition_exists(&*store, &qualified_name) {
704            self.sink.push(diagnostics::infer::duplicate_definition(
705                "function", name, *name_span,
706            ));
707        }
708
709        let module = self.current_module_mut(store);
710        module.definitions.insert(
711            qualified_name,
712            Definition {
713                visibility: item_visibility,
714                ty: fn_ty,
715                name: None,
716                name_span: Some(*name_span),
717                doc: doc.clone(),
718                body: DefinitionBody::Value {
719                    allowed_lints: extract_attribute_flags(attributes, "allow"),
720                    go_hints: extract_attribute_flags(attributes, "go"),
721                    go_name: extract_go_name(attributes),
722                    const_value: None,
723                },
724            },
725        );
726    }
727
728    fn register_const_value(
729        &mut self,
730        store: &mut Store,
731        item: &Expression,
732        visibility: &Visibility,
733    ) {
734        let Expression::Const {
735            identifier,
736            identifier_span,
737            annotation: maybe_annotation,
738            expression,
739            span,
740            visibility: syntactic_visibility,
741            doc,
742            ..
743        } = item
744        else {
745            return;
746        };
747
748        let has_value = !expression.is_noop();
749
750        if !has_value && self.is_lis(&*store) {
751            self.sink
752                .push(diagnostics::infer::valueless_const_outside_typedef(*span));
753        }
754
755        if !has_value && maybe_annotation.is_none() && self.is_d_lis(&*store) {
756            self.sink
757                .push(diagnostics::infer::valueless_const_missing_annotation(
758                    *span,
759                ));
760        }
761
762        let qualified_name = self.qualify_name(identifier);
763
764        let before = self.sink.len();
765        let const_ty = if let Some(annotation) = maybe_annotation {
766            self.convert_to_type(store, annotation, span)
767        } else {
768            self.type_from_literal_expression(expression)
769                .unwrap_or_else(|| self.new_type_var())
770        };
771        self.sink.truncate(before);
772
773        let item_visibility =
774            self.compute_item_visibility(&*store, syntactic_visibility, visibility);
775
776        if self.is_lis(&*store) && self.definition_exists(&*store, &qualified_name) {
777            self.sink.push(diagnostics::infer::duplicate_definition(
778                "constant",
779                identifier,
780                *identifier_span,
781            ));
782        }
783
784        let const_value = canonical_const_literal(expression);
785
786        let module = self.current_module_mut(store);
787        module.const_names.insert(qualified_name.clone());
788        module.definitions.insert(
789            qualified_name,
790            Definition {
791                visibility: item_visibility,
792                ty: const_ty,
793                name: None,
794                name_span: Some(*identifier_span),
795                doc: doc.clone(),
796                body: DefinitionBody::Value {
797                    allowed_lints: vec![],
798                    go_hints: vec![],
799                    go_name: None,
800                    const_value,
801                },
802            },
803        );
804    }
805
806    fn register_variable_declaration(
807        &mut self,
808        store: &mut Store,
809        item: &Expression,
810        visibility: &Visibility,
811    ) {
812        let Expression::VariableDeclaration {
813            name,
814            name_span,
815            annotation,
816            span,
817            visibility: syntactic_visibility,
818            doc,
819            ..
820        } = item
821        else {
822            return;
823        };
824
825        if self.is_lis(&*store) {
826            self.sink
827                .push(diagnostics::infer::variable_declaration_outside_typedef(
828                    *span,
829                ));
830        }
831
832        let qualified_name = self.qualify_name(name);
833        let var_ty = self.convert_to_type(&*store, annotation, span);
834
835        let item_visibility =
836            self.compute_item_visibility(&*store, syntactic_visibility, visibility);
837
838        let module = self.current_module_mut(store);
839        module.definitions.insert(
840            qualified_name,
841            Definition {
842                visibility: item_visibility,
843                ty: var_ty,
844                name: None,
845                name_span: Some(*name_span),
846                doc: doc.clone(),
847                body: DefinitionBody::Value {
848                    allowed_lints: vec![],
849                    go_hints: vec![],
850                    go_name: None,
851                    const_value: None,
852                },
853            },
854        );
855    }
856
857    fn register_tuple_struct_constructor(&mut self, store: &mut Store, item: &Expression) {
858        let Expression::Struct {
859            name,
860            generics,
861            fields,
862            kind: StructKind::Tuple,
863            span,
864            ..
865        } = item
866        else {
867            return;
868        };
869
870        let qualified_name = self.qualify_name(name);
871        let struct_ty = store
872            .get_type(&qualified_name)
873            .expect("struct type scheme must exist")
874            .clone();
875
876        self.scopes.push();
877        self.put_in_scope(generics);
878
879        let field_types: Vec<Type> = fields
880            .iter()
881            .map(|f| self.convert_to_type(&*store, &f.annotation, span))
882            .collect();
883
884        self.scopes.pop();
885
886        let constructor_ty =
887            tuple_struct_constructor_type_from_fields(&field_types, &struct_ty, generics);
888
889        let scope = self.scopes.current_mut();
890        scope
891            .values
892            .insert(qualified_name.to_string(), constructor_ty.clone());
893        scope
894            .values
895            .insert(name.to_string(), constructor_ty.clone());
896
897        let module = self.current_module_mut(store);
898        if let Some(def) = module.definitions.get_mut(qualified_name.as_str())
899            && let DefinitionBody::Struct { constructor, .. } = &mut def.body
900        {
901            *constructor = Some(constructor_ty);
902        }
903    }
904
905    pub(crate) fn extract_signature_parts(
906        &mut self,
907        store: &Store,
908        generics: &[Generic],
909        params: &[Binding],
910        return_annotation: &Annotation,
911        span: &Span,
912    ) -> Type {
913        self.scopes.push();
914        self.put_in_scope(generics);
915
916        let mut bounds = vec![];
917
918        for g in generics {
919            let qualified_name = self.qualify_name(&g.name);
920
921            for b in &g.bounds {
922                let bound_ty = self.register_bound_annotation(store, b, span);
923
924                self.scopes
925                    .current_mut()
926                    .trait_bounds
927                    .get_or_insert_with(HashMap::default)
928                    .entry(qualified_name.clone())
929                    .or_default()
930                    .push(bound_ty.clone());
931
932                bounds.push(syntax::types::Bound {
933                    param_name: g.name.clone(),
934                    generic: Type::Parameter(g.name.clone()),
935                    ty: bound_ty,
936                });
937            }
938        }
939
940        let before = self.sink.len();
941
942        let param_types: Vec<Type> = params
943            .iter()
944            .map(|binding| {
945                binding
946                    .annotation
947                    .as_ref()
948                    .map(|a| self.convert_to_type(store, a, span))
949                    .unwrap_or_else(|| self.new_type_var())
950            })
951            .collect();
952
953        let return_ty = match return_annotation {
954            Annotation::Unknown => self.type_unit(),
955            _ => self.convert_to_type(store, return_annotation, span),
956        };
957
958        self.sink.truncate(before);
959
960        self.scopes.pop();
961
962        let param_mutability: Vec<bool> = params.iter().map(|b| b.mutable).collect();
963
964        let base_fn_ty = Type::function(param_types, param_mutability, bounds, return_ty.into());
965
966        if generics.is_empty() {
967            base_fn_ty
968        } else {
969            Type::Forall {
970                vars: generics.iter().map(|g| g.name.clone()).collect(),
971                body: Box::new(base_fn_ty),
972            }
973        }
974    }
975}
976
977pub(super) fn enum_variant_constructor_type(
978    enum_variant: &EnumVariant,
979    enum_ty: &Type,
980    generics: &[Generic],
981) -> Type {
982    if enum_variant.fields.is_empty() {
983        return enum_ty.clone();
984    }
985
986    let return_type = match enum_ty {
987        Type::Forall { body, .. } => body.as_ref().clone(),
988        _ => enum_ty.clone(),
989    };
990
991    let fn_ty = Type::function(
992        enum_variant.fields.iter().map(|f| f.ty.clone()).collect(),
993        vec![false; enum_variant.fields.len()],
994        Default::default(),
995        return_type.into(),
996    );
997
998    if generics.is_empty() {
999        fn_ty
1000    } else {
1001        Type::Forall {
1002            vars: generics.iter().map(|g| g.name.clone()).collect(),
1003            body: Box::new(fn_ty),
1004        }
1005    }
1006}
1007
1008fn tuple_struct_constructor_type_from_fields(
1009    field_types: &[Type],
1010    struct_ty: &Type,
1011    generics: &[Generic],
1012) -> Type {
1013    let return_type = match struct_ty {
1014        Type::Forall { body, .. } => body.as_ref().clone(),
1015        _ => struct_ty.clone(),
1016    };
1017
1018    let fn_ty = Type::function(
1019        field_types.to_vec(),
1020        vec![false; field_types.len()],
1021        Default::default(),
1022        return_type.into(),
1023    );
1024
1025    if generics.is_empty() {
1026        fn_ty
1027    } else {
1028        Type::Forall {
1029            vars: generics.iter().map(|g| g.name.clone()).collect(),
1030            body: Box::new(fn_ty),
1031        }
1032    }
1033}
1034
1035pub(super) fn wrap_with_impl_generics(
1036    fn_ty: &Type,
1037    generics: &[Generic],
1038    impl_bounds: &[syntax::types::Bound],
1039) -> Type {
1040    if generics.is_empty() {
1041        return fn_ty.clone();
1042    }
1043
1044    let impl_vars: Vec<syntax::EcoString> = generics.iter().map(|g| g.name.clone()).collect();
1045
1046    let add_impl_bounds = |existing_bounds: &[syntax::types::Bound]| -> Vec<syntax::types::Bound> {
1047        impl_bounds
1048            .iter()
1049            .cloned()
1050            .chain(existing_bounds.iter().cloned())
1051            .collect()
1052    };
1053
1054    match fn_ty {
1055        Type::Forall { vars, body } => {
1056            let new_body = match body.as_ref() {
1057                Type::Function(f) => Type::function(
1058                    f.params.clone(),
1059                    f.param_mutability.clone(),
1060                    add_impl_bounds(&f.bounds),
1061                    f.return_type.clone(),
1062                ),
1063                _ => *body.clone(),
1064            };
1065            Type::Forall {
1066                vars: impl_vars.into_iter().chain(vars.clone()).collect(),
1067                body: Box::new(new_body),
1068            }
1069        }
1070        Type::Function(f) => Type::Forall {
1071            vars: impl_vars,
1072            body: Box::new(Type::function(
1073                f.params.clone(),
1074                f.param_mutability.clone(),
1075                add_impl_bounds(&f.bounds),
1076                f.return_type.clone(),
1077            )),
1078        },
1079        _ => Type::Forall {
1080            vars: impl_vars,
1081            body: Box::new(fn_ty.clone()),
1082        },
1083    }
1084}
1085
1086fn type_contains_constructor(target_id: &str, ty: &Type) -> bool {
1087    walk_type(ty, &|id, _| id == target_id)
1088}
1089
1090/// Check if a type contains a recursive generic instantiation.
1091/// E.g., a method on `Box<T>` returning `Box<Box<T>>` creates a Go instantiation cycle.
1092/// Returns true if `ty` contains `target_id` nested within itself (e.g. `Box<Box<T>>`).
1093pub(super) fn has_recursive_instantiation(target_id: &str, ty: &Type) -> bool {
1094    walk_type(ty, &|id, params| {
1095        id == target_id
1096            && params
1097                .iter()
1098                .any(|p| type_contains_constructor(target_id, p))
1099    })
1100}
1101
1102fn walk_type(ty: &Type, predicate: &dyn Fn(&str, &[Type]) -> bool) -> bool {
1103    if let Type::Nominal { id, params, .. } = ty
1104        && predicate(id, params)
1105    {
1106        return true;
1107    }
1108    ty.children().iter().any(|c| walk_type(c, predicate))
1109}