diff --git a/ndc_analyser/src/name_resolution.rs b/ndc_analyser/src/name_resolution.rs index 16421e1f..262f405f 100644 --- a/ndc_analyser/src/name_resolution.rs +++ b/ndc_analyser/src/name_resolution.rs @@ -59,6 +59,16 @@ pub(crate) struct NameResolver { type_names: AHashMap, /// What each struct declaration binds, keyed by the declaration's node. structs: AHashMap, + /// Each native's position in the function registry, which is also its + /// global slot in the VM. With `new(["len", "+"])`, `len` is 0 and `+` is 1. + #[cfg_attr( + not(test), + expect( + dead_code, + reason = "read by the layout pass, which is not written yet" + ) + )] + native_slots: AHashMap, /// Errors from statements that failed to resolve. Resolution carries on with /// the next statement, so `a; b;` reports both unknown names. errors: Vec, @@ -298,9 +308,11 @@ impl NameResolver { let mut scopes = ScopeTree::new(); let mut symbols = SymbolTable::default(); - for builtin in builtins { + let mut native_slots = AHashMap::default(); + for (slot, builtin) in builtins.into_iter().enumerate() { let symbol = symbols.declare(builtin.to_string(), BindingKind::Function); scopes.bind(symbol); + native_slots.insert(symbol, slot); } for name in StaticType::BUILTIN_TYPE_NAMES { let symbol = symbols.declare((*name).to_string(), BindingKind::Type); @@ -321,6 +333,7 @@ impl NameResolver { setters: Default::default(), type_names: Default::default(), structs: Default::default(), + native_slots, errors: Default::default(), } } diff --git a/ndc_analyser/src/name_resolution/resolver_tests.rs b/ndc_analyser/src/name_resolution/resolver_tests.rs index 627af79a..e361fb54 100644 --- a/ndc_analyser/src/name_resolution/resolver_tests.rs +++ b/ndc_analyser/src/name_resolution/resolver_tests.rs @@ -1209,3 +1209,21 @@ fn statements_after_an_error_still_resolve() { assert_eq!(uses(&resolver, x), 1); assert!(resolver.take_errors().is_empty()); } + +#[test] +fn natives_record_their_registry_slot() { + let mut resolver = NameResolver::new(["len", "+", "len"]); + resolve_source(&mut resolver, "let x = 1; len;").unwrap(); + + // Overloads of one name keep their own slots, in registration order. + let [newest_len, oldest_len] = only_reference(&resolver).0[0][..] else { + panic!("expected both `len` overloads"); + }; + let plus = builtin(&resolver, "+"); + assert_eq!(resolver.native_slots[&oldest_len], 0); + assert_eq!(resolver.native_slots[&plus], 1); + assert_eq!(resolver.native_slots[&newest_len], 2); + // Program symbols aren't natives. + let [x] = declared(&resolver); + assert!(!resolver.native_slots.contains_key(&x)); +}