Skip to main content

lisette_syntax/program/
emit_input.rs

1use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
2
3use ecow::EcoString;
4
5use crate::ast::{BindingId as AstBindingId, Pattern, RestPattern, Span};
6use crate::types::Type;
7
8use super::{Definition, File, ModuleInfo};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11struct ReceiverId(Span);
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum ReceiverCoercion {
15    /// Insert `&` to convert `T` to `Ref<T>`
16    AutoAddress,
17    /// Insert `*` to convert `Ref<T>` to `T`
18    AutoDeref,
19}
20
21#[derive(Debug, Clone, Default)]
22pub struct CoercionInfo {
23    receivers: HashMap<ReceiverId, ReceiverCoercion>,
24}
25
26impl CoercionInfo {
27    pub fn mark_coercion(&mut self, span: Span, coercion: ReceiverCoercion) {
28        self.receivers.insert(ReceiverId(span), coercion);
29    }
30
31    pub fn get_coercion(&self, span: Span) -> Option<ReceiverCoercion> {
32        self.receivers.get(&ReceiverId(span)).copied()
33    }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37struct BindingId(Span);
38
39#[derive(Debug, Clone, Default)]
40pub struct UnusedInfo {
41    bindings: HashSet<BindingId>,
42    definitions: HashSet<BindingId>,
43    pub imports_by_module: HashMap<EcoString, HashSet<EcoString>>,
44}
45
46impl UnusedInfo {
47    pub fn mark_binding_unused(&mut self, span: Span) {
48        self.bindings.insert(BindingId(span));
49    }
50
51    pub fn is_unused_binding(&self, pattern: &Pattern) -> bool {
52        match pattern {
53            Pattern::Identifier { span, .. } => self.bindings.contains(&BindingId(*span)),
54            Pattern::AsBinding { span, name, .. } => {
55                let name_span = Span::new(
56                    span.file_id,
57                    span.byte_offset + span.byte_length - name.len() as u32,
58                    name.len() as u32,
59                );
60                self.bindings.contains(&BindingId(name_span))
61            }
62            _ => false,
63        }
64    }
65
66    pub fn is_unused_rest_binding(&self, rest: &RestPattern) -> bool {
67        match rest {
68            RestPattern::Bind { span, .. } => self.bindings.contains(&BindingId(*span)),
69            _ => false,
70        }
71    }
72
73    pub fn mark_definition_unused(&mut self, span: Span) {
74        self.definitions.insert(BindingId(span));
75    }
76
77    pub fn is_unused_definition(&self, span: &Span) -> bool {
78        self.definitions.contains(&BindingId(*span))
79    }
80}
81
82#[derive(Debug, Clone, Default)]
83pub struct MutationInfo {
84    bindings: HashSet<AstBindingId>,
85}
86
87impl MutationInfo {
88    pub fn mark_binding_mutated(&mut self, id: AstBindingId) {
89        self.bindings.insert(id);
90    }
91
92    pub fn is_mutated(&self, id: AstBindingId) -> bool {
93        self.bindings.contains(&id)
94    }
95}
96
97/// What a dot access resolved to during type checking.
98///
99/// Pre-computed in semantics to avoid re-derivation in the emitter.
100/// The emitter can use this to skip cascading `try_classify_*` lookups.
101/// `is_exported` indicates whether the Go name should be capitalized.
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103pub enum DotAccessKind {
104    /// Named struct field access
105    StructField { is_exported: bool },
106    /// Tuple struct field access (e.g., `point.0` on `struct Point(int, int)`).
107    /// `is_newtype` is true when the struct has exactly 1 field and no generics,
108    /// meaning access should emit a type cast rather than `.F0`.
109    TupleStructField { is_newtype: bool },
110    /// Tuple element access (e.g., `t.0`, `t.1`)
111    TupleElement,
112    /// Module member access (e.g., `mod.func`)
113    ModuleMember,
114    /// Value enum variant (Go constant, e.g., `reflect.String`)
115    ValueEnumVariant,
116    /// ADT enum variant constructor (e.g., `makeColorRed[T]()`)
117    EnumVariant,
118    /// Instance method (has `self` receiver)
119    InstanceMethod { is_exported: bool },
120    /// Instance method used as a first-class value (not called).
121    /// E.g., `Point.area` used as a callback. The emitter needs to know
122    /// whether the receiver is a pointer to emit Go method expression syntax.
123    InstanceMethodValue {
124        is_exported: bool,
125        is_pointer_receiver: bool,
126    },
127    /// Static method (no `self` receiver)
128    StaticMethod { is_exported: bool },
129}
130
131/// What kind of native built-in type (Slice, Map, Channel, etc.) a call targets.
132///
133/// Defined in `syntax` so that semantics can classify calls without depending on
134/// emit-specific types. The emitter maps this to its internal `NativeGoType`.
135#[derive(Debug, Clone, Copy, PartialEq, Eq)]
136pub enum NativeTypeKind {
137    Slice,
138    EnumeratedSlice,
139    Map,
140    Channel,
141    Sender,
142    Receiver,
143    String,
144}
145
146impl NativeTypeKind {
147    pub fn from_type(ty: &Type) -> Option<Self> {
148        let resolved = ty.resolve().strip_refs();
149        if let Type::Constructor { ref id, .. } = resolved
150            && (id.starts_with("@import/go:") || id.starts_with("go:"))
151        {
152            return None;
153        }
154        let name = resolved.get_name()?;
155        Self::from_name(name)
156    }
157
158    pub fn from_name(name: &str) -> Option<Self> {
159        match name {
160            "Slice" => Some(Self::Slice),
161            "EnumeratedSlice" => Some(Self::EnumeratedSlice),
162            "Map" => Some(Self::Map),
163            "Channel" => Some(Self::Channel),
164            "Sender" => Some(Self::Sender),
165            "Receiver" => Some(Self::Receiver),
166            "string" => Some(Self::String),
167            _ => None,
168        }
169    }
170}
171
172/// What a call expression resolved to during type checking.
173///
174/// Pre-computed in semantics to avoid re-derivation in the emitter's call dispatch.
175#[derive(Debug, Clone, Copy, PartialEq, Eq)]
176pub enum CallKind {
177    /// Regular function or method call
178    Regular,
179    /// Tuple struct constructor (e.g., `Point(1, 2)`)
180    TupleStructConstructor,
181    /// Type assertion (`assert_type`)
182    AssertType,
183    /// UFCS method call: `receiver.method()` where method is a free function
184    UfcsMethod,
185    /// Native type constructor (e.g., `Channel.new`, `Map.new`, `Slice.new`)
186    NativeConstructor(NativeTypeKind),
187    /// Native type instance method via dot access (e.g., `slice.append(x)`)
188    NativeMethod(NativeTypeKind),
189    /// Native type method via identifier (e.g., `Slice.contains(s, x)`)
190    NativeMethodIdentifier(NativeTypeKind),
191    /// Receiver method in UFCS syntax: `Type.method(receiver, args)`
192    ReceiverMethodUfcs { is_public: bool },
193}
194
195#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
196struct ResolutionId(Span);
197
198/// Pre-computed resolution metadata from type checking.
199///
200/// Follows the same pattern as `CoercionInfo`: keyed by expression span,
201/// populated during inference, consumed by the emitter.
202#[derive(Debug, Clone, Default)]
203pub struct ResolutionInfo {
204    dot_accesses: HashMap<ResolutionId, DotAccessKind>,
205    calls: HashMap<ResolutionId, CallKind>,
206}
207
208impl ResolutionInfo {
209    pub fn mark_dot_access(&mut self, span: Span, kind: DotAccessKind) {
210        self.dot_accesses.insert(ResolutionId(span), kind);
211    }
212
213    pub fn get_dot_access(&self, span: Span) -> Option<DotAccessKind> {
214        self.dot_accesses.get(&ResolutionId(span)).copied()
215    }
216
217    pub fn mark_call(&mut self, span: Span, meta: CallKind) {
218        self.calls.insert(ResolutionId(span), meta);
219    }
220
221    pub fn get_call(&self, span: Span) -> Option<CallKind> {
222        self.calls.get(&ResolutionId(span)).copied()
223    }
224}
225
226pub struct EmitInput {
227    pub files: HashMap<u32, File>,
228    pub definitions: HashMap<EcoString, Definition>,
229    pub modules: HashMap<String, ModuleInfo>,
230    pub entry_module_id: String,
231    pub unused: UnusedInfo,
232    pub mutations: MutationInfo,
233    pub coercions: CoercionInfo,
234    pub resolutions: ResolutionInfo,
235    pub cached_modules: HashSet<String>,
236    pub ufcs_methods: HashSet<(String, String)>,
237    pub go_package_names: HashMap<String, String>,
238}