1pub mod freeze;
2pub mod infer;
3pub(crate) mod registration;
4pub mod scopes;
5pub mod type_env;
6
7use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
8use std::cell::RefCell;
9use std::rc::Rc;
10use std::sync::Arc;
11
12use crate::facts::{BindingIdAllocator, Facts};
13use crate::promotion;
14use crate::store::Store;
15use diagnostics::LocalSink;
16use ecow::EcoString;
17use scopes::Scopes;
18use syntax::ast::Visibility as AstVisibility;
19use syntax::ast::{Annotation, Expression, Generic, ImportAlias, Span, StructFieldDefinition};
20use syntax::program::{Definition, DefinitionBody, File, FileImport, MethodSignatures, Module};
21use syntax::types::{SubstitutionMap, Symbol, Type, substitute};
22
23pub use type_env::{EnvResolve, Speculation, TypeEnv, VarState};
24
25#[derive(Debug, Clone)]
26pub struct Cursor {
27 pub module_id: String,
28 pub file_id: Option<u32>,
29}
30
31impl Default for Cursor {
32 fn default() -> Self {
33 Self {
34 module_id: "std".to_string(),
35 file_id: None,
36 }
37 }
38}
39
40impl Cursor {
41 pub fn new() -> Self {
42 Self::default()
43 }
44}
45
46#[derive(Debug, Default)]
47pub struct ImportState {
48 pub imported_modules: HashMap<String, (Arc<[StructFieldDefinition]>, Type)>,
51 pub prefix_to_module: HashMap<String, String>,
53 pub unprefixed_imports: HashSet<String>,
55 pub failed_imports: HashSet<String>,
58}
59
60impl ImportState {
61 pub fn new() -> Self {
62 Self::default()
63 }
64
65 pub fn clear(&mut self) {
66 let prelude = self.imported_modules.remove("prelude");
68 self.imported_modules.clear();
69 if let Some(p) = prelude {
70 self.imported_modules.insert("prelude".to_string(), p);
71 }
72 let prelude_mapping = self.prefix_to_module.remove("prelude");
73 self.prefix_to_module.clear();
74 if let Some(m) = prelude_mapping {
75 self.prefix_to_module.insert("prelude".to_string(), m);
76 }
77 self.unprefixed_imports.clear();
78 self.failed_imports.clear();
79 }
80}
81
82#[derive(Debug, Clone, Copy)]
83pub(crate) enum FileContextKind {
84 Standard,
85 ImportedTypedef,
86 Prelude,
87}
88
89struct SavedFileContext {
90 file_id: Option<u32>,
91 scopes: Scopes,
92 imports: ImportState,
93}
94
95type BuiltinCache = HashMap<String, Type>;
98
99pub struct TaskState<'s> {
101 pub env: TypeEnv,
102 pub scopes: Scopes,
103 pub cursor: Cursor,
104 pub imports: ImportState,
105 pub builtins: BuiltinCache,
106 pub sink: &'s LocalSink,
107 pub facts: Facts,
108 pub satisfying_stack: rustc_hash::FxHashSet<(String, String)>,
112 method_cache: RefCell<HashMap<EcoString, Rc<MethodSignatures>>>,
113 module_fields_cache: RefCell<HashMap<EcoString, Arc<[StructFieldDefinition]>>>,
116 pub module_fields_shared: Option<Arc<HashMap<EcoString, Arc<[StructFieldDefinition]>>>>,
119 pub ufcs_methods: HashSet<(String, String)>,
120 pub ufcs_shared: Option<Arc<HashSet<(String, String)>>>,
122 pub typed_files: Vec<(String, File)>,
124 pub bound_position_depth: u32,
128}
129
130impl<'s> TaskState<'s> {
131 pub fn new(sink: &'s LocalSink, binding_ids: Arc<BindingIdAllocator>) -> Self {
132 Self {
133 env: TypeEnv::new(),
134 scopes: Scopes::new(),
135 cursor: Cursor::new(),
136 imports: ImportState::new(),
137 builtins: BuiltinCache::default(),
138 sink,
139 facts: Facts::new(binding_ids),
140 satisfying_stack: rustc_hash::FxHashSet::default(),
141 method_cache: RefCell::new(HashMap::default()),
142 module_fields_cache: RefCell::new(HashMap::default()),
143 module_fields_shared: None,
144 ufcs_methods: HashSet::default(),
145 ufcs_shared: None,
146 typed_files: Vec::new(),
147 bound_position_depth: 0,
148 }
149 }
150
151 pub fn with_fresh_allocator(sink: &'s LocalSink) -> Self {
152 Self::new(sink, Arc::new(BindingIdAllocator::new()))
153 }
154
155 fn effective_ufcs_methods(&self) -> &HashSet<(String, String)> {
156 self.ufcs_shared.as_deref().unwrap_or(&self.ufcs_methods)
157 }
158
159 pub fn new_type_var(&mut self) -> Type {
160 let id = self.env.fresh(None);
161 Type::Var { id, hint: None }
162 }
163
164 pub fn new_type_var_with_hint(&mut self, hint: &str) -> Type {
165 let hint: EcoString = hint.into();
166 let id = self.env.fresh(Some(hint.clone()));
167 Type::Var {
168 id,
169 hint: Some(hint),
170 }
171 }
172
173 pub fn type_from_literal_expression(&mut self, expression: &Expression) -> Option<Type> {
174 use syntax::ast::{Expression, Literal};
175 match expression {
176 Expression::Literal { literal, .. } => match literal {
177 Literal::Integer { .. } => Some(self.type_int()),
178 Literal::Float { .. } => Some(self.type_float()),
179 Literal::Boolean(_) => Some(self.type_bool()),
180 Literal::String { .. } => Some(self.type_string()),
181 Literal::Char(_) => Some(self.type_char()),
182 _ => None,
183 },
184 Expression::Unary { expression, .. } => self.type_from_literal_expression(expression),
185 _ => None,
186 }
187 }
188
189 pub fn instantiate(&mut self, ty: &Type) -> (Type, SubstitutionMap) {
190 match ty {
191 Type::Forall { vars, body } => {
192 let map: SubstitutionMap = vars
193 .iter()
194 .map(|name| {
195 let id = self.env.fresh(Some(name.clone()));
196 let fresh_var = Type::Var {
197 id,
198 hint: Some(name.clone()),
199 };
200 (name.clone(), fresh_var)
201 })
202 .collect();
203
204 (substitute(body, &map), map)
205 }
206 _ => (ty.clone(), HashMap::default()),
207 }
208 }
209
210 pub fn new_file_id(&mut self, store: &Store) -> u32 {
211 store.new_file_id()
212 }
213
214 pub fn is_d_lis(&self, store: &Store) -> bool {
215 let Some(file_id) = self.cursor.file_id else {
216 return false;
217 };
218
219 let Some(module) = store.get_module(&self.cursor.module_id) else {
220 return false;
221 };
222
223 module.typedefs.contains_key(&file_id)
224 }
225
226 pub fn is_lis(&self, store: &Store) -> bool {
227 !self.is_d_lis(store)
228 }
229
230 pub(crate) fn current_module<'a>(&self, store: &'a Store) -> &'a Module {
231 store
232 .get_module(&self.cursor.module_id)
233 .expect("current module must exist in store")
234 }
235
236 pub(crate) fn current_module_mut<'a>(&self, store: &'a mut Store) -> &'a mut Module {
237 store
238 .get_module_mut(&self.cursor.module_id)
239 .expect("current module must exist in store")
240 }
241
242 pub(crate) fn qualify_name(&self, name: &str) -> Symbol {
243 Symbol::from_parts(&self.cursor.module_id, name)
244 }
245
246 pub(crate) fn put_in_scope(&mut self, generics: &[Generic]) {
247 for (index, generic) in generics.iter().enumerate() {
248 self.scopes
249 .current_mut()
250 .type_params
251 .get_or_insert_with(HashMap::default)
252 .insert(generic.name.to_string(), index);
253 }
254 }
255
256 pub(crate) fn validate_generic_bounds(
258 &mut self,
259 store: &Store,
260 generics: &[Generic],
261 span: &Span,
262 ) {
263 for g in generics {
264 for b in &g.bounds {
265 self.register_bound_annotation(store, b, span);
266 }
267 }
268 }
269
270 pub(crate) fn register_bound_annotation(
271 &mut self,
272 store: &Store,
273 bound: &Annotation,
274 span: &Span,
275 ) -> Type {
276 let resolved = self.convert_bound_to_type(store, bound, span);
277 if self.is_lis(store) && resolved.contains_unknown() {
278 self.sink
279 .push(diagnostics::infer::unknown_in_bound_position(
280 bound.get_span(),
281 ));
282 }
283 resolved
284 }
285
286 fn resolve_in_imported_module<'m>(
291 &self,
292 module: &'m Module,
293 simple_name: &str,
294 ) -> Option<(String, &'m Definition)> {
295 let module_prefix = format!("{}.", module.id);
296
297 let direct = format!("{}{}", module_prefix, simple_name);
299 if let Some(definition) = module.definitions.get(direct.as_str())
300 && definition.visibility().is_public()
301 {
302 return Some((direct, definition));
303 }
304
305 let suffix = format!(".{}", simple_name);
310 for (qn, definition) in &module.definitions {
311 if qn.ends_with(suffix.as_str())
312 && qn.starts_with(module_prefix.as_str())
313 && definition.visibility().is_public()
314 {
315 let rest = &qn[module_prefix.len()..];
316 if rest.contains('.') {
318 return Some((qn.to_string(), definition));
319 }
320 }
321 }
322
323 None
324 }
325
326 pub(crate) fn lookup_qualified_name(
327 &self,
328 store: &Store,
329 type_name: &str,
330 ) -> Option<EcoString> {
331 self.lookup_qualified_name_in_scope(store, type_name, false)
332 }
333
334 fn lookup_qualified_name_in_type_position(
335 &self,
336 store: &Store,
337 type_name: &str,
338 ) -> Option<EcoString> {
339 self.lookup_qualified_name_in_scope(store, type_name, true)
340 }
341
342 fn lookup_qualified_name_in_scope(
343 &self,
344 store: &Store,
345 type_name: &str,
346 prefer_type: bool,
347 ) -> Option<EcoString> {
348 if let Some((prefix, simple_name)) = type_name.split_once('.')
349 && let Some(module_id) = self.imports.prefix_to_module.get(prefix)
350 && let Some(imported_module) = store.get_module(module_id)
351 && let Some((qualified_name, _)) =
352 self.resolve_in_imported_module(imported_module, simple_name)
353 {
354 return Some(qualified_name.into());
355 }
356
357 let module_ids = std::iter::once(self.cursor.module_id.as_str())
358 .chain(self.imports.unprefixed_imports.iter().map(String::as_str));
359
360 let mut value_fallback: Option<EcoString> = None;
361 for module_id in module_ids {
362 let Some(module) = store.get_module(module_id) else {
363 continue;
364 };
365 let qualified_name = Symbol::from_parts(module_id, type_name);
366 let Some(definition) = module.definitions.get(qualified_name.as_str()) else {
367 continue;
368 };
369
370 if prefer_type && definition.is_value(qualified_name.as_str()) {
371 if value_fallback.is_none() {
372 value_fallback = Some(qualified_name.as_eco().clone());
373 }
374 } else {
375 return Some(qualified_name.as_eco().clone());
376 }
377 }
378
379 value_fallback
380 }
381
382 pub(crate) fn get_definition_name_span(
383 &self,
384 store: &Store,
385 qualified_name: &str,
386 ) -> Option<Span> {
387 store.get_definition(qualified_name)?.name_span()
388 }
389
390 pub(crate) fn is_const_name(&self, store: &Store, qualified_name: &str) -> bool {
391 if qualified_name.starts_with("go:") {
392 return false;
393 }
394 store
395 .module_for_qualified_name(qualified_name)
396 .and_then(|module_id| store.get_module(module_id))
397 .is_some_and(|module| module.const_names.contains(qualified_name))
398 }
399
400 pub(crate) fn is_const_var(&self, store: &Store, var_name: &str) -> bool {
401 if self.scopes.lookup_binding_id(var_name).is_some() {
402 return false;
403 }
404 if self.scopes.lookup_const(var_name) {
405 return true;
406 }
407 self.lookup_qualified_name(store, var_name)
408 .is_some_and(|qname| self.is_const_name(store, &qname))
409 }
410
411 pub(crate) fn track_name_usage(
413 &mut self,
414 store: &Store,
415 qualified_name: &str,
416 span: &Span,
417 name_len: u32,
418 ) {
419 if let Some(definition_span) = self.get_definition_name_span(store, qualified_name) {
420 let usage_span = Span::new(span.file_id, span.byte_offset, name_len);
421 self.facts.add_usage(usage_span, definition_span);
422 }
423 }
424
425 pub(crate) fn lookup_generic_index(&self, type_name: &str) -> Option<usize> {
426 self.scopes.lookup_type_param(type_name)
427 }
428
429 fn resolve_definition_value_type(&self, store: &Store, definition: &Definition) -> Type {
432 if let DefinitionBody::Struct {
433 constructor: Some(ctor_ty),
434 ..
435 } = &definition.body
436 {
437 return ctor_ty.clone();
438 }
439
440 if let DefinitionBody::TypeAlias { .. } = &definition.body {
442 let alias_ty = &definition.ty;
443 let underlying = match alias_ty {
444 Type::Forall { body, .. } => body.as_ref(),
445 other => other,
446 };
447 if let Type::Nominal { id, .. } = underlying
448 && let Some(Definition {
449 body:
450 DefinitionBody::Struct {
451 constructor: Some(ctor_ty),
452 ..
453 },
454 ..
455 }) = store.get_definition(id)
456 {
457 return ctor_ty.clone();
458 }
459 }
460
461 definition.ty().clone()
462 }
463
464 pub(crate) fn lookup_type(&self, store: &Store, value_name: &str) -> Option<Type> {
465 if let Some(ty) = self.scopes.lookup_value(value_name) {
466 return Some(ty.clone());
467 }
468
469 if let Some((_definition, ty)) = self.imports.imported_modules.get(value_name) {
470 return Some(ty.clone());
471 }
472
473 if let Some((prefix, rest)) = value_name.split_once('.')
474 && let Some(module_id) = self.imports.prefix_to_module.get(prefix)
475 && let Some(imported_module) = store.get_module(module_id)
476 && let Some((_, definition)) = self.resolve_in_imported_module(imported_module, rest)
477 {
478 return Some(self.resolve_definition_value_type(store, definition));
479 }
480
481 let module = store.get_module(&self.cursor.module_id)?;
482 let qualified_name = Symbol::from_parts(&module.id, value_name);
483
484 if let Some(definition) = module.definitions.get(qualified_name.as_str()) {
485 return Some(self.resolve_definition_value_type(store, definition));
486 }
487
488 for imported_module_id in &self.imports.unprefixed_imports {
489 if let Some(imported_module) = store.get_module(imported_module_id) {
490 let qualified_name = Symbol::from_parts(imported_module_id, value_name);
491 if let Some(definition) = imported_module.definitions.get(qualified_name.as_str()) {
492 return Some(self.resolve_definition_value_type(store, definition));
493 }
494 }
495 }
496
497 None
498 }
499
500 pub(crate) fn is_enum_type(&self, store: &Store, ty: &Type) -> bool {
501 let Type::Nominal { id, .. } = ty else {
502 return false;
503 };
504 let Some(definition) = store.get_definition(id) else {
505 return false;
506 };
507 matches!(definition.body, DefinitionBody::Enum { .. })
508 }
509
510 pub(crate) fn resolve_type_name(
511 &mut self,
512 store: &Store,
513 type_name: &str,
514 ) -> Option<(String, Type)> {
515 if self.scopes.lookup_type_param(type_name).is_some() {
516 return None;
517 }
518
519 let qualified_name = self.lookup_qualified_name_in_type_position(store, type_name)?;
520 let ty = store.get_type(&qualified_name)?.clone();
521
522 Some((qualified_name.to_string(), ty))
523 }
524
525 pub(crate) fn resolve_type_from_prelude(
526 &self,
527 store: &Store,
528 type_name: &str,
529 ) -> Option<(String, Type)> {
530 let qualified_name = format!("prelude.{}", type_name);
531 let ty = store.get_type(&qualified_name)?.clone();
532 Some((qualified_name, ty))
533 }
534
535 pub(crate) fn get_all_methods(&self, store: &Store, ty: &Type) -> Rc<MethodSignatures> {
536 if let Type::Parameter(name) = ty {
537 let trait_bounds = self.scopes.collect_all_trait_bounds();
538 let qualified_name = self.qualify_name(name);
539 return Rc::new(store.get_methods_from_bounds(&qualified_name, &trait_bounds));
540 }
541
542 let resolved = ty.strip_refs().resolve_in(&self.env);
543 let cache_key: EcoString = match &resolved {
544 Type::Nominal { id, .. } => id.as_eco().clone(),
545 Type::Compound { kind, .. } => format!("prelude.{}", kind.leaf_name()).into(),
546 Type::Simple(kind) => format!("prelude.{}", kind.leaf_name()).into(),
547 _ => return Rc::new(MethodSignatures::default()),
548 };
549
550 let peeled = store.peel_alias(&resolved);
552 if let Type::Nominal { id: peeled_id, .. } = &peeled
553 && store.get_interface(peeled_id).is_some()
554 {
555 let empty = HashMap::default();
556 return Rc::new(store.get_all_methods(&peeled, &empty));
557 }
558
559 let is_embedder = promotion::has_direct_embed(store, &resolved);
560 let is_generic = matches!(&resolved, Type::Nominal { params, .. } if !params.is_empty());
561 let cacheable = !(is_embedder && is_generic);
562
563 if cacheable && let Some(cached) = self.method_cache.borrow().get(cache_key.as_str()) {
564 return cached.clone();
565 }
566
567 let methods = if is_embedder {
568 Rc::new(promotion::promoted_method_set(store, &resolved))
569 } else {
570 let empty = HashMap::default();
571 Rc::new(store.get_all_methods(&resolved, &empty))
572 };
573 if cacheable {
574 self.method_cache
575 .borrow_mut()
576 .insert(cache_key, methods.clone());
577 }
578 methods
579 }
580
581 pub fn reset_scopes(&mut self) {
582 self.scopes.reset();
583 self.imports.clear();
584 }
585
586 pub(crate) fn with_module_cursor<T>(
587 &mut self,
588 module_id: &str,
589 f: impl FnOnce(&mut Self) -> T,
590 ) -> T {
591 if self.cursor.module_id == module_id {
592 return f(self);
593 }
594
595 let previous_module_id = std::mem::replace(&mut self.cursor.module_id, module_id.into());
596 let result = f(self);
597 self.cursor.module_id = previous_module_id;
598 result
599 }
600
601 pub(crate) fn with_file_context<T>(
602 &mut self,
603 store: &Store,
604 module_id: &str,
605 file_id: u32,
606 imports: &[FileImport],
607 kind: FileContextKind,
608 f: impl FnOnce(&mut Self, &Store) -> T,
609 ) -> T {
610 self.with_module_cursor(module_id, |this| {
611 let saved = this.enter_file_context(store, module_id, file_id, imports, kind);
612 let result = f(this, store);
613 this.exit_file_context(saved);
614 result
615 })
616 }
617
618 pub(crate) fn with_file_context_mut<T>(
619 &mut self,
620 store: &mut Store,
621 module_id: &str,
622 file_id: u32,
623 imports: &[FileImport],
624 kind: FileContextKind,
625 f: impl FnOnce(&mut Self, &mut Store) -> T,
626 ) -> T {
627 self.with_module_cursor(module_id, |this| {
628 let saved = this.enter_file_context(&*store, module_id, file_id, imports, kind);
629 let result = f(this, store);
630 this.exit_file_context(saved);
631 result
632 })
633 }
634
635 fn enter_file_context(
636 &mut self,
637 store: &Store,
638 module_id: &str,
639 file_id: u32,
640 imports: &[FileImport],
641 kind: FileContextKind,
642 ) -> SavedFileContext {
643 let saved = SavedFileContext {
644 file_id: self.cursor.file_id.replace(file_id),
645 scopes: std::mem::take(&mut self.scopes),
646 imports: std::mem::take(&mut self.imports),
647 };
648
649 match kind {
650 FileContextKind::Standard => {
651 self.put_prelude_in_scope(store);
652 self.put_unprefixed_module_in_scope(store, module_id);
653 }
654 FileContextKind::ImportedTypedef => {
655 self.put_prelude_in_scope(store);
656 let self_alias = store
657 .go_package_names
658 .get(module_id)
659 .cloned()
660 .unwrap_or_else(|| go_module_last_segment(module_id).to_string());
661 self.imports
662 .prefix_to_module
663 .insert(self_alias, module_id.into());
664 }
665 FileContextKind::Prelude => {
666 self.put_unprefixed_module_in_scope(store, module_id);
667 }
668 }
669 self.put_imported_modules_in_scope(store, imports);
670
671 saved
672 }
673
674 fn exit_file_context(&mut self, saved: SavedFileContext) {
675 self.scopes = saved.scopes;
676 self.imports = saved.imports;
677 self.cursor.file_id = saved.file_id;
678 }
679
680 pub fn failed(&self) -> bool {
681 self.sink.has_errors()
682 }
683
684 pub fn put_prelude_in_scope(&mut self, store: &Store) {
685 self.put_unprefixed_module_in_scope(store, "prelude");
686 if self.imports.imported_modules.contains_key("prelude") {
687 return;
688 }
689 self.put_module_in_scope(store, "prelude", Some("prelude".to_string()));
690 }
691
692 pub fn put_unprefixed_module_in_scope(&mut self, store: &Store, module_id: &str) {
693 self.put_module_in_scope(store, module_id, None)
694 }
695
696 pub fn put_imported_modules_in_scope(&mut self, store: &Store, imports: &[FileImport]) {
697 let mut seen_aliases: HashMap<String, String> = HashMap::default(); let mut seen_paths: HashSet<String> = HashSet::default();
699
700 for import in imports {
701 if seen_paths.contains(import.name.as_str()) {
702 self.sink.push(diagnostics::infer::duplicate_import_path(
703 &import.name,
704 import.name_span,
705 ));
706 continue;
707 }
708 seen_paths.insert(import.name.to_string());
709
710 if matches!(import.alias, Some(ImportAlias::Blank(_))) {
711 continue;
712 }
713
714 if let Some(ImportAlias::Named(alias, alias_span)) = &import.alias
715 && is_reserved_import_alias(alias)
716 {
717 self.sink.push(diagnostics::infer::reserved_import_alias(
718 alias,
719 *alias_span,
720 ));
721 continue;
722 }
723
724 let Some(effective) = import.effective_alias(&store.go_package_names) else {
725 continue;
726 };
727
728 if let Some(existing_path) = seen_aliases.get(&effective)
729 && existing_path != &import.name
730 {
731 self.sink.push(diagnostics::infer::import_conflict(
732 &effective,
733 existing_path,
734 &import.name,
735 import.name_span,
736 ));
737 continue;
738 }
739
740 seen_aliases.insert(effective.clone(), import.name.to_string());
741
742 let module = store.get_module(&import.name);
743 if module.is_none() || module.is_some_and(Module::is_empty_stub) {
744 self.imports.failed_imports.insert(effective);
745 continue;
746 }
747
748 self.put_module_in_scope(store, &import.name, Some(effective));
749 }
750 }
751
752 fn module_struct_fields(&self, module: &Module) -> Arc<[StructFieldDefinition]> {
753 if let Some(shared) = &self.module_fields_shared
754 && let Some(fields) = shared.get(module.id.as_str())
755 {
756 return fields.clone();
757 }
758 if let Some(cached) = self.module_fields_cache.borrow().get(module.id.as_str()) {
759 return cached.clone();
760 }
761
762 let module_prefix = format!("{}.", module.id);
763 let fields: Vec<StructFieldDefinition> = module
764 .definitions
765 .iter()
766 .filter(|(qn, _)| module.is_public(qn))
767 .filter(|(qn, _)| {
768 qn.strip_prefix(&module_prefix)
769 .is_some_and(|rest| !rest.contains('.'))
770 })
771 .map(|(qn, definition)| {
772 let simple_name = qn
773 .strip_prefix(&module_prefix)
774 .expect("qualified_name must start with module prefix");
775 let ty = if let DefinitionBody::Struct {
776 constructor: Some(ctor_ty),
777 ..
778 } = &definition.body
779 {
780 ctor_ty.clone()
781 } else {
782 definition.ty().clone()
783 };
784 StructFieldDefinition {
785 doc: None,
786 attributes: vec![],
787 visibility: AstVisibility::Public,
788 name: simple_name.into(),
789 name_span: Span::dummy(),
790 annotation: Annotation::Unknown,
791 ty,
792 embedded: false,
793 }
794 })
795 .collect();
796
797 let shared: Arc<[StructFieldDefinition]> = fields.into();
798 self.module_fields_cache
799 .borrow_mut()
800 .insert(module.id.clone().into(), shared.clone());
801 shared
802 }
803
804 pub fn module_fields_snapshot(&self) -> HashMap<EcoString, Arc<[StructFieldDefinition]>> {
806 self.module_fields_cache.borrow().clone()
807 }
808
809 pub fn put_module_in_scope(&mut self, store: &Store, module_id: &str, prefix: Option<String>) {
810 let Some(prefix) = prefix else {
811 self.imports
812 .unprefixed_imports
813 .insert(module_id.to_string());
814 return;
815 };
816
817 let module = store
818 .get_module(module_id)
819 .expect("module must exist when putting in scope");
820
821 let imported_module_id = module.id.clone();
822
823 let module_struct_fields = self.module_struct_fields(module);
824
825 let ty = Type::ImportNamespace(imported_module_id.clone().into());
826
827 self.imports
828 .imported_modules
829 .insert(prefix.clone(), (module_struct_fields, ty));
830 self.imports
831 .prefix_to_module
832 .insert(prefix, imported_module_id);
833 }
834
835 pub(crate) fn speculatively<T, E>(
838 &mut self,
839 f: impl FnOnce(&mut Self) -> Result<T, E>,
840 ) -> Result<T, E> {
841 let spec = self.env.begin_speculation();
842 let result = f(self);
843 self.env.end_speculation(spec, result.is_err());
844 result
845 }
846}
847
848fn go_module_last_segment(module_id: &str) -> &str {
849 module_id
850 .strip_prefix("go:")
851 .unwrap_or(module_id)
852 .rsplit('/')
853 .next()
854 .unwrap_or(module_id)
855}
856
857fn is_reserved_import_alias(name: &str) -> bool {
862 matches!(
863 name,
864 "break"
866 | "case"
867 | "chan"
868 | "const"
869 | "continue"
870 | "default"
871 | "defer"
872 | "else"
873 | "fallthrough"
874 | "for"
875 | "func"
876 | "go"
877 | "goto"
878 | "if"
879 | "interface"
880 | "map"
881 | "package"
882 | "range"
883 | "return"
884 | "select"
885 | "struct"
886 | "switch"
887 | "type"
888 | "var"
889 | "nil"
891 | "iota"
892 | "true"
893 | "false"
894 | "bool"
896 | "byte"
897 | "complex64"
898 | "complex128"
899 | "error"
900 | "float32"
901 | "float64"
902 | "int"
903 | "int8"
904 | "int16"
905 | "int32"
906 | "int64"
907 | "rune"
908 | "string"
909 | "uint"
910 | "uint8"
911 | "uint16"
912 | "uint32"
913 | "uint64"
914 | "uintptr"
915 | "append"
917 | "cap"
918 | "clear"
919 | "close"
920 | "complex"
921 | "copy"
922 | "delete"
923 | "imag"
924 | "len"
925 | "make"
926 | "max"
927 | "min"
928 | "new"
929 | "panic"
930 | "print"
931 | "println"
932 | "real"
933 | "recover"
934 | "any"
936 | "comparable"
937 | "init"
939 | "main"
940 | "Option"
942 | "Result"
943 | "Comparable"
944 | "Ordered"
945 | "Some"
946 | "None"
947 | "Ok"
948 | "Err"
949 | "assert_type"
951 | "imaginary"
952 )
953}