1use crate::{
2 ast::{PrimitiveType, QualifiedIdentifier, Type, TypeDef},
3 codegen::{MprotoJs, MprotoLang, MprotoRust},
4 Database,
5};
6
7pub struct CodegenCx<'a> {
8 pub db: &'a Database,
9 pub local_def_source: Option<&'a str>,
10 pub is_package: bool,
11 pub type_param_bindings: TypeParamBindings<'a>,
12}
13
14impl<'a> CodegenCx<'a> {
15 pub fn new(db: &'a Database, local_def_source: Option<&'a str>, is_package: bool) -> Self {
16 Self {
17 db,
18 local_def_source,
19 is_package,
20 type_param_bindings: TypeParamBindings::empty(),
21 }
22 }
23
24 pub fn new_with_type_params(
25 db: &'a Database,
26 local_def_source: Option<&'a str>,
27 is_package: bool,
28 type_params: &'a [impl AsRef<str>],
29 ) -> Self {
30 Self {
31 db,
32 local_def_source,
33 is_package,
34 type_param_bindings: TypeParamBindings::from_type_params(type_params),
35 }
36 }
37
38 pub fn rust_import_qualified(
39 &self,
40 qualified_identifier: &QualifiedIdentifier,
41 ) -> genco::lang::rust::Tokens {
42 MprotoRust::import_qualified(self.db, self.local_def_source, qualified_identifier)
43 }
44
45 pub fn js_import_qualified(
46 &self,
47 qualified_identifier: &QualifiedIdentifier,
48 ) -> genco::lang::js::Tokens {
49 MprotoJs::import_qualified(self.db, self.local_def_source, qualified_identifier)
50 }
51
52 pub fn with_type_param_bindings(&self, type_param_bindings: &TypeParamBindings<'a>) -> Self {
53 Self {
54 db: self.db,
55 local_def_source: self.local_def_source,
56 is_package: self.is_package,
57 type_param_bindings: type_param_bindings.clone(),
58 }
59 }
60
61 pub fn with_type_params(&self, type_params: &'a [impl AsRef<str>]) -> Self {
62 Self {
63 db: self.db,
64 local_def_source: self.local_def_source,
65 is_package: self.is_package,
66 type_param_bindings: TypeParamBindings::from_type_params(type_params),
67 }
68 }
69
70 pub fn with_type_args(
71 &'a self,
72 type_params: &'a [impl AsRef<str>],
73 type_args: &'a [Type],
74 ) -> Self {
75 Self {
76 db: self.db,
77 local_def_source: self.local_def_source,
78 is_package: self.is_package,
79 type_param_bindings: TypeParamBindings::from_type_args(
80 &self.type_param_bindings,
81 type_params,
82 type_args,
83 ),
84 }
85 }
86
87 pub fn resolve_type_param_binding(&self, type_name: &str) -> Option<TypeParamBinding<'a>> {
88 self.type_param_bindings.resolve(type_name)
89 }
90
91 pub fn resolve_type(&self, ident: &QualifiedIdentifier) -> Option<ResolvedType<'a>> {
92 if ident.module.is_none() {
93 if let Some(type_param_binding) = self.resolve_type_param_binding(&ident.name) {
94 return match type_param_binding {
95 TypeParamBinding::Unbound => Some(ResolvedType::UnboundParam),
96 TypeParamBinding::Bound { value, binding_cx } => {
97 Some(ResolvedType::BoundParam { value, binding_cx })
98 }
99 };
100 }
101 }
102
103 if let Some(type_def) = self.db.lookup_type_def(ident) {
104 Some(ResolvedType::Defined(type_def))
105 } else {
106 None
107 }
108 }
109}
110
111pub enum ResolvedType<'a> {
112 Defined(&'a TypeDef),
114 UnboundParam,
116 BoundParam {
118 value: &'a Type,
119 binding_cx: &'a TypeParamBindings<'a>,
120 },
121}
122
123#[derive(Copy, Clone)]
124pub enum TypeParamBinding<'a> {
125 Unbound,
126 Bound {
127 value: &'a Type,
128 binding_cx: &'a TypeParamBindings<'a>,
129 },
130}
131
132#[derive(Clone)]
133pub struct TypeParamBindings<'a> {
134 bindings: Vec<(&'a str, TypeParamBinding<'a>)>,
135}
136
137impl<'a> TypeParamBindings<'a> {
138 pub fn empty() -> Self {
139 Self {
140 bindings: Vec::new(),
141 }
142 }
143
144 pub fn from_type_params(type_params: &'a [impl AsRef<str>]) -> Self {
145 Self {
146 bindings: type_params
147 .iter()
148 .map(|x| (x.as_ref(), TypeParamBinding::Unbound))
149 .collect(),
150 }
151 }
152
153 pub fn from_type_args(
154 parent_bindings: &'a TypeParamBindings<'a>,
155 type_params: &'a [impl AsRef<str>],
156 type_args: &'a [Type],
157 ) -> Self {
158 Self {
159 bindings: type_params
160 .iter()
161 .map(|x| x.as_ref())
162 .zip(type_args.iter().map(|type_arg| TypeParamBinding::Bound {
163 value: type_arg,
164 binding_cx: parent_bindings,
165 }))
166 .collect(),
167 }
168 }
169
170 pub fn resolve(&self, type_name: &str) -> Option<TypeParamBinding<'a>> {
171 for &(binding_name, binding) in &self.bindings {
172 if binding_name == type_name {
173 return Some(binding);
174 }
175 }
176
177 None
178 }
179}
180
181pub fn type_uses_param(cx: &CodegenCx, ty: &Type, param_name: &str) -> bool {
182 match ty {
183 Type::Primitive(PrimitiveType::Box(inner_ty)) => type_uses_param(cx, inner_ty, param_name),
184 Type::Primitive(PrimitiveType::List(item_ty)) => type_uses_param(cx, item_ty, param_name),
185 Type::Primitive(PrimitiveType::Option(inner_ty)) => {
186 type_uses_param(cx, inner_ty, param_name)
187 }
188 Type::Primitive(PrimitiveType::Result(ok_ty, err_ty)) => {
189 type_uses_param(cx, ok_ty, param_name) || type_uses_param(cx, err_ty, param_name)
190 }
191 Type::Defined { ident, args } => match cx.resolve_type(ident) {
192 Some(ResolvedType::Defined(_)) => {
193 for arg in args {
194 if type_uses_param(cx, arg, param_name) {
195 return true;
196 }
197 }
198
199 false
200 }
201 Some(ResolvedType::UnboundParam) => ident == &QualifiedIdentifier::local(param_name),
202 Some(ResolvedType::BoundParam { .. }) => false,
203 None => {
204 panic!("type_uses_type_param failed to resolve type: {:?}", ident);
205 }
206 },
207 _ => false,
208 }
209}
210
211pub fn type_uses_type_param(cx: &CodegenCx, ty: &Type) -> bool {
212 match ty {
213 Type::Primitive(PrimitiveType::Box(inner_ty)) => type_uses_type_param(cx, inner_ty),
214 Type::Primitive(PrimitiveType::List(item_ty)) => type_uses_type_param(cx, item_ty),
215 Type::Primitive(PrimitiveType::Option(inner_ty)) => type_uses_type_param(cx, inner_ty),
216 Type::Primitive(PrimitiveType::Result(ok_ty, err_ty)) => {
217 type_uses_type_param(cx, ok_ty) || type_uses_type_param(cx, err_ty)
218 }
219 Type::Defined { ident, args } => match cx.resolve_type(ident) {
220 Some(ResolvedType::Defined(_)) => {
221 for arg in args {
222 if type_uses_type_param(cx, arg) {
223 return true;
224 }
225 }
226
227 false
228 }
229 Some(ResolvedType::UnboundParam) => true,
230 Some(ResolvedType::BoundParam { .. }) => false,
231 None => {
232 panic!("type_uses_type_param failed to resolve type: {:?}", ident);
233 }
234 },
235 _ => false,
236 }
237}