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.
|
/// Canonical exported/declared item name.
|
||||||
/// Invariants:
|
/// 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_]
|
/// - remaining chars: [A-Za-z0-9_]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||||
@ -97,7 +97,7 @@ impl ItemName {
|
|||||||
if s.is_empty() { return Err(CanonError::Empty("ItemName")); }
|
if s.is_empty() { return Err(CanonError::Empty("ItemName")); }
|
||||||
let mut chars = s.chars();
|
let mut chars = s.chars();
|
||||||
match chars.next() {
|
match chars.next() {
|
||||||
Some(c) if c.is_ascii_uppercase() => {}
|
Some(c) if c.is_ascii_alphabetic() => {}
|
||||||
_ => return Err(CanonError::InvalidStart("ItemName")),
|
_ => return Err(CanonError::InvalidStart("ItemName")),
|
||||||
}
|
}
|
||||||
if !s[1..].chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
|
if !s[1..].chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
|
||||||
@ -310,7 +310,7 @@ mod tests {
|
|||||||
fn item_name_rules() {
|
fn item_name_rules() {
|
||||||
assert!(ItemName::new("Test").is_ok());
|
assert!(ItemName::new("Test").is_ok());
|
||||||
assert!(ItemName::new("Log2").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("_Test").is_err());
|
||||||
assert!(ItemName::new("Te-st").is_err());
|
assert!(ItemName::new("Te-st").is_err());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -440,7 +440,8 @@ mod tests {
|
|||||||
|
|
||||||
let mut lib_exports = BTreeMap::new();
|
let mut lib_exports = BTreeMap::new();
|
||||||
use frontend_api::types::{ExportItem, ItemName, CanonicalFnKey, SignatureRef};
|
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 });
|
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 {
|
let lib_module = CompiledModule {
|
||||||
@ -476,7 +477,7 @@ mod tests {
|
|||||||
key: ImportKey {
|
key: ImportKey {
|
||||||
dep_alias: "mylib".into(),
|
dep_alias: "mylib".into(),
|
||||||
module_path: "math".into(),
|
module_path: "math".into(),
|
||||||
symbol_name: "add".into(),
|
symbol_name: "Add".into(),
|
||||||
},
|
},
|
||||||
relocation_pcs: vec![call_pc],
|
relocation_pcs: vec![call_pc],
|
||||||
}];
|
}];
|
||||||
|
|||||||
@ -135,6 +135,16 @@ pub fn compile_project(
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
#[derive(Hash, Eq, PartialEq)]
|
#[derive(Hash, Eq, PartialEq)]
|
||||||
struct DepKey(String, ExportItem); // (module_path, item)
|
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();
|
let mut dep_seen: HashSet<DepKey> = HashSet::new();
|
||||||
for (alias, project_id) in &step.deps {
|
for (alias, project_id) in &step.deps {
|
||||||
if let Some(compiled) = dep_modules.get(project_id) {
|
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());
|
let k = DepKey(sp.clone(), key.item.clone());
|
||||||
if !dep_seen.insert(k) {
|
if !dep_seen.insert(k) {
|
||||||
return Err(CompileError::DuplicateExport {
|
return Err(CompileError::DuplicateExport {
|
||||||
symbol: format!("{:?}", key.item),
|
symbol: display_export_item(&key.item),
|
||||||
first_dep: alias.clone(),
|
first_dep: alias.clone(),
|
||||||
second_dep: alias.clone(),
|
second_dep: alias.clone(),
|
||||||
});
|
});
|
||||||
@ -191,7 +201,7 @@ pub fn compile_project(
|
|||||||
let dep_key = DepKey(module_path.clone(), it.clone());
|
let dep_key = DepKey(module_path.clone(), it.clone());
|
||||||
if dep_seen.contains(&dep_key) {
|
if dep_seen.contains(&dep_key) {
|
||||||
return Err(CompileError::DuplicateExport {
|
return Err(CompileError::DuplicateExport {
|
||||||
symbol: format!("{:?}", it),
|
symbol: display_export_item(&it),
|
||||||
first_dep: "dependency".to_string(),
|
first_dep: "dependency".to_string(),
|
||||||
second_dep: "local".to_string(),
|
second_dep: "local".to_string(),
|
||||||
});
|
});
|
||||||
@ -265,13 +275,18 @@ pub fn compile_project(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
ExportItem::Function { fn_key } => {
|
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() {
|
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 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.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(
|
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)) },
|
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();
|
let compiled = compile_project(step, &HashMap::new(), &fe, &mut file_manager).unwrap();
|
||||||
|
|
||||||
// Both should be in the same module "gfx"
|
// 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")));
|
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();
|
let compiled = compile_project(step, &HashMap::new(), &fe, &mut file_manager).unwrap();
|
||||||
|
|
||||||
// Both should be in the root module ""
|
// 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::Type { 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() == "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)
|
## PR-03.06 — Deterministic overload resolution across deps (arity is not enough)
|
||||||
|
|
||||||
### Title
|
### Title
|
||||||
|
|||||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user