Fork of daniellemaywood.uk/gleam — Wasm codegen work
11 kB
321 lines
1use ecow::EcoString;
2
3use crate::{
4 ast::{Publicity, SrcSpan, UnqualifiedImport, UntypedImport},
5 build::Origin,
6 reference::{EntityKind, ReferenceKind},
7 type_::{
8 Environment, Error, ModuleInterface, Problems, ValueConstructorVariant,
9 error::InvalidImportKind,
10 },
11};
12
13use super::Imported;
14
15#[derive(Debug)]
16pub struct Importer<'context, 'problems> {
17 origin: Origin,
18 environment: Environment<'context>,
19 problems: &'problems mut Problems,
20}
21
22impl<'context, 'problems> Importer<'context, 'problems> {
23 pub fn new(
24 origin: Origin,
25 environment: Environment<'context>,
26 problems: &'problems mut Problems,
27 ) -> Self {
28 Self {
29 origin,
30 environment,
31 problems,
32 }
33 }
34
35 pub fn run<'code>(
36 origin: Origin,
37 env: Environment<'context>,
38 imports: &'code [UntypedImport],
39 problems: &'problems mut Problems,
40 ) -> Environment<'context> {
41 let mut importer = Self::new(origin, env, problems);
42 for import in imports {
43 importer.register_import(import)
44 }
45 importer.environment
46 }
47
48 fn register_import(&mut self, import: &UntypedImport) {
49 let location = import.location;
50 let name = import.module.clone();
51
52 // Find imported module
53 let Some(module_info) = self.environment.importable_modules.get(&name) else {
54 self.problems.error(Error::UnknownModule {
55 location,
56 name: name.clone(),
57 suggestions: self.environment.suggest_modules(&name, Imported::Module),
58 });
59 return;
60 };
61
62 if let Err(e) = self.check_for_invalid_imports(module_info, location, name.clone()) {
63 self.problems.error(e);
64 return;
65 }
66
67 if let Err(e) = self.register_module(import, module_info) {
68 self.problems.error(e);
69 return;
70 }
71
72 // Insert unqualified imports into scope
73 let module_name = &module_info.name;
74 for type_ in &import.unqualified_types {
75 self.register_unqualified_type(type_, module_name.clone(), module_info);
76 }
77 for value in &import.unqualified_values {
78 self.register_unqualified_value(value, module_name.clone(), module_info);
79 }
80 }
81
82 fn register_unqualified_type(
83 &mut self,
84 import: &UnqualifiedImport,
85 module_name: EcoString,
86 module: &ModuleInterface,
87 ) {
88 let imported_name = import.as_name.as_ref().unwrap_or(&import.name);
89
90 // Register the unqualified import if it is a type constructor
91 let Some(type_info) = module.get_public_type(&import.name) else {
92 // TODO: refine to a type specific error
93 self.problems.error(Error::UnknownModuleType {
94 location: import.location,
95 name: import.name.clone(),
96 module_name: module.name.clone(),
97 type_constructors: module.public_type_names(),
98 value_with_same_name: module.get_public_value(&import.name).is_some(),
99 });
100 return;
101 };
102
103 let type_info = type_info.clone().with_location(import.location);
104
105 self.environment.names.type_in_scope(
106 imported_name.clone(),
107 type_info.type_.as_ref(),
108 &type_info.parameters,
109 );
110
111 self.environment.references.register_type(
112 imported_name.clone(),
113 EntityKind::ImportedType {
114 module: module_name,
115 },
116 import.location,
117 Publicity::Private,
118 );
119
120 self.environment.references.register_type_reference(
121 type_info.module.clone(),
122 import.name.clone(),
123 imported_name,
124 import.imported_name_location,
125 ReferenceKind::Import,
126 );
127
128 if let Err(e) = self
129 .environment
130 .insert_type_constructor(imported_name.clone(), type_info)
131 {
132 self.problems.error(e);
133 }
134 }
135
136 fn register_unqualified_value(
137 &mut self,
138 import: &UnqualifiedImport,
139 module_name: EcoString,
140 module: &ModuleInterface,
141 ) {
142 let import_name = &import.name;
143 let location = import.location;
144 let used_name = import.as_name.as_ref().unwrap_or(&import.name);
145
146 // Register the unqualified import if it is a value
147 let variant = match module.get_public_value(import_name) {
148 Some(value) => {
149 let implementations = value.variant.implementations();
150 // Check the target support of the imported value
151 if self.environment.target_support.is_enforced()
152 && !implementations.supports(self.environment.target)
153 {
154 self.problems.error(Error::UnsupportedExpressionTarget {
155 target: self.environment.target,
156 location,
157 })
158 }
159
160 self.environment.insert_variable(
161 used_name.clone(),
162 value.variant.clone(),
163 value.type_.clone(),
164 value.publicity,
165 value.deprecation.clone(),
166 );
167 &value.variant
168 }
169 None => {
170 self.problems.error(Error::UnknownModuleValue {
171 location,
172 name: import_name.clone(),
173 module_name: module.name.clone(),
174 value_constructors: module.public_value_names(),
175 type_with_same_name: module.get_public_type(import_name).is_some(),
176 context: crate::type_::error::ModuleValueUsageContext::UnqualifiedImport,
177 });
178 return;
179 }
180 };
181
182 match variant {
183 ValueConstructorVariant::Record { name, module, .. } => {
184 self.environment.names.named_constructor_in_scope(
185 module.clone(),
186 name.clone(),
187 used_name.clone(),
188 );
189 self.environment.references.register_value(
190 used_name.clone(),
191 EntityKind::ImportedConstructor {
192 module: module_name,
193 },
194 location,
195 Publicity::Private,
196 );
197
198 self.environment.references.register_value_reference(
199 module.clone(),
200 import_name.clone(),
201 used_name,
202 import.imported_name_location,
203 ReferenceKind::Import,
204 );
205 }
206 ValueConstructorVariant::ModuleConstant { module, .. }
207 | ValueConstructorVariant::ModuleFn { module, .. } => {
208 self.environment.references.register_value(
209 used_name.clone(),
210 EntityKind::ImportedValue {
211 module: module_name,
212 },
213 location,
214 Publicity::Private,
215 );
216 self.environment.references.register_value_reference(
217 module.clone(),
218 import_name.clone(),
219 used_name,
220 import.imported_name_location,
221 ReferenceKind::Import,
222 );
223 }
224 _ => {}
225 };
226
227 // Check if value already was imported
228 if let Some(previous) = self.environment.unqualified_imported_names.get(used_name) {
229 self.problems.error(Error::DuplicateImport {
230 location,
231 previous_location: *previous,
232 name: import_name.clone(),
233 });
234 return;
235 }
236
237 // Register the name as imported so it can't be imported a
238 // second time in future
239 let _ = self
240 .environment
241 .unqualified_imported_names
242 .insert(used_name.clone(), location);
243 }
244
245 /// Check for invalid imports, such as `src` importing `test` or `dev`.
246 fn check_for_invalid_imports(
247 &mut self,
248 module_info: &ModuleInterface,
249 location: SrcSpan,
250 imported_module: EcoString,
251 ) -> Result<(), Error> {
252 let kind = match (self.origin, module_info.origin) {
253 // `src` cannot import `test` or `dev`
254 (Origin::Src, Origin::Test) => InvalidImportKind::SrcImportingTest,
255 (Origin::Src, Origin::Dev) => InvalidImportKind::SrcImportingDev,
256 // `dev` cannot import `test`
257 (Origin::Dev, Origin::Test) => InvalidImportKind::DevImportingTest,
258 _ => return Ok(()),
259 };
260
261 Err(Error::InvalidImport {
262 location,
263 importing_module: self.environment.current_module.clone(),
264 imported_module,
265 kind,
266 })
267 }
268
269 fn register_module(
270 &mut self,
271 import: &UntypedImport,
272 import_info: &'context ModuleInterface,
273 ) -> Result<(), Error> {
274 if let Some(used_name) = import.used_name() {
275 self.check_not_a_duplicate_import(&used_name, import.location)?;
276
277 if let Some(alias_location) = import.alias_location() {
278 self.environment.references.register_aliased_module(
279 used_name.clone(),
280 import.module.clone(),
281 alias_location,
282 import.location,
283 );
284 } else {
285 self.environment.references.register_module(
286 used_name.clone(),
287 import.module.clone(),
288 import.location,
289 );
290 }
291
292 // Insert imported module into scope
293 let _ = self
294 .environment
295 .imported_modules
296 .insert(used_name.clone(), (import.location, import_info));
297
298 self.environment
299 .names
300 .imported_module(import.module.clone(), used_name);
301 };
302
303 Ok(())
304 }
305
306 fn check_not_a_duplicate_import(
307 &self,
308 used_name: &EcoString,
309 location: SrcSpan,
310 ) -> Result<(), Error> {
311 // Check if a module was already imported with this name
312 if let Some((previous_location, _)) = self.environment.imported_modules.get(used_name) {
313 return Err(Error::DuplicateImport {
314 location,
315 previous_location: *previous_location,
316 name: used_name.clone(),
317 });
318 }
319 Ok(())
320 }
321}