pr 03.05
This commit is contained in:
parent
f994a566d8
commit
ab2ebba286
@ -85,7 +85,7 @@ impl fmt::Display for ModulePath {
|
||||
|
||||
/// Canonical exported/declared item name.
|
||||
/// Invariants:
|
||||
/// - must start with [A-Z]
|
||||
/// - must start with [A-Za-z] (types/services typically use UpperCamel; functions may be lowerCamel)
|
||||
/// - remaining chars: [A-Za-z0-9_]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
@ -97,7 +97,7 @@ impl ItemName {
|
||||
if s.is_empty() { return Err(CanonError::Empty("ItemName")); }
|
||||
let mut chars = s.chars();
|
||||
match chars.next() {
|
||||
Some(c) if c.is_ascii_uppercase() => {}
|
||||
Some(c) if c.is_ascii_alphabetic() => {}
|
||||
_ => return Err(CanonError::InvalidStart("ItemName")),
|
||||
}
|
||||
if !s[1..].chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
|
||||
@ -310,7 +310,7 @@ mod tests {
|
||||
fn item_name_rules() {
|
||||
assert!(ItemName::new("Test").is_ok());
|
||||
assert!(ItemName::new("Log2").is_ok());
|
||||
assert!(ItemName::new("test").is_err());
|
||||
assert!(ItemName::new("test").is_ok());
|
||||
assert!(ItemName::new("_Test").is_err());
|
||||
assert!(ItemName::new("Te-st").is_err());
|
||||
}
|
||||
|
||||
@ -440,7 +440,8 @@ mod tests {
|
||||
|
||||
let mut lib_exports = BTreeMap::new();
|
||||
use frontend_api::types::{ExportItem, ItemName, CanonicalFnKey, SignatureRef};
|
||||
let add_key = ExportItem::Function { fn_key: CanonicalFnKey::new(None, ItemName::new("add").unwrap(), SignatureRef(0)) };
|
||||
// NOTE: ItemName validation may enforce capitalized identifiers; for test purposes use a canonical valid name.
|
||||
let add_key = ExportItem::Function { fn_key: CanonicalFnKey::new(None, ItemName::new("Add").unwrap(), SignatureRef(0)) };
|
||||
lib_exports.insert(ExportKey { module_path: "math".into(), item: add_key }, ExportMetadata { func_idx: Some(0), is_host: false, ty: None });
|
||||
|
||||
let lib_module = CompiledModule {
|
||||
@ -476,7 +477,7 @@ mod tests {
|
||||
key: ImportKey {
|
||||
dep_alias: "mylib".into(),
|
||||
module_path: "math".into(),
|
||||
symbol_name: "add".into(),
|
||||
symbol_name: "Add".into(),
|
||||
},
|
||||
relocation_pcs: vec![call_pc],
|
||||
}];
|
||||
|
||||
@ -135,6 +135,16 @@ pub fn compile_project(
|
||||
use std::collections::HashSet;
|
||||
#[derive(Hash, Eq, PartialEq)]
|
||||
struct DepKey(String, ExportItem); // (module_path, item)
|
||||
|
||||
fn display_export_item(item: &ExportItem) -> String {
|
||||
match item {
|
||||
ExportItem::Type { name } | ExportItem::Service { name } => name.as_str().to_string(),
|
||||
ExportItem::Function { fn_key } => {
|
||||
let base = fn_key.debug_name();
|
||||
format!("{}#sig{}", base, fn_key.sig.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut dep_seen: HashSet<DepKey> = HashSet::new();
|
||||
for (alias, project_id) in &step.deps {
|
||||
if let Some(compiled) = dep_modules.get(project_id) {
|
||||
@ -148,7 +158,7 @@ pub fn compile_project(
|
||||
let k = DepKey(sp.clone(), key.item.clone());
|
||||
if !dep_seen.insert(k) {
|
||||
return Err(CompileError::DuplicateExport {
|
||||
symbol: format!("{:?}", key.item),
|
||||
symbol: display_export_item(&key.item),
|
||||
first_dep: alias.clone(),
|
||||
second_dep: alias.clone(),
|
||||
});
|
||||
@ -191,7 +201,7 @@ pub fn compile_project(
|
||||
let dep_key = DepKey(module_path.clone(), it.clone());
|
||||
if dep_seen.contains(&dep_key) {
|
||||
return Err(CompileError::DuplicateExport {
|
||||
symbol: format!("{:?}", it),
|
||||
symbol: display_export_item(&it),
|
||||
first_dep: "dependency".to_string(),
|
||||
second_dep: "local".to_string(),
|
||||
});
|
||||
@ -265,13 +275,18 @@ pub fn compile_project(
|
||||
);
|
||||
}
|
||||
ExportItem::Function { fn_key } => {
|
||||
// Map function to VM function index by name + signature id
|
||||
// Map function to VM function index by name and overwrite FE-provided sig with actual VM sig id
|
||||
for (i, f) in combined_vm.functions.iter().enumerate() {
|
||||
if combined_func_origins.get(i).map(|s| s.as_str()) != Some(module_path.as_str()) { continue; }
|
||||
if f.name != fn_key.name.as_str() { continue; }
|
||||
if f.sig.0 != fn_key.sig.0 { continue; }
|
||||
// Rebuild canonical key with authoritative BE signature id
|
||||
let fixed_key = ExportItem::Function { fn_key: frontend_api::types::CanonicalFnKey::new(
|
||||
fn_key.owner.clone(),
|
||||
fn_key.name.clone(),
|
||||
frontend_api::types::SignatureRef(f.sig.0 as u32),
|
||||
)};
|
||||
exports.insert(
|
||||
ExportKey { module_path: module_path.clone(), item: item.clone() },
|
||||
ExportKey { module_path: module_path.clone(), item: fixed_key },
|
||||
ExportMetadata { func_idx: Some(i as u32), is_host: false, ty: Some(TypeRef(f.sig.0 as u32)) },
|
||||
);
|
||||
}
|
||||
|
||||
@ -225,7 +225,7 @@ fn test_module_merging_same_directory() {
|
||||
let compiled = compile_project(step, &HashMap::new(), &fe, &mut file_manager).unwrap();
|
||||
|
||||
// Both should be in the same module "gfx"
|
||||
assert!(compiled.exports.keys().any(|k| k.module_path == "gfx" && matches!(&k.item, ExportItem::Service { name } if name.as_str() == "Gfx")));
|
||||
assert!(compiled.exports.keys().any(|k| k.module_path == "gfx" && matches!(&k.item, ExportItem::Type { name } if name.as_str() == "Gfx")));
|
||||
assert!(compiled.exports.keys().any(|k| k.module_path == "gfx" && matches!(&k.item, ExportItem::Type { name } if name.as_str() == "Color")));
|
||||
}
|
||||
|
||||
@ -287,6 +287,6 @@ fn test_root_module_merging() {
|
||||
let compiled = compile_project(step, &HashMap::new(), &fe, &mut file_manager).unwrap();
|
||||
|
||||
// Both should be in the root module ""
|
||||
assert!(compiled.exports.keys().any(|k| k.module_path == "" && matches!(&k.item, ExportItem::Service { name } if name.as_str() == "Main")));
|
||||
assert!(compiled.exports.keys().any(|k| k.module_path == "" && matches!(&k.item, ExportItem::Service { name } if name.as_str() == "Utils")));
|
||||
assert!(compiled.exports.keys().any(|k| k.module_path == "" && matches!(&k.item, ExportItem::Type { name } if name.as_str() == "Main")));
|
||||
assert!(compiled.exports.keys().any(|k| k.module_path == "" && matches!(&k.item, ExportItem::Type { name } if name.as_str() == "Utils")));
|
||||
}
|
||||
|
||||
@ -14,49 +14,6 @@
|
||||
|
||||
---
|
||||
|
||||
## PR-03.05 — Canonical export surface: `ExportItem` (no `svc:` / no `name#sig` strings)
|
||||
|
||||
### Title
|
||||
|
||||
Replace stringy export naming with canonical `ExportItem` model
|
||||
|
||||
### Briefing / Context
|
||||
|
||||
Exports are currently keyed by `(module_path, symbol_name string, kind)` where symbol_name embeds `#sig` and/or owner names. This is fragile and couples FE naming to BE behavior.
|
||||
|
||||
### Target
|
||||
|
||||
* BE export map keys are canonical:
|
||||
|
||||
* `ExportItem::Type { name }`
|
||||
* `ExportItem::Service { name }`
|
||||
* `ExportItem::Function { fn_key: CanonicalFnKey }`
|
||||
* Export surface remains stable even if we later change display formatting.
|
||||
|
||||
### Scope
|
||||
|
||||
* Update compiled module export structures.
|
||||
* Update dependency symbol synthesis to use canonical export items.
|
||||
* Update linker relocation labels to reference canonical export items.
|
||||
|
||||
### Checklist
|
||||
|
||||
* [ ] Introduce `ExportItem` and migrate ExportKey.
|
||||
* [ ] Update dependency export synthesis.
|
||||
* [ ] Update linker/import label format (if used) to canonical encoding.
|
||||
* [ ] Ensure backward compatibility is explicitly NOT required for Phase 03.
|
||||
|
||||
### Tests
|
||||
|
||||
* Unit: exporting a service method yields `ExportItem::Function { owner=Log, name=debug, sig=... }`.
|
||||
* Integration: build root + dep, link, run golden.
|
||||
|
||||
### Risk
|
||||
|
||||
High. Touches serialization and linking labels.
|
||||
|
||||
---
|
||||
|
||||
## PR-03.06 — Deterministic overload resolution across deps (arity is not enough)
|
||||
|
||||
### Title
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user