Skip to main content

lisette_semantics/checker/registration/
mod.rs

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