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