lisette_syntax/program/resolution.rs
1//! Resolution metadata attached to `Expression::Call` and
2//! `Expression::DotAccess` during type checking. Inference populates these
3//! so downstream consumers (the emitter in particular) do not re-derive the
4//! classification from the typed AST.
5
6use crate::types::Type;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum ReceiverCoercion {
10 /// Insert `&` to convert `T` to `Ref<T>`
11 AutoAddress,
12 /// Insert `*` to convert `Ref<T>` to `T`
13 AutoDeref,
14}
15
16/// What a dot access resolved to during type checking.
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum DotAccessKind {
19 /// Named struct field access
20 StructField { is_exported: bool },
21 /// Tuple struct field access (e.g., `point.0` on `struct Point(int, int)`).
22 /// `is_newtype` is true when the struct has exactly 1 field and no generics,
23 /// meaning access should emit a type cast rather than `.F0`.
24 TupleStructField { is_newtype: bool },
25 /// Tuple element access (e.g., `t.0`, `t.1`)
26 TupleElement,
27 /// Module member access (e.g., `mod.func`)
28 ModuleMember,
29 /// ADT enum variant constructor (e.g., `makeColorRed[T]()`)
30 EnumVariant,
31 /// Instance method (has `self` receiver)
32 InstanceMethod { is_exported: bool },
33 /// Instance method used as a first-class value (not called).
34 /// E.g., `Point.area` used as a callback. The emitter needs to know
35 /// whether the receiver is a pointer to emit Go method expression syntax.
36 InstanceMethodValue {
37 is_exported: bool,
38 is_pointer_receiver: bool,
39 },
40 /// Static method (no `self` receiver)
41 StaticMethod { is_exported: bool },
42}
43
44/// What kind of native built-in type (Slice, Map, Channel, etc.) a call targets.
45/// Defined here so semantics can classify calls without depending on
46/// emit-specific types. The emitter maps this to its internal `NativeGoType`.
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum NativeTypeKind {
49 Slice,
50 EnumeratedSlice,
51 Map,
52 Channel,
53 Sender,
54 Receiver,
55 String,
56}
57
58impl NativeTypeKind {
59 pub fn from_type(ty: &Type) -> Option<Self> {
60 let resolved = ty.strip_refs();
61 // Skip module namespaces and Go-imported types: their leaf name can
62 // collide with a native type (e.g. `Slice`), but they are not native.
63 if resolved.as_import_namespace().is_some() {
64 return None;
65 }
66 if let Type::Nominal { ref id, .. } = resolved
67 && id.as_str().starts_with("go:")
68 {
69 return None;
70 }
71 let name = resolved.get_name()?;
72 Self::from_name(name)
73 }
74
75 pub fn from_name(name: &str) -> Option<Self> {
76 match name {
77 "Slice" => Some(Self::Slice),
78 "EnumeratedSlice" => Some(Self::EnumeratedSlice),
79 "Map" => Some(Self::Map),
80 "Channel" => Some(Self::Channel),
81 "Sender" => Some(Self::Sender),
82 "Receiver" => Some(Self::Receiver),
83 "string" => Some(Self::String),
84 _ => None,
85 }
86 }
87}
88
89/// What a call expression resolved to during type checking.
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub enum CallKind {
92 /// Regular function or method call
93 Regular,
94 /// Tuple struct constructor (e.g., `Point(1, 2)`)
95 TupleStructConstructor,
96 /// Type assertion (`assert_type`)
97 AssertType,
98 /// UFCS method call: `receiver.method()` where method is a free function
99 UfcsMethod,
100 /// Native type constructor (e.g., `Channel.new`, `Map.new`, `Slice.new`)
101 NativeConstructor(NativeTypeKind),
102 /// Native type instance method via dot access (e.g., `slice.append(x)`)
103 NativeMethod(NativeTypeKind),
104 /// Native type method via identifier (e.g., `Slice.contains(s, x)`)
105 NativeMethodIdentifier(NativeTypeKind),
106 /// Receiver method in UFCS syntax: `Type.method(receiver, args)`
107 ReceiverMethodUfcs { is_public: bool },
108}