Fork of daniellemaywood.uk/gleam — Wasm codegen work
12 kB
348 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, Warning,
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) {
63 self.problems.error(e);
64 }
65
66 if let Err(e) = self.register_module(import, module_info) {
67 self.problems.error(e);
68 return;
69 }
70
71 // Insert unqualified imports into scope
72 let module_name = &module_info.name;
73 for type_ in &import.unqualified_types {
74 self.register_unqualified_type(type_, module_name.clone(), module_info);
75 }
76 for value in &import.unqualified_values {
77 self.register_unqualified_value(value, module_name.clone(), module_info);
78 }
79 }
80
81 fn register_unqualified_type(
82 &mut self,
83 import: &UnqualifiedImport,
84 module_name: EcoString,
85 module: &ModuleInterface,
86 ) {
87 let imported_name = import.as_name.as_ref().unwrap_or(&import.name);
88
89 // Register the unqualified import if it is a type constructor
90 let Some(type_info) = module.get_public_type(&import.name) else {
91 // TODO: refine to a type specific error
92 self.problems.error(Error::UnknownModuleType {
93 location: import.location,
94 name: import.name.clone(),
95 module_name: module.name.clone(),
96 type_constructors: module.public_type_names(),
97 value_with_same_name: module.get_public_value(&import.name).is_some(),
98 });
99 return;
100 };
101
102 let type_info = type_info.clone().with_location(import.location);
103
104 self.environment.names.type_in_scope(
105 imported_name.clone(),
106 type_info.type_.as_ref(),
107 &type_info.parameters,
108 );
109
110 self.environment.references.register_type(
111 imported_name.clone(),
112 EntityKind::ImportedType {
113 module: module_name,
114 },
115 import.location,
116 Publicity::Private,
117 );
118
119 self.environment.references.register_type_reference(
120 type_info.module.clone(),
121 import.name.clone(),
122 imported_name,
123 import.imported_name_location,
124 ReferenceKind::Import,
125 );
126
127 if let Err(e) = self
128 .environment
129 .insert_type_constructor(imported_name.clone(), type_info)
130 {
131 self.problems.error(e);
132 }
133 }
134
135 fn register_unqualified_value(
136 &mut self,
137 import: &UnqualifiedImport,
138 module_name: EcoString,
139 module: &ModuleInterface,
140 ) {
141 let import_name = &import.name;
142 let location = import.location;
143 let used_name = import.as_name.as_ref().unwrap_or(&import.name);
144
145 // Register the unqualified import if it is a value
146 let variant = match module.get_public_value(import_name) {
147 Some(value) => {
148 let implementations = value.variant.implementations();
149 // Check the target support of the imported value
150 if self.environment.target_support.is_enforced()
151 && !implementations.supports(self.environment.target)
152 {
153 self.problems.error(Error::UnsupportedExpressionTarget {
154 target: self.environment.target,
155 location,
156 })
157 }
158
159 self.environment.insert_variable(
160 used_name.clone(),
161 value.variant.clone(),
162 value.type_.clone(),
163 value.publicity,
164 value.deprecation.clone(),
165 );
166 &value.variant
167 }
168 None => {
169 self.problems.error(Error::UnknownModuleValue {
170 location,
171 name: import_name.clone(),
172 module_name: module.name.clone(),
173 value_constructors: module.public_value_names(),
174 type_with_same_name: module.get_public_type(import_name).is_some(),
175 context: crate::type_::error::ModuleValueUsageContext::UnqualifiedImport,
176 });
177 return;
178 }
179 };
180
181 match variant {
182 ValueConstructorVariant::Record { name, module, .. } => {
183 self.environment.names.named_constructor_in_scope(
184 module.clone(),
185 name.clone(),
186 used_name.clone(),
187 );
188 self.environment.references.register_value(
189 used_name.clone(),
190 EntityKind::ImportedConstructor {
191 module: module_name,
192 },
193 location,
194 Publicity::Private,
195 );
196
197 self.environment.references.register_value_reference(
198 module.clone(),
199 import_name.clone(),
200 used_name,
201 import.imported_name_location,
202 ReferenceKind::Import,
203 );
204 }
205 ValueConstructorVariant::ModuleConstant { module, .. }
206 | ValueConstructorVariant::ModuleFn { module, .. } => {
207 self.environment.references.register_value(
208 used_name.clone(),
209 EntityKind::ImportedValue {
210 module: module_name,
211 },
212 location,
213 Publicity::Private,
214 );
215 self.environment.references.register_value_reference(
216 module.clone(),
217 import_name.clone(),
218 used_name,
219 import.imported_name_location,
220 ReferenceKind::Import,
221 );
222 }
223 ValueConstructorVariant::LocalVariable { .. } => {}
224 };
225
226 // Check if value already was imported
227 if let Some(previous) = self.environment.unqualified_imported_names.get(used_name) {
228 self.problems.error(Error::DuplicateImport {
229 location,
230 previous_location: *previous,
231 name: import_name.clone(),
232 });
233 return;
234 }
235
236 // Register the name as imported so it can't be imported a
237 // second time in future
238 let _ = self
239 .environment
240 .unqualified_imported_names
241 .insert(used_name.clone(), location);
242 }
243
244 /// Check for invalid imports, such as `src` importing `test` or `dev`.
245 fn check_for_invalid_imports(
246 &mut self,
247 module_info: &ModuleInterface,
248 location: SrcSpan,
249 ) -> Result<(), Error> {
250 if self.origin.is_src()
251 && self
252 .environment
253 .dev_dependencies
254 .contains(&module_info.package)
255 {
256 return Err(Error::SrcImportingDevDependency {
257 importing_module: self.environment.current_module.clone(),
258 imported_module: module_info.name.clone(),
259 package: module_info.package.clone(),
260 location,
261 });
262 }
263
264 let kind = match (self.origin, module_info.origin) {
265 // `src` cannot import `test` or `dev`
266 (Origin::Src, Origin::Test) => InvalidImportKind::SrcImportingTest,
267 (Origin::Src, Origin::Dev) => InvalidImportKind::SrcImportingDev,
268 // `dev` cannot import `test`
269 (Origin::Dev, Origin::Test) => InvalidImportKind::DevImportingTest,
270 _ => return Ok(()),
271 };
272
273 Err(Error::InvalidImport {
274 location,
275 importing_module: self.environment.current_module.clone(),
276 imported_module: module_info.name.clone(),
277 kind,
278 })
279 }
280
281 fn register_module(
282 &mut self,
283 import: &UntypedImport,
284 import_info: &'context ModuleInterface,
285 ) -> Result<(), Error> {
286 let Some(used_name) = import.used_name() else {
287 return Ok(());
288 };
289
290 self.check_not_a_duplicate_import(&used_name, import.location)?;
291
292 if let Some(alias_location) = import.alias_location() {
293 self.environment.references.register_aliased_module(
294 used_name.clone(),
295 import.module.clone(),
296 alias_location,
297 import.location,
298 );
299 } else {
300 self.environment.references.register_module(
301 used_name.clone(),
302 import.module.clone(),
303 import.location,
304 );
305 }
306
307 // Insert imported module into scope
308 let _ = self
309 .environment
310 .imported_modules
311 .insert(used_name.clone(), (import.location, import_info));
312
313 // Register this module as being imported
314 //
315 // Emit a warning if the module had already been imported.
316 // This isn't an error so long as the modules have different local aliases. In Gleam v2
317 // this will likely become an error.
318 if let Some(previous) = self.environment.names.imported_module(
319 import.module.clone(),
320 used_name,
321 import.location,
322 ) {
323 self.problems.warning(Warning::ModuleImportedTwice {
324 name: import.module.clone(),
325 first: previous,
326 second: import.location,
327 });
328 }
329
330 Ok(())
331 }
332
333 fn check_not_a_duplicate_import(
334 &self,
335 used_name: &EcoString,
336 location: SrcSpan,
337 ) -> Result<(), Error> {
338 // Check if a module was already imported with this name
339 if let Some((previous_location, _)) = self.environment.imported_modules.get(used_name) {
340 return Err(Error::DuplicateImport {
341 location,
342 previous_location: *previous_location,
343 name: used_name.clone(),
344 });
345 }
346 Ok(())
347 }
348}