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