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