/** * emit-shared-rust-typed.ts — Rust typed 共有 emit エンジン(旧 emit-straightline-typed-rust.ts, bc#48 / Layer B2)。 * #90/#99 で rust-typed emitter plugin surface は撤去。この module は native(rust) が消費する共有 typed * ヘルパ(rustTypedInternals: typedFieldAccess/materializeExpr/emitMarshallers/emitTypeDecls/serializeTyped 等)のみ。 * * typed-codegen.md §4.3/§4.4 の Rust 具体化。B2-ts で decls-only、B2-go(#47)で * runtime-materialization 対応に拡張された {@link TypedMaterializer} seam に **同じ形で乗る** * (seam 拡張が go 専用でないことの検証を兼ねる — #48 は rust の materialization/型描画のみを足す)。 * * ── TS oracle との決定的な違い(実 de-box、rust が最大効果)─────────────────────────── * TS は型消去のため runtime の脱box が無い(生成モジュールの runtime は straight-line と byte-identical)。 * **Rust は違う**(かつ go より脱 box の利得が大きい): `outType`/`outputType` を持つノード/component の * exec 経路は、動的 `Value`(`serde_json::Value` も含む)ではなく **outType 由来の具体 Rust struct** * (`T0`/`T1`/…)で値を保持する(=Phase B 脱 box の実体を Rust で)。生成物の構成: * * 1. **straight-line 本体**(`rust-straightline` と byte 同一の `run_*`): 生の scope(Vec<(String,Value)>)を * 組む。handler は境界注入で Value を返す mock(bc conformance では "raw" = この Value)。 * 2. **struct 宣言**(typed のみ): 型プランの named obj を Rust struct へ(決定的名+dedup — typed.ts 共有)。 * 3. **raw→typed marshaller**(§4.4): 各 struct に `marshal_T*(&Value) -> Result` を emit。 * 動的 Value の各 field を **typed に読み出して具体 struct を組む**(generic Value 走査を exec から除去)。 * 4. **struct-native runner**(実 exec 経路): `run_typed_(handlers, input) -> Result`。 * plan は `run_plan` primitive(plan SSoT)で駆動し、各 typed node の exec thunk は handler 結果を * **その場で** marshaller で具体 struct へ de-box し、**struct のローカル**(RefCell cell)へ格納する。 * 動的 Value の node-result scope(Vec<(String,Value)> results)は **組まない**。`ref`/field access は * **struct field 直参照**(`.field` — map/obj_get lookup でない)で辿り、output は **具体 struct/Vec/scalar * として組み立て**、最終 return 境界で **1 回だけ** canonical Value へ戻す(観測等価 pin)。 * 5. **Bind**: typed component は run_typed_* 経路(実 de-box)、untyped component は straight-line 経路。 * * ── struct-native の規律(bc#47 監査 rework・rust も従う)───────────────────────────── * LAYERED(Value-then-struct: 生 Value scope/output tree を組んでから struct へ変換)は不可。struct 空間で * 実行する: handler 結果は境界で de-box、downstream は struct field 直参照、output は struct literal、Value 化は * 最終 return の 1 箇所のみ。plan 駆動・handler dispatch・operator 意味論は既存 SSoT(run_plan / handlers.exec_ctx / * A0 primitive)を再利用(二重実装しない)。struct 空間へ移すのは結果 materialization(handler 結果・scope・output)だけ。 * componentRef のみ typed 対象。typed component の map/cond ノード・output 位置の非 concat 演算子は **LOUD * fail-closed**(`UNSUPPORTED_NODE_STRAIGHTLINE` — go と一致。silent な Value フォールバックを禁ずる)。 * * ── 後方互換 ──────────────────────────────────────────────────────────────────── * `outType`/`outputType` が IR のどこにも無ければ typed 層は emit されず、出力は `rust-straightline` と * byte-identical(後方互換 pin)。 */ import type { EmitContext } from "./core.js"; import { type SlotRefLowering, type TypePlan, type TypeRef } from "./typed.js"; import { setRustForceRunPlanImports } from "./emit-shared-rust.js"; import type { Component } from "../behavior.js"; /** 可搬スカラ → Rust の具体型名(de-box 後の struct field 型)。int=i64 / float=f64 で区別。 */ declare function rustScalar(scalar: "string" | "int" | "float" | "bool" | "null"): string; /** TypeRef を Rust の具体型名へ(named→struct 名、arr→Vec、opt→Option、scalar→具体型)。 */ declare function renderTypeRef(ref: TypeRef): string; /** * typeRefIsCopy — その TypeRef の native Rust 型が `Copy` か。`Copy` な値に `.clone()` を書くと * clippy::clone_on_copy(`-D warnings` でエラー)を踏むので、所有値を取り出す側はこれで分岐する。 * i64/f64/bool と、それらの `Option`(`Option: Copy ⇔ T: Copy`)だけが Copy。`String` / `Vec` / * `BTreeMap` / named struct は非 Copy。`null` 単独型は `Value`(非 Copy)へ落ちる。 */ declare function typeRefIsCopy(ref: TypeRef): boolean; /** wire field 名 → Rust の struct field 名(決定的・安全化)。元名は struct コメント/marshaller で保持。 */ declare function rustFieldName(wire: string): string; /** string を Rust 文字列リテラルへ。 */ declare function rustStrLit(s: string): string; /** * TypeRef が Rust の `Default` を導出可能か(null 単独型は `Value` に落ち Default 非実装 → false)。 * scalar(non-null)/opt/arr は常に Default 導出可、named は各 field が導出可なら導出可、arr/opt はラップ先に依存。 */ declare function isDefaultable(ref: TypeRef, plan: TypePlan): boolean; /** 型プランの named obj を Rust struct 宣言へ(decls 順=決定的)。field は宣言順。 */ declare function emitTypeDecls(plan: TypePlan): string; /** * emitMarshallers — 各 named struct の raw→typed marshaller(§4.4)。動的 `&Value`(`Value::Obj` 期待)を * 受け、各 field を typed に読み出して具体 struct を組む。generic Value 走査を exec 経路から除去する de-box の実体。 * field 型ごとに再帰: named→marshal_T*、arr→要素ループ、opt→null 分岐、scalar→型 match。 */ declare function emitMarshallers(plan: TypePlan): string; /** テキストブロックを indent 段(4 space/段)だけ字下げする。 */ declare function indentBlock(block: string, levels: number): string; /** * materializeExpr — raw な `&Value` 式 `rawExpr` を型 `ref` の具体 typed 値へ変換する **単一式**(exec 経路の * de-box)。named→`marshal_T*(...)?`、scalar/arr/opt→インライン材化 block。呼び側が typed ローカルへ束ねる。 * これが seam の runtime-materialization hook の Rust 実装本体(go の同型 hook)。 */ declare function materializeExpr(rawExpr: string, ref: TypeRef, plan: TypePlan): string; /** * typedFieldAccess — typed base 式 `baseExpr`(型 `baseRef`)に field path を **struct field 直参照**で辿る。 * map/obj_get lookup ではなく `.field` 連結(=de-box されている証拠)。辿り着いた TypeRef も返す。 */ declare function typedFieldAccess(baseExpr: string, baseRef: TypeRef, fields: string[], plan: TypePlan): { expr: string; ref: TypeRef; }; /** * serializeTyped — 具体 typed 値式を canonical 直列化用の動的 Value へ戻す式(観測等価 pin)。exec は * typed のまま進み、component 境界でだけ Value 化して golden と突き合わせる。named→ser_T*、scalar→Value::variant、 * arr→要素 ser の map collect、opt→None/Some 分岐。式一発で返す。`typedExpr` は所有値(by-value)想定。 */ declare function serializeTyped(typedExpr: string, ref: TypeRef, plan: TypePlan): string; /** 生成コード内の typed ローカル cell 名(materialize 済み struct を RefCell で保持)。 */ declare function typedCell(nodeId: string): string; /** * rustCellRead — the ONE spelling of "read a prior node's result out of its cell". Every reader goes * through here so the ownership decision cannot drift between the port path, the output assembly and the * produced-aware output. * * `moveHeads` names the results THIS read site is the LAST reader of (movableRefs): that reader TAKES the * value, so a result set is never deep-copied on its way to a downstream leaf or to the caller. A reader * that is not the last copies, because the cell still has to hold the value for the ones after it. A bare * Copy scalar is deref-copied — `.clone()` on a Copy type is clippy::clone_on_copy, and copying an i64 * costs nothing anyway. */ declare function rustCellRead(head: string, ref: TypeRef, bareCopy: boolean, moveHeads: ReadonlySet): string; /** * rustCellPathRead — read a prior node's result AT A PATH as an owned value. Every reader of a prior node * (port plane, output assembly, produced-aware output) goes through this ONE function, so the ownership * decision cannot drift between them. * * The path is what makes the decision: a reader that wants ONE FIELD of a multi-reader row must NOT own the * row to get it. Cloning the row first (`cell.borrow().clone().field`) deep-copies every other field of * every row, per field, per node boundary — the exact cost #229 says "native" excludes ("native has to mean * it does not copy to read"). Borrowing to the leaf and owning only the leaf costs the one value asked for, * and a Copy leaf costs nothing at all. */ declare function rustCellPathRead(head: string, baseRef: TypeRef, path: readonly string[], plan: TypePlan, moveHeads: ReadonlySet): { expr: string; ref: TypeRef; }; /** * emitTypedValue — output(Φ 合流式)IR を、期待型 `expected` の **具体 Rust typed 値**式へ再帰 lower し、 * `{ expr, ref }` を返す。これが AC の「exec が具体 struct を保持」の実体(go と同型): * - `{ref:[typedNode, …fields]}` → typed struct field 直参照(typedFieldAccess — map lookup でない。struct は clone)。 * - `{ref:[input/非 typed node]}` → 型付け不能な生 Value。expected があれば materialize(de-box)。 * - `{obj:{…}}` → 具体 struct literal(expected が named のとき各 field を期待型で lower)。 * - `{arr:[…]}` → 具体 Vec literal(要素は要素型で lower)。 * - scalar literal → 具体 Rust 値。 * - concat → de-box された typed 値で native String 連結(意味論保存)。他演算子は LOUD fail-closed。 * * `slotRef`({@link SlotRefLowering})は node 結果参照位置の per-emitter lowering seam(go と同型)。 * **この 1 本の再帰が降りる全スロット**で自分の `ref` 処理より先に consult される。produced-aware 出力 * lowering(#86 pt1)はこれで乗る — 以前は obj 分岐を逐語コピーした 2 本目の再帰だった(#293/#295)。 */ declare function emitTypedValue(node: unknown, expected: TypeRef | undefined, typedNodes: Map, plan: TypePlan, compName?: string, fieldPath?: string, moveHeads?: ReadonlySet, slotRef?: SlotRefLowering): { expr: string; ref: TypeRef | undefined; }; /** * collectTypedNodes — component の body ノードで outType を持つもの(componentRef / map / cond)を * id → 結果 TypeRef に index 化する(#66: map/cond も struct 空間で結果を保持)。 */ declare function collectTypedNodes(comp: Component, plan: TypePlan): Map; declare function hasAnyTypeAnnotation(ctx: EmitContext): boolean; /** typed component(run_typed_* 経路で駆動するもの)か。 */ declare function isTypedComponent(comp: Component): boolean; /** marshaller helper(BehaviorError 生成)。生成モジュールに 1 度だけ emit。 */ declare function emitMarshalHelpers(): string; /** Default 導出不能な named struct(null 単独型 Value field を持つもの)だけ手書き Default を emit。 */ declare function emitDefaults(plan: TypePlan): string; /** TypeRef の Rust default 値式(Default::default() を持たない Value 型に対応するため明示)。 */ declare function defaultExpr(ref: TypeRef): string; /** * rustTypedInternals — shared Rust typed-emit helpers the native emitter (emit-typed-native-rust.ts) * reuses so the native de-box path shares one implementation of the type-plan/naming/serializer/ * marshaller discipline. This module carries NO emitter plugin (#90/#99): the rust-typed and * rust-straightline emitter surfaces were removed — rust converges to the native target only. */ export declare const rustTypedInternals: { typeRefIsCopy: typeof typeRefIsCopy; isDefaultable: typeof isDefaultable; defaultExpr: typeof defaultExpr; sanitize: (name: string) => string; rustFieldName: typeof rustFieldName; rustStrLit: typeof rustStrLit; renderTypeRef: typeof renderTypeRef; rustScalar: typeof rustScalar; typedCell: typeof typedCell; typedFieldAccess: typeof typedFieldAccess; isTypedComponent: typeof isTypedComponent; hasAnyTypeAnnotation: typeof hasAnyTypeAnnotation; collectTypedNodes: typeof collectTypedNodes; emitTypeDecls: typeof emitTypeDecls; emitDefaults: typeof emitDefaults; emitMarshallers: typeof emitMarshallers; emitMarshalHelpers: typeof emitMarshalHelpers; materializeExpr: typeof materializeExpr; serializeTyped: typeof serializeTyped; emitTypedValue: typeof emitTypedValue; rustCellRead: typeof rustCellRead; rustCellPathRead: typeof rustCellPathRead; checkOutputFieldType: import("./typed.js").OutputFieldTypeCheck; indentBlock: typeof indentBlock; emitOpsLiteral: (plan: import("./straightline.js").ComponentPlan) => string; emitPlanLiteral: (plan: unknown) => string; rustStraightlineInternals: { P: string; SCOPE: string; emitExpr: (node: unknown, scopeVar: string) => string; emitOpsLiteral: (plan: import("./straightline.js").ComponentPlan) => string; emitPlanLiteral: (plan: unknown) => string; rustStrLit: (s: string) => string; sanitize: (name: string) => string; dialect: import("./straightline.js").StraightlineDialect; }; setRustForceRunPlanImports: typeof setRustForceRunPlanImports; }; export {}; //# sourceMappingURL=emit-shared-rust-typed.d.ts.map