Skip to main content

lisette_semantics/checker/registration/
mod.rs

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