Skip to main content

lisette_semantics/checker/registration/
mod.rs

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