pr 03.04
This commit is contained in:
parent
2ba8507284
commit
a4cc1487c4
@ -192,17 +192,32 @@ impl ExportRef {
|
||||
}
|
||||
}
|
||||
|
||||
/// Canonical function key, identifying an overload by arity only (for now).
|
||||
/// This will be extended in PR-03.04 with types and calling convention.
|
||||
/// Canonical function identity.
|
||||
///
|
||||
/// JVM-like canonical pieces to uniquely identify an overload across projects:
|
||||
/// - `owner`: optional service/type name for methods (e.g., `Some("Log")`) or `None` for free fns
|
||||
/// - `name`: unqualified function/method name (e.g., `"debug"`)
|
||||
/// - `sig`: canonical signature id produced by the frontend
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct CanonicalFnKey {
|
||||
pub import: ImportRef,
|
||||
pub arity: u16,
|
||||
pub owner: Option<ItemName>,
|
||||
pub name: ItemName,
|
||||
pub sig: SignatureRef,
|
||||
}
|
||||
|
||||
impl CanonicalFnKey {
|
||||
pub fn new(import: ImportRef, arity: u16) -> Self { Self { import, arity } }
|
||||
pub fn new(owner: Option<ItemName>, name: ItemName, sig: SignatureRef) -> Self {
|
||||
Self { owner, name, sig }
|
||||
}
|
||||
|
||||
/// Returns a human-friendly display name like "Log.debug" or just "debug".
|
||||
pub fn debug_name(&self) -> String {
|
||||
match &self.owner {
|
||||
Some(o) => format!("{}.{}", o.as_str(), self.name.as_str()),
|
||||
None => self.name.as_str().to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Opaque canonical reference to a type known to the Frontend.
|
||||
|
||||
@ -293,8 +293,13 @@ impl BytecodeEmitter {
|
||||
stack_height = (stack_height - (*arg_count as i32)).max(0);
|
||||
}
|
||||
}
|
||||
InstrKind::ImportCall { dep_alias, module_path, base_name, sig, arg_count } => {
|
||||
let label = format!("@{}::{}:{}#sig{}", dep_alias, module_path, base_name, sig.0);
|
||||
InstrKind::ImportCall { dep_alias, module_path, owner, base_name, sig, arg_count } => {
|
||||
let display_name = if let Some(o) = owner {
|
||||
format!("{}.{}", o, base_name)
|
||||
} else {
|
||||
base_name.clone()
|
||||
};
|
||||
let label = format!("@{}::{}:{}#sig{}", dep_alias, module_path, display_name, sig.0);
|
||||
asm_instrs.push(Asm::Op(OpCode::Call, vec![Operand::Label(label)]));
|
||||
stack_height = (stack_height - (*arg_count as i32)).max(0);
|
||||
}
|
||||
|
||||
@ -1160,6 +1160,7 @@ impl<'a> Lowerer<'a> {
|
||||
self.emit(InstrKind::ImportCall {
|
||||
dep_alias,
|
||||
module_path,
|
||||
owner: None,
|
||||
base_name,
|
||||
sig,
|
||||
arg_count: n.args.len() as u32,
|
||||
@ -1385,10 +1386,11 @@ impl<'a> Lowerer<'a> {
|
||||
};
|
||||
|
||||
if let Some(sig) = sig_opt {
|
||||
let base_name = format!("{}.{}", obj_name, member_name);
|
||||
let base_name = member_name.to_string();
|
||||
self.emit(InstrKind::ImportCall {
|
||||
dep_alias,
|
||||
module_path,
|
||||
owner: Some(obj_name.to_string()),
|
||||
base_name,
|
||||
sig,
|
||||
arg_count: n.args.len() as u32,
|
||||
|
||||
@ -24,10 +24,12 @@ pub enum InstrKind {
|
||||
/// Placeholder for function calls.
|
||||
Call(FunctionId, u32),
|
||||
/// External calls (imports).
|
||||
/// Carries dependency alias, module path, base function name, precise signature id, and arg count.
|
||||
/// Carries dependency alias, module path, optional owner (service), base function name,
|
||||
/// precise signature id, and arg count.
|
||||
ImportCall {
|
||||
dep_alias: String,
|
||||
module_path: String,
|
||||
owner: Option<String>,
|
||||
base_name: String,
|
||||
sig: SigId,
|
||||
arg_count: u32,
|
||||
|
||||
@ -134,7 +134,11 @@ pub enum InstrKind {
|
||||
ImportCall {
|
||||
dep_alias: String,
|
||||
module_path: String,
|
||||
/// Optional service/type owner for methods (e.g., "Log"). `None` for free functions.
|
||||
owner: Option<String>,
|
||||
/// Unqualified function/method name (e.g., "debug").
|
||||
base_name: String,
|
||||
/// Exact signature id selected by the frontend.
|
||||
sig: SigId,
|
||||
arg_count: u32,
|
||||
},
|
||||
@ -249,6 +253,7 @@ mod tests {
|
||||
InstrKind::ImportCall {
|
||||
dep_alias: "std".to_string(),
|
||||
module_path: "math".to_string(),
|
||||
owner: None,
|
||||
base_name: "abs".to_string(),
|
||||
sig: SigId(1),
|
||||
arg_count: 1,
|
||||
@ -343,6 +348,7 @@ mod tests {
|
||||
"ImportCall": {
|
||||
"dep_alias": "std",
|
||||
"module_path": "math",
|
||||
"owner": null,
|
||||
"base_name": "abs",
|
||||
"sig": 1,
|
||||
"arg_count": 1
|
||||
|
||||
@ -124,7 +124,7 @@ pub fn lower_function(
|
||||
arg_count: *arg_count
|
||||
}, None));
|
||||
}
|
||||
ir_core::InstrKind::ImportCall { dep_alias, module_path, base_name, sig, arg_count } => {
|
||||
ir_core::InstrKind::ImportCall { dep_alias, module_path, owner, base_name, sig, arg_count } => {
|
||||
// Pop arguments from type stack
|
||||
for _ in 0..*arg_count {
|
||||
stack_types.pop();
|
||||
@ -133,6 +133,7 @@ pub fn lower_function(
|
||||
vm_func.body.push(ir_vm::Instruction::new(ir_vm::InstrKind::ImportCall {
|
||||
dep_alias: dep_alias.clone(),
|
||||
module_path: module_path.clone(),
|
||||
owner: owner.clone(),
|
||||
base_name: base_name.clone(),
|
||||
sig: *sig,
|
||||
arg_count: *arg_count,
|
||||
|
||||
@ -4,112 +4,13 @@
|
||||
>
|
||||
> Strategy: **surgical PRs** that (1) stop PBS types from leaking, (2) replace stringy protocols with canonical models, and (3) make imports/exports/overloads deterministic across deps.
|
||||
|
||||
---
|
||||
# Notes / Operating Rules (for Junie)
|
||||
|
||||
## PR-03.03 — Canonical import syntax → `ImportRef` (no dual styles)
|
||||
|
||||
### Title
|
||||
|
||||
Define single canonical import model and parse PBS imports into `ImportRef`
|
||||
|
||||
### Briefing / Context
|
||||
|
||||
We currently support multiple synthetic import path styles (`"alias/module"` and `"@alias:module"`). This amplifies ambiguity and is a root cause of mismatch in imported service method overloads.
|
||||
|
||||
We want **one** canonical representation:
|
||||
|
||||
* PBS syntax: `import { Test } from "@sdk:input/testing"`
|
||||
* Canonical model: `ImportRef { project: "sdk", module: "input/testing", item: "Test" }`
|
||||
|
||||
### Target
|
||||
|
||||
* PBS FE produces a list of canonical `ImportRef`.
|
||||
* BE consumes only `ImportRef`.
|
||||
* Remove support for dual synthetic path style in the BE pipeline.
|
||||
|
||||
### Scope
|
||||
|
||||
* In PBS FE:
|
||||
|
||||
* Parse `@<alias>:<module_path>` into `ImportRef`.
|
||||
* Validate module path normalization.
|
||||
* Validate that `item` is a single symbol name (service/struct/host/contract/etc).
|
||||
* In BE:
|
||||
|
||||
* Replace “synthetic path generation” with canonical module lookup using `(alias, module_path)`.
|
||||
|
||||
### Out of scope
|
||||
|
||||
* Export naming canonicalization (PR-03.04/03.05).
|
||||
|
||||
### Checklist
|
||||
|
||||
* [ ] Implement import parser → `ImportRef`.
|
||||
* [ ] Remove `alias/module` synthetic path support.
|
||||
* [ ] Update resolver/module-provider lookup to accept `(alias, module_path)`.
|
||||
* [ ] Add diagnostics for invalid import string.
|
||||
|
||||
### Tests
|
||||
|
||||
* Unit tests in PBS FE for:
|
||||
|
||||
* valid: `"@sdk:input/testing"`
|
||||
* invalid forms
|
||||
* normalization edge cases (leading `/`, `./`, `\\` on Windows paths)
|
||||
* Integration test (golden-style) compiling a small project importing a service.
|
||||
|
||||
### Risk
|
||||
|
||||
Medium. Changes import resolution plumbing.
|
||||
|
||||
---
|
||||
|
||||
## PR-03.04 — Canonical function identity: `CanonicalFnKey` (JVM-like)
|
||||
|
||||
### Title
|
||||
|
||||
Introduce canonical function identity for exports/import calls (no string prefix matching)
|
||||
|
||||
### Briefing / Context
|
||||
|
||||
Phase 03 currently tries to match overloads using `name#sigN` strings + prefix logic + origin checks. This breaks easily and is exactly what produced `E_OVERLOAD_NOT_FOUND` for `Log.debug`.
|
||||
|
||||
We need a **canonical function key** that is not “string protocol”:
|
||||
|
||||
* `CanonicalFnKey { owner: Option<ItemName>, name: ItemName, sig: SigId }`
|
||||
|
||||
* Free fn: `owner=None, name=foo, sig=...`
|
||||
* Service method: `owner=Some(Log), name=debug, sig=...`
|
||||
|
||||
### Target
|
||||
|
||||
* BE uses `CanonicalFnKey` for export surface and import relocation.
|
||||
* FE supplies `owner/name` and produces/requests signatures deterministically.
|
||||
|
||||
### Scope
|
||||
|
||||
* Add `CanonicalFnKey` to `frontend-api`.
|
||||
* Update VM import call instruction payload to carry canonical pieces:
|
||||
|
||||
* `ImportCall { dep_alias, module_path, fn_key: CanonicalFnKey, arg_count }`
|
||||
* (or equivalent)
|
||||
* Eliminate string matching / prefix matching for overload selection.
|
||||
|
||||
### Checklist
|
||||
|
||||
* [ ] Define `CanonicalFnKey` and helpers.
|
||||
* [ ] Update IR / bytecode instruction structures if needed.
|
||||
* [ ] Update lowering call sites.
|
||||
* [ ] Ensure debug info keeps readable names (owner.name).
|
||||
|
||||
### Tests
|
||||
|
||||
* Unit: canonical formatting for debug name `Log.debug`.
|
||||
* Integration: two overloads of `Log.debug` across deps resolved by exact signature.
|
||||
|
||||
### Risk
|
||||
|
||||
High-ish. Touches instruction encoding and matching logic.
|
||||
1. **BE is the source of truth**: `frontend-api` defines canonical models; FE conforms.
|
||||
2. **No string protocols** across layers. Strings may exist only as *display/debug*.
|
||||
3. **No FE implementation imports from other FE implementations**.
|
||||
4. **No BE imports PBS modules** (hard boundary).
|
||||
5. **Overload resolution is signature-based** (arity alone is not valid).
|
||||
|
||||
---
|
||||
|
||||
@ -240,14 +141,4 @@ After canonical models are in place, we must delete compatibility code paths (`a
|
||||
|
||||
### Risk
|
||||
|
||||
Low/Medium. Mostly deletion + docs, but could expose hidden dependencies.
|
||||
|
||||
---
|
||||
|
||||
# Notes / Operating Rules (for Junie)
|
||||
|
||||
1. **BE is the source of truth**: `frontend-api` defines canonical models; FE conforms.
|
||||
2. **No string protocols** across layers. Strings may exist only as *display/debug*.
|
||||
3. **No FE implementation imports from other FE implementations**.
|
||||
4. **No BE imports PBS modules** (hard boundary).
|
||||
5. **Overload resolution is signature-based** (arity alone is not valid).
|
||||
Low/Medium. Mostly deletion + docs, but could expose hidden dependencies.
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user