diff --git a/dsl/config/codegen_paths.dag b/dsl/config/codegen_paths.dag index be67f31798a..7a9378bebbd 100644 --- a/dsl/config/codegen_paths.dag +++ b/dsl/config/codegen_paths.dag @@ -1,13 +1,10 @@ - module config.codegen_paths - data codegen_out_dir: String = "target/codegen" data bin_dir: String = "target/codegen/bin" data lib_dir: String = "target/codegen/lib" data stamp_file: String = "target/codegen/.codegen-stamp" - data generated_roots: List = [ "core/ir/src", "core/exec/src", @@ -15,7 +12,6 @@ data generated_roots: List = [ "generated" ] - data path_templates: List = [ "target/codegen/\{module\}.json", "target/codegen/\{module\}.lock", diff --git a/dsl/ctrl/code_change_workflow.dag b/dsl/ctrl/code_change_workflow.dag index 6f285901f60..3627e6c2282 100644 --- a/dsl/ctrl/code_change_workflow.dag +++ b/dsl/ctrl/code_change_workflow.dag @@ -1,16 +1,3 @@ -// ctrl/code_change_workflow.dag — internal code-change lifecycle (node-bound FSM). -// -// Authority scope: the workflow substrate is independent of GitHub PR — a code -// change is a process-graph node with a fail-closed lifecycle: -// open → under-review → merge-ready → merged (or closed). -// GitHub PullRequest is a 🟡 STAGED realization input on transition requests -// only (draft/open/head checks); it is not part of the core stage vocabulary. -// -// Current runtime authority remains in gunb-ai/ctrl session-dashboard until -// cut-over. Trio convergence with review_verdict + pr_digests unchanged. -// -// STAGED → AUTHORITY trigger: ctrl cut-over once emission + parity land. - module ctrl.code_change_workflow import ctrl.process_algebra { ProcessNodeId } @@ -19,35 +6,24 @@ import ctrl.review_verdict { MergeReadinessTally } import extdeps.github.pulls { PullRequest, Open, Closed } import std.types { CommitSha, NonEmptyStr, Timestamp, Url } -// 🟢 TERMINAL — active code-change coordinates. Successor edges are defined -// only over this sum. type CodeChangeActiveStage = ChangeOpen | ChangeUnderReview | ChangeMergeReady -// 🟢 TERMINAL — terminal bookends. No successor edges originate here -// (process_algebra ProcessCloseState pattern). type CodeChangeTerminalStage = ChangeMerged | ChangeClosed -// 🟢 TERMINAL — active vs terminal progress split. Dissolution pattern -// rejected: flat stage enum + Bool terminal predicate. type CodeChangeStage = CodeChangeActive { stage: CodeChangeActiveStage } | CodeChangeTerminal { stage: CodeChangeTerminalStage } -// 🟢 TERMINAL — binds a process-graph node to a code-change coordinate. -// PR/repo fields are intentionally absent; GitHub attachment is a separate -// staged realization input on transition requests. type CodeChangeBinding { node_id: ProcessNodeId change_ref: NonEmptyStr } -// 🟡 STAGED — lifecycle facts for one code-change node. -// Dissolution trigger: emitted from the dashboard event log. type CodeChangeFacts { binding: CodeChangeBinding stage: CodeChangeStage @@ -58,7 +34,6 @@ type CodeChangeFacts { closed_at: Timestamp? } -// 🟢 TERMINAL — fail-closed refusal reasons. type CodeChangeRefusal = CodeChangeAlreadyTerminal { stage: CodeChangeStage } | ChangeDraftBlocksReview @@ -74,25 +49,16 @@ type CodeChangeRefusal | CodeChangePolicyNotPermitted | CodeChangeInvalidSuccessor { from: CodeChangeStage, event: CodeChangeEventTag } -// 🟢 TERMINAL — lifecycle transition events without policy payloads. type CodeChangeEventTag = MarkReadyForReview | RecordMergeReady | RecordMerged | RecordClosed -// 🟢 TERMINAL coproduct — transition guard result (ProcessClosureDecision pattern). -// -// Decomposition receipt (DESIGN.md §2): -// - Classification: 🟢 TERMINAL. Success and refusal are disjoint repair paths. -// - Trigger: none for M2 scope; new refusal codes extend CodeChangeRefusal. type CodeChangeDecision = CanTransition { to: CodeChangeStage } | TransitionRefused { refusal: CodeChangeRefusal } -// 🟡 STAGED — GitHub PR realization context for handlers that still source -// draft/open/head facts from extdeps.github.pulls. Dissolution trigger: per-PR -// checks move to a gunbc/ctrl GitHub handler; core FSM stays PR-agnostic. type CodeChangeGithubPrContext { pr: PullRequest } @@ -141,8 +107,6 @@ fn successor_stage( } } -// 🟡 STAGED — GitHub PR terminal anchor projection only. Not authoritative for -// active UnderReview / MergeReady stages. fn github_pr_terminal_anchor(pr: PullRequest) -> CodeChangeStage? { match pr.merged_at { Present { value: _ } => diff --git a/dsl/ctrl/pr_digests.dag b/dsl/ctrl/pr_digests.dag index 78c14d9d0c0..ebf604291c0 100644 --- a/dsl/ctrl/pr_digests.dag +++ b/dsl/ctrl/pr_digests.dag @@ -1,105 +1,28 @@ -// ctrl/pr_digests.dag — PR digest helpers (status / conflicts / merge-readiness / URL extraction). -// -// Current authority (consumer-side): ctrl/scripts/session-dashboard/{pr_attached_urls, -// pr_ci_digest, pr_conflict_digest, pr_merge_ready_digest, pr_rest_fallback}.mjs -// -// STAGED → AUTHORITY trigger (single event, per single-authority/boundary -// discipline — DESIGN.md §3): the -// ctrl PR cut-over deleting ONLY WHOLE .mjs files whose ENTIRE function -// set is fully modeled in this brief's narrowed scope. Per single- -// authority discipline, the unit of authority is the .mjs FILE, not -// a function body — partial-file cut-over (keep the .mjs, delete some -// functions) would leave TS and .dag as co-authorities and violate -// single-authority discipline. With the narrowed service surface, only TWO files -// qualify: -// - pr_attached_urls.mjs (extract_attached_urls fully covers) -// - pr_rest_fallback.mjs (classify_rest_fallback fully covers) -// The remaining 3 (pr_ci_digest.mjs, pr_conflict_digest.mjs, -// pr_merge_ready_digest.mjs) stay TS-authoritative as WHOLE FILES -// because at least one of their functions is unmodeled here (CI / -// conflict / mergeability branches). They become cut-over-eligible -// only when a follow-up Phase 1.5 PR models the remaining functions -// and a dedicated ctrl-side cut-over PR fires for those files. The -// gunbc-side render_pr_summary_line and merge_readiness_verdict in -// the modeled service are net-additive .dag-only authority (no -// 1-to-1 .mjs replacement); their existence does NOT mark any .mjs -// file as cut-over-eligible on its own. Operator BLOCKING fixes -// 2026-05-12 at worker brief :47 + :50. -// Trio convergence below is the gating precondition that authorizes -// cut-over dispatch — it is NOT itself the authority flip: -// (a) digest source-fact authority (consuming dsl/extdeps/github/pulls.dag PullRequest -// as fetched source-of-record; if a narrow GitHub-domain digest-summary record -// is needed, lands at dsl/extdeps/github/pull_digest.dag importing pulls.dag — -// NOT as new fields on PullRequest itself per Director/Emission-Targets ratification -// 2026-05-12), -// (b) gunbc-owned render projection over dsl/std/render.dag (proposed -// name dsl/gunbc/digest_render.dag); any Markdown-specific wrapper -// is a separate authority decision, not assumed live, -// (c) named parity-harness gate green - module ctrl.pr_digests import extdeps.github.pulls { PullRequest, PullReview, ReviewComment } import extdeps.github.errors { GitHubErrorShape } import std.types { Url } -// Anchor: verdict is a ctrl-side aggregate over existing GitHub pull -// source facts (`PullRequest.state`, `draft`, `merged_at`, and each -// `PullReview.state` string from `github.Pulls.ListReviews`); it does not -// declare new extdeps fields. -// -// 🟡 STAGED — coproduct dissolution status (`dsl/ctrl/README.md` -// §Practice-4 receipts). Mechanical classification marker -// for coproduct dissolution status (not prose-only). -// Named trigger: follow-up Phase 1.5 + extdeps carriers when CI/conflict -// mergeability reasons must join this verdict (worker brief -// `docs/briefs/r4-ctrl-migration-pr-digests-worker.md`); ctrl cut-over PR for -// `pr_merge_ready_digest.mjs` once fully modeled. `Ready` / `NotReady` reasons -// stay drawn only from existing `PullRequest` / `PullReview` fields until then. type MergeReadinessVerdict = Ready | NotReady { first_reason: String, more_reasons: List } -// Anchor: attaches to `std.types.Url`; URLs are extracted from text fields on -// `PullRequest`, `PullReview`, and `ReviewComment` carriers in -// `extdeps.github.pulls` (`ListReviews` / `ListComments` outputs). type AttachedUrl { url: Url container: AttachedUrlContainer context: AttachedUrlTextContext } -// 🟡 STAGED — coproduct dissolution status (`dsl/ctrl/README.md` -// §Practice-4 receipts). Mechanical classification marker. -// Variants align to imported body carriers: `PullRequest.body`, each -// `PullReview.body`, each `ReviewComment.body` from `github.Pulls.ListReviews` / -// `github.Pulls.ListComments` respectively. -// Named trigger: extdeps `pulls.dag` gains list output for **issue-level** PR -// conversation comments (`ListIssueComments` or equivalent); then add -// `IssueCommentBody` (or equivalent) only in this file (cost-of-change 1). -// Line-level review comments are **in scope** today via `ReviewComment`. type AttachedUrlContainer = PrBody | PullReviewBody | ReviewCommentBody -// 🟡 STAGED — coproduct dissolution status, YELLOW scaffold -// (`dsl/ctrl/README.md` §Practice-4 receipts). Mechanical classification marker. -// Named trigger: richer Markdown context (e.g. fenced blocks) is modeled -// structurally and consumers migrate off the `Prose` / `InlineCode` split. type AttachedUrlTextContext = Prose | InlineCode -// 🟢 TERMINAL — coproduct dissolution status, GREEN terminal -// (`dsl/ctrl/README.md` §Practice-4 receipts). Mechanical classification marker. -// Ledger (dissolution patterns 1–4 at this declaration): (1) fact placement — -// not cheaper than this sum (single ctrl classifier returning this coproduct). -// (2) variant-as-data — three arms are semantically distinct failure classes for -// GraphQL-vs-REST fallback, not relabelings of one fact. (3) numeric/parametric -// merge — N/A. (4) scatter — N/A (one consumer). Dissolution when ctrl emission -// consumes richer `GitHubErrorShape` evidence and this enum grows or retires; -// until then add variants only in this file. type RestFallbackReason = GraphqlIncomplete | GraphqlRateLimited diff --git a/dsl/ctrl/process_algebra.dag b/dsl/ctrl/process_algebra.dag index a6cce0735b6..c36a9dc6cab 100644 --- a/dsl/ctrl/process_algebra.dag +++ b/dsl/ctrl/process_algebra.dag @@ -1,30 +1,3 @@ -// ctrl/process_algebra.dag -- staged process decomposition algebra. -// -// Status: 🟡 STAGED. This file models the ctrl/ decomposition algebra as -// substrate, but it is not runtime authority until the cut-over trio lands: -// emission target consumes it, parity tests match ctrl PRs #1192/#1193/#1195/ -// #1197 behavior, and the ctrl TypeScript implementation is deleted or made -// generated-only. -// -// DFS receipt (DESIGN.md §2/§6 — DFS the concept DAG, reuse before inventing): -// - Reuses `std.types.Timestamp` / `NonEmptyStr` for time and actor identity. -// - Reuses `std.algebra.FreeMonoid` for append-only event-log shape. -// - Reuses `std.effects.OperationEffect` for layer-local per-operation -// external effect facts instead of importing v3 workflow carriers or -// claiming blanket monotonicity. -// - Does not define `Witness`; `v3.std.dimensions.Witness` remains the -// analysis proof carrier. This file's human intent record is `Attestation`. -// - Does not define a bidirectional Lens; `v3.std.lens.Lens` remains the -// analysis carrier for future structural receipts. -// - Keeps workflow phase / run-key facts in `gunbc.workflow.types` orthogonal -// to process decomposition mode. -// -// Current ctrl runtime authority until cut-over: -// - ctrl/lib/dag_schema.mjs -// - ctrl/lib/dag_writes.mjs -// - ctrl/scripts/session-dashboard/dag_api.mjs -// - ctrl PRs #1192/#1193/#1195/#1197. - module ctrl.process_algebra import std.algebra { FreeMonoid } @@ -36,91 +9,31 @@ type ProcessSessionId = NonEmptyStr where brand("ProcessSessionId") type ProcessRuleName = NonEmptyStr where brand("ProcessRuleName") type ProcessEventPosition = Int where range(min: 0), brand("ProcessEventPosition") -// 🟢 TERMINAL coproduct at Phase-1 scope — declared process decomposition -// modes, mirroring ctrl PR #1192's non-null mode values. -// -// Decomposition receipt (DESIGN.md §2): -// - Classification: 🟢 TERMINAL for declared modes. Leaf, Composite, and -// Bucket are durable graph-shape coordinates. -// - Dissolution pattern rejected: undeclared/cutoff state is not a declared -// mode; closure snapshots carry it as `ProcessModeMissing` so declared-mode -// consumers cannot accept a transitional missing declaration as real data. -// - Trigger: none for declared modes. New ctrl declaration modes must extend -// this sum here first. type DeclaredProcessNodeMode = Leaf | Composite | Bucket -// 🟡 STAGED coproduct — evidence channels attached to a human-authored -// attestation. Kept distinct from `v3.std.dimensions.Witness`. -// -// Decomposition receipt (DESIGN.md §2): -// - Classification: 🟡 STAGED. The three arms are real current evidence -// classes, but the target set can tighten only after emission/parity work -// proves which receipts ctrl actually consumes. -// - Dissolution pattern: fact-placement. Structural lens results should move -// to typed `Lens` / `Witness` references once cross-namespace typed -// declaration refs are available; integration-test and prose receipts remain -// separate human/process facts. -// - Trigger: ctrl cut-over parity names the exact structural lens and test -// receipt carriers; string-name receipt fields are replaced by typed refs. type AttestationEvidence = StructuralLensReceipt { lens_name: NonEmptyStr } | IntegrationTestReceipt { test_name: NonEmptyStr, passed: Bool } | ProseReceipt { text: NonEmptyStr } -// 🟡 STAGED record — human/process intent attached to an operation. This is -// not a proof carrier and does not compete with `Witness`. -// -// Dissolution trigger: when ctrl audit-event emission lands, `author` and -// `evidence` are populated by emitted audit events rather than handwritten API -// payloads. Event time is owned by `ProcessEvent.timestamp`, not duplicated here. type Attestation { author: ProcessSessionId text: NonEmptyStr evidence: List } -// 🟢 TERMINAL record — a non-empty list of process nodes. Used where the -// substrate fact says at least one node exists, so an empty `List` cannot -// contradict the enclosing state. -// -// Dissolution trigger: replace with a shared `NonEmptyList` when -// that carrier is available in `dsl/std`. type NonEmptyProcessNodeIds { first: ProcessNodeId rest: List } -// 🟢 TERMINAL coproduct at Phase-1 scope — explicit closure guard results -// matching ctrl PR #1195. This is fail-closed: every known refusal has a -// distinct inhabitant, including the undeclared post-cutoff and bucket-drain -// cases. -// -// Decomposition receipt (DESIGN.md §2): -// - Classification: 🟢 TERMINAL for this guard surface. Each variant maps to -// a distinct ctrl close guard branch and HTTP/audit behavior. -// - Dissolution pattern rejected: these are not data fields on one refusal; -// callers branch differently on not-found, idempotent close, undeclared -// mode, undrained bucket, and open children. -// - Trigger: none for the Phase-1 guard vocabulary. New ctrl guard reasons -// must extend this sum here first. type ProcessClosureDecision = CanClose | CloseRefused(ProcessClosureRefusal) -// 🟢 TERMINAL coproduct at Phase-1 scope — refusal reasons are kept separate -// from success so no optional-reason product can represent contradictory -// states. -// -// Decomposition receipt (DESIGN.md §2): -// - Classification: 🟢 TERMINAL. The five refusal variants preserve the -// ctrl `canCloseNode` reason codes without stringly matching. -// - Dissolution pattern rejected: dimensional collapse would hide separate -// repair actions (declare mode, drain bucket, close children, ignore -// already-closed, fix missing node). -// - Trigger: none for PR #1195 parity; future reason codes extend here. type ProcessClosureRefusal = ProcessNodeNotFound | ProcessAlreadyClosed @@ -128,31 +41,12 @@ type ProcessClosureRefusal | ProcessBucketNotDrained | ProcessCompositeHasOpenChildren { open_children: NonEmptyProcessNodeIds } -// 🟢 TERMINAL coproduct — closure eligibility effect per operation. This -// replaces the rejected blanket monotonicity claim with typed per-operation -// behavior. -// -// Decomposition receipt (DESIGN.md §2): -// - Classification: 🟢 TERMINAL. Improve/retract/neutral/override are the -// irreducible closure-eligibility deltas needed by downstream consumers. -// - Dissolution pattern rejected: a Bool or monotonicity tag would collapse -// operator override and ordinary improvement/retraction into one axis. -// - Trigger: none; operation-specific payloads below carry the detailed cause. type ClosureEligibilityEffect = ClosureImproves | ClosureRetracts | ClosureNeutral | ClosureOverride -// 🟡 STAGED record — a typed effect summary is projected per operation. The -// `external_effect` field reuses the layer-local `std.effects.OperationEffect` -// authority instead of importing v3 workflow carriers into `dsl/std`. This row -// is reserved for the future generated effect projection; it is not an authored -// sibling field on `ProcessOperation` arms. -// -// Dissolution trigger: when ctrl HTTP/SQL/audit emission consumes this file, -// operation effect rows are generated from each `ProcessOperation` arm and -// validated against emitted behavior. type ProcessOperationEffect { closure_effect: ClosureEligibilityEffect external_effect: OperationEffect @@ -169,19 +63,6 @@ type RegressionAttestation { attestation: Attestation } -// 🟢 TERMINAL coproduct at Phase-1 scope — friendly ctrl operations are closed -// and structurally distinct. Reopen/regress are explicit so closure decisions -// are not assumed monotonic. -// -// Decomposition receipt (DESIGN.md §2): -// - Classification: 🟢 TERMINAL for the decomposition-algebra operation -// vocabulary. Each arm has distinct payload and closure effect. -// - Dissolution pattern rejected: variants are not values of a single generic -// mutation; declare, decompose, drain, close, refused-close, replan, -// escalate, pause, reopen, regress, and override have different repair paths -// and audit payloads. -// - Trigger: none for Phase 1. Adding a ctrl operation must extend this one -// file before REST/SQL/CLI projections can expose it. type ProcessOperation = DeclareProcessNode { node: ProcessNodeId @@ -233,86 +114,28 @@ type ProcessOperation attestation: Attestation } -// 🟡 STAGED record — append-only event row for replay-as-truth. `position` -// is a monotone sequence witness by convention until a shared MonotonicSeq -// carrier lands. -// -// Dissolution trigger: replace `ProcessEventPosition` with a shared monotonic -// sequence substrate once one exists; then reuse this row as the concrete -// `TimestampedEvent` specialization for process operations. type ProcessEvent { position: ProcessEventPosition timestamp: Timestamp operation: ProcessOperation } -// 🟡 STAGED alias — append-only operation history. This uses `FreeMonoid` -// rather than a process-local log algebra. -// -// Dissolution trigger: when a generic `EventLog` primitive lands, this -// becomes `type ProcessEventLog = EventLog`; until then the -// concrete process log names the same FreeMonoid structure directly. type ProcessEventLog = FreeMonoid -// 🟢 TERMINAL coproduct at Phase-1 scope — bucket drain state. Avoids using -// `Timestamp?` for a state that callers branch on structurally. -// -// Decomposition receipt (DESIGN.md §2): -// - Classification: 🟢 TERMINAL. Drained and not-drained are the two states -// the ctrl closure guard distinguishes for buckets. -// - Dissolution pattern rejected: nullable timestamp would reintroduce the -// optional-product shape this file avoids for guard decisions. -// - Trigger: none for PR #1195 parity; richer drain receipts extend the -// drained arm payload. type BucketDrainState = BucketNotDrained | BucketDrained { drained_at: Timestamp } -// 🟢 TERMINAL coproduct at Phase-1 scope — process closure state. Avoids -// `closed_at: Timestamp?` beside every mode state. -// -// Decomposition receipt (DESIGN.md §2): -// - Classification: 🟢 TERMINAL. Open and closed are disjoint lifecycle -// states for the close guard. -// - Dissolution pattern rejected: nullable timestamp permits code to treat -// "missing close timestamp" as data absence instead of an explicit open -// state. -// - Trigger: none for PR #1195 parity; richer close receipts extend the -// closed arm payload. type ProcessCloseState = ProcessOpen | ProcessClosed { closed_at: Timestamp } -// 🟢 TERMINAL coproduct at Phase-1 scope — declaration and mode-specific closure -// facts for existing process nodes. The declared mode and its closure facts live -// in the same arm, so the snapshot cannot say "declared leaf" while storing -// bucket facts. -// -// Decomposition receipt (DESIGN.md §2): -// - Classification: 🟢 TERMINAL for the closure guard snapshot. Missing -// declaration and declared-mode facts have different repair paths, and each -// declared mode owns only the facts needed for its closure rule. -// - Dissolution pattern rejected: nullable sibling fields collapse distinct -// repair paths and admit contradictory states. Separate `mode` plus fact -// fields can represent impossible combinations; this carrier cannot. -// - Dissolution pattern applied: fact placement. The missing-declaration state -// is stored as declaration absence, not as a fourth declared mode. -// - Trigger: none for PR #1195 parity; richer declaration provenance or new -// mode-specific closure facts extend the corresponding arm here. type ProcessNodeDeclarationState = ProcessModeMissing | ProcessLeafDeclared | ProcessCompositeDeclared { open_children: List } | ProcessBucketDeclared { drain: BucketDrainState } -// 🟡 STAGED node facts for closure projection. Identity is carried by the -// `ProcessGraph.nodes` map key below, not by this closure-fact row, so mode/close -// facts cannot treat ProcessNodeId as another mutable sibling field. Missing -// mode declaration is represented as declaration absence, not by storing a -// fourth mode here. -// -// Dissolution trigger: when `state(log: ProcessEventLog)` is emitted, this -// snapshot is generated from the event log and no longer hand-authored. type ProcessNodeFacts { declaration: ProcessNodeDeclarationState close: ProcessCloseState @@ -321,11 +144,3 @@ type ProcessNodeFacts { type ProcessGraph { nodes: Map } - -// Projection surfaces intentionally remain comments in this Phase-1 substrate: -// current_process_mode(ProcessGraph, ProcessNodeId) -> ProcessNodeDeclarationState, -// process_operation_effect(ProcessOperation) -> ProcessOperationEffect, and -// can_close_process_node(ProcessGraph, ProcessNodeId, Timestamp) -> -// ProcessClosureDecision. They become declared functions in the PR that also -// lands an emission/parity consumer, so this file does not introduce unused host -// boundaries. diff --git a/dsl/ctrl/review_verdict.dag b/dsl/ctrl/review_verdict.dag index d68717407cd..5d2660c276e 100644 --- a/dsl/ctrl/review_verdict.dag +++ b/dsl/ctrl/review_verdict.dag @@ -1,27 +1,7 @@ -// ctrl/review_verdict.dag -- staged substrate for ctrl PR-review verdicts. -// -// Current runtime authority remains in gunb-ai/ctrl: -// scripts/session-dashboard/findings_extract.mjs -// scripts/session-dashboard/findings_parser.mjs -// scripts/session-dashboard/findings_store.mjs -// scripts/session-dashboard/findings_triage.mjs -// scripts/session-dashboard/pr_feedback_format.mjs -// scripts/session-dashboard/review_subprocess_runner.mjs -// scripts/api-reviewer/review-one.mjs -// scripts/api-reviewer/review-with-cli.mjs -// scripts/api-reviewer/review-meta-with-cli.mjs -// -// This file is a Phase 1.5 staged contract for the ctrl/ -> .dag migration -// program. It becomes authority only after the review-verdict emission target -// and parity tests land, and the ctrl TS parser/tally files are deleted. - module ctrl.review_verdict import std.types { ArtifactId, CommitSha, ContentHash, NonEmptyStr, Timestamp, Url } -// Coproduct receipt: terminal for the current provider coordinate. -// Adding a provider is a new declaration row or ProviderOther value, not a -// rewrite of parser/tally logic. type ReviewProvider = OpenaiPro | Codex @@ -30,11 +10,6 @@ type ReviewProvider | HumanReviewer | ProviderOther { name: NonEmptyStr } -// Coproduct receipt: terminal. Semantic source-kind axis for who authored a -// review. Distinct from ReviewProvider (transport/provider coordinate) and -// Reviewer (structured identity including policy-backed reviewers). -// Unattributed is the fail-closed open case (§5): unknown provider coordinates -// map here instead of silently inheriting a guessed kind. type ReviewSource = Llm | Human @@ -46,25 +21,18 @@ type PolicyRef { version: NonEmptyStr } -// Coproduct receipt: terminal. LLM-only provider rows for AgentReviewer. -// ReviewProvider (transport) retains HumanReviewer and ProviderOther for ctrl; -// those coordinates lift to HumanSeatReviewer / UnattributedReviewer arms here. type LlmReviewProvider = OpenaiPro | Codex | Claude | Cursor -// Coproduct receipt: terminal. Structured reviewer identity for verdict -// carriers. Source kind is structural per arm — reviewer_source is tag -// projection only (no nested ReviewProvider match undoing a conflation). type Reviewer = AgentReviewer { provider: LlmReviewProvider } | HumanSeatReviewer | PolicyReviewer { policy: PolicyRef } | UnattributedReviewer { provider_name: NonEmptyStr } -// Total tag projection: each Reviewer arm maps 1:1 to ReviewSource. fn reviewer_source(reviewer: Reviewer) -> ReviewSource { match reviewer { AgentReviewer { provider: _ } => Llm @@ -74,22 +42,12 @@ fn reviewer_source(reviewer: Reviewer) -> ReviewSource { } } -// Coproduct receipt: staged. GitHub review/comment transport is the first -// consumer; dashboard-only artifacts remain modeled so extraction can preserve -// source identity instead of flattening every artifact to raw Markdown. -// Dissolution trigger: when extdeps.github review/comment facts are reflected, -// replace GitHubReviewState with typed GitHub state references. type ReviewArtifactKind = GitHubReview { state: GitHubReviewState } | GitHubReviewComment | GitHubIssueComment | DashboardReviewArtifact -// Coproduct receipt: staged mirror of GitHub review states consumed by ctrl -// today. This is not the extdeps authority for GitHub's review wire model. -// Dissolution trigger: replace this mirror with typed state references from -// the extdeps.github review/comment authority once that source-fact carrier -// exists. type GitHubReviewState = Approved | ChangesRequested @@ -97,9 +55,6 @@ type GitHubReviewState | Dismissed | Pending -// Coproduct receipt: terminal. This is the semantic verdict coordinate that -// downstream merge-readiness logic consumes; it is not the same axis as raw -// GitHub review state. type ReviewVerdict = Approve | ApproveWithComments @@ -107,11 +62,6 @@ type ReviewVerdict | NeedsHumanTriage | NoVerdict -// Coproduct receipt: staged. BLOCKING/P0/P1 currently drive action-routing in -// ctrl pr_feedback_format.mjs. Priority and severity are split so the relay can -// preserve both reviewer intent and scheduling weight. -// Dissolution trigger: generated dashboard relay consumes this file and deletes -// the regex classifier in pr_feedback_format.mjs. type FindingSeverity = Blocking | NonBlocking @@ -124,10 +74,6 @@ type FindingPriority | P3 | Unprioritized -// Coproduct receipt: staged. Textual recognizers remain named evidence until -// the review providers emit structural verdict rows directly. -// Dissolution trigger: review providers return ReviewVerdictReport as JSON or -// another typed transport payload, making TextualVerdictMarker unnecessary. type VerdictEvidence = ExplicitVerdictLine { verdict: ReviewVerdict } | GitHubReviewStateEvidence { state: GitHubReviewState } @@ -136,20 +82,10 @@ type VerdictEvidence | HumanOverride { reason: String } | TextualVerdictMarker { marker: NonEmptyStr } -// Coproduct receipt: staged. These are successful report extraction outcomes, -// not verdicts. Parser fallback is explicit so "whole comment stored as one -// finding" is a typed degraded success instead of silently looking like a clean -// parse. -// Dissolution trigger: provider outputs become structural and the Markdown -// section parser is retired. type ReportExtractionState = ParsedDiscreteFindings | FallbackWholeCommentStored -// Coproduct receipt: terminal subset for parse attempts that did not produce -// a report. Kept separate from ReportExtractionState so ReviewVerdictReport -// cannot carry no-report states, and ParseDeferred cannot carry successful -// report states. type ParseDeferredState = DeferredCommentNotFound | DeferredAmbiguousShaMarker @@ -161,9 +97,7 @@ type ReviewArtifact { kind: ReviewArtifactKind repo: NonEmptyStr pr_number: Int - // Source-native commit coordinate when the transport provides one. Issue - // comments and dashboard artifacts may omit it; Parse extracts successful - // head affiliation into ReviewVerdictReport.head_sha. + source_commit_sha: CommitSha? source_url: Url? body_hash: ContentHash? @@ -199,8 +133,7 @@ type MergeReadinessTally { repo: NonEmptyStr pr_number: Int head_sha: CommitSha - // Non-authoritative lineage pointer only. ReviewVerdictReport values remain - // the Tally service input; the tally output owns only derived verdict counts. + source_artifact_ids: List approval_count: Int request_changes_count: Int @@ -213,12 +146,6 @@ type ReviewVerdictParseRequest { expected_head_sha: CommitSha } -// Coproduct receipt: staged boundary result. ParsedReport and ParseDeferred -// intentionally carry disjoint state axes: reports can only carry successful -// ReportExtractionState, while deferred results can only carry no-report -// ParseDeferredState. Dissolution trigger: provider outputs become structural -// enough for Parse to return a provider-authored ReviewVerdictReport directly -// or a typed extdeps failure carrier, retiring this parser boundary sum. type ReviewVerdictParseResult = ParsedReport { report: ReviewVerdictReport } | ParseDeferred { state: ParseDeferredState, artifact: ReviewArtifact } diff --git a/dsl/examples/cost_estimate/cost_estimate.dag b/dsl/examples/cost_estimate/cost_estimate.dag index 28fa21fabf5..c71b461c663 100644 --- a/dsl/examples/cost_estimate/cost_estimate.dag +++ b/dsl/examples/cost_estimate/cost_estimate.dag @@ -1,4 +1,3 @@ - module examples.cost_estimate import std.float { Float } diff --git a/dsl/examples/html_markup_smoke/html_markup_smoke.dag b/dsl/examples/html_markup_smoke/html_markup_smoke.dag index f65d67957f5..81d5da37a07 100644 --- a/dsl/examples/html_markup_smoke/html_markup_smoke.dag +++ b/dsl/examples/html_markup_smoke/html_markup_smoke.dag @@ -1,4 +1,3 @@ - module examples.html_markup_smoke import std.process { ProcessExit, ExitSuccess, ExitFailure, exit_code_general_error } diff --git a/dsl/examples/weather/weather.dag b/dsl/examples/weather/weather.dag index 5ce570e431d..a9bbc764d78 100644 --- a/dsl/examples/weather/weather.dag +++ b/dsl/examples/weather/weather.dag @@ -1,7 +1,5 @@ - module examples.weather - type Temperature { celsius: Float } @@ -19,7 +17,6 @@ type Forecast { condition: Condition } - fn to_fahrenheit(temp: Temperature) -> Float { temp.celsius * 1.8 + 32.0 } diff --git a/dsl/gunbc/ci_spec.dag b/dsl/gunbc/ci_spec.dag index 3686be03cdb..b6078b60304 100644 --- a/dsl/gunbc/ci_spec.dag +++ b/dsl/gunbc/ci_spec.dag @@ -166,7 +166,6 @@ fn ci_release_build_script() -> String { ) } - fn claim_executor_bin_shell() -> String { "\"$ROOT/target/release/claim_executor\"" } diff --git a/dsl/product/compute_fabric.dag b/dsl/product/compute_fabric.dag index 4f0c18b04c3..0d220cdfc63 100644 --- a/dsl/product/compute_fabric.dag +++ b/dsl/product/compute_fabric.dag @@ -1,4 +1,3 @@ - module product.compute_fabric import std.nat { Nat } @@ -90,7 +89,6 @@ type Idempotency = Node type IdentityElement = Node type InstructionSet = Node - type NetworkLocality = Loopback | Lan @@ -136,14 +134,12 @@ type ComputeArtifactLocality | PerHostColocation | CrossHostFetch - type StorageMount { mount_path: NonEmptyStr device: StorageDevice lifecycle: PersistenceKind } - type MemoryFacts { capacity: ByteSize, memory_kind: MemoryKind } type GpuComputeCapability { @@ -293,7 +289,6 @@ fn machine_view_label(host: ComputeHost, surface: ExecutionSurface) -> NonEmptyS } } - fn placement_supply_row(identity: HostIdentity, cpu: CpuFacts, ram_bytes_total: ByteSize) -> PlacementSupplyRow { PlacementSupplyRow { identity: identity, @@ -433,7 +428,6 @@ fn work_demand_shard_count(demand: WorkDemand) -> Int { } } - type ResourceEnvelope { cpu: CpuRequirement? gpu: GpuRequirement? @@ -481,7 +475,6 @@ type WorkDemand { effects: List } - fn projected_concurrent_peak(width: HardwareThreadCount, per_shard_peak: ByteSize) -> ByteSize { measure_scale_fraction_ceil(m: per_shard_peak, num: hardware_thread_count_value(width), den: 1) } @@ -537,7 +530,6 @@ fn demand_envelope_fits_budget(envelope: ResourceEnvelope, budget: ByteSize) -> memory_bytes_honor_demand(supply: budget, demand: demand_envelope_memory(envelope: envelope)) } - type ParallelismShape = SingleWorkItem | IndependentShards { shard_count: Int } @@ -564,7 +556,6 @@ type ExecutionBudget { type WatchdogLimit { max_wall: Duration } - type DemandDimension = DemandCpu | DemandGpu @@ -682,7 +673,6 @@ fn satisfies(offer: ComputeOffer, demand: WorkDemand) -> ComputeLeaseEligibility } } - type WorkUnitId = NonEmptyStr where brand("WorkUnitId") type WorkUnit { @@ -693,7 +683,6 @@ type WorkUnit { requirements: WorkDemand } - type MeasurementConfidence = SingleSample | Range { low: Float, high: Float } @@ -785,7 +774,6 @@ type ExecutionReceipt { finished_at: LogicalTime } - data example_host_identity: HostIdentity = "example-host" data example_shared_host_provider: ProviderIdentity = "example-shared-host" data example_vm_provider: ProviderIdentity = "example-vm" @@ -899,7 +887,6 @@ fn witness_vm_offer_admits_vm_demand() -> Bool { } } - data example_resource_cpu_catalog: CpuModelCatalogRow = CpuModelCatalogRow { model_number: "GENERIC-ARM-64C", oem_listing_id: none, @@ -1022,7 +1009,6 @@ fn witness_resource_offer_admits_feasible_demand() -> Bool { } } - fn witness_run_demand_envelope_fits_budget() -> Bool { let envelope = parallel_run_demand_envelope_from_budget( shard_count: 7, diff --git a/dsl/product/hardware_selection.dag b/dsl/product/hardware_selection.dag index fff4ff3a184..26efdaaf2c6 100644 --- a/dsl/product/hardware_selection.dag +++ b/dsl/product/hardware_selection.dag @@ -1,4 +1,3 @@ - module product.hardware_selection import std.types { Int, NonEmptyStr, Bool, List } @@ -207,7 +206,6 @@ fn config_is_realizable(cfg: SystemConfig) -> Bool { dram_module_is_realizable(module: cfg.memory.module, jedec: cfg.memory.jedec) } - fn bandwidth_count(bw: Bandwidth) -> Int { measure_count(m: bw) } diff --git a/dsl/product/network_topology.dag b/dsl/product/network_topology.dag index 1688166d3e8..44fa678f4eb 100644 --- a/dsl/product/network_topology.dag +++ b/dsl/product/network_topology.dag @@ -1,4 +1,3 @@ - module product.network_topology import product.compute_fabric { NetworkLocality, Lan, DockerBridge, TailscaleOverlay } diff --git a/dsl/product/placement_supply.dag b/dsl/product/placement_supply.dag index 653dc146ffb..e277e9e2937 100644 --- a/dsl/product/placement_supply.dag +++ b/dsl/product/placement_supply.dag @@ -1,4 +1,3 @@ - module product.placement_supply import std.measure { diff --git a/dsl/shared/dag_util.dag b/dsl/shared/dag_util.dag index d15385bb181..aeb6ef4d2f9 100644 --- a/dsl/shared/dag_util.dag +++ b/dsl/shared/dag_util.dag @@ -1,4 +1,3 @@ - module shared.dag_util import std.types { @@ -22,7 +21,6 @@ fn all_succeeded(stages: List) -> Bool { all(stages, s => s.success || s.skipped) } - fn format_report(summary: Summary, stages: List) -> String { let summary_line = "Total: {summary.total}, Passed: {summary.passed}, Failed: {summary.failed}" let detail_items = map(stages, s => if s.success { @@ -38,7 +36,6 @@ fn format_report(summary: Summary, stages: List) -> String { "# Report\n\{summary_line\}\n\n\{detail_lines\}\n" } - fn stage_result(name: String, success: Bool, stderr: String) -> StageResult { { name: name, success: success, stdout: "", stderr: stderr, skipped: false } } @@ -51,12 +48,10 @@ fn stage_from_output(name: String, success: Bool, stdout: String, stderr: String { name: name, success: success, stdout: stdout, stderr: stderr, skipped: skipped } } - fn generated_header(tool: String) -> String { "# Generated by gunbc \{tool\}\n" } - fn text_line(text: String) -> DocumentLine { { text: text, is_comment: false, is_blank: false } } diff --git a/dsl/test/claim/roadmap_emit_test.dag b/dsl/test/claim/roadmap_emit_test.dag index 288510dae22..cfc54109c15 100644 --- a/dsl/test/claim/roadmap_emit_test.dag +++ b/dsl/test/claim/roadmap_emit_test.dag @@ -17,7 +17,6 @@ fn parent_node() -> RoadmapNode { } } - fn child_node() -> RoadmapNode { RoadmapNode { node: child_id, diff --git a/dsl/test/claim/roadmap_gate_test.dag b/dsl/test/claim/roadmap_gate_test.dag index 0024370dc62..74c96cbf72c 100644 --- a/dsl/test/claim/roadmap_gate_test.dag +++ b/dsl/test/claim/roadmap_gate_test.dag @@ -1,16 +1,3 @@ -// dsl/test/claim/roadmap_gate_test.dag — §5 by-execution proof for the RoadmapGate drift logic. -// -// The PURE half of the capstone (the ci_yaml_gate witness shape over the markdown medium): proves the -// drift predicate `roadmap_drifted` has the right teeth WITHOUT a host effect (Filesystem.Read lives in -// the effectful gate `tools.roadmap_gate`, which cannot run in a pure witness). -// -// TWO distinct teeth (warm-lark-306 ruling): -// - SET-perturbation (THE inversion proof): drop #5470/#5478 from the merged snapshot → the measure -// line's box flips [x]→[ ] → the projected bytes differ → drift RED. This proves the box is genuinely -// DERIVED from the set (a coincidental byte-match can't pass), the whole point of the inversion. -// - HAND-EDIT byte-diff: any edit to the committed bytes that diverges from the authority → drift RED. -// And the clean case: the authority's own projection is drift-free (the gate is green on a faithful file). - module test.claim.roadmap_gate import extdeps.languages.markdown { serialize_markdown_source } @@ -19,21 +6,16 @@ import gunbc.roadmap_authority { roadmap_authority, authored_merged_prs, expected_roadmap_md, roadmap_drifted, } -// The projection under a perturbed merged-set (the committed snapshot minus #5470/#5478, the measure PRs). fn emit_dropping_measure() -> String { let perturbed = [5428, 5445, 5453, 5433, 5476, 5473, 5456, 5421, 5425, 5236, 5437, 5434, 5441, 5449, 5471, 5430, 5481, 5491, 5508, 5520, 5525, 5527, 5444] serialize_markdown_source(doc: project_roadmap_to_markdown(doc: roadmap_authority(), merged: perturbed)) } -// Clean: the authority's own projection is NOT drift (the gate is green on a faithful ROADMAP.md). fn witness_clean_not_drift() -> Bool { !roadmap_drifted(committed: expected_roadmap_md()) } -// SET-perturbation tooth (the inversion proof): the measure-dropped projection differs from the -// authored-set projection (so it would drift), AND the difference is specifically the measure box -// flipping [x]→[ ] — the box tracks the set, not a baked literal. fn witness_set_perturbation_drifts() -> Bool { let dropped = emit_dropping_measure() roadmap_drifted(committed: dropped) @@ -42,13 +24,10 @@ fn witness_set_perturbation_drifts() -> Bool { && string_contains(s: expected_roadmap_md(), pattern: "- [x] **Section 1 spawn-width foundation — std.measure expressibility** (#5470/#5478)") } -// HAND-EDIT tooth: any byte that diverges from the authority is drift (a hand-toggle, a prose edit). fn witness_hand_edit_drifts() -> Bool { roadmap_drifted(committed: concat(expected_roadmap_md(), "\n- [x] **a hand-added line**")) } -// Non-vacuity: the authored set genuinely renders the measure line done (so the perturbation flip is a -// real change, not a line that was [ ] either way). fn witness_authored_set_nonvacuous() -> Bool { count(authored_merged_prs()) > 0 && string_contains(s: expected_roadmap_md(), pattern: "- [x] **numeric-tower grounding** (#5428)") diff --git a/scripts/fixtures/v2_discovery_completeness_slice/edit_locus_resolver.dag b/scripts/fixtures/v2_discovery_completeness_slice/edit_locus_resolver.dag index ddb90d8acc2..513a8552d76 100644 --- a/scripts/fixtures/v2_discovery_completeness_slice/edit_locus_resolver.dag +++ b/scripts/fixtures/v2_discovery_completeness_slice/edit_locus_resolver.dag @@ -1,7 +1,3 @@ -// src/v2/lens/affected_set/edit_locus_resolver_test.dag -// Scope: git diff → edit_locus Node resolver receipts (path-regex bindings). -// Status: T-21 wire claim; host git projection lands with BinaryShim runner. - module v2.test.claim.lens_affected_set.edit_locus_resolver import v2.lens.edit_locus { @@ -40,7 +36,6 @@ import v2.test.claim.lens_common.infer_fixture { claim_inferred_tree } - import v2.std.verification { BoolWitness, BoolWitnessClaim, @@ -274,11 +269,8 @@ data edit_locus_source_provenance_parse_root_span_claim_passes: Bool = edit_locu data edit_locus_source_provenance_affected_set_wire_claim_passes: Bool = edit_locus_source_provenance_affected_set_wire_holds() data edit_locus_source_provenance_producer_rejects_malformed_source_claim_passes: Bool = edit_locus_source_provenance_producer_rejects_malformed_source_holds() -// Transport Symbol pin — independent authority for substrate-equivalence field-preservation laws. data edit_locus_narrow_resolution_holds_symbol: Symbol = edit_locus_narrow_resolution_holds -// Consolidation #4553 Phase 2 distributed BoolWitnessClaim marker. -// Co-located with `edit_locus_narrow_resolution_holds` so glob discovery finds the witness without the central roster. data unified_claim_edit_locus_narrow: UnifiedTestClaim = BoolWitnessClaim { witness: BoolWitness { entry: "src/v2/lens/affected_set/edit_locus_resolver_test.dag", @@ -286,8 +278,6 @@ data unified_claim_edit_locus_narrow: UnifiedTestClaim = BoolWitnessClaim { } } -// Consolidation #4553 Phase 2 distributed BoolWitnessClaim marker. -// Co-located with `edit_locus_fail_closed_holds` so glob discovery finds the witness without the central roster. data unified_claim_edit_locus_fail_closed: UnifiedTestClaim = BoolWitnessClaim { witness: BoolWitness { entry: "src/v2/lens/affected_set/edit_locus_resolver_test.dag", @@ -295,8 +285,6 @@ data unified_claim_edit_locus_fail_closed: UnifiedTestClaim = BoolWitnessClaim { } } -// Consolidation #4553 Phase 2 distributed BoolWitnessClaim marker. -// Co-located with `edit_locus_source_provenance_producer_holds` so glob discovery finds the witness without the central roster. data unified_claim_edit_locus_source_provenance_producer: UnifiedTestClaim = BoolWitnessClaim { witness: BoolWitness { entry: "src/v2/lens/affected_set/edit_locus_resolver_test.dag", @@ -304,8 +292,6 @@ data unified_claim_edit_locus_source_provenance_producer: UnifiedTestClaim = Boo } } -// Consolidation #4553 Phase 2 distributed BoolWitnessClaim marker. -// Co-located with `edit_locus_source_provenance_affected_set_wire_holds` so glob discovery finds the witness without the central roster. data unified_claim_edit_locus_source_provenance_affected_set_wire: UnifiedTestClaim = BoolWitnessClaim { witness: BoolWitness { entry: "src/v2/lens/affected_set/edit_locus_resolver_test.dag", @@ -313,8 +299,6 @@ data unified_claim_edit_locus_source_provenance_affected_set_wire: UnifiedTestCl } } -// Consolidation #4553 Phase 2 distributed BoolWitnessClaim marker. -// Co-located with `edit_locus_source_provenance_producer_rejects_malformed_source_holds` so glob discovery finds the witness without the central roster. data unified_claim_edit_locus_source_provenance_producer_rejects_malformed_source: UnifiedTestClaim = BoolWitnessClaim { witness: BoolWitness { entry: "src/v2/lens/affected_set/edit_locus_resolver_test.dag", diff --git a/src/v1/00_core.dag b/src/v1/00_core.dag index 695c3de78d2..0ccb3943a53 100644 --- a/src/v1/00_core.dag +++ b/src/v1/00_core.dag @@ -1,5 +1,3 @@ -// v2 core types -- the compiler's domain model. - module v1.std.core import std.types { FilePath, NonEmptyStr, SourceSpan, kernel_type_set, is_kernel_type, container_type_arity, is_container_type, container_expected_arity } @@ -11,14 +9,6 @@ import std.syntax { import std.algebra { CollectionSizeEffect, CostShape, AlgebraFieldTemplate } import std.induction { SubValueRelation } -// ========================================================================= -// Tokens -// ========================================================================= - -// Token: Layer 5 compositional type (see Composition Stack in ROADMAP). -// Token = { text: String, span: SourceSpan, shape: Shape } -// text carries semantic content (identifier name, literal value, keyword text). -// shape carries payload-insensitive structural classification. type Token { text: String span: SourceSpan @@ -42,28 +32,10 @@ type TokenShape | ShNewline | ShEof | ShUnknown - -// ========================================================================= -// AST -- Abstract Syntax Tree -// ========================================================================= - -// Module, Import, ImportNames dissolved into Node compositions. -// See dissolution helpers: module_node(), import_node(), module_imports(), -// module_items(), import_is_all(), import_specific_names_at(). - -// Connective -- the logical primitive for type structure. -// Conj = conjunction (product): all children hold simultaneously (record fields). -// Disj = disjunction (coproduct): exactly one child holds (sum variants). -// Arrow = exponential (function type). params carry input types, -// inferred carries return type. type Connective = Conj | Disj | NoConnective | Arrow type Cardinality = Required | CardOptional - -// Field, Variant, Param dissolved into Node (D1c). -// See make_param_node, make_field_node constructors and param_node_*/field_node_* accessors. - type FieldAccessStyle = StoredField | EnumAccessor | OptionalUnwrap | TupleFirst | TupleSecond type FieldValueShape = PlainValue | OptionalValue @@ -73,18 +45,6 @@ type FieldSummary { value_shape: FieldValueShape } -// Kernel and container type sets: canonical definitions live in -// dsl/std/types.dag. Imported via the top-level import above and -// re-exported for backward compatibility with v1.std.core consumers. - -// Arity bridge DELETED (P3.7). The compiler no longer hardcodes which -// types are parameterized. Arity comes from .dag declarations (params -// field on type definitions). See std/types.dag for List, Map, Set. - -// P1.9 InferredNode: compiler errors are orthogonal to the node graph. -// When inference fails, the result is structurally distinct from a type node. -// Error and Dynamic type sentinels become CompilerError variants. -// Consumers match InferredNode? directly — no bridge accessors needed. type InferredNode = Resolved { node: Node } | CompilerError { message: String, span: SourceSpan } @@ -127,21 +87,6 @@ type MethodSemantics type ExprErrorKind = ParseRecoveryError | SemanticExprError | InternalExprError - -// ========================================================================= -// Expressions -// ========================================================================= - -// ExprData -- expression discriminator for Node-embedded expressions. -// When a Node carries computation (expr_data != NoExprData), the Node's -// inferred and span provide the resolved type and source location. -// Sub-expressions are Node values (not Expr). -// ExprData: pure operator metadata tag (P5.11 dissolution). -// All child Nodes live in node.children. ExprData retains only -// non-Node metadata that identifies the expression kind. -// -// AlgebraFieldKind coproduct (Track 6): replaces former String? dispatch. -// Defined in std/syntax.dag, used by ExprBinOp and OperatorSpec. type ExprData = NoExprData | ExprLiteral { value: LiteralValue } @@ -172,14 +117,6 @@ type MatchPattern | VariantPattern { name: String, parent_enum: String?, field_bindings: List } | Wildcard -// FieldBinding dissolved into Node (D2c): -// field_name → node.name -// binding → node.match_pattern -// Constructor: make_field_binding_node -// Accessors: field_binding_name_at, field_binding_pattern - - - type UnaryOpKind = Not | Neg type StringPart @@ -188,10 +125,6 @@ type StringPart type OperationModifier = Idempotent | Readonly | Hermetic -// ========================================================================= -// Compiler output -// ========================================================================= - type CompileResult { files: List diagnostics: List @@ -202,10 +135,6 @@ type TextFile { content: String } -// ========================================================================= -// Typed diagnostics — root cause by construction. -// ========================================================================= - type CompilerDiagnostic = UnresolvedImport { module_path: String, importing_module: String, span: SourceSpan } | MissingExport { name: String, module_path: String, importing_module: String, span: SourceSpan } @@ -279,16 +208,6 @@ fn is_error_diagnostic(d: CompilerDiagnostic) -> Bool { true } -// Scoped to the `dag run` path only. The interpreter carves out exactly -// one diagnostic class — ComplexityUnknown — because the CX analysis is -// documented as partial in ROADMAP M1 (active reduction, currently -// non-blocking compile gate). OwnershipViolation is a real semantic -// error (the repo explicitly promoted it from warning); the interpreter -// must not execute programs that violate ownership. -// -// Adding more carve-outs requires upstream justification: a structural -// reason the diagnostic class is non-fatal at the interpreter boundary -// specifically, not a convenience to make `dag run` not fail. fn is_interpreter_blocking_diagnostic(d: CompilerDiagnostic) -> Bool { match d { ComplexityUnknown { func_name: _, reason: _, span: _ } => false @@ -296,14 +215,6 @@ fn is_interpreter_blocking_diagnostic(d: CompilerDiagnostic) -> Bool { } } -// SCAFFOLD — parse-resilience exposes whole-tree v2 typecheck debt in discovery corpus -// (~779 errors: FreeMonoid Empty/Cons bootstrap-gap + residual real errors). Visible but -// non-blocking ONLY when a discovery caller threads ResolveTypecheckGate:: -// DiscoveryCorpusAdvisory (cli_run floor discovery paths). gunbc compile/run and -// compile_to_resolved stay fail-closed via is_interpreter_blocking_diagnostic / -// is_error_diagnostic — this predicate is never consulted on those paths. -// dissolve-on: src/v2 whole-tree reaches compile-clean (779 remediated) — delete this -// carve-out and discovery re-tightens to is_interpreter_blocking_diagnostic. fn is_discovery_corpus_advisory_typecheck_diagnostic(d: CompilerDiagnostic) -> Bool { match d { UnresolvedType { name: _, span: _ } => true @@ -332,7 +243,6 @@ fn make_error_node(diagnostic: CompilerDiagnostic, module_name: String) -> Error ErrorNode { diagnostic: diagnostic, module_name: module_name } } - type DeclaredFuncSig { name: String params: List @@ -346,10 +256,6 @@ type DeclaredFuncEnv { signatures: Map } -// ========================================================================= -// Node -- unified recursive structure -// ========================================================================= - type Node { name: String ident: Int? @@ -357,8 +263,7 @@ type Node { ident_span: SourceSpan? children: List connective: Connective - // Collection classification -- structural, not name-based (P5.7b) - // Ports -- what flows in and out (dissolved from Param, D1c) + params: List inferred: InferredNode? return_cardinality: Cardinality @@ -369,7 +274,7 @@ type Node { type_annotation: Node? is_self_recursive: Bool has_non_tail_self_call: Bool - // Match arm pattern -- set only on arm Nodes (P5.11 compositional dissolution) + match_pattern: MatchPattern? expr_data: ExprData } @@ -382,8 +287,6 @@ fn node_name_span(n: Node) -> SourceSpan { match n.ident_span { Present { value: s } => s Absent => n.span } } -// Construct a Node carrying expression data (P5.11 dissolved form). -// Children are passed explicitly — ExprData is pure operator metadata. fn make_expr_node(expr_data: ExprData, children: List, inferred: InferredNode?, span: SourceSpan) -> Node { Node { name: "", span: span, ident_span: none, children: children, @@ -396,8 +299,6 @@ fn make_expr_node(expr_data: ExprData, children: List, inferred: InferredN } } -// Construct a Node carrying expression data with a dissolved identifier (D3). -// The name field carries the identifier that was formerly in ExprData. fn make_named_expr_node(name: String, expr_data: ExprData, children: List, inferred: InferredNode?, span: SourceSpan, name_span: SourceSpan) -> Node { Node { name: name, span: span, ident_span: default_ident_span(name: name, span: name_span), children: children, @@ -422,14 +323,6 @@ fn make_expr_error_node(kind: ExprErrorKind, message: String, span: SourceSpan) } } -// ========================================================================= -// Compositional child Node constructors (P5.11) -// -// Arguments, match arms, field inits, and string interp parts are Nodes. -// This follows the same pattern as Param→Node (Decision 8). -// Varargs emerge naturally as an N-sized list of arg Nodes. -// ========================================================================= - fn make_arg_node(name: String?, value: Node, span: SourceSpan, name_span: SourceSpan) -> Node { let arg_name = match name { Present { value: n } => n Absent => "" } Node { @@ -486,7 +379,6 @@ fn make_field_init_node(name: String, value: Node, span: SourceSpan, name_span: } } -// Field binding node (D2c): name carries the field name, match_pattern carries the binding pattern. fn make_field_binding_node(field_name: String, binding: MatchPattern, span: SourceSpan, name_span: SourceSpan) -> Node { Node { name: field_name, span: span, ident_span: default_ident_span(name: field_name, span: name_span), children: [], connective: NoConnective, @@ -526,28 +418,6 @@ fn make_interp_part_node(expr: Node, span: SourceSpan) -> Node { } } -// ========================================================================= -// Param / Field / Variant dissolution (D1c) -// -// Param, Field, and Variant are dissolved into Node. This follows the same -// pattern as FieldInit and ResourceUse dissolutions (D1a/D1b). -// -// Param node layout: -// name = param name -// children = [type_expr] or [type_expr, default_value] -// span = source span -// -// Field node layout (extends Param): -// name = field name -// children = [type_expr] or [type_expr, default_value] -// return_cardinality = field cardinality -// properties = [from_key_node] if from_key is present -// span = source span -// -// Variant node: a Node with name + span + children (the dissolved Field nodes). -// Already close to how variants work in the AST. -// ========================================================================= - fn make_param_node(name: String, type_expr: Node, default_value: Node?, span: SourceSpan, name_span: SourceSpan) -> Node { let children = match default_value { Present { value: dv } => [type_expr, dv] Absent => [type_expr] } Node { @@ -592,10 +462,6 @@ fn authored_name_at(source_indices: Map, node: Node) -> St } } -// BRIDGE DEBT (M4 Lane 2): child lookup by authored string name. -// Deletion point: when Node.name is removed, replace all call sites -// with positional access (children[i]) or structural field identity. -// Until then, routes through authored_name_at for source-derived names. fn find_child_named(n: Node, name: String, source_indices: Map) -> Node? { match n.children |> filter(c => authored_name_at(source_indices: source_indices, node: c) == name) |> first { Present { value: ch } => Present { value: ch } @@ -663,8 +529,6 @@ fn field_node_from_key(n: Node, source_indices: Map) -> St fn field_node_span(n: Node) -> SourceSpan { n.span } -// Variant constructor: a Node with name + span + children (dissolved Field nodes). -// name_span is the span of the variant identifier token (for ident_span accuracy). fn make_variant_node(name: String, fields: List, span: SourceSpan, name_span: SourceSpan) -> Node { Node { name: name, span: span, ident_span: default_ident_span(name: name, span: name_span), children: fields, connective: NoConnective, @@ -679,13 +543,6 @@ fn variant_node_name_at(n: Node, source_indices: Map) -> S fn variant_node_fields(n: Node) -> List { n.children } fn variant_node_span(n: Node) -> SourceSpan { n.span } -// ========================================================================= -// IR Child Layout Model -// ========================================================================= - -// Bootstrap constraint: `accessor` is a function name (String). In the current -// .dag type system, function names ARE the structural identity. When .dag supports -// function references, accessor should become a structural reference. type ChildRole { name: String accessor: String @@ -762,17 +619,10 @@ fn is_child_accessor_in_model(name: String) -> Bool { || (map_values(wrapper_child_roles) |> any(roles => roles |> any(r => r.accessor == name))) } -// Model query: get fixed child roles for an ExprData variant. -// Returns None if the variant is not in the model (fail-closed: callers -// must handle missing layout explicitly, not assume "no children"). fn child_roles_for_variant(variant_name: String) -> List? { map_get(expr_child_roles, variant_name) } -// ========================================================================= -// Node field roles — structural descent facts for complexity analysis -// ========================================================================= - type NodeFieldRole = ChildrenListField | SubValueField @@ -801,10 +651,6 @@ fn is_sub_value_field(field_name: String) -> Bool { } } -// ========================================================================= -// Function size effects — structural descent facts for complexity analysis -// ========================================================================= - type FunctionSizeEffect = TreeSizePreserving | TreeSizeReducing @@ -851,17 +697,6 @@ fn is_property_contraction(func_name: String) -> Bool { } } -// ========================================================================= -// Compositional child Node accessors (P5.11) -// -// Implementations of the child projections declared in expr_child_roles -// and wrapper_child_roles above. The model is the source of truth; -// these functions implement the projections. -// -// Required children return an error node when missing (fail-fast), -// preventing silent self-recursion from malformed nodes. -// ========================================================================= - fn expr_child_at(texpr: Node, index: Int, role: String) -> Node { match texpr.children |> skip(index) |> first { Present { value: v } => v @@ -869,7 +704,6 @@ fn expr_child_at(texpr: Node, index: Int, role: String) -> Node { } } - fn arg_name_at(n: Node, source_indices: Map) -> String? { let name = authored_name_at(source_indices: source_indices, node: n) if name == "" { none } else { Present { value: name } } @@ -920,17 +754,14 @@ fn binop_right(texpr: Node) -> Node { expr_child_at(texpr: texpr, index: 1, role fn unaryop_operand(texpr: Node) -> Node { expr_child_at(texpr: texpr, index: 0, role: "unaryop operand") } -// -- ExprVar (D3: name dissolved into Node.name) -- fn expr_var_name_at(texpr: Node, source_indices: Map) -> String { authored_name_at(source_indices: source_indices, node: texpr) } -// -- ExprFieldAccess (D3: field dissolved into Node.name) -- fn field_access_base(texpr: Node) -> Node { expr_child_at(texpr: texpr, index: 0, role: "field access base") } fn field_access_field_at(texpr: Node, source_indices: Map) -> String { authored_name_at(source_indices: source_indices, node: texpr) } fn expr_field_access_summary(texpr: Node) -> FieldSummary? { match texpr.expr_data { ExprFieldAccess { summary: s } => s _ => none } } -// -- ExprCall (D3: func dissolved into Node.name) -- fn expr_call_func_at(texpr: Node, source_indices: Map) -> String { authored_name_at(source_indices: source_indices, node: texpr) } fn expr_call_descent_evidence(texpr: Node) -> List? { @@ -940,7 +771,6 @@ fn expr_call_descent_evidence(texpr: Node) -> List? { } } -// -- ExprMethodCall (D3: method dissolved into Node.name) -- fn method_receiver(texpr: Node) -> Node { expr_child_at(texpr: texpr, index: 0, role: "method receiver") } fn method_arg_nodes(texpr: Node) -> List { texpr.children |> skip(1) } fn expr_method_name_at(texpr: Node, source_indices: Map) -> String { authored_name_at(source_indices: source_indices, node: texpr) } @@ -948,11 +778,9 @@ fn expr_method_call_semantics(texpr: Node) -> MethodSemantics? { match texpr.expr_data { ExprMethodCall { method_semantics: ms } => ms _ => none } } -// -- ExprLambda (D3: params dissolved into child Nodes after body) -- fn lambda_body(texpr: Node) -> Node { expr_child_at(texpr: texpr, index: 0, role: "lambda body") } fn lambda_param_names_at(texpr: Node, source_indices: Map) -> List { texpr.children |> skip(1) |> map(n => authored_name_at(source_indices: source_indices, node: n)) } -// -- ExprLet (D3: name dissolved into Node.name) -- fn let_value(texpr: Node) -> Node { expr_child_at(texpr: texpr, index: 0, role: "let value") } fn let_body(texpr: Node) -> Node? { texpr.children |> skip(1) |> first } fn let_binding_name_at(texpr: Node, source_indices: Map) -> String { authored_name_at(source_indices: source_indices, node: texpr) } @@ -960,7 +788,6 @@ fn let_binding_name_at(texpr: Node, source_indices: Map) - fn cast_expr(texpr: Node) -> Node { expr_child_at(texpr: texpr, index: 0, role: "cast expr") } fn cast_target(texpr: Node) -> Node { expr_child_at(texpr: texpr, index: 1, role: "cast target") } -// -- ExprForEach (D3: variable dissolved into Node.name) -- fn foreach_collection(texpr: Node) -> Node { expr_child_at(texpr: texpr, index: 0, role: "foreach collection") } fn foreach_body(texpr: Node) -> Node { expr_child_at(texpr: texpr, index: 1, role: "foreach body") } fn foreach_variable_at(texpr: Node, source_indices: Map) -> String { authored_name_at(source_indices: source_indices, node: texpr) } @@ -976,24 +803,11 @@ fn return_value(texpr: Node) -> Node { expr_child_at(texpr: texpr, index: 0, rol fn block_stmts(texpr: Node) -> List { texpr.children } -// -- ExprRecordLit (D3: type_name dissolved into Node.name, parent_enum stays in ExprData) -- fn record_lit_type_name_at(texpr: Node, source_indices: Map) -> String? { let name = authored_name_at(source_indices: source_indices, node: texpr) if name == "" { none } else { Present { value: name } } } - -// ========================================================================= -// Transport Node constructors -// -// Transport is a Node where name = transport kind, properties = config, -// children = ordered argv (shell only). -// -// Property name constants — single authority for transport config keys. -// Constructors, predicates, accessors, and is_config_reserved_key all -// reference these instead of bare strings. -// ========================================================================= - data transport_url_key: String = "base_url" data transport_path_key: String = "base_path" data transport_auth_token_key: String = "auth_token" @@ -1071,10 +885,6 @@ fn file_transport_node(base_path: Node, span: SourceSpan) -> Node { make_transport_node(properties: [path_field], children: [], body: none, span: span) } -// ========================================================================= -// Transport Node accessors -// ========================================================================= - fn find_property(props: List, prop_name: String, source_indices: Map) -> Node? { match props |> filter(p => field_init_node_name_at(n: p, source_indices: source_indices) == prop_name) |> first() { Present { value: fi } => Present { value: field_init_node_value(n: fi) } @@ -1182,10 +992,6 @@ fn transport_env(t: Node, source_indices: Map) -> List Node) -> Node { Node { name: node.name, ident: node.ident, span: node.span, ident_span: node.ident_span, @@ -1201,10 +1007,6 @@ fn map_children(node: Node, transform: fn(Node) -> Node) -> Node { } } -// ========================================================================= -// Self-call detection -- Node expression walkers -// ========================================================================= - fn expr_has_self_call(texpr: Node, fn_name: String, source_indices: Map) -> Bool { match texpr.expr_data { ExprCall { call_semantics: _, descent_evidence: _ } => @@ -1268,24 +1070,6 @@ fn expr_has_non_tail_self_call(texpr: Node, fn_name: String, in_tail: Bool, sour } } -// ========================================================================= -// Dissolution infrastructure -- Node-based representations of types that -// will eventually dissolve. Each type gets: -// 1. A Node constructor that encodes the type's fields as Node properties -// 2. Accessor functions that extract fields from the Node representation -// 3. A predicate that identifies nodes of this kind -// -// The migration path: producers switch to constructors, consumers switch -// to accessors, then the dedicated type definition is deleted. -// ========================================================================= - - -// --- ServiceConfig as Node properties --- -// A ServiceConfig dissolves into Node properties where: -// "svc_endpoint" = endpoint Node, "svc_auth" = auth Node, -// "svc_rate_limit" = rate_limit Node, "svc_retry" = retry Node. -// Only non-none fields are included. - fn service_config_properties(endpoint: Node, auth: Node?, auth_input: Node?, auth_source: Node?, rate_limit: Node?, retry: Node?) -> List { let zero_span = make_span(start: 0, end: 0) let ep_prop = [make_field_init_node(name: "svc_endpoint", value: endpoint, span: zero_span, name_span: zero_span)] @@ -1340,13 +1124,6 @@ fn service_config_auth_source(n: Node, source_indices: Map find_property(props: n.properties, prop_name: "svc_auth_source", source_indices: source_indices) } -// --- Module as Node --- -// A Module dissolves into a Node where: -// name = module name, params = import nodes, children = item nodes. -// Import nodes: name = module_path, children = specific imported names. -// Wildcard import: children is empty. Specific import: children are name nodes. -// No metadata markers — structural position (params vs children) IS the distinction. - fn module_node(name: String, imports: List, items: List, span: SourceSpan) -> Node { Node { name: name, span: span, ident_span: default_ident_span(name: name, span: span), children: items, connective: NoConnective, @@ -1388,10 +1165,6 @@ fn module_items(n: Node) -> List { n.children } -// ========================================================================= -// Node vocabulary (structural constructors and predicates) -// ========================================================================= - fn leaf_node_with_span(name: String, span: SourceSpan) -> Node { Node { name: name, span: span, ident_span: default_ident_span(name: name, span: span), children: [], connective: NoConnective, params: [], inferred: none, return_cardinality: Required, uses: [], body: none, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: NoExprData } } @@ -1422,10 +1195,6 @@ fn no_span() -> SourceSpan { make_span(start: 0, end: 0) } -// ========================================================================= -// NewlineIndex — byte-offset-to-line:col translation -// ========================================================================= - type LineCol { line: Int col: Int @@ -1479,13 +1248,6 @@ fn source_text_at(index: NewlineIndex, span: SourceSpan) -> String { chars_to_string(chars: index.char_codes, start: span.start, end: span.end) } -// ========================================================================= -// Intern table — parser-owned identifier authority (Phase 1: type + functions only) -// -// InternTable is built during parsing and carried on FrontendResult. -// Identity consumer and TypeEnv threading deferred until first real use site. -// ========================================================================= - type InternTable { strings: List index: Map diff --git a/src/v1/02_parse.dag b/src/v1/02_parse.dag index cc881ae239c..9b438a33d51 100644 --- a/src/v1/02_parse.dag +++ b/src/v1/02_parse.dag @@ -1,5 +1,3 @@ -// v2 parser -- recursive descent, first-error-halt. - module v1.compiler.parse import v1.std.core { @@ -59,10 +57,6 @@ import v1.compiler.languages { import extdeps.languages.dag.syntax { dag_syntax_spec, dag_non_name_keywords } -// ========================================================================= -// Parser state types -// ========================================================================= - type ParseContext { source_indices: Map intern_table: InternTable @@ -78,7 +72,6 @@ type ParseWithTableResult { intern_table: InternTable } - type AdvanceResult = AdvanceOk { token: Token, tokens: List } | AdvanceEof @@ -118,8 +111,6 @@ type ItemPrefixResult { err: ErrorNode? } -// Parser-local intermediate: ServiceConfig is dissolved into Node properties at the -// service Node construction boundary. This type exists only inside the parser. type ServiceConfig { endpoint: Node, auth: Node?, auth_input: Node?, auth_source: Node?, rate_limit: Node?, retry: Node? } type ConfigResult { config: ServiceConfig, tokens: List, ctx: ParseContext, err: ErrorNode? } @@ -218,10 +209,6 @@ type ParserResultWitness type DescResult { desc: String?, tokens: List } -// ========================================================================= -// Helpers -// ========================================================================= - fn token_span(tok: Token?) -> SourceSpan { match tok { Present { value: t } => t.span Absent => make_span(start: 0, end: 0) } } @@ -292,7 +279,6 @@ fn is_newline_shape(shape: TokenShape) -> Bool { match shape { ShNewline => true fn is_eof_shape(shape: TokenShape) -> Bool { match shape { ShEof => true _ => false } } fn is_keyword_shape(shape: TokenShape) -> Bool { match shape { ShKeyword => true _ => false } } - fn is_lbrace_shape(shape: TokenShape) -> Bool { match shape { ShLBrace => true _ => false } } fn is_rbrace_shape(shape: TokenShape) -> Bool { match shape { ShRBrace => true _ => false } } fn is_lparen_shape(shape: TokenShape) -> Bool { match shape { ShLParen => true _ => false } } @@ -325,13 +311,6 @@ fn is_null_coalesce_shape(shape: TokenShape) -> Bool { match shape { ShNullCoale fn is_pipe_shape(shape: TokenShape) -> Bool { match shape { ShPipe => true _ => false } } fn is_pipe_arrow_shape(shape: TokenShape) -> Bool { match shape { ShPipeArrow => true _ => false } } - -// ========================================================================= -// Minimal-information token inspectors: take Token?, not List. -// Callers extract the token explicitly — no ambiguity about which list. -// See INVARIANTS.md "Minimal information per interface". -// ========================================================================= - fn tok_is_keyword(tok: Token?, kw: String) -> Bool { match tok { Present { value: t } => is_keyword_shape(shape: t.shape) && t.text == kw Absent => false } } @@ -351,11 +330,7 @@ fn drop_leading_type_modifier(tokens: List, modifier: String) -> List) -> List { if tok_is_ident_text(tok: tokens |> first, text: "test") && (tok_keyword_text(tok: tokens |> skip(1) |> first) != "") { tokens |> skip(1) @@ -442,9 +417,6 @@ fn tok_is_keyword_name(tok: Token?) -> Bool { match tok_keyword_to_name(tok: tok) { Present { value: _ } => true Absent => false } } - - - fn shape_display_name(shape: TokenShape) -> String { match shape { ShKeyword => "keyword" @@ -610,10 +582,6 @@ fn expect_name(tokens: List) -> NameResult { } } - -// Keywords that are valid as names (field names, type names). -// Derived structurally: any keyword NOT in dag_non_name_keywords -// (literals + lifecycle). Single authority: dag/syntax.dag. fn is_name_keyword(token: Token) -> Bool { if is_keyword_shape(shape: token.shape) { match lookup(dag_non_name_keywords, key: token.text) { @@ -764,17 +732,10 @@ fn parser_result_witness(expr: Node, source_indices: Map) } } - -// ========================================================================= -// Node construction helpers -// ========================================================================= - fn leaf_type_node(name: String, span: SourceSpan) -> Node { Node { name: name, span: span, ident_span: if name == "" { none } else { Present { value: span } }, children: [], connective: NoConnective, params: [], inferred: none, return_cardinality: Required, uses: [], body: none, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: NoExprData } } -// Gate #60: decimal width literal inside `<…>` type-argument lists only -// (e.g. `MachineWidth<8>`). Standalone `let x: 64 = …` must stay a parse error. fn literal_width_nat_type_node(value: Int, span: SourceSpan) -> Node { Node { name: "", span: span, ident_span: none, children: [], connective: NoConnective, params: [], inferred: none, return_cardinality: Required, uses: [], body: none, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: ExprLiteral { value: LitInt { value: value } } } } @@ -801,9 +762,6 @@ fn is_conj_with_children(n: Node) -> Bool { n.connective == Conj && n.children |> count > 0 } -// Extract the inferred type from a child Node. -// Typed: use resolved type. All other states: propagate error_type (fail-closed). -// Note: node_inferred_to_outputs gate ensures only Typed children reach here. fn child_inferred_or_empty(ch: Node) -> Node { match ch.inferred { Present { value: Resolved { node: rt } } => rt @@ -811,9 +769,6 @@ fn child_inferred_or_empty(ch: Node) -> Node { } } -// Convert a Node return type to output fields. -// Fail-closed: if ANY child is not fully typed, return [] (no partial boundaries). -// Otherwise -> Conj with children extracts fields, scalar wraps as single "value" field. fn node_inferred_to_outputs(rt: Node, source_indices: Map) -> List { if is_conj_with_children(n: rt) { let all_children_typed = rt.children |> all(ch => match ch.inferred { Present { value: Resolved { node: _ } } => true _ => false }) @@ -828,10 +783,6 @@ fn node_inferred_to_outputs(rt: Node, source_indices: Map) } } -// ========================================================================= -// Helper: parse a dotted identifier path (e.g., "v1.std.core") -// ========================================================================= - fn parse_dotted_ident(tokens: List) -> NameResult { let r = expect_name(tokens: tokens) if has_err(err: r.err) { return r } @@ -850,10 +801,6 @@ fn parse_dotted_ident_rest(tokens: List, acc: String, span: SourceSpan) - } } -// ========================================================================= -// 1. Entry point -// ========================================================================= - fn parse_with_table(tokens: List, source_indices: Map, intern_table: InternTable) -> ParseWithTableResult { let pre_interned = pre_intern_tokens(tokens: tokens, table: intern_table) let ctx = ParseContext { source_indices: source_indices, intern_table: pre_interned } @@ -875,10 +822,6 @@ fn parse(tokens: List, source_indices: Map) -> Pars parse_with_table(tokens: tokens, source_indices: source_indices, intern_table: empty_intern_table()).result } -// ========================================================================= -// 2. parse_module -// ========================================================================= - fn parse_module(tokens: List, ctx: ParseContext) -> ModuleResult { let tokens = skip_newlines(tokens: tokens) let start_span = token_span(tok: tokens |> first) @@ -950,10 +893,6 @@ fn parse_items_acc(tokens: List, ctx: ParseContext, acc: List) -> I } } -// ========================================================================= -// 3. parse_import -// ========================================================================= - fn parse_import(tokens: List, ctx: ParseContext) -> ImportResult { let start_span = token_span(tok: tokens |> first) let err_import = import_node(module_path: "", is_all: false, specific_names: [], span: start_span, name_span: start_span) @@ -1015,10 +954,6 @@ fn parse_import_names_acc(tokens: List, ctx: ParseContext, acc: List, keyword: String) -> ItemForm? { let matches = forms |> filter(f => f.keyword == keyword) match matches |> count { @@ -1120,10 +1055,6 @@ fn parse_item_by_form(tokens: List, ctx: ParseContext, form: ItemForm) -> } } -// ========================================================================= -// Type structure -> child Node conversion -// ========================================================================= - fn field_to_child_node(field: Node, source_indices: Map) -> Node { let ret_type = field_node_type_expr(n: field) let props = match field_node_from_key(n: field, source_indices: source_indices) { @@ -1158,14 +1089,6 @@ fn outputs_to_inferred(outputs: List, span: SourceSpan, source_indices: Ma } } -// ========================================================================= -// Operation / Capability Node constructors (D2d/D2e) -// -// OperationDef and CapabilityDef were parser intermediates that dissolved -// into Node at the service/resource parse sites. Now the parser functions -// construct Node directly using these constructors. -// ========================================================================= - fn make_operation_node(name: String, ident_span: SourceSpan?, inputs: List, outputs: List, response_props: List, mock_props: List, exit_props: List, modifier_props: List, transport: Node?, span: SourceSpan, source_indices: Map) -> Node { let all_props = concat(modifier_props, response_props, exit_props, mock_props) Node { name: name, span: span, ident_span: ident_span, @@ -1195,10 +1118,6 @@ fn make_capability_node(name: String, ident_span: SourceSpan?, inputs: List, ctx: ParseContext) -> ItemResult { let start_span = token_span(tok: tokens |> first) let dummy = Node { name: "", span: start_span, ident_span: none, children: [], params: [], inferred: none, return_cardinality: Required, uses: [], body: none, connective: NoConnective, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: NoExprData } @@ -1279,11 +1198,7 @@ fn parse_type_body_from_prefix(prefix: ItemPrefixResult, start_span: SourceSpan) fn parse_type_body_after_eq(tokens: List, ctx: ParseContext, name: String, name_span: SourceSpan, start_span: SourceSpan, type_params: List) -> ItemResult { let dummy = Node { name: name, span: start_span, ident_span: Present { value: name_span }, children: [], params: type_params, inferred: none, return_cardinality: Required, uses: [], body: none, connective: NoConnective, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: NoExprData } - // An OPTIONAL leading pipe forces the variant-list interpretation: `= | A` - // is a single-variant nullary sum (a deliberate novel-tag introduction), and - // `= | A | B` falls out as a normal multi-variant sum. A bare `= A` (no - // leading pipe) stays an alias and fails closed (UnresolvedType) when the RHS - // is not a declared type — that is the alias-vs-single-variant discriminator. + match eat(tokens: tokens, expected: ExpectPipe) { EatConsumed { token: _, tokens: __lead } => let tokens = skip_newlines(tokens: __lead) @@ -1545,14 +1460,6 @@ fn parse_more_variants_acc(tokens: List, ctx: ParseContext, acc: List, ctx: ParseContext) -> TypeResult { let tokens = skip_newlines(tokens: tokens) let tok = tokens |> first @@ -1584,9 +1491,7 @@ fn parse_type_expr(tokens: List, ctx: ParseContext) -> TypeResult { } fn parse_callable_type_expr(tokens: List, ctx: ParseContext, start_span: SourceSpan) -> TypeResult { - // Error recovery produces an empty-named node, not "Callable" — errors - // must be orthogonal to the node graph (invariant #2). A "Callable"-named - // dummy would leak into emit and be indistinguishable from a real callable. + let dummy_te = leaf_type_node(name: "", span: start_span) let r = expect(tokens: tokens, expected: ExpectLParen) if has_err(err: r.err) { return TypeResult { type_expr: dummy_te, tokens: r.tokens, ctx: ctx, err: r.err } } @@ -1714,10 +1619,6 @@ fn maybe_optional(tokens: List, ctx: ParseContext, te: Node, start_span: } } -// ========================================================================= -// Field list: name: Type, name2: Type? -// ========================================================================= - fn parse_field_list(tokens: List, ctx: ParseContext) -> FieldsResult { parse_field_list_acc(tokens: tokens, ctx: ctx, acc: []) } @@ -1810,10 +1711,6 @@ fn parse_optional_from_key(tokens: List, ctx: ParseContext) -> FromKeyRes } } -// ========================================================================= -// 8. parse_fn_def: fn name(params) -> ReturnType = expr | { body } -// ========================================================================= - fn parse_fn_def(tokens: List, ctx: ParseContext) -> ItemResult { let start_span = token_span(tok: tokens |> first) let dummy = Node { name: "", span: start_span, ident_span: none, children: [], params: [], inferred: none, return_cardinality: Required, uses: [], body: none, connective: NoConnective, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: NoExprData } @@ -1863,15 +1760,7 @@ fn parse_fn_body_from_prefix(prefix: ItemPrefixResult, start_span: SourceSpan) - let params = prefix.params let inferred = prefix.inferred let ctx = prefix.ctx - // Type params prepend to params (same slot as type definitions). - // At parse time they're distinguishable via span-identity - // (collect_type_param_names builds both the param's name_span and the - // type_expr's span from the same token). Post-resolve, consumers use - // the grounded invariant installed by resolve_item_types:909 — - // type_expr.inferred == TypeVariable && param.name == type_expr.name. - // See is_function_type_param in src/v1/05_emit_rust.dag for the - // emit-time classifier. Dissolution: partition this slot into - // type_params/value_params or add a ParamKind discriminator. + let all_params = concat(type_params, params) let named_dummy = Node { name: name, span: start_span, ident_span: Present { value: name_span }, children: [], params: all_params, inferred: none, return_cardinality: Required, uses: [], body: none, connective: NoConnective, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: NoExprData } @@ -1897,10 +1786,6 @@ fn parse_fn_body_from_prefix(prefix: ItemPrefixResult, start_span: SourceSpan) - ItemResult { item: item, tokens: skip_newlines(tokens: body_result.tokens), ctx: body_result.ctx, err: none } } -// ========================================================================= -// 9. parse_func_def: func name(params) -> RetType uses res: Type { body } -// ========================================================================= - fn parse_func_def(tokens: List, ctx: ParseContext) -> ItemResult { let start_span = token_span(tok: tokens |> first) let dummy = Node { name: "", span: start_span, ident_span: none, children: [], params: [], inferred: none, return_cardinality: Required, uses: [], body: none, connective: NoConnective, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: NoExprData } @@ -2095,10 +1980,6 @@ fn parse_optional_inferred(tokens: List, ctx: ParseContext) -> OptRetResu } } -// ========================================================================= -// 10. parse_service_def -// ========================================================================= - fn parse_service_def(tokens: List, ctx: ParseContext) -> ItemResult { let start_span = token_span(tok: tokens |> first) let dummy = Node { name: "", span: start_span, ident_span: none, children: [], params: [], inferred: none, return_cardinality: Required, uses: [], body: none, connective: NoConnective, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: NoExprData } @@ -2129,9 +2010,6 @@ fn parse_service_after_kw(tokens: List, ctx: ParseContext, start_span: So let r2 = expect(tokens: tokens, expected: ExpectRBrace) if has_err(err: r2.err) { return ItemResult { item: named_dummy, tokens: r2.tokens, ctx: r.ctx, err: r2.err } } - // Operations are already Nodes (D2d dissolution) -- use directly as children - // Preserve namespace root as a structural property so downstream stages - // don't need to reverse-engineer it by string-splitting. let ns_prop = make_field_init_node(name: "namespace_root", value: make_expr_node(expr_data: ExprLiteral { value: LitStr { value: namespace_root } }, children: [], inferred: none, span: start_span), span: start_span, name_span: no_span()) let svc_props = match r.config { Present { value: cfg } => service_config_properties(endpoint: cfg.endpoint, auth: cfg.auth, auth_input: cfg.auth_input, auth_source: cfg.auth_source, rate_limit: cfg.rate_limit, retry: cfg.retry) @@ -2583,12 +2461,6 @@ fn parse_op_body_entries( } } -// ========================================================================= -// PortContract dissolution helpers -// -// Convert parsed operation/capability metadata into Node properties. -// ========================================================================= - fn modifier_to_prop(name: String, span: SourceSpan) -> Node { make_field_init_node(name: name, value: make_expr_node(expr_data: ExprLiteral { value: LitBool { value: true } }, children: [], inferred: none, span: span), span: span, name_span: span) } @@ -2649,12 +2521,6 @@ fn last_child_or_self(n: Node) -> Node { } } -// Extract a string name from a Node for use in property values. -// Keyed-container detection derives from container_type_arity (single authority -// for container identity and arity) plus structural child count — no parallel name list. -// Recursion is structurally bounded: all self-calls pass a strict child of n. -// Optional handling uses with_required_cardinality (TreeSizePreserving wrapper); -// the CX analyzer sees through this via wrapper transparency in SCC edge classification. fn node_to_name_str(n: Node, source_indices: Map) -> String { let is_optional = n.return_cardinality == CardOptional let effective_n = if is_optional { with_required_cardinality(n: n) } else { n } @@ -2736,9 +2602,6 @@ fn parse_operation_modifiers_acc(tokens: List, ctx: ParseContext, acc: Li } } -// ========================================================================= -// ========================================================================= - fn parse_status_pattern(tokens: List, ctx: ParseContext) -> ExprResult { let tok = tokens |> first let sh = match tok { Present { value: t } => Present { value: t.shape } Absent => none } @@ -2867,10 +2730,6 @@ fn parse_mock_response_entries_acc(tokens: List, ctx: ParseContext, acc: } } -// ========================================================================= -// 11. parse_resource_def -// ========================================================================= - fn parse_resource_def(tokens: List, ctx: ParseContext) -> ItemResult { let start_span = token_span(tok: tokens |> first) let dummy = Node { name: "", span: start_span, ident_span: none, children: [], params: [], inferred: none, return_cardinality: Required, uses: [], body: none, connective: NoConnective, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: NoExprData } @@ -2897,7 +2756,6 @@ fn parse_resource_after_kw(tokens: List, ctx: ParseContext, start_span: S let r2 = expect(tokens: skip_newlines(tokens: r.tokens), expected: ExpectRBrace) if has_err(err: r2.err) { return ItemResult { item: named_dummy, tokens: r2.tokens, ctx: r.ctx, err: r2.err } } - // Capabilities are already Nodes (D2e dissolution) -- use directly as children let item = Node { name: name, span: start_span, ident_span: Present { value: name_span }, children: r.capabilities, params: [], inferred: none, return_cardinality: Required, uses: [], body: none, connective: NoConnective, @@ -3056,10 +2914,6 @@ fn parse_io_blocks_acc(tokens: List, ctx: ParseContext, inputs: List, ctx: ParseContext) -> ItemResult { let start_span = token_span(tok: tokens |> first) let dummy = Node { name: "", span: start_span, ident_span: none, children: [], params: [], inferred: none, return_cardinality: Required, uses: [], body: none, connective: NoConnective, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: NoExprData } @@ -3098,10 +2952,6 @@ fn parse_data_after_kw(tokens: List, ctx: ParseContext, start_span: Sourc ItemResult { item: item, tokens: skip_newlines(tokens: r.tokens), ctx: r.ctx, err: none } } -// ========================================================================= -// 13. parse_params: (name: Type, name2: Type) -// ========================================================================= - fn parse_params(tokens: List, ctx: ParseContext) -> ParamsResult { let r = expect(tokens: tokens, expected: ExpectLParen) if has_err(err: r.err) { return ParamsResult { params: [], tokens: r.tokens, ctx: ctx, err: r.err } } @@ -3179,10 +3029,6 @@ fn parse_param(tokens: List, ctx: ParseContext) -> ParamResult { } } -// ========================================================================= -// 14. parse_block: { stmt1 \n stmt2 \n result_expr } -// ========================================================================= - fn parse_block(tokens: List, ctx: ParseContext) -> ExprResult { let dummy_expr = parse_recovery_placeholder() let block_span = token_span(tok: tokens |> first) @@ -3365,18 +3211,10 @@ fn parse_bare_assignment(tokens: List, ctx: ParseContext) -> ExprResult { } } -// ========================================================================= -// 15. parse_expr -- Pratt precedence climbing entry -// ========================================================================= - fn parse_expr(tokens: List, ctx: ParseContext) -> ExprResult { parse_expr_bp(tokens: tokens, ctx: ctx, min_bp: 0) } -// ========================================================================= -// 16. parse_expr_bp -- precedence climbing core -// ========================================================================= - fn parse_expr_bp(tokens: List, ctx: ParseContext, min_bp: Int) -> ExprResult { let r = parse_prefix(tokens: tokens, ctx: ctx) if has_err(err: r.err) { return r } @@ -3439,7 +3277,6 @@ fn parse_expr_loop(tokens: List, ctx: ParseContext, lhs: Node, min_bp: In } } - fn find_operator_bp(ops: List, symbol: String) -> BindingPower? { let matching = ops |> filter(op => op.symbol == symbol) if matching |> count > 0 { @@ -3501,10 +3338,6 @@ fn parse_prefix(tokens: List, ctx: ParseContext) -> ExprResult { } } -// ========================================================================= -// 16b. parse_caret_expr — `^IDENT` symbol literal | `^(Expr)` discriminant sugar -// ========================================================================= - fn parse_caret_expr(tokens: List, ctx: ParseContext) -> ExprResult { let span = token_span(tok: tokens |> first) let dummy_expr = parse_recovery_placeholder() @@ -3549,10 +3382,6 @@ fn parse_caret_expr(tokens: List, ctx: ParseContext) -> ExprResult { } } -// ========================================================================= -// 17. parse_primary -// ========================================================================= - fn parse_primary(tokens: List, ctx: ParseContext) -> ExprResult { let tok = tokens |> first let sh = match tok { Present { value: t } => Present { value: t.shape } Absent => none } @@ -3626,10 +3455,6 @@ fn parse_primary(tokens: List, ctx: ParseContext) -> ExprResult { } } -// ========================================================================= -// Lambda body -- single expression OR implicit block (let/return start) -// ========================================================================= - fn parse_lambda_body(tokens: List, ctx: ParseContext) -> ExprResult { let tokens = skip_newlines(tokens: tokens) let tok = tokens |> first @@ -3660,10 +3485,6 @@ fn parse_lambda_stmts(tokens: List, ctx: ParseContext, acc: List) - } } -// ========================================================================= -// Identifier-initiated expressions -// ========================================================================= - fn parse_ident_expr(tokens: List, ctx: ParseContext, name: String) -> ExprResult { let span = token_span(tok: tokens |> first) let tokens = tokens |> skip(1) @@ -3686,19 +3507,13 @@ fn is_uppercase_start(name: String) -> Bool { ch >= "A" && ch <= "Z" } -// ========================================================================= -// 18. try_postfix -- .field, (args), as Type, ? -// ========================================================================= - fn try_postfix(tokens: List, ctx: ParseContext, lhs: Node, min_bp: Int) -> PostfixResult { let tok = tokens |> first let sh = match tok { Present { value: t } => Present { value: t.shape } Absent => none } let span = token_span(tok: tok) match sh { Present { value: ShLParen } => { - // Postfix call binds tighter than every binary operator (max right_bp 20); - // gate at 21, not 14, or a call in a `*`/`/`/`%` RHS (right_bp 16) is - // rejected and its `(...)` mis-attaches to the binop as an `` call. + if 21 < min_bp { PostfixResult { expr: lhs, changed: false, tokens: tokens, ctx: ctx, err: none } } else { @@ -3722,8 +3537,7 @@ fn try_postfix(tokens: List, ctx: ParseContext, lhs: Node, min_bp: Int) - } } Present { value: ShLBracket } => { - // Postfix index binds tighter than every binary operator (see ShLParen); - // gate at 21, not 14, for the same `*`/`/`/`%`-RHS reason. + if 21 < min_bp { PostfixResult { expr: lhs, changed: false, tokens: tokens, ctx: ctx, err: none } } else { @@ -3824,10 +3638,6 @@ fn make_call_expr(lhs: Node, args: List, span: SourceSpan, source_indices: } } -// ========================================================================= -// Index/Slice parsing: base[index] or base[start..end] -// ========================================================================= - fn parse_index_or_slice(tokens: List, ctx: ParseContext, base: Node, span: SourceSpan) -> ExprResult { let dummy_expr = parse_recovery_placeholder() let r = expect(tokens: tokens, expected: ExpectLBracket) @@ -3853,10 +3663,6 @@ fn parse_index_or_slice(tokens: List, ctx: ParseContext, base: Node, span } } -// ========================================================================= -// 25. parse_call_args: (arg: val, arg2: val2) -// ========================================================================= - fn parse_call_args(tokens: List, ctx: ParseContext) -> ArgsResult { let r = expect(tokens: tokens, expected: ExpectLParen) if has_err(err: r.err) { return ArgsResult { args: [], tokens: r.tokens, ctx: ctx, err: r.err } } @@ -3928,10 +3734,6 @@ fn parse_single_arg(tokens: List, ctx: ParseContext) -> ArgResult { } } -// ========================================================================= -// 19. parse_match -// ========================================================================= - fn parse_match(tokens: List, ctx: ParseContext) -> ExprResult { let span = token_span(tok: tokens |> first) let dummy_expr = parse_recovery_placeholder() @@ -3973,13 +3775,7 @@ fn parse_expr_loop_no_brace(tokens: List, ctx: ParseContext, lhs: Node, m } else { let loop_span = token_span(tok: tok) let tokens = skip_continuation_newlines(tokens: tokens) - // Postfix application/index bind TIGHTER than every binary operator (max - // operator right_bp is `.` at 20), so the gate is 21 -- not 14. At 14, a - // call/index in the RHS of `*`/`/`/`%` (right_bp 16) was rejected - // (`14 >= 16` false) and the trailing `(...)` then attached to the whole - // binop via make_call_expr -> an unresolvable `` call. `+`/`-` - // (right_bp 14) masked the bug; multiplication (unused before the numeric - // Measure work) exposed it on `a(x) * b(y)`. + if tok_is_lparen(tok: tokens |> first) && 21 >= min_bp { let r = parse_call_args(tokens: tokens, ctx: ctx) if has_err(err: r.err) { return ExprResult { expr: lhs, tokens: r.tokens, ctx: r.ctx, err: r.err } } @@ -4050,10 +3846,6 @@ fn parse_match_arms_acc(tokens: List, ctx: ParseContext, acc: List) } } -// ========================================================================= -// 20. parse_match_arm: Pattern => expr -// ========================================================================= - fn parse_match_arm(tokens: List, ctx: ParseContext) -> ArmResult { let arm_span = token_span(tok: tokens |> first) let dummy_arm = make_arm_node(pattern: Wildcard, guard: none, body: parse_recovery_placeholder(), span: make_span(start: 0, end: 0)) @@ -4184,10 +3976,6 @@ fn parse_optional_guard(tokens: List, ctx: ParseContext) -> GuardResult { } } -// ========================================================================= -// 21. parse_pattern -// ========================================================================= - fn parse_pattern(tokens: List, ctx: ParseContext) -> PatternResult { let tok = tokens |> first let sh = match tok { Present { value: t } => Present { value: t.shape } Absent => none } @@ -4281,10 +4069,6 @@ fn parse_variant_bindings_brace_acc(tokens: List, ctx: ParseContext, acc: } } -// ========================================================================= -// 22. parse_if -// ========================================================================= - fn parse_if(tokens: List, ctx: ParseContext) -> ExprResult { let span = token_span(tok: tokens |> first) let dummy_expr = parse_recovery_placeholder() @@ -4318,10 +4102,6 @@ fn parse_if(tokens: List, ctx: ParseContext) -> ExprResult { } } -// ========================================================================= -// 23. parse_let: let name = value -// ========================================================================= - fn parse_let(tokens: List, ctx: ParseContext) -> ExprResult { let span = token_span(tok: tokens |> first) let dummy_expr = parse_recovery_placeholder() @@ -4342,10 +4122,6 @@ fn parse_let(tokens: List, ctx: ParseContext) -> ExprResult { ExprResult { expr: make_named_expr_node(name: name, expr_data: ExprLet, children: [r.expr], inferred: none, span: span, name_span: name_span), tokens: r.tokens, ctx: r.ctx, err: none } } -// ========================================================================= -// Return: return expr or return { field: val } -// ========================================================================= - fn parse_return(tokens: List, ctx: ParseContext) -> ExprResult { let span = token_span(tok: tokens |> first) let dummy_expr = parse_recovery_placeholder() @@ -4357,10 +4133,6 @@ fn parse_return(tokens: List, ctx: ParseContext) -> ExprResult { ExprResult { expr: make_expr_node(expr_data: ExprReturn, children: [r.expr], inferred: none, span: span), tokens: r.tokens, ctx: r.ctx, err: none } } -// ========================================================================= -// 24. parse_for: for x in collection { body } -// ========================================================================= - fn parse_for(tokens: List, ctx: ParseContext) -> ExprResult { let span = token_span(tok: tokens |> first) let dummy_expr = parse_recovery_placeholder() @@ -4387,10 +4159,6 @@ fn parse_for(tokens: List, ctx: ParseContext) -> ExprResult { ExprResult { expr: for_expr, tokens: r.tokens, ctx: r.ctx, err: none } } -// ========================================================================= -// 26. parse_record_literal: Name { field: val, field2: val2 } -// ========================================================================= - fn parse_record_literal(tokens: List, ctx: ParseContext, name: String, span: SourceSpan) -> ExprResult { let dummy_expr = parse_recovery_placeholder() let r = expect(tokens: tokens, expected: ExpectLBrace) @@ -4459,10 +4227,6 @@ fn parse_field_init(tokens: List, ctx: ParseContext) -> FieldInitResult { } } -// ========================================================================= -// 27. parse_list_literal: [expr1, expr2] -// ========================================================================= - fn parse_list_literal(tokens: List, ctx: ParseContext) -> ExprResult { let span = token_span(tok: tokens |> first) let dummy_expr = parse_recovery_placeholder() @@ -4499,14 +4263,6 @@ fn parse_expr_list_until_acc(tokens: List, ctx: ParseContext, end_expecte } } -// ========================================================================= -// 28. parse_lambda: x => expr or (x, y) => expr -// ========================================================================= - -// ========================================================================= -// Parenthesized expression or multi-param lambda -// ========================================================================= - fn parse_paren_expr(tokens: List, ctx: ParseContext) -> ExprResult { let span = token_span(tok: tokens |> first) let dummy_expr = parse_recovery_placeholder() @@ -4533,9 +4289,6 @@ fn parse_paren_expr(tokens: List, ctx: ParseContext) -> ExprResult { } } -// ========================================================================= -// ========================================================================= - fn parse_fn_lambda(tokens: List, ctx: ParseContext) -> ExprResult { let span = token_span(tok: tokens |> first) let dummy = parse_recovery_placeholder() @@ -4605,10 +4358,6 @@ fn collect_lambda_idents(tokens: List, ctx: ParseContext, acc: List, ctx: ParseContext) -> ExprResult { let tok = tokens |> first let span = token_span(tok: tok) @@ -4665,10 +4414,6 @@ fn parse_interp_parts(tokens: List, ctx: ParseContext, parts: List, ctx: ParseContext) -> ExprResult { let span = token_span(tok: tokens |> first) let dummy_expr = parse_recovery_placeholder() diff --git a/src/v1/03_normalize.dag b/src/v1/03_normalize.dag index fe398815c86..6a62c9089c4 100644 --- a/src/v1/03_normalize.dag +++ b/src/v1/03_normalize.dag @@ -1,14 +1,3 @@ -// v2 normalization pass -- structural graph rewrite between resolve and infer. -// -// P3.7: arity bridge deleted. Arity enforcement via hardcoded -// parameterized_type_arity is removed. Arity is now structural: -// type declarations carry params, and generic substitution in -// resolve_node_bounded checks declaration params at resolution time. -// -// The normalizer validates structural invariants that should hold -// after resolve and before infer: -// - No bare container types (List, Map, Set, etc. with 0 type parameters) - module v1.compiler.normalize import v1.std.core { @@ -21,19 +10,11 @@ import v1.std.core { import v1.compiler.resolve { ModuleGraph, ResolvedModule } import std.types { container_expected_arity } -// ========================================================================= -// Output types -// ========================================================================= - type NormalizeResult { graph: ModuleGraph diagnostics: List } -// ========================================================================= -// Bare container detection -// ========================================================================= - fn check_bare_containers(n: Node, module_name: String, source_indices: Map) -> List { let has_structure = match n.body { Present { value: _ } => true Absent => false } || (n.uses |> count) > 0 || (n.properties |> count) > 0 let is_expr = match n.expr_data { NoExprData => false _ => true } @@ -74,10 +55,6 @@ fn check_bare_containers(n: Node, module_name: String, source_indices: Map d) } -// ========================================================================= -// Normalization entry point -// ========================================================================= - fn normalize_graph(graph: ModuleGraph, source_indices: Map) -> NormalizeResult { let diags = flat_map(graph.modules, m => { let items = module_items(n: m.module) diff --git a/src/v1/03_resolve.dag b/src/v1/03_resolve.dag index eea9a1b7019..b426e122d89 100644 --- a/src/v1/03_resolve.dag +++ b/src/v1/03_resolve.dag @@ -1,14 +1,8 @@ -// v2 module resolver -- pure function from parsed modules to dependency-ordered graph. - module v1.compiler.resolve import std.types { kernel_type_set } import v1.std.core { Node, Connective, Conj, Disj, NoConnective, ErrorNode, make_error_node, UnresolvedImport, MissingExport, DuplicateModule, CircularDependency, no_span, module_node, import_node, import_is_all, import_specific_names_at, module_imports, module_items, NewlineIndex, authored_name_at } -// ========================================================================= -// Output types -// ========================================================================= - type ModuleGraph { modules: List diagnostics: List @@ -27,17 +21,11 @@ type ResolvedImport { target_module: Node? } -// ========================================================================= -// Internal types -- used by the topological sort and cycle detection -// ========================================================================= - type DepEdge { from_module: String to_module: String } - -// Accumulator for Phase 2 -- builds imports-by-name map and collects diagnostics. type ResolveAccum { imports_by_name: Map> diagnostics: List @@ -50,12 +38,8 @@ fn map_has(m: Map, key: String) -> Bool { } } -// ========================================================================= -// Entry point -// ========================================================================= - fn resolve_modules(modules: List, source_indices: Map) -> ModuleGraph { - // Phase 1: check for duplicate module declarations + let dup_diags = check_duplicate_modules(modules: modules, source_indices: source_indices) let module_index = modules |> fold(init: empty_map(), f: (acc, m) => @@ -68,9 +52,6 @@ fn resolve_modules(modules: List, source_indices: Map fold(init: ResolveAccum { imports_by_name: empty_map(), diagnostics: [] @@ -84,17 +65,12 @@ fn resolve_modules(modules: List, source_indices: Map [diag] Absent => [] } - // Phase 4: assign dep_order from the sorted order. - // Modules not in the sorted output (cycle members) get no dep_order - // and are excluded from the acyclic result. - // Build a map from module name to its position in the sorted list for O(1) lookup. let sorted_names = topo_result.sorted let sorted_order_map = sorted_names |> enumerate |> fold(init: empty_map(), f: (acc, pair) => map_insert(acc, pair.second, pair.first) @@ -123,18 +99,10 @@ fn resolve_modules(modules: List, source_indices: Map, path: String) -> Node? { map_get(module_index, path) } -// ========================================================================= -// Import resolution -// ========================================================================= - type ModuleResolveResult { resolved_imports: List diagnostics: List @@ -206,10 +174,6 @@ fn resolve_import( } } -// ========================================================================= -// Exported names -// ========================================================================= - fn get_exported_names(module: Node, source_indices: Map) -> List { let item_names = module_items(n: module) |> map(item => get_item_name(item: item, source_indices: source_indices)) let variant_names = module_items(n: module) @@ -229,10 +193,6 @@ fn get_variant_names(item: Node, source_indices: Map) -> L else { [] } } -// ========================================================================= -// Duplicate module detection -// ========================================================================= - type DuplicateCheckState { seen_names: Map diagnostics: List @@ -260,10 +220,6 @@ fn check_duplicate_modules(modules: List, source_indices: Map cycle_error: ErrorNode? @@ -278,37 +234,23 @@ fn adjacency_add_edge(adjacency: Map>, from_module: String, } fn topo_sort_key(name: String) -> String { - // Bootstrap bridge: declaration lookup currently needs std.types - // available everywhere, so resolution sorts it first until FF-9 - // becomes fully import-driven. + if name == "std.types" { "" } else { name } } fn topological_sort(modules: List, source_indices: Map) -> TopoResult { let module_names = modules |> map(m => authored_name_at(source_indices: source_indices, node: m)) - // Set of names actually present in this resolve set — an import of a module - // NOT in the set (unresolved / external) carries no intra-set ordering - // constraint, so it must not contribute an edge or an in-degree term. + let module_name_set = module_names |> fold(init: empty_map(), f: (acc, name) => map_insert(acc, name, true) ) - // §5 fail-closed (truthful diagnostic): count only in-set import edges. If - // in-degree counted unresolved imports (whose adjacency edge is keyed by a - // name never dequeued), a module blocked solely on a missing import would - // never reach in-degree 0 and the leftover set would be reported as a FALSE - // circular dependency — a fabricated wrong answer masking the real - // unresolved-import error. Filtering both sides keeps adjacency and in-degree - // consistent so only genuine cycles surface as CircularDependency. + let explicit_edges = modules |> flat_map(m => module_imports(n: m) |> filter(imp => map_has(m: module_name_set, key: authored_name_at(source_indices: source_indices, node: imp))) |> map(imp => DepEdge { from_module: authored_name_at(source_indices: source_indices, node: imp), to_module: authored_name_at(source_indices: source_indices, node: m) }) ) - // §3 single-authority: ordering is import-driven only. The std.types - // implicit-parent bootstrap bridge was removed — it coupled every module - // (across the v1 and v2 trees) into one global sort, so unrelated import - // edges perturbed the order and let same-named v1/v2 types resolve to the - // wrong authority by merge order. Kahn now respects explicit edges alone. + let adjacency = explicit_edges |> fold(init: empty_map(), f: (acc, edge) => adjacency_add_edge(adjacency: acc, from_module: edge.from_module, to_module: edge.to_module) ) diff --git a/src/v1/04_access.dag b/src/v1/04_access.dag index 3b0e7d1557b..82909195e17 100644 --- a/src/v1/04_access.dag +++ b/src/v1/04_access.dag @@ -1,5 +1,3 @@ -// Access checks -- pure type checks for index and slice operations. - module v1.compiler.infer_access import std.types { SourceSpan, is_ordered_element_collection } @@ -18,10 +16,6 @@ import v1.compiler.infer_types { node_is_element_collection, for_each_element_type_node } -// ========================================================================= -// Types -// ========================================================================= - type AccessCheckResultNode { inferred: InferredNode? diagnostics: List @@ -32,10 +26,6 @@ type KeyedCollectionParts { value_type: Node } -// ========================================================================= -// Functions -// ========================================================================= - fn access_error(message: String, span: SourceSpan, module_name: String) -> ErrorNode { make_error_node(diagnostic: InternalError { message: message, span: span }, module_name: module_name) } diff --git a/src/v1/04_cycle.dag b/src/v1/04_cycle.dag index 1e8a9d21447..747791a64b1 100644 --- a/src/v1/04_cycle.dag +++ b/src/v1/04_cycle.dag @@ -1,5 +1,3 @@ -// ========================================================================= - module v1.compiler.infer_cycle import v1.compiler.infer_env { TypeBinding } diff --git a/src/v1/04_emit_info.dag b/src/v1/04_emit_info.dag index e9e385f05f1..c5cd3acaef1 100644 --- a/src/v1/04_emit_info.dag +++ b/src/v1/04_emit_info.dag @@ -1,5 +1,3 @@ -// Emit graph info: type summaries, variant-to-enum maps, and field metadata - module v1.compiler.infer_emit_info import v1.std.core { @@ -25,10 +23,6 @@ fn is_type_variable(inferred: InferredNode) -> Bool { } } -// ========================================================================= -// Types -// ========================================================================= - type TypeRepr = StructRepr | EnumRepr { unit_only: Bool } type TypeSummary { @@ -41,62 +35,23 @@ type TypeSummary { has_fn_fields: Bool } -// RustCorpusRepr: which container/text REALIZATION HANDLER the Rust emitter -// selects for the WHOLE corpus. The spec is unchanged (List = FreeMonoid -// at std/types.dag; String) — this names WHICH of the §2 N handlers realizes it. -// It is a whole-CORPUS property, computed ONCE at the emit boundary -// (rust_corpus_repr in 05_emit_rust.dag) and threaded down as an individual -// emit-info field (like shared_types), never decided per-module: a corpus that -// bundles any v1 seed source (src/v1) emits the HOST representation end-to-end -// (§7 — the bootstrap seed is host: List→Vec, String, Nat/Int→i64); a pure v2 -// target keeps the faithful FreeMonoid carrier. Selecting once and threading the -// selection makes per-module divergence UNWRITABLE (§5 construction-not-check). -// §6-TRANSITIONAL: HostNative dissolves when src/v1 is deleted (seed→0 ⇒ no seed -// source ⇒ rust_corpus_repr is FaithfulFreeMonoid uniformly). type RustCorpusRepr = HostNative | FaithfulFreeMonoid -// EmitGraphInfo: the reconcile→emit boundary contract. -// -// Invariant: emit never calls back into infer. All data the emitter needs -// about graph-level type structure is precomputed here during reconcile -// (build_emit_graph_info in 04_infer.dag). -// -// Fields and guarantees: -// type_summaries — one entry per named type definition (Conj or Disj) -// in the typed module graph. Keyed by type name. -// Guaranteed non-empty for modules with type definitions. -// For enums, variant_name_set provides O(1) membership. -// variant_to_enum and enum_variant_membership are derived -// from type_summaries at the point of use (not stored). -// recursive_type_set — type names that participate in recursive cycles -// (detected by 04_cycle.dag). Used by emit to decide -// Rc wrapping (Rust) or pointer indirection (Go/C). type EmitGraphInfo { type_summaries: Map recursive_type_set: Set fielded_variants: Set positional_payload_variants: Set - // Per-type sharing decision: type names that need shared-pointer - // wrapping (Rc in Rust). Computed from type_summaries × - // recursive_type_set. The Rust emitter also adds collection types. - // This is the SINGLE authority for sharing decisions — build_type_rendering - // reads it, and direct emit sites read it for Rc-aware pattern matching - // and construction. + shared_types: Set ownership_index: Map> movable: Set variant_to_enum: Map owned_bindings: Set - // Per-function read-only params: func_name → param names borrowable by &Rc. - // A param is read-only when all usage edges are Read/Projected/Threaded - // (no Consumed) and fan-out > 1. These can be passed by borrow (&Rc). + read_only_params_index: Map> read_only_params: Set - // Corpus-global Rust container/text realization handler (§2 one-spec-N-handlers). - // Computed ONCE at the emit boundary (rust_corpus_repr) and read by the - // type-renderers' leaf gates (rust_seed_host_container_base / _numeric_alias / - // rust_named_type_base) so every module emits the SAME representation — the - // single authority for the host-vs-faithful selection. §6-transitional. + corpus_repr: RustCorpusRepr } @@ -104,10 +59,6 @@ type EmitInfoBuildState { type_summaries: Map } -// ========================================================================= -// Constructors and lookups -// ========================================================================= - fn empty_emit_graph_info() -> EmitGraphInfo { EmitGraphInfo { type_summaries: empty_map(), @@ -121,17 +72,11 @@ fn empty_emit_graph_info() -> EmitGraphInfo { owned_bindings: empty_set(), read_only_params_index: empty_map(), read_only_params: empty_set(), - // Default to the faithful v2-target carrier; emit_rust overrides to HostNative - // for a corpus that bundles v1 seed source (rust_corpus_repr, computed once). + corpus_repr: FaithfulFreeMonoid } } - -// ========================================================================= -// Variant and lookup helpers -// ========================================================================= - fn variant_has_fields(emit_info: EmitGraphInfo, enum_name: String, variant_name: String) -> Bool { let key = concat(enum_name, "::", variant_name) set_contains(emit_info.fielded_variants, key) @@ -197,10 +142,6 @@ fn find_variant_parent(type_summaries: Map, variant_name: S ) |> first } -// ========================================================================= -// Field summary helpers -// ========================================================================= - fn field_value_shape_from_type_node(type_node: Node) -> FieldValueShape { let normed = normalize_access_type_node(n: type_node) let is_optional = normed.return_cardinality == CardOptional @@ -212,10 +153,6 @@ fn is_tuple_type(n: Node) -> Bool { n.connective == Conj && n.ident_span == none && n.children |> count == 2 } -// ========================================================================= -// Struct and enum field summary builders -// ========================================================================= - fn build_struct_field_summaries(parent: Node, source_indices: Map) -> Map { let is_pair = is_tuple_type(n: parent) fold(parent.children |> enumerate, init: empty_map(), f: (acc, pair) => @@ -287,18 +224,13 @@ fn build_enum_field_summaries(variants: List, source_indices: Map, source_indices: Map) -> Map { fold(children, init: empty_map(), f: (acc, child) => match child.inferred { Present { value: Resolved { node: ft } } => let resolved_name = authored_name_at(source_indices: source_indices, node: normalize_access_type_node(n: ft)) let ft_is_type_var = if ft.inferred != none { is_type_variable(inferred: ft.inferred.value) } else { false } - // Fail-closed: exclude empty names, type variables, and placeholder types. - // Dynamic is a compatibility placeholder that must not enter the authority surface. + let key = authored_name_at(source_indices: source_indices, node: child) if resolved_name != "" && !ft_is_type_var && resolved_name != "Dynamic" { map_insert(acc, key, resolved_name) diff --git a/src/v1/04_env.dag b/src/v1/04_env.dag index 5d9a9887b87..9fcf7cf0f5f 100644 --- a/src/v1/04_env.dag +++ b/src/v1/04_env.dag @@ -1,13 +1,8 @@ -// v2 type environment -- structural type bindings and lookup. module v1.compiler.infer_env import v1.std.core { Node, NewlineIndex, InternTable, empty_intern_table, merge_intern_tables, intern, intern_find, intern_str, source_text_at, authored_name_at } import std.induction { InductiveField, RecursionShape, DirectRecursion, ListRecursion, OptionalRecursion, SubValueRelation, SubValueUnknown, PreservedValue } -// ========================================================================= -// Types -// ========================================================================= - type TypeEnv { bindings: Map recursive_types: List @@ -23,10 +18,6 @@ type TypeBinding { provenance: SubValueRelation } -// ========================================================================= -// Environment operations -// ========================================================================= - fn is_recursive_type(env: TypeEnv, ident: Int) -> Bool { match map_get(env.recursive_type_set, ident) { Present { value: _ } => true diff --git a/src/v1/04_infer.dag b/src/v1/04_infer.dag index a65e63baae4..fb0d53f126a 100644 --- a/src/v1/04_infer.dag +++ b/src/v1/04_infer.dag @@ -1,5 +1,3 @@ -// v2 namespace reconciler -- verifies that named structural compositions - module v1.compiler.infer import std.types { SourceSpan, container_param_name } @@ -199,8 +197,6 @@ type BlockInferState { typed_stmts: List } -// SG-9: Tail-recursive block inference. Each accumulated list is a standalone -// parameter (Rc refcount=1) so list_push avoids deep clone. fn infer_block_stmts(remaining: List, remaining_count: Int, scope: InferScope, typed_stmts: List, diag_chunks: List>, last_type: Node, expected: Node?) -> BlockInferState { @@ -247,7 +243,6 @@ type PatternScopeResult { diagnostics: List } - type StringPartInferResult { typed_part: StringPart diagnostics: List @@ -284,10 +279,6 @@ type CycleDetectState { global_visited: Map } -// ========================================================================= -// Scope merge helpers -// ========================================================================= - type InferScopeComponents { func_sigs: Map svc_registry: Map> @@ -461,10 +452,6 @@ fn resolved_callable_type(func_params: List, ret: Node) -> Node { make_callable_type(func_params: func_params, ret: ret) } -// ========================================================================= -// Service namespace locals -// ========================================================================= - fn namespace_root_from_properties(properties: List, name: String, source_indices: Map) -> String { match properties |> filter(p => field_init_node_name_at(n: p, source_indices: source_indices) == "namespace_root") |> first { Present { value: ns_prop } => match field_init_node_value(n: ns_prop).expr_data { @@ -475,11 +462,6 @@ fn namespace_root_from_properties(properties: List, name: String, source_i } } -// ========================================================================= -// Expression inference -// ========================================================================= - - fn expr_span(texpr: Node) -> SourceSpan { texpr.span } @@ -500,10 +482,7 @@ fn internal_expr_error_node(message: String, span: SourceSpan) -> Node { } fn lookup_variant_parent_enum(scope: InferScope, name: String) -> String? { - // BRIDGE DEBT (M4): uses has_child_named to verify variant membership. - // scope.locals contains ALL locals (params, let bindings, match bindings), - // not just variant bindings — the Disj check alone is insufficient. - // Deletion point: when variant bindings carry a structural parent edge. + match map_get(scope.locals, name) { Present { value: binding } => match lookup_type_for(env: scope.type_env, node: binding.resolved) { @@ -542,9 +521,7 @@ fn type_mismatch_error(expected: String, got: String, span: SourceSpan, module_n } fn rejects_string_for_optional_coproduct_field(expected: Node, got: Node, source_indices: Map) -> Bool { - // Regression pin for legacy authoring forms that wrote a String into an - // optional coproduct field. Broader coproduct assignability belongs to the - // general record-field type-check path. + let expected_inner = with_required_cardinality(n: expected) let expected_is_optional_coproduct = expected.return_cardinality == CardOptional && expected_inner.connective == Disj let got_is_string = authored_name_at(source_indices: source_indices, node: got) == "String" @@ -574,10 +551,6 @@ fn record_lit_expected_fields(type_name: String?, scope: InferScope) -> List), use the resolved -// expanded struct fields (fn types with A/R already substituted) instead of -// the bare generic template from lookup_type_by_name. fn record_lit_expanded_from_expected(type_name: String?, expected: Node?, scope: InferScope) -> Node? { match type_name { Present { value: tn } => @@ -616,8 +589,6 @@ fn record_lit_fields_from_expected(type_name: String?, expected: Node?, scope: I } } -// Build instantiated struct fields by substituting type-arg slots from an applied -// generic expected type (NodeFoldTopDown) into the template fields. fn record_lit_instantiated_fields(type_name: String?, expected: Node?, scope: InferScope) -> List? { match type_name { Present { value: tn } => @@ -714,27 +685,16 @@ fn set_element_types_mismatch(recv_elem: Node, operand_elem: Node, source_indice && !node_type_compatible(left: recv_elem, right: operand_elem, source_indices: source_indices) } -// Callable/formal-fn positions (params present) are outside PD-3 brand scope — -// e.g. v2 grammar models fn-vs-Callable without nominal brand identity. fn type_node_is_callable(n: Node) -> Bool { n.params |> count > 0 } -// TRANSITION (A3 / PD-3): compiler substrate (v2.* std + v1.compiler.* self-host) -// models fn-vs-Callable, witness carriers, and symbolic nominal pairs -// (Symbol/GeneratorId, ConfigPatchRecord/patch nodes) outside PD-3 brand scope. -// Bound: prefix match v2.* and v1.compiler.* only — user modules run the hook. -// Dissolution trigger: ROADMAP.md Active Deferrals row PD-3-DOGFOOD -// (delete when v2-lens-ci + v2-rust-full-tree-emit-probe pass without this skip). fn module_skips_direct_call_arg_check(module_name: String) -> Bool { let is_v4 = string_length(s: module_name) >= 3 && substring(s: module_name, start: 0, end: 3) == "v2." let is_compiler_substrate = string_length(s: module_name) >= 13 && substring(s: module_name, start: 0, end: 13) == "v1.compiler." is_v4 || is_compiler_substrate } -// Bounded direct-call arg check (A3 / E1): surface nominal brand mismatches -// that share a structural carrier (UserId vs AccountId over Refined) -// without widening to every !node_type_compatible pair (e.g. fn vs Bool). fn nominal_call_arg_brand_mismatch(formal: Node, actual: Node, source_indices: Map) -> Bool { let formal_name = authored_name_at(source_indices: source_indices, node: formal) let actual_name = authored_name_at(source_indices: source_indices, node: actual) @@ -747,8 +707,6 @@ fn nominal_call_arg_brand_mismatch(formal: Node, actual: Node, source_indices: M && !is_declared_container_alias_spelling(name: actual_name) } -// Same container spelling (List vs List): recurse into element types for -// same-carrier brand-twin rejection without widening to unrelated mismatches. fn container_element_nominal_brand_mismatch( formal: Node, actual: Node, @@ -775,9 +733,6 @@ fn container_element_nominal_brand_mismatch( } else { false } } -// Call-site brand-twin reject only (A3): node_type_compatible stays relation-compat -// (container-alias tolerance at 04_types.dag:738-742); this hook rejects same-carrier -// nominal twins at direct ExprCall sites without widening relation-wide. fn direct_call_arg_type_mismatch( formal: Node, actual: Node, @@ -1102,7 +1057,6 @@ fn annotate_pattern_parent_enums(pattern: MatchPattern, scrutinee_subject: Patte } } - fn build_params_scope(scope: InferScope, params: List) -> InferScope { let new_locals = fold(params, init: scope.locals, f: (acc, p) => map_insert(acc, param_node_name_at(n: p, source_indices: scope.type_env.source_indices), TypeBinding { name: param_node_name_at(n: p, source_indices: scope.type_env.source_indices), resolved: param_node_type_expr(n: p), provenance: PreservedValue }) @@ -1119,7 +1073,6 @@ fn build_params_scope(scope: InferScope, params: List) -> InferScope { } } - fn extend_scope(scope: InferScope, name: String, resolved: Node, provenance: SubValueRelation) -> InferScope { InferScope { type_env: scope.type_env, @@ -1178,12 +1131,6 @@ fn scope_after_stmt_node(stmt: Node, stmt_type: Node, scope: InferScope) -> Infe } } -// Derive provenance for a match-arm field binding. -// If the scrutinee has known provenance and the field is inductive (recursive), -// the binding is a StrictSubValue. Otherwise SubValueUnknown (fail-closed). -// Derive provenance for a match-arm field binding via compose_sub_value -// (std/induction.dag authority). Looks up whether the field is inductive; -// if so, composes the scrutinee's provenance with the field. fn derive_field_provenance(scope: InferScope, scrutinee_provenance: SubValueRelation, scrutinee_subject: PatternSubject, variant_name: String, field_name: String) -> SubValueRelation { match scrutinee_provenance { SubValueUnknown => SubValueUnknown @@ -1262,10 +1209,6 @@ fn extract_fold_init_info(method_name: String?, method_args: List, min_arg } else { none } } -// Derive element provenance from a collection expression. -// Single authority for the "collection has known provenance AND element type -// has ListRecursion inductive fields → IteratedSubValue" pattern. -// Used by ExprForEach, ExprCall bridge, and ExprMethodCall. fn derive_element_provenance(coll_expr: Node, elem_type: Node, scope: InferScope) -> SubValueRelation { let coll_prov = classify_binding_provenance(val: coll_expr, scope: scope) match coll_prov { @@ -1346,8 +1289,6 @@ fn infer_method_args_with_fold(method_name: String?, method_args: List, fo ) } - - fn is_lambda_expr(e: Node) -> Bool { match e.expr_data { ExprLambda => true @@ -1374,15 +1315,11 @@ fn empty_override_map() -> Map { seed_override_map() } -// refine_collection_result_type was deleted — callers removed by M2 Phase C (PR #332). -// Method return types now resolve structurally via AlgebraTypeVariable instantiation. - fn infer_arg_with_element_type(arg: Node, element_type: Node, scope: InferScope) -> ArgInferResult { let ar = infer_expr(texpr: arg_value(n: arg), scope: scope, expected: Present { value: element_type }) ArgInferResult { typed_arg: make_arg_node(name: arg_name_at(n: arg, source_indices: scope.type_env.source_indices), value: ar.typed, span: arg.span, name_span: arg.span), diagnostics: ar.diagnostics } } - fn infer_expr(texpr: Node, scope: InferScope, expected: Node?) -> InferResult { match texpr.expr_data { ExprLiteral { value: lit } => @@ -1475,17 +1412,7 @@ fn infer_expr(texpr: Node, scope: InferScope, expected: Node?) -> InferResult { let field_type_lookup = lookup_field_type_node(n: resolved_base, field_name: field_name, source_indices: scope.type_env.source_indices) match field_type_lookup { Present { value: field_type } => - // The field TYPE was found via `resolved_base` (the alias-chain- - // expanded structural record, G3). Derive the FieldSummary from the - // SAME expansion so the two stay consistent: `field_summary_for_type` - // peels with `resolve_scrutinee_type_node` alone, which does not - // chase the structural-name hop a cross-module / multi-hop alias - // needs (e.g. `Gibibyte` -> `Measure` -> the - // `Measure { count }` struct). Try the authored type first (keeps the - // optional `.value` fast-path unchanged) and fall back to the - // expanded record only when the authored type yields no summary -- - // otherwise emission fails closed to `compile_error!("field access - // missing reconcile summary ...")`. + let field_summary = match field_summary_for_type(base_type: base_rt, env: scope.type_env, field: field_name) { Present { value: s } => Present { value: s } Absent => field_summary_for_type(base_type: resolved_base, env: scope.type_env, field: field_name) @@ -1552,7 +1479,7 @@ fn infer_expr(texpr: Node, scope: InferScope, expected: Node?) -> InferResult { let func_name = expr_call_func_at(texpr: texpr, source_indices: scope.type_env.source_indices) let span = texpr.span let call_args = texpr.children - // Pre-fetch function sig for callable-aware lambda typing (P4.7e). + let sig = lookup_func_sig(func_env: scope.func_env, name: func_name) let sig_params = match sig { Present { value: s } => s.params @@ -1582,16 +1509,7 @@ fn infer_expr(texpr: Node, scope: InferScope, expected: Node?) -> InferResult { [] } } } else { - // Infer each arg; for lambdas where the formal param is Callable, - // thread the Callable's param types to the lambda (P4.7e). - // - // Generic-aware: declared generics live among sig.params, so the - // value-formal for value-arg i is value_params[i] (generics filtered - // out). Bind generics from concrete (non-lambda) args left-to-right and - // substitute into later formals — e.g. unify o: Outcome against - // actual Outcome ⇒ T=Concrete, then substitute into - // f: fn(T) -> ... so the lambda param binds to the concrete success - // type. A single left-to-right fold infers each arg exactly once. + let value_params = sig_params |> filter(p => !param_is_generic_decl(p: p, source_indices: scope.type_env.source_indices)) let generic_names = sig_params |> filter(p => param_is_generic_decl(p: p, source_indices: scope.type_env.source_indices)) @@ -1622,10 +1540,7 @@ fn infer_expr(texpr: Node, scope: InferScope, expected: Node?) -> InferResult { final_state } let arg_infer_results = arg_call.results - // Per-call generic substitution (from unifying value args). Apply it to - // the call's return type too — completes ②: param types were instantiated - // in the fold above; the return position (e.g. list_snoc_item -> FreeMonoid) - // is instantiated here (substitute_generics(sig.inferred, subst)). + let call_subst = arg_call.subst let typed_args = arg_infer_results |> map(air => air.typed_arg) let arg_diags = flat_map(arg_infer_results, air => air.diagnostics) @@ -2683,10 +2598,6 @@ fn infer_variant_constructor_call(func_name: String, call_args: List, span } } - -// Unbound generic type parameters (e.g. Set member `fn(d)` before T=DiffId -// substitution) and inferred type variables must not chase alias expansion — -// field access defers like resolve_scrutinee_type_node's type-var early return. fn is_deferred_field_access_base(n: Node, env: TypeEnv) -> Bool { if n.inferred != none && is_type_variable(inferred: n.inferred.value) { true } else if n.connective == NoConnective && n.children |> count == 0 && n.inferred == none { @@ -2749,10 +2660,6 @@ fn structural_from_expanded_type(normed: Node) -> Node { } } -// A record (Conj/Disj) field whose resolved TYPE is still an unsubstituted -// generic param: an explicit TypeVariable, a bare leaf named among the record's -// (retained) params, or a bare leaf matching the codebase type-var-name -// convention (T/K/V/...). A concrete field type (Nat, Int, ...) is none of these. fn record_field_type_is_unresolved_param(field: Node, param_names: List, env: TypeEnv) -> Bool { match product_field_result_type(field: field) { Present { value: ft } => @@ -2775,31 +2682,6 @@ fn record_has_unresolved_param_field(record: Node, env: TypeEnv) -> Bool { record.children |> any(f => record_field_type_is_unresolved_param(field: f, param_names: param_names, env: env)) } -// Multi-hop alias field-access (G3): follow the alias chain to the structural -// record (Conj/Disj) that carries the field. Each hop reuses the single-hop -// machinery (parametric-struct generic instantiation + leaf-alias scrutinee -// resolution); when that does not reach a record, follow ONE STRUCTURAL-NAME hop -// (n.name, not the preserved alias brand) and recurse. The structural name is -// the next link in the chain (MoneyMicros's resolved carrier names MoneyAmount, -// which names Measure); the brand stays pinned to the alias's own name, so -// chain-following must key on the structural name. `seen` (structural names -// already visited) terminates genuine self-references (`type Nat` resolving to -// itself) and cycles. -// -// SCOPE BOUNDARY (G3 fail-closed): following a STRUCTURAL-NAME hop drops the -// use-site's type args -- the alias param list is not retained on the resolved -// binding (TypeBinding stores only `resolved`, see 04_env.dag). When the chain is -// `lossy` (a name-hop dropped args) AND the reached record still has a field -// typed by an unsubstituted param, we CANNOT type that field, so we fail closed -// (return a bare nominal leaf -> "no field" diagnostic) rather than silently -// returning the raw type variable. Param-INDEPENDENT fields (Measure.count: Nat, -// where the alias param is phantom) resolve normally. The unsubstituted-param -// detection rides the type-var-name convention (T/K/V/...) since the reached -// record's params are dropped too; a field typed by a non-convention param name -// through a lossy chain is the same uninhabited gap. Dissolution trigger: the -// first real consumer with a param-DEPENDENT field reached through a parametric- -// alias chain -> follow-up "TypeBinding param-list preservation for param- -// substitution through parametric-alias chains". fn expand_alias_chain_for_field_access(n: Node, env: TypeEnv, module_name: String, origin_name: String, seen: Map, lossy: Bool) -> Node { let peeled = if needs_alias_field_expansion(n: n, env: env) { peel_alias_once_for_field_access(n: n, env: env, module_name: module_name) } else { n } let structural = structural_from_expanded_type(normed: resolve_scrutinee_type_node(env: env, n: peeled)) @@ -2811,9 +2693,7 @@ fn expand_alias_chain_for_field_access(n: Node, env: TypeEnv, module_name: Strin } else { structural } } else { let next_name = structural.name - // Optional types carry `.value` via the optional-unwrap path downstream; do - // not chain-follow them (following strips the cardinality). Also stop at an - // empty name or an already-visited hop (self-reference / cycle). + let is_optional = structural.return_cardinality == CardOptional let stop = is_optional || next_name == "" || emit_map_has(m: seen, key: next_name) if stop { @@ -2856,21 +2736,6 @@ fn record_lit_alias_struct_fields(type_name: String, scope: InferScope) -> List< } } -// Strict record construction (§5 fail-closed): the SOURCE field-name set of the concrete -// record a construction targets, or `none` whenever we cannot enumerate it with confidence — -// so the undefined-field check stays silent rather than false-positive. We fire ONLY when the -// constructed name resolves to a plain, fully-grounded record: non-generic (`params == 0`) and -// a direct product (`connective == Conj`) with declared fields. Everything else returns `none`: -// - generics (`Monoid`, `Vendor`): newtype flattening rewrites a single-field -// wrapper to its innermost name (`Monoid` resolves to `[op, identity]`, not `[semigroup, -// identity]`), so the resolved set is not source-faithful; -// - concrete aliases of single-field generics (`Watt = Measure`): the wrapper -// dissolves to its `` carrier, so the resolved node is not a record at all; -// - a name resolving to a coproduct/enum — including the losing side of a §3 name collision -// (the `Vendor` enum shadowing the `Vendor` record): no product fields to check. -// This keeps full teeth on plain records, where every real bug it finds lives (`BoolWitness -// { holds }`, `NodeLocus { node }`, `PortLocus { port }`). Dissolution trigger: when field-name -// resolution is source-faithful under flattening, widen the gate to generics and aliases. fn record_lit_construction_field_names(type_name: String, scope: InferScope) -> List? { let si = scope.type_env.source_indices match lookup_type_by_name(env: scope.type_env, name: type_name) { @@ -2884,15 +2749,6 @@ fn record_lit_construction_field_names(type_name: String, scope: InferScope) -> } } -// Strict record construction: a constructed NAME can legitimately arm two distinct coproducts -// with different field sets (e.g. `TextNode` is `Fragment.TextNode { content }` in one and -// `MarkupNode.TextNode { text }` in another). The import-scoped `struct_fields`/`arm_fields` -// only see the arm reachable from `expected`; this secondary scan, run ONLY on a would-be -// error, asks whether `field` is valid on a same-named arm of ANY coproduct in the env — so -// `TextNode { text }` stays silent when *some* `TextNode` arm has `text`, while a field present -// on NO same-named arm keeps its loud error (§5). Record-name collisions are NOT handled here: -// duplicate record names across layers are a §3 violation that must be de-conflicted at the -// source (cf. the `TextFile` → `DocumentFile` split), not papered over by a suppressor. fn field_in_any_variant_named(type_name: String, field: String, scope: InferScope) -> Bool { let si = scope.type_env.source_indices scope.type_env.bindings |> map_values |> any(binding => @@ -2948,10 +2804,7 @@ fn infer_record_lit(type_name: String?, field_inits: List, span: SourceSpa } let fi_infer_results = map(field_inits, fi => let fi_name = field_init_node_name_at(n: fi, source_indices: scope.type_env.source_indices) - // Strict record construction (§5 fail-closed): a field that matches no field of a - // confidently-RESOLVED type is a loud, located error, not silently ignored. `none` - // (unresolved / generic / anonymous record) keeps the path silent — closes the - // fail-open `infer_record_lit` hole without false-positives. + let unknown_field_diags = match construction_field_names { Present { value: names } => if (names |> any(n => n == fi_name)) || field_in_any_variant_named(type_name: tn_str, field: fi_name, scope: scope) { @@ -3074,23 +2927,13 @@ fn infer_record_lit(type_name: String?, field_inits: List, span: SourceSpa } } -// ========================================================================= -// CX-L2: Descent evidence annotation (post-inference) -// -// After a function body is type-inferred, this pass walks the typed body -// and annotates each self-call ExprCall node with descent_evidence. -// The pass reads InductiveField facts from TypeEnv — it never pattern- -// matches AST to discover descent. Adding a new recursive type requires -// zero changes here. -// ========================================================================= - type SizeExpr = ParamSize { param: String } | DividedSize { param: String, divisor: Int } type DescentContext { fn_name: String - param_names: Map // param_name → type_name (M4: String proxy) + param_names: Map param_order: List type_env: TypeEnv sub_value_vars: Map @@ -3206,9 +3049,7 @@ fn classify_collection_shrink(arg_expr: Node, param_name: String, ctx: DescentCo } else { SubValueUnknown } _ => SubValueUnknown } - // Affine `mid + positive_literal` is only accepted for `skip`: skipping more than - // a known `count(param)/d` threshold is at least as shrinking as the divisor witness. - // `take(mid + k)`, subtraction, and non-positive/unknown offsets stay fail-closed. + ExprBinOp { op: op, algebra_field: _ } => match op { Add => @@ -3423,7 +3264,7 @@ fn classify_let_value(val: Node, ctx: DescentContext) -> SubValueRelation? { match val.expr_data { ExprVar { binding_kind: _ } => let vname = expr_var_name_at(texpr: val, source_indices: ctx.type_env.source_indices) - // Read carried facts from scope_locals (Experiment 2: reconstruction deleted) + match map_get(ctx.scope_locals, vname) { Present { value: binding } => match binding.provenance { @@ -3445,7 +3286,7 @@ fn classify_let_value(val: Node, ctx: DescentContext) -> SubValueRelation? { match from_per_field { Present { value: rel } => Present { value: rel } Absent => - // Check scope_locals for base type, then param_names (Experiment 2: sub_value_vars reconstruction deleted) + let type_name = match map_get(ctx.scope_locals, bname) { Present { value: binding } => authored_name_at(source_indices: ctx.type_env.source_indices, node: binding.resolved) Absent => @@ -3603,7 +3444,7 @@ fn classify_let_value(val: Node, ctx: DescentContext) -> SubValueRelation? { Present { value: a } => match arg_value(n: a).expr_data { ExprLiteral { value: LitInt { value: k } } => if k > 0 { k } else { 0 } - _ => 0 // fail-closed: skip(0) is legal, unknown amount is not strict decrease + _ => 0 } Absent => 0 } @@ -3674,14 +3515,6 @@ fn classify_call_arg_provenance(arg_expr: Node, ctx: DescentContext) -> SubValue } } -// Merge SubValueRelations across branches using the structural lattice -// meet from std/induction.dag. No lossy projection — meet_sub_value is -// the single authority for SubValueRelation lattice operations. -// -// Uses first-element-as-init because the lattice has no unique top -// (structural variants are incomparable). Empty list → SubValueUnknown -// (fail-closed; shouldn't occur in practice since match/if always has -// at least one branch). fn merge_argument_relations(rels: List) -> SubValueRelation { match rels |> first { Absent => SubValueUnknown @@ -3775,10 +3608,7 @@ fn classify_argument(arg_expr: Node, param_name: String, ctx: DescentContext) -> } ExprMethodCall { method_semantics: _ } => classify_collection_shrink(arg_expr: arg_expr, param_name: param_name, ctx: ctx) - // Arithmetic descent: param - k, param / k, param + k (position advance on bounded domain). - // P4: literal `k` is bridged only through `std.termination` Peano materializers, which - // fail closed for k outside (0, 256] (subtract) / [2, 256] (divide) — no unbounded recursion - // on user-sized integers (see `positive_descent_amount_from_positive_int` caps). + ExprBinOp { op: op } => let left = binop_left(texpr: arg_expr) let right = binop_right(texpr: arg_expr) @@ -3799,8 +3629,7 @@ fn classify_argument(arg_expr: Node, param_name: String, ctx: DescentContext) -> _ => SubValueUnknown } } else { - // k - param (e.g., 0 - n) is NOT well-founded descent. - // f(n) = f(0 - n) can oscillate. Fail closed. + SubValueUnknown } Div => @@ -3829,9 +3658,7 @@ fn classify_argument(arg_expr: Node, param_name: String, ctx: DescentContext) -> SubValueUnknown _ => SubValueUnknown } - // ── Transparent wrappers: propagate evidence through control flow ── - // These are not value constructors — they pass through an existing value. - // Worst-case across branches ensures soundness. + ExprMatch => let arm_rels = match_arm_nodes(texpr: arg_expr) |> map(arm => classify_argument(arg_expr: arm_body(n: arm), param_name: param_name, ctx: ctx) @@ -3971,11 +3798,7 @@ fn descent_source_type(found: String, arg_node: Node, ctx: DescentContext) -> St fn annotate_descent(body: Node, ctx: DescentContext) -> Node { match body.expr_data { ExprCall { call_semantics: cs, descent_evidence: _ } => - // Annotate ALL calls with descent evidence — not just self-calls. - // Evidence says "this argument is a sub-value of the caller's parameter." - // For self-calls, this proves direct recursion terminates. - // For cross-calls, the SCC proof system reads this to build cross-edges. - // Single authority: no parallel analysis needed. + let evidence = build_call_evidence(call_node: body, ctx: ctx) let source_type = body.children |> fold(init: "", f: (found, arg_node) => descent_source_type(found: found, arg_node: arg_node, ctx: ctx) @@ -4042,9 +3865,7 @@ fn annotate_descent(body: Node, ctx: DescentContext) -> Node { } } _ => - // Fail closed: non-variable scrutinees have no proven provenance - // from the caller's parameters. Using inferred type alone would - // fabricate sub-value evidence for arm bindings. + "" } let scrut_inducing_field = match scrut.expr_data { @@ -4073,11 +3894,7 @@ fn annotate_descent(body: Node, ctx: DescentContext) -> Node { _ => none } let scrut_has_inductive = (scrut_type != "" && inductive_fields_for(env: ctx.type_env, type_name: scrut_type) |> count > 0) || scrut_inducing_field != none - // CX-R: Detect scrutinee ExprCall with variant_provenance (sum-type return). - // When matching on a function call that returns a sum type, each arm's - // variant bindings can be classified using the function's per-variant - // per-field provenance, enabling structural descent evidence for - // `match advance(tokens: tokens) { AdvanceOk { tokens: rest } => self(tokens: rest) }`. + let scrut_variant_sig = match scrut.expr_data { ExprCall { call_semantics: _, descent_evidence: _ } => let callee = expr_call_func_at(texpr: scrut, source_indices: ctx.type_env.source_indices) @@ -4382,13 +4199,7 @@ fn annotate_descent(body: Node, ctx: DescentContext) -> Node { lparams |> skip(elem_position.value) |> first Absent => none } - // Fold has an accumulator — captured sub_value_vars can alias it, - // making descent evidence unsound. (e.g., [1,2] |> fold(f: (acc, x) => self(t: child)) - // is T(n) = 2T(n-1) = O(2^n), not O(n).) - // Map/filter/any/all have no accumulator — each invocation is independent, - // so captured sub_value_vars remain valid descent evidence. - // S7: detect fold structurally — element at position > 0 means earlier params - // are accumulators, which implies fold semantics. + let is_fold = elem_position.value > 0 let inner_ctx = match lambda_elem_info { Present { value: elem_name } => @@ -4494,17 +4305,6 @@ fn resolved_type_name(n: Node, source_indices: Map) -> Str } } -// ========================================================================= -// Output provenance inference -// -// Determine the structural relationship between a function's return value -// and its parameters by walking the typed body. This enables the descent -// analysis to see through helper function calls: if f(x) returns a -// sub-value of x, then `let r = f(p)` registers r as a sub-value of p. -// -// fail-closed: returns empty_map() for any body shape we can't analyze. -// ========================================================================= - fn infer_output_provenance(body: Node, params: List, type_env: TypeEnv, scope_locals: Map, func_sigs: Map) -> List> { let param_names = params |> map(p => param_node_name_at(n: p, source_indices: type_env.source_indices)) let param_types = fold(params, init: empty_map(), f: (acc, p) => @@ -4512,9 +4312,7 @@ fn infer_output_provenance(body: Node, params: List, type_env: TypeEnv, sc let ptype = resolved_type_name(n: p, source_indices: type_env.source_indices) if pname != "" && ptype != "" { map_insert(acc, pname, ptype) } else { acc } ) - // Try scalar provenance first (preserves existing consumer compatibility). - // Only use per-field when scalar is empty — per-field captures struct-return - // provenance that scalar can't express. + let scalar = classify_body_provenance( expr: body, param_names: param_names, param_types: param_types, type_env: type_env, func_sigs: func_sigs, let_prov: empty_map() @@ -4612,10 +4410,7 @@ fn classify_body_provenance(expr: Node, param_names: List, param_types: let scrut_prov = classify_body_provenance(expr: scrut, param_names: param_names, param_types: param_types, type_env: type_env, func_sigs: func_sigs, let_prov: let_prov) let arms = match_arm_nodes(texpr: expr) let arm_provs = arms |> map(arm => - // Extend let_prov with arm bindings composed through inductive fields. - // Uses field_binding_pattern (Bind { name }) for the introduced binding name, - // field_binding_name_at for the field label in inductive field lookup. - // Includes variant_name in the filter. Fails closed on unmatched fields. + let arm_let_prov = match arm_pattern(n: arm) { VariantPattern { name: vname, parent_enum: _, field_bindings: bindings } => fold(bindings, init: let_prov, f: (acc, binding) => @@ -4764,10 +4559,6 @@ fn compose_callee_provenance(callee: String, arg_by_name: Map, par } } -// Walk body to find record literal returns and classify each field's provenance. -// Non-recursive: walks through ExprLet/ExprBlock chains to find the terminal -// expression, then checks one level of ExprMatch/ExprIf for record arms. -// Returns empty list if no record literal found (fail-closed to scalar mode). fn classify_body_per_field(expr: Node, param_names: List, param_types: Map, type_env: TypeEnv, func_sigs: Map, let_prov: Map>) -> List> { let unwrapped = unwrap_body_terminal(expr: expr, let_prov: let_prov, type_env: type_env) let terminal = unwrapped.expr @@ -4885,9 +4676,6 @@ fn intersect_prov_maps(a: Map, b: Map DescentContext? { match arm_pattern(n: arm_node) { VariantPattern { name: vname, parent_enum: _, field_bindings: bindings } => @@ -4941,9 +4729,6 @@ fn compose_callee_param_map(callee_prov: Map, arg_by_n ) } -// Compose two sub-value relations across a function call boundary. -// Delegates to std/induction.dag's compose_sub_value_relations — the -// single authority for SubValueRelation sequential composition. fn compose_output_relations(arg_rel: SubValueRelation, callee_rel: SubValueRelation) -> SubValueRelation { compose_sub_value_relations(arg_rel: arg_rel, callee_rel: callee_rel) } @@ -5192,7 +4977,6 @@ fn annotate_descent_evidence(body: Node, fn_name: String, params: List, ty annotate_descent(body: body, ctx: ctx) } - type InferPropertiesResult { props: List diagnostics: List @@ -5273,7 +5057,6 @@ fn infer_transport_node(transport: Node?, scope: InferScope) -> InferTransportRe } } - fn infer_item(item: Node, scope: InferScope) -> TypedItemResult { let typed_anno = item.type_annotation let transport_scope = if item.params |> count > 0 { @@ -5307,7 +5090,7 @@ fn infer_item(item: Node, scope: InferScope) -> TypedItemResult { let body_typed = body_result.typed let body_diags = body_result.diagnostics let inferred_ret = if item.inferred != none { item.inferred } else { body_typed.inferred } - // CX-L2: annotate self-call sites with descent evidence + let is_recursive = expr_has_self_call(texpr: body_typed, fn_name: authored_name_at(source_indices: scope.type_env.source_indices, node: item), source_indices: scope.type_env.source_indices) let annotated_body = if is_recursive { annotate_descent_evidence(body: body_typed, fn_name: authored_name_at(source_indices: scope.type_env.source_indices, node: item), params: item.params, type_env: scope.type_env, scope_locals: fn_scope.locals, func_sigs: scope.func_env.signatures) @@ -5384,48 +5167,14 @@ fn infer_item(item: Node, scope: InferScope) -> TypedItemResult { } } - fn infer_items(items: List, scope: InferScope) -> List { map(items, item => infer_item(item: item, scope: scope)) } -// ========================================================================= -// Kernel type environment -// ========================================================================= - -// Kernel primitives (String, Int, Bool, Float, Secret, Json, Unit, Bytes) -// are now defined as bare type declarations in dsl/std/types.dag. -// Today the compiler still injects std.types as an ambient bootstrap bridge -// when needed; that keeps declaration lookup working during Lane 1, but it -// is not the final import-driven FF-9 state. - -// ========================================================================= -// Type environment lookup -// ========================================================================= - -// ========================================================================= -// Build type environment for a single module -// ========================================================================= - -// Names from the algebra type system that should not leak as ambient -// type bindings when importing from std.types. These are template -// metavariables, not user-facing type definitions. fn is_type_variable_name(name: String) -> Bool { name == "T" || name == "K" || name == "V" || name == "MappedElement" || name == "FoldAccumulator" } -// ── Minimal generic instantiation for value-arg calls (R2) ─────────────── -// A function's declared generics are stored as params ALONGSIDE its value -// params (e.g. bind_outcome(o,f) has params [T, U, o, f]), so a naive -// sig.params[arg_index] lands on a generic, not the value-formal. A generic -// DECLARATION param is self-named — its value-name equals its type-name -// (`T: T`, `U: U`) — whereas a value param is not (`o: Outcome`). Filtering -// out the generic decls yields the value params positionally, no offset -// arithmetic, robust to any generic name (incl. U, which is not in the -// is_type_variable_name set) and to omitted/optional value args. - -// Fold state for generic-aware value-arg inference: accumulates typed args in -// order while threading the generic substitution built from concrete args. type ArgGenericFoldState { results: List, subst: Map } fn type_node_label(n: Node, source_indices: Map) -> String { @@ -5448,31 +5197,18 @@ fn param_is_generic_decl(p: Node, source_indices: Map) -> Present { value: inf } => is_type_variable(inferred: inf) Absent => false } - // Bare-leaf type AND self-named: a generic declaration is spelled `T: T` / - // `U: U` (value-name == type-name), which distinguishes it from a value param - // whose type happens to be a generic (`x: T` — names differ) or a concrete - // type (`node: Node`). After resolve, type params may carry TypeVariable - // inferred nodes whose authored_name is empty — fall back to param/type names. + (pt.children |> count == 0 && pt.connective == NoConnective) && pname != "" && (pname == tname || (type_expr_is_var && pname == pt.name)) } -// Bind generic names by unifying a value-formal against the actual arg type. -// Scoped to R2's shape: a bare type-variable leaf binds to the actual, and a -// single-type-parameter container (Outcome vs Outcome) recurses -// into its one type-arg. Multi-generic / nested / arrow unification is the -// deferred roadmap (A) — added only when a witness forces it. fn unify_generics(formal: Node, actual: Node, generic_names: List, source_indices: Map, acc: Map) -> Map { let bind_name = type_node_label(n: formal, source_indices: source_indices) let f_bare = formal.children |> count == 0 && formal.connective == NoConnective if (f_bare || match formal.inferred { Present { value: TypeVariable { id: _ } } => true _ => false }) && (generic_names |> any(g => g == bind_name)) { - // Fail-closed on conflict: bind a generic ONCE (first occurrence wins); never - // silently overwrite. A later occurrence that disagrees is not papered over by - // a fresh binding here — it is caught when that arg is inferred against the - // formal already substituted with the first binding (a typed mismatch at the - // arg), so inference cannot proceed with a silent unstated choice. + match map_get(acc, bind_name) { Absent => map_insert(acc, bind_name, actual) Present { value: _ } => acc @@ -5555,17 +5291,8 @@ fn unify_record_lit_generics(formal: Node, record: Node, generic_names: List Outcome needs the input T -// replaced for the lambda param to bind concretely. fn substitute_generics(n: Node, subst: Map, source_indices: Map) -> Node { - // No bindings ⇒ identity. Critical: the rebuild below allocates a fresh Node, - // which would duplicate a type's canonical declaration in the nodes table and - // change identity for downstream consumers. Returning n unchanged keeps every - // non-generic call (and any generic call that bound nothing) a true no-op. - // Empty check stays at this entry only — subst is unchanged through recursion, - // so map_keys must not run per tree node (was super-linear on body_producer infer). + if map_is_empty(subst) { n } else { substitute_generics_apply(n: n, subst: subst, source_indices: source_indices) } } @@ -5586,16 +5313,7 @@ fn substitute_generics_apply(n: Node, subst: Map, source_indices: Absent => n } } else { - // Identity-preserving recursion. Recurse children/params/inferred first, then - // return the original n unchanged when nothing was substituted. rc_vec_ptr_eq / - // rc_ptr_eq are SOUND IDENTITY HINTS: a true result implies structural equality, - // so returning n is always structure-preserving. The compiled realization answers - // by pointer identity (conservative: may rebuild a structurally-equal node), the - // interpreter by value equality (exact) -- both honor the contract, so output - // structure (and thus content_hash / the self-host fixed point) is identical - // either way. Skipping the fresh-Node rebuild on the common no-op path is the - // perf win; the per-node child-vec pointer compare is a linear constant factor, - // not a return to the super-linear rebuild this guards against. + let new_children = n.children |> map(c => substitute_generics_apply(n: c, subst: subst, source_indices: source_indices)) let new_params = n.params |> map(p => substitute_generics_apply(n: p, subst: subst, source_indices: source_indices)) let new_inferred = match n.inferred { @@ -5624,8 +5342,6 @@ fn substitute_generics_apply(n: Node, subst: Map, source_indices: } } -// Merge type bindings from parent environments, filtering out algebra -// type variable names that should not be visible to importing modules. fn collect_parent_bindings_filtered(envs: List) -> Map { fold(envs, init: empty_map(), f: (acc, env) => fold(map_keys(env.bindings), init: acc, f: (bacc, ident) => @@ -5675,25 +5391,15 @@ fn build_type_env(module: ResolvedModule, parent_index: Map let absent_variant = Node { name: "Absent", span: kernel_span(name: "Absent"), ident_span: Present { value: kernel_span(name: "Absent") }, children: [], connective: NoConnective, params: [], inferred: none, return_cardinality: Required, uses: [], body: none, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: NoExprData } let kernel_optional = Node { name: "Optional", span: kernel_span(name: "Optional"), ident_span: Present { value: kernel_span(name: "Optional") }, children: [present_variant, absent_variant], connective: Disj, params: [], inferred: none, return_cardinality: Required, uses: [], body: none, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: NoExprData } let kernel_bindings = map_insert(kernel_bindings, intern(table: intern_table, s: "Optional").id, TypeBinding { name: "Optional", resolved: kernel_optional, provenance: SubValueUnknown }) - // Compiler types' inductive fields are declared in std/node.dag. - // This provides the structural facts for CX-L2 to prove tree-walk - // functions are O(n). See ROADMAP CX-NEXT Phase 2. + let node_fields = inductive_fields_list_to_map(fields: compiler_inductive_fields) let kernel_recursive_types = map_keys(compiler_recursive_types) |> map(name => intern(table: intern_table, s: name).id) let kernel_recursive_type_set = fold(map_keys(compiler_recursive_types), init: empty_map(), f: (acc, name) => map_insert(acc, intern(table: intern_table, s: name).id, true)) let kernel = TypeEnv { bindings: kernel_bindings, recursive_types: kernel_recursive_types, recursive_type_set: kernel_recursive_type_set, inductive_fields: node_fields, source_indices: source_indices, intern_table: intern_table } - // §3 single-authority: a name resolves through each module's explicit - // import graph only. The std.types base-injection bootstrap bridge was - // removed — it was a second, implicit authority that let v1/v2 same-named - // types (FreeMonoid / Empty / Cons) collide by topological merge order. - // Modules that use kernel primitives keep resolving them from the kernel - // env; anything beyond the kernel must be imported explicitly. let module_name_str = authored_name_at(source_indices: source_indices, node: module.module) let parent_envs = flat_map(module.resolved_imports, imp => match map_get(parent_index, imp.module_path) { Present { value: typed_parent } => [typed_parent.type_env] Absent => [] }) - // No implicit base bindings: the import fold below starts empty and is - // seeded only by the module's explicit imports (std.types type-variable - // filtering still applies when std.types is imported explicitly). + let import_bindings = fold(module.resolved_imports, init: collect_parent_bindings_filtered(envs: []), f: (acc, imp) => match map_get(parent_index, imp.module_path) { Present { value: typed_parent } => @@ -5743,10 +5449,7 @@ fn build_type_env(module: ResolvedModule, parent_index: Map let ref_node = nominal_ref_node(name: authored_name_at(source_indices: source_indices, node: item), span: item.span, ident_span: item.ident_span) map_insert(acc, item_ident, TypeBinding { name: authored_name_at(source_indices: source_indices, node: item), resolved: ref_node, provenance: SubValueUnknown }) } else if item.params |> count > 0 && item.connective == NoConnective && item.body == none && item.transport == none { - // Parameterized type declaration (e.g., type List = FreeMonoid). - // Register as a nominal type with params so generics can find the declaration. - // Preserve .inferred so alias chains (List → FreeMonoid) survive into the - // TypeEnv for structural resolution (Phase 2 alias following). + let bare_node = Node { name: item.name, span: item.span, ident_span: item.ident_span, children: [], connective: NoConnective, params: item.params, inferred: item.inferred, @@ -5761,12 +5464,7 @@ fn build_type_env(module: ResolvedModule, parent_index: Map acc } ) - // Register type parameter names from generic type declarations so inference - // can resolve slot references (e.g., "A" and "B" in type Pair). - // Slot names resolve to leaf nodes; normalization substitutes them at - // use sites. Phase 3 MVP: registered globally, not scoped to declaration. - // Only register params from items already recognized as type bindings — - // prevents function params from leaking into the type namespace. + let param_bindings = fold(module_items(n: module.module), init: empty_map(), f: (acc, item) => let is_type_decl = match map_get(local_bindings, intern(table: intern_table, s: authored_name_at(source_indices: source_indices, node: item)).id) { Present { value: _ } => true Absent => false } if item.params |> count > 0 && is_type_decl { @@ -5784,8 +5482,7 @@ fn build_type_env(module: ResolvedModule, parent_index: Map let all_deps_map = fold(merged.bindings |> map_values, init: empty_map(), f: (acc, b) => map_insert(acc, b.name, node_type_deps(n: b.resolved, source_indices: source_indices)) ) - // Bridge: detect_type_cycles_kahn expects Map bindings. - // Build a String-keyed copy from the Int-keyed merged.bindings. + let str_bindings = fold(merged.bindings |> map_values, init: empty_map(), f: (acc, b) => map_insert(acc, b.name, b) ) @@ -5829,8 +5526,7 @@ fn build_type_env_unresolved(module: ResolvedModule, parent_index: Map match map_get(parent_index, imp.module_path) { Present { value: typed_parent } => [typed_parent.type_env] Absent => [] }) let import_bindings = fold(module.resolved_imports, init: collect_parent_bindings_filtered(envs: []), f: (acc, imp) => @@ -5878,7 +5574,7 @@ fn build_type_env_unresolved(module: ResolvedModule, parent_index: Map map_values, init: empty_map(), f: (acc, b) => map_insert(acc, b.name, node_type_deps(n: b.resolved, source_indices: source_indices)) ) - // Bridge: detect_type_cycles_kahn expects Map. + let str_bindings = fold(merged.bindings |> map_values, init: empty_map(), f: (acc, b) => map_insert(acc, b.name, b) ) @@ -5893,7 +5589,6 @@ fn build_type_env_unresolved(module: ResolvedModule, parent_index: Map, module_name: String) -> ItemInfo { let kind = item_kind(item: item) let res_names = item.uses |> map(u => resource_use_name_at(n: u, source_indices: source_indices)) @@ -6100,24 +5795,16 @@ fn build_module_context(contributions: List, } } -// ========================================================================= -// Typecheck a single module -// ========================================================================= - type TypecheckModuleResult { typed: TypedModule diagnostics: List } - fn typecheck_module(resolved: ResolvedModule, parent_index: Map, source_indices: Map, intern_table: InternTable) -> TypecheckModuleResult { let env_result = build_type_env(module: resolved, parent_index: parent_index, source_indices: source_indices, intern_table: intern_table) let env = env_result.env let env_diags = env_result.diagnostics - // Gate: if type environment construction produced errors (unresolved types), - // halt before inference. This makes unresolved nodes structurally impossible - // downstream -- inference and emit never see them. let env_errors = env_diags |> filter(d => is_error_diagnostic(d: d.diagnostic)) let seed_diags = builtin_kernel_seed_diagnostics() if env_errors |> count > 0 { @@ -6204,10 +5891,6 @@ fn typecheck_module(resolved: ResolvedModule, parent_index: Map @@ -6305,10 +5988,6 @@ fn topo_resolve_types( ) } -// ========================================================================= -// Collect parent type environments for a module -// ========================================================================= - fn collect_parent_envs(resolved: ResolvedModule, module_index: Map, source_indices: Map) -> ParentModulesResult { let modules = flat_map(resolved.resolved_imports, imp => match map_get(module_index, imp.module_path) { @@ -6326,7 +6005,6 @@ fn collect_parent_envs(resolved: ResolvedModule, module_index: Map) -> Bool { variant.children |> count == 1 && match variant.children |> first { Present { value: f } => field_node_name_at(n: f, source_indices: source_indices) == "0" @@ -6385,14 +6063,6 @@ fn build_positional_payload_variants(modules: List, type_summaries: ) } -// Corpus-global Rust representation selector (§2 handler selection). HostNative -// iff the corpus bundles any v1 seed source (src/v1) — then the whole crate emits -// the host representation (List→Vec, String, Nat/Int→i64) so the bootstrap seed -// compiles (§7); otherwise the faithful v2-target FreeMonoid carrier. Computed -// ONCE here (the single authority on EmitGraphInfo.corpus_repr); every emit site -// COPIES the field and the type-renderers READ it — never recomputed downstream, -// which is what makes per-module representation divergence unwritable (§5). -// §6-transitional: collapses to FaithfulFreeMonoid when src/v1 is deleted. fn rust_corpus_repr(modules: List) -> RustCorpusRepr { let has_seed = modules |> any(m => map_keys(m.type_env.source_indices) |> any(k => contains(k, "/v1/") || contains(k, "src/v1"))) @@ -6432,26 +6102,8 @@ fn build_emit_graph_info(modules: List) -> EmitGraphInfo { } } -// ========================================================================= -// Main entry point: typecheck -// -// Processes modules in dependency order (from the resolver's dep_order). -// Each module sees the type environments of all its dependencies. -// -// Invariant: after this function returns, no unresolved Named references -// survive (except recursive cycle-breakers that are guaranteed to be -// defined in the graph). -// ========================================================================= - fn typecheck(graph: ModuleGraph, source_indices: Map, intern_table: InternTable) -> TypedGraph { - // SG-9: Use explicit recursion with standalone parameters instead of fold - // with a struct accumulator. Standalone Rc parameters have refcount 1 in - // TCO loops, enabling O(1) list_push/map_insert via Rc::try_unwrap. - // A fold accumulator struct forces O(N) clones on every field access. - // - // Diagnostics use chunked accumulation (List>) instead of - // concat to avoid O(M^2) cloning. Each iteration pushes diagnostic lists; - // flat_map flattens once at the end. + typecheck_modules( remaining: graph.modules, modules: [], @@ -6493,15 +6145,6 @@ fn typecheck_modules( } } -// ========================================================================= -// Reconcile namespaces in one call. -// -// Returns the reconciled graph with graph-level item metadata attached. -// Historical note: collect_unresolved_in_expr and the rest of the -// validate_no_unresolved walk family were removed once the env-error gate -// made unresolved expression-tree validation redundant downstream. -// ========================================================================= - fn reconcile(graph: ModuleGraph, source_indices: Map, intern_table: InternTable) -> ResolvedGraph { let typed = typecheck(graph: graph, source_indices: source_indices, intern_table: intern_table) let emit_info = build_emit_graph_info(modules: typed.modules) diff --git a/src/v1/04_items.dag b/src/v1/04_items.dag index d32efffd802..9d3f0bc88eb 100644 --- a/src/v1/04_items.dag +++ b/src/v1/04_items.dag @@ -1,5 +1,3 @@ -// Item analysis -- classifies module items and builds item metadata. - module v1.compiler.infer_items import std.types { SourceSpan } @@ -28,10 +26,6 @@ import v1.compiler.infer_emit_info { EmitGraphInfo } -// ========================================================================= -// Types -// ========================================================================= - type ItemKind = FnItem | FuncItem | TypeItem | DataItem | ServiceItem | OtherItem type ItemInfo { @@ -66,10 +60,6 @@ type ResolvedGraph { emit_graph_info: EmitGraphInfo } -// ========================================================================= -// Functions -// ========================================================================= - fn inferred_to_outputs(inferred: InferredNode?, span: SourceSpan, source_indices: Map) -> List { if inferred == none { [] } else { match inferred.value { diff --git a/src/v1/04_lookup.dag b/src/v1/04_lookup.dag index 322de837f34..d56528ae614 100644 --- a/src/v1/04_lookup.dag +++ b/src/v1/04_lookup.dag @@ -1,5 +1,3 @@ -// Lookup and resolution helpers -- pure type lookups and scrutinee - module v1.compiler.infer_lookup import v1.std.core { @@ -57,20 +55,12 @@ fn is_witness_type_name(name: String) -> Bool { name == "Witness" || name == "witness" } -// ========================================================================= -// Types -// ========================================================================= - type KnownMethodResolution { semantics: MethodSemantics? result_type: Node? diagnostics: List } -// ========================================================================= -// Scope lookups -// ========================================================================= - fn lookup_in_scope(locals: Map, name: String) -> Node? { match map_get(locals, name) { Present { value: binding } => Present { value: binding.resolved } @@ -82,10 +72,6 @@ fn lookup_func_sig(func_env: ResolvedFuncEnv, name: String) -> ResolvedFuncSig? map_get(func_env.signatures, name) } -// ========================================================================= -// Field lookup -// ========================================================================= - fn lookup_field_type_node(n: Node, field_name: String, source_indices: Map) -> Node? { let is_optional = n.return_cardinality == CardOptional if is_optional { @@ -120,8 +106,7 @@ fn lookup_field_type_node(n: Node, field_name: String, source_indices: Map lookup_field_type_node(n: target, field_name: field_name, source_indices: source_indices) @@ -149,10 +134,6 @@ fn lookup_coproduct_common_field_node(variants: List, field_name: String, } } -// ========================================================================= -// Scrutinee type resolution -// ========================================================================= - fn resolve_scrutinee_type_node(env: TypeEnv, n: Node) -> Node { resolve_scrutinee_type_node_seen(env: env, n: n, seen: empty_map()) } @@ -271,10 +252,6 @@ fn field_summary_for_type(base_type: Node, env: TypeEnv, field: String) -> Field } } -// ========================================================================= -// Known method resolution -// ========================================================================= - type MethodFieldResult { field_node: Node result_type: Node @@ -369,11 +346,7 @@ fn lookup_structural_method(receiver_type: Node, method_name: String, source_ind } fn resolve_known_method_node(receiver: Node, receiver_type: Node, method_name: String, fold_accumulator_type: Node?, service_registry: Map>, source_indices: Map) -> KnownMethodResolution { - // Tier 0: Structural field lookup for ALL types (including collections). - // The algebra registry (enrich_kernel_type) provides fields for kernel - // types; user-defined types have their own structural children. - // Return types from the algebra field are passed through unsubstituted — - // Phase C structural resolution handles TypeVariable resolution at the call site. + let tier0 = lookup_structural_method(receiver_type: receiver_type, method_name: method_name, source_indices: source_indices) match tier0.resolution { Present { value: mfr } => diff --git a/src/v1/04_method.dag b/src/v1/04_method.dag index bd11b7107b5..e345363d2b3 100644 --- a/src/v1/04_method.dag +++ b/src/v1/04_method.dag @@ -1,12 +1,3 @@ -// v2 method helpers -- builtin call type inference and type constructors. -// -// Intrinsic/bridge method classification was deleted 2026-03-28 (48 string -// branches replaced by algebra registry + Tier 0 lookup_structural_method). -// What remains: builtin_function_registry for free-function call syntax and -// type constructor helpers used by inference. -// -// Dependency: v1.compiler.infer_types (Node constructors) - module v1.compiler.infer_method import std.types { SourceSpan } @@ -23,15 +14,6 @@ import v1.compiler.infer_types { make_container_type, make_map_type } -// ========================================================================= -// Builtin call type inference -// -// Method classification (intrinsic/bridge) and method return type inference -// were deleted 2026-03-28. Methods resolve structurally through Tier 0 -// lookup_structural_method; return types come from algebra fields + -// AlgebraTypeVariable instantiation (M2 Phase C). -// ========================================================================= - fn type_variable_node(id: String) -> Node { Node { name: "", span: make_span(start: 0, end: 0), ident_span: none, children: [], connective: NoConnective, params: [], inferred: Present { value: TypeVariable { id: id } }, return_cardinality: Required, uses: [], body: none, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: NoExprData } } @@ -52,14 +34,6 @@ fn witness_of_element(element: Node) -> Node { make_container_type(kind_name: "Witness", element: element).ty } -// Builtin function return type registry. -// -// BRIDGE: This map_insert chain is a duplicate authority over facts that -// should come from .dag function declarations (extern fn signatures). -// Deletion point: when builtins are actual .dag definitions that the -// compiler loads and resolves, this registry is deleted. See review -// feedback on BuiltinReturnKind — the proper model is definition nodes, -// not a return-type proxy enum. fn seed_node_map(key: String, value: Node) -> Map { map_insert(empty_map(), key, value) } @@ -90,11 +64,7 @@ fn builtin_function_registry() -> Map { let m = map_insert(m, "chars_to_string", string_type) let m = map_insert(m, "record_source_chars_index_lookup", unit_type) let m = map_insert(m, "to_string", string_type) - // `discriminant(v)` reifies a coproduct/record value's own constructor name as a Symbol - // (interned string). One intrinsic dissolves every hand-written `fn ..._discriminant(v) -> - // Symbol { match v { Ctor{..} => ctor_tag ... } }` bridge that shadows a coproduct's arm-set - // with a parallel `data ctor_tag: Symbol` vocabulary. Symbol is operationally a String - // (Value::Str at runtime; the `data X: Symbol = X` idiom resolves to its interned name). + let m = map_insert(m, "discriminant", string_type) let m = map_insert(m, "concat", string_type) let m = map_insert(m, "map_insert", map_of_type_variables()) @@ -107,7 +77,7 @@ fn builtin_function_registry() -> Map { let m = map_insert(m, "map_contains_key", bool_type) let m = map_insert(m, "map_has", bool_type) let m = map_insert(m, "map_is_empty", bool_type) - // Sound identity hints (substitute_generics short-circuit): true => structurally equal. + let m = map_insert(m, "rc_ptr_eq", bool_type) let m = map_insert(m, "rc_vec_ptr_eq", bool_type) let m = map_insert(m, "emit_map_has", bool_type) @@ -153,8 +123,6 @@ fn builtin_function_registry() -> Map { m } -// NOTE: builtin_function_registry() constructs the map on each call in .dag -// source. The stage0 Rust caches it via thread_local! to avoid O(N×M) rebuild. fn infer_builtin_call_type(name: String) -> Node? { map_get(builtin_function_registry(), name) } diff --git a/src/v1/04_patterns.dag b/src/v1/04_patterns.dag index 104a432937f..09c088c161f 100644 --- a/src/v1/04_patterns.dag +++ b/src/v1/04_patterns.dag @@ -1,5 +1,3 @@ -// Pattern matching and variant lookup -- read-only type checks for - module v1.compiler.infer_patterns import std.types { SourceSpan } @@ -28,10 +26,6 @@ import v1.compiler.infer_env { lookup_type, lookup_type_by_name } -// ========================================================================= -// Types -// ========================================================================= - fn is_witness_type_name(name: String) -> Bool { name == "Witness" || name == "witness" } @@ -57,13 +51,6 @@ fn is_type_variable(inferred: InferredNode) -> Bool { } } -// ========================================================================= -// Variant lookup -// ========================================================================= - -// Synthesize the canonical `Present { value: T }` variant node for cardinality -// Optional pattern matching. Optional is represented by `return_cardinality`; -// it has no explicit `Present` child in the v2 type node. fn synthesize_optional_present_variant(scrut: Node) -> Node { let inner = extract_optional_inner_node(n: scrut) let value_field = Node { name: "value", span: scrut.span, ident_span: Present { value: kernel_span(name: "value") }, children: [], connective: NoConnective, params: [], inferred: Present { value: Resolved { node: inner } }, return_cardinality: Required, uses: [], body: none, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: NoExprData } @@ -87,9 +74,7 @@ fn synthesize_witness_violates_variant(scrut: Node) -> Node { } fn pattern_subject_from_node(n: Node) -> PatternSubject { - // Structural check: CompilerError in inferred means error sentinel. - // Dynamic/Error name checks dissolved — no .dag source declares these types, - // and error_type uses name: "" with CompilerError in inferred. + let is_error = if n.inferred != none { is_compiler_error(inferred: n.inferred.value) } else { false } if is_error { PatternLookupBlocked @@ -168,15 +153,11 @@ fn lookup_variant_in_type(scrut: PatternSubject, variant_name: String, module_na node_lookup_resolved(node: synthesize_witness_violates_variant(scrut: scrut_node)) } else { let scrut_name = authored_name_at(source_indices: source_indices, node: scrut_node) - // Optional coproduct: after generic instantiation the applied type may be a - // bare `Optional` node carrying only the type arg (no Disj children). Synthesize - // Present/Absent the same way cardinality-optionals do once direct child lookup fails. + let optional_coproduct_subject = scrut_name == "Optional" && (variant_name == "Present" || variant_name == "Absent") let direct_match = find_child_named(n: scrut_node, name: variant_name, source_indices: source_indices) - // Record / generic-record destructuring: `LocusAnchor { at: x }` against - // `LocusAnchor` (Conj) — requires brace syntax (field_binding_count > 0). - // Bare `LocusAnchor` without `{…}` must not match; direct_match on sum variants still wins. + let record_destructure = field_binding_count > 0 && scrut_node.connective == Conj && scrut_name == variant_name @@ -214,10 +195,6 @@ fn lookup_field_in_variant(variant: PatternSubject, field_name: String, module_n } } -// ========================================================================= -// Match exhaustiveness checking -// ========================================================================= - fn check_match_exhaustiveness( scrutinee_type: Node, arms: List, diff --git a/src/v1/04_resolve.dag b/src/v1/04_resolve.dag index 2e805dcfdf0..8de11cb2bd7 100644 --- a/src/v1/04_resolve.dag +++ b/src/v1/04_resolve.dag @@ -1,5 +1,3 @@ -// Type resolution walk for the v2 namespace reconciler. - module v1.compiler.infer_resolve import std.types { SourceSpan, container_param_name } @@ -42,9 +40,6 @@ import v1.compiler.infer_env { TypeEnv, TypeBinding, lookup_type, lookup_type_by_name, lookup_type_for, is_recursive_type, is_recursive_type_by_name, is_recursive_type_for, authored_name } -// G1 Measure phantom tags: unit coproduct variants (Quantity Memory, -// Scale One) are valid phantom type arguments even though they are not -// standalone type declarations. fn is_unit_variant_node(variant: Node) -> Bool { (variant.children |> count) == 0 } @@ -71,13 +66,6 @@ fn structural_type_for_variant_lookup(env: TypeEnv, ty: Node) -> Node { } else { ty } } -// Fail-closed phantom-tag resolution: scan all type bindings, collect EVERY -// coproduct whose unit-variant name matches, and resolve ONLY when exactly one -// matches. An ambiguous tag (>1 coproduct defining the same short variant name, -// e.g. two unrelated coproducts both with `One`) resolves to `none` rather than -// silently picking the first by map-key iteration order — the unresolved -// phantom then surfaces a downstream diagnostic instead of a nondeterministic -// shadow. (Was: first-match-wins fold; see #4868 review.) fn lookup_unit_variant_phantom_type(env: TypeEnv, variant_name: String) -> Node? { let matches = collect_unit_variant_phantom_matches(env: env, variant_name: variant_name) if matches |> count == 1 { @@ -105,7 +93,6 @@ fn collect_unit_variant_phantom_matches(env: TypeEnv, variant_name: String) -> L ) } -// Gate #60 Slice Z: anonymous decimal width literals in `<…>` type-arg position. fn is_width_nat_type_literal(n: Node) -> Bool { match n.expr_data { ExprLiteral { value: LitInt { value: _ } } => true @@ -119,9 +106,6 @@ fn is_type_variable(inferred: InferredNode) -> Bool { } } -// When peeling a one-level type alias to its structural target, keep the -// declaration/reference ident_span so authored_name_at still reports the brand -// name (UserId vs AccountId) instead of the shared carrier (Refined). fn with_authored_identity(identity: Node, structural: Node) -> Node { Node { name: structural.name, ident: structural.ident, span: structural.span, ident_span: identity.ident_span, @@ -134,7 +118,6 @@ fn with_authored_identity(identity: Node, structural: Node) -> Node { } } -// After env binding resolve, keep declaration-site brand on the structural carrier. fn preserve_nominal_brand_on_resolve( identity: Node, structural: Node, @@ -147,8 +130,6 @@ fn preserve_nominal_brand_on_resolve( } else { structural } } -// Peel alias bindings for call-site arg check: structural carrier with -// reference-site brand identity (bounded A3 — not a full re-resolve). fn peel_nominal_alias_identity(n: Node, env: TypeEnv, module_name: String) -> Node { let source_indices = env.source_indices let brand = authored_name_at(source_indices: source_indices, node: n) @@ -178,10 +159,6 @@ fn peel_nominal_alias_identity(n: Node, env: TypeEnv, module_name: String) -> No } } -// ========================================================================= -// Result types for type resolution -// ========================================================================= - type NodeResolveResult { resolved: Node diagnostics: List @@ -237,18 +214,10 @@ type ResourceUseResult { diagnostics: List } -// ========================================================================= -// Resolve a Node type reference -// ========================================================================= - fn resolve_node(n: Node, env: TypeEnv, module_name: String) -> NodeResolveResult { resolve_node_bounded(n: n, env: env, module_name: module_name, depth: 0) } -// Applied-generic use sites may still carry the alias brand in ident_span -// (ByteSize) while the authored carrier name is structural (Measure). Env -// lookup by brand returns the alias decl (0 params); generic instantiation -// must resolve the structural decl instead. fn resolve_generic_use_decl(env: TypeEnv, n: Node) -> Node { let brand = authored_name(env: env, node: n) match lookup_type_for(env: env, node: n) { @@ -350,9 +319,6 @@ fn is_parametric_type_alias_decl(item: Node) -> Bool { (item.params |> count) > 0 && item.connective == NoConnective && item.body == none && item.transport == none && item.inferred != none } -// Parametric alias RHS (`type List = FreeMonoid`) must keep the authored -// applied-type shell for emit; full resolve expands the base into its structural -// definition and erases nominal base/type-arg/module facts (P2/P3). fn resolve_nominal_alias_rhs(n: Node, env: TypeEnv, module_name: String) -> NodeResolveResult { if n.connective == NoConnective && n.children |> count > 0 { let arg_results = n.children |> map(child => resolve_nominal_alias_rhs(n: child, env: env, module_name: module_name)) @@ -552,22 +518,7 @@ fn resolve_node_bounded(n: Node, env: TypeEnv, module_name: String, depth: Int) } else { match lookup_type_for(env: env, node: n) { Present { value: resolved } => - // Topological resolution ensures bindings are fully resolved before - // any dependent type is processed. Re-resolving the looked-up node - // would redundantly walk the entire expanded subtree, causing - // exponential blowup on diamond-shaped dependency graphs (OOM). - // - // C2.3: For alias nodes (leaf with .inferred but no structure), - // follow ONE level of .inferred to return the structural target. - // This is O(1), not recursive — the target is already resolved - // by topo ordering. Without this, downstream consumers see an - // alias_node wrapper and must re-resolve through TypeEnv (layer - // downgrade from structural to name-based). - // - // Preserve CardOptional from the reference node: the env binding - // always stores the canonical (Required) definition, but the - // reference site may carry CardOptional (e.g. String? → String - // in env). Without this, optionality is silently discarded. + let structurally_resolved = if resolved.connective == NoConnective && resolved.children |> count == 0 && resolved.inferred != none { match resolved.inferred { Present { value: Resolved { node: target } } => target @@ -581,8 +532,7 @@ fn resolve_node_bounded(n: Node, env: TypeEnv, module_name: String, depth: Int) let n_is_error = if n.inferred != none { is_compiler_error(inferred: n.inferred.value) } else { false } let n_is_callable = n.params |> count > 0 let n_is_type_var = if n.inferred != none { is_type_variable(inferred: n.inferred.value) } else { false } - // Arrow nodes (callable types) handled by has_structure above. - // Dynamic/Error sentinel names dissolved — no .dag source declares them. + if is_width_nat_type_literal(n: n) || is_kernel_type(name: authored_name(env: env, node: n)) || n_is_type_var || n_is_error || n_is_callable { NodeResolveResult { resolved: n, diagnostics: [] } } else { @@ -629,10 +579,6 @@ fn resolve_optional_node(n: InferredNode?, env: TypeEnv, module_name: String) -> } } -// ========================================================================= -// Field resolution -// ========================================================================= - fn resolve_field(field: Node, env: TypeEnv, module_name: String) -> FieldResult { let authored_type = field_node_type_expr(n: field) let type_result = resolve_node(n: authored_type, env: env, module_name: module_name) @@ -662,10 +608,6 @@ fn resolve_field(field: Node, env: TypeEnv, module_name: String) -> FieldResult } } -// ========================================================================= -// Param resolution -// ========================================================================= - fn resolve_param(param: Node, env: TypeEnv, module_name: String) -> ParamResult { let authored_type = param_node_type_expr(n: param) let type_result = resolve_node(n: authored_type, env: env, module_name: module_name) @@ -701,14 +643,6 @@ fn resolve_param(param: Node, env: TypeEnv, module_name: String) -> ParamResult } } -// ========================================================================= -// Operation resolution -// ========================================================================= - -// ========================================================================= -// ResourceUse resolution -// ========================================================================= - fn resolve_resource_use(ru: Node, env: TypeEnv, module_name: String) -> ResourceUseResult { let type_result = resolve_node(n: resource_use_resource(n: ru), env: env, module_name: module_name) let type_resolved = type_result.resolved @@ -784,10 +718,6 @@ fn resolve_string_part(part: StringPart, env: TypeEnv, module_name: String) -> S } } -// resolve_header_def, resolve_env_def, resolve_auth_config, resolve_service_config -// deleted — transport and service config dissolution. Config properties are now -// resolved by the generic resolve_field_init loop in resolve_item_types. - fn resolve_transport_binding(transport: Node, env: TypeEnv, module_name: String) -> TransportResolveResult { if is_local_transport(t: transport, source_indices: env.source_indices) { TransportResolveResult { transport: transport, diagnostics: [] } @@ -815,8 +745,6 @@ fn resolve_transport_binding(transport: Node, env: TypeEnv, module_name: String) } } -// P5.11: resolve_expr_types uses map_children for single-child cases, -// make_expr_node with explicit children for multi-child cases. fn resolve_expr_types(texpr: Node, env: TypeEnv, module_name: String) -> ExprResolveResult { match texpr.expr_data { ExprLiteral { value: _ } => ExprResolveResult { expr: texpr, diagnostics: [] } @@ -833,9 +761,7 @@ fn resolve_expr_types(texpr: Node, env: TypeEnv, module_name: String) -> ExprRes } ExprResolveResult { expr: map_children(node: texpr, transform: child => r.expr), diagnostics: r.diagnostics } ExprCall { call_semantics: cs, descent_evidence: de } => - // bind-once: resolve each arg child a SINGLE time, then split into - // resolved children + diagnostics (resolving twice makes a nested - // constructor literal O(2^depth) — see ExprListLit/resolve_node_bounded). + let arg_results = texpr.children |> map(arg_node => let val = match arg_node.children |> first { Present { value: v } => v Absent => arg_node } let vr = resolve_expr_types(texpr: val, env: env, module_name: module_name) @@ -845,7 +771,7 @@ fn resolve_expr_types(texpr: Node, env: TypeEnv, module_name: String) -> ExprRes let all_diags = flat_map(arg_results, r => r.diagnostics) ExprResolveResult { expr: make_named_expr_node(name: expr_call_func_at(texpr: texpr, source_indices: env.source_indices), expr_data: ExprCall { call_semantics: cs, descent_evidence: de }, children: resolved_children, inferred: texpr.inferred, span: texpr.span, name_span: node_name_span(n: texpr)), diagnostics: all_diags } ExprMethodCall { method_semantics: ms } => - // bind-once: resolve each child a SINGLE time (see ExprCall). + let mc_results = texpr.children |> enumerate |> map(pair => let idx = pair.first let child = pair.second @@ -862,7 +788,7 @@ fn resolve_expr_types(texpr: Node, env: TypeEnv, module_name: String) -> ExprRes let all_diags = flat_map(mc_results, r => r.diagnostics) ExprResolveResult { expr: make_named_expr_node(name: expr_method_name_at(texpr: texpr, source_indices: env.source_indices), expr_data: ExprMethodCall { method_semantics: ms }, children: resolved_children, inferred: texpr.inferred, span: texpr.span, name_span: node_name_span(n: texpr)), diagnostics: all_diags } ExprMatch => - // bind-once: resolve each scrutinee/arm a SINGLE time (see ExprCall). + let match_results = texpr.children |> enumerate |> map(pair => let idx = pair.first let child = pair.second @@ -899,9 +825,7 @@ fn resolve_expr_types(texpr: Node, env: TypeEnv, module_name: String) -> ExprRes let resolved_children = match br { Present { value: r } => [vr.expr, r.expr] Absent => [vr.expr] } ExprResolveResult { expr: make_named_expr_node(name: let_binding_name_at(texpr: texpr, source_indices: env.source_indices), expr_data: ExprLet, children: resolved_children, inferred: texpr.inferred, span: texpr.span, name_span: node_name_span(n: texpr)), diagnostics: concat(vr.diagnostics, match br { Present { value: r } => r.diagnostics Absent => [] }) } ExprRecordLit { parent_enum: pe } => - // bind-once: resolve each field-init value a SINGLE time (see ExprCall). - // This is the arm hit by deep nested constructor literals (Cons/record - // chains) — resolving twice per field was O(2^depth). + let fi_results = texpr.children |> map(fi_node => let val = match fi_node.children |> first { Present { value: v } => v Absent => fi_node } let vr = resolve_expr_types(texpr: val, env: env, module_name: module_name) @@ -928,7 +852,7 @@ fn resolve_expr_types(texpr: Node, env: TypeEnv, module_name: String) -> ExprRes let r = match texpr.children |> first { Present { value: b } => resolve_expr_types(texpr: b, env: env, module_name: module_name) Absent => ExprResolveResult { expr: texpr, diagnostics: [] } } ExprResolveResult { expr: make_expr_node(expr_data: ExprLambda, children: concat([r.expr], lam_param_nodes), inferred: texpr.inferred, span: texpr.span), diagnostics: r.diagnostics } ExprStringInterp => - // bind-once: resolve each interpolation part a SINGLE time (see ExprCall). + let part_results = texpr.children |> map(part_node => match part_node.expr_data { ExprLiteral { value: _ } => ExprResolveResult { expr: part_node, diagnostics: [] } @@ -977,22 +901,12 @@ fn resolve_expr_types(texpr: Node, env: TypeEnv, module_name: String) -> ExprRes } } -// ========================================================================= -// Resolve all type expressions within a single Node -// ========================================================================= - fn fn_type_param_names(item: Node, source_indices: Map) -> List { item.params |> filter(p => param_node_name_at(n: p, source_indices: source_indices) == authored_name_at(source_indices: source_indices, node: param_node_type_expr(n: p)) ) |> map(p => param_node_name_at(n: p, source_indices: source_indices)) } -// Detect the `fn f(T: T)` name-shadowing ambiguity. Both the declared -// type param and the value param satisfy the self-reference heuristic, so -// fn_type_param_names returns duplicate entries. Surface as a gunbc -// Diagnostic at the detection point (P3 / modeling-discipline §1): -// fail-closed means the compiler boundary rejects with a typed diagnostic, -// not that it succeeds and outsources rejection to rustc. fn has_duplicate_type_param_name(names: List) -> Bool { names |> enumerate |> any(pair => let idx = pair.first @@ -1068,7 +982,6 @@ fn resolve_item_types(item: Node, env: TypeEnv, module_name: String) -> ItemResu let resolved_transport = if item.transport == none { none } else { Present { value: transport_resolved.transport } } let transport_diags = transport_resolved.diagnostics - // Resolve properties (includes svc_* config properties after ServiceConfig dissolution) let prop_results = map(item.properties, p => resolve_field_init(field_init: p, env: env, module_name: module_name)) let resolved_props = map(prop_results, pr => pr.field_init) let prop_diags = flat_map(prop_results, pr => pr.diagnostics) diff --git a/src/v1/04_service.dag b/src/v1/04_service.dag index 4599efee0f6..5f276550640 100644 --- a/src/v1/04_service.dag +++ b/src/v1/04_service.dag @@ -1,5 +1,3 @@ -// Service call analysis -- pure graph walks for detecting and expanding - module v1.compiler.infer_service import std.types { SourceSpan } @@ -25,10 +23,6 @@ import v1.compiler.infer_items { inferred_to_outputs } -// ========================================================================= -// Types -// ========================================================================= - type UniqueAccum { seen: Map result: List @@ -45,10 +39,6 @@ type ServiceMethodResult { op_params: List } -// ========================================================================= -// Typed-receiver service call detection -// ========================================================================= - fn is_typed_service_call_receiver(receiver: Node, source_indices: Map) -> Bool { match receiver.expr_data { ExprFieldAccess { summary: _ } => @@ -181,10 +171,6 @@ fn expand_transitive_services(modules: List, registry: Map>, source_indices: Map) -> Node? { if base_type.connective == NoConnective && base_type.children |> count == 0 { let path = concat(authored_name_at(source_indices: source_indices, node: base_type), ".", field) diff --git a/src/v1/04_sigs.dag b/src/v1/04_sigs.dag index 57ddfdffbfb..e92b595c069 100644 --- a/src/v1/04_sigs.dag +++ b/src/v1/04_sigs.dag @@ -1,5 +1,3 @@ -// SCC-aware function signature resolution. - module v1.compiler.infer_sigs import v1.std.core { @@ -14,10 +12,6 @@ import v1.std.core { import v1.compiler.infer_types { emit_map_has } import std.induction { SubValueRelation } -// ========================================================================= -// Types -// ========================================================================= - type ResolvedFuncSig { name: String params: List @@ -46,10 +40,6 @@ type CallEdge { callee: String } -// ========================================================================= -// Call graph construction -// ========================================================================= - fn collect_func_call_edges(items: List, local_func_set: Map, source_indices: Map) -> List { flat_map(items, item => if item.params |> count > 0 && item.body != none { @@ -87,10 +77,6 @@ fn func_reaches_self(root: String, current: String, call_edges: List, } } -// ========================================================================= -// Signature resolution -// ========================================================================= - fn build_name_set(names: List) -> Map { fold(names, init: empty_map(), f: (acc, name) => map_insert(acc, name, true)) } diff --git a/src/v1/04_types.dag b/src/v1/04_types.dag index 5a97194ce79..bc733ce56a3 100644 --- a/src/v1/04_types.dag +++ b/src/v1/04_types.dag @@ -1,5 +1,3 @@ -// v2 type predicates, constructors, and comparisons. - module v1.compiler.infer_types import std.types { SourceSpan, is_container_type, container_expected_arity, container_param_name, container_template_algebra, container_template_alias_algebra } @@ -46,15 +44,6 @@ fn type_variable_node(id: String) -> Node { Node { name: "", span: make_span(start: 0, end: 0), ident_span: none, children: [], connective: NoConnective, params: [], inferred: Present { value: TypeVariable { id: id } }, return_cardinality: Required, uses: [], body: none, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: NoExprData } } -// ========================================================================= -// Node constructors -// ========================================================================= - -// Extract the resolved type Node from n.inferred. Fail-closed: returns -// error_type for any non-Resolved state. Use this at declaration and -// post-inference sites where Resolved is the expected invariant. -// Dissolution target: consumers that need variant-specific behavior -// should match InferredNode? directly instead of calling this. fn resolved_type(n: Node) -> Node { match n.inferred { Present { value: Resolved { node: rt } } => rt @@ -73,15 +62,6 @@ fn child_type_at(n: Node, index: Int) -> Node? { } } -// ========================================================================= -// Structural collection predicates (L1 dissolution) -// -// After resolution, only container types retain children without connective. -// Products have Conj, coproducts have Disj, callables use the params field, -// tuples have Conj. These predicates derive collection-ness from node shape -// alone — no name checks, no CollectionKind enum. Pass scrambled-name test. -// ========================================================================= - fn node_is_collection(n: Node, source_indices: Map) -> Bool { (n.children |> count) > 0 && n.connective == NoConnective && is_declared_container_alias_spelling(name: authored_name_at(source_indices: source_indices, node: n)) } @@ -98,14 +78,6 @@ fn node_is_set_collection(n: Node, source_indices: Map) -> node_is_element_collection(n: n, source_indices: source_indices) && authored_name_at(source_indices: source_indices, node: n) == "Set" } -// Canonical sequence/container algebra for a type node's template name, via the -// single modeled authority (dsl/std/types.dag container_template_algebra: -// List->FreeMonoid, Set->BooleanAlgebra, Map->PartialFunction; algebra names map -// to themselves). Used to compare aliased spellings transparently — e.g. -// List (Container) and FreeMonoid (Node) are the SAME declared type -// (collection.dag: type List = FreeMonoid), but resolve keeps List -// unexpanded as a container, so they must be reconciled at COMPARE time without -// expanding the container (preserving the unexpanded-container invariant). fn canonical_template_name(n: Node, source_indices: Map) -> String { let nm = authored_name_at(source_indices: source_indices, node: n) match container_template_algebra(name: nm) { @@ -121,8 +93,6 @@ fn is_declared_container_alias_spelling(name: String) -> Bool { } } -// Carrier template after identity-preserving alias peel: authored_name_at reads -// the brand (UserId) while n.name holds the structural head (Refined). fn structural_carrier_template_name(n: Node, source_indices: Map) -> String { if n.name != "" { canonical_template_name(n: Node { @@ -138,10 +108,6 @@ fn structural_carrier_template_name(n: Node, source_indices: Map Bool { n.connective == Conj } fn is_coproduct_type(n: Node) -> Bool { n.connective == Disj } fn is_leaf_type(n: Node) -> Bool { n.connective == NoConnective } @@ -189,17 +155,6 @@ fn bare_set_node() -> Node? { } } -// ========================================================================= -// Shared type construction authorities -// -// Single-authority builders for synthesized type nodes. All container param -// names derive from algebra via container_param_name (fail-closed on miss). -// These centralize type-shape knowledge that was previously inlined as -// raw Node literals across multiple files. -// -// Dissolution point: Tier 3 / FF-9 (declaration-driven construction). -// ========================================================================= - fn kernel_container_profile_miss_diagnostic(kind_name: String) -> ErrorNode { let msg = concat("missing kernel container profile: ", kind_name) make_error_node( @@ -208,10 +163,6 @@ fn kernel_container_profile_miss_diagnostic(kind_name: String) -> ErrorNode { ) } -// Type-position sentinel when `kernel_algebra_profile` has no entry for -// `kind_name`. Paired with `kernel_container_profile_miss_diagnostic` wherever -// a diagnostic list is threaded; expr_data uses InternalExprError (compiler -// invariant), not user-facing SemanticExprError. fn missing_kernel_container_profile_type(kind_name: String) -> Node { let msg = concat("missing kernel container profile: ", kind_name) Node { @@ -289,27 +240,6 @@ fn make_tuple_type(first: Node, second: Node) -> Node { ], connective: Conj, params: [], inferred: none, return_cardinality: Required, uses: [], body: none, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: NoExprData } } -// P3.7: arity bridge deleted. type_node_arity_ok removed — arity is now -// checked via declaration params in the type environment, not hardcoded names. - -// ========================================================================= -// P5.13B: Algebraic field enrichment for kernel types -// -// These helpers build child Nodes representing algebraic structure fields -// (add, mul, concat, etc.) on kernel types. Called once during -// build_type_env kernel seed to produce Conj product types with structural -// fields, enabling Tier 0 method lookup to resolve methods from type -// structure instead of string matching. -// -// The return types use bare nominal references — inference resolves -// names via the type environment, so circular references are avoided. -// -// BS-2: Algebra profile data table. The mapping from kernel type name -// to algebraic structure is DATA (kernel_algebra_profile), not code. -// Per-profile builder functions produce the field lists; enrich_kernel_type -// does a single lookup and dispatches to the appropriate builder. -// ========================================================================= - fn algebra_value_field(name: String, type_node: Node) -> Node { Node { name: name, span: kernel_span(name: name), ident_span: Present { value: kernel_span(name: name) }, children: [], connective: NoConnective, params: [], inferred: Present { value: Resolved { node: type_node } }, return_cardinality: Required, uses: [], body: none, transport: none, properties: [], type_annotation: none, is_self_recursive: false, has_non_tail_self_call: false, match_pattern: none, expr_data: NoExprData } } @@ -403,10 +333,6 @@ fn enrich_kernel_type(name: String, base: Node, source_indices: Map, source_indices: Map) -> Map { match template { AlgebraTypeVariable { id: var_id } => @@ -643,11 +569,6 @@ fn callable_inferred(n: Node) -> Node { } else { n } } - -// ========================================================================= -// Normalization -// ========================================================================= - fn normalize_access_type_node(n: Node) -> Node { let has_structure = n.connective != NoConnective let unwrapped = if n.type_annotation != none && has_structure { @@ -659,11 +580,6 @@ fn normalize_access_type_node(n: Node) -> Node { } } - -// ========================================================================= -// Type shape and comparison -// ========================================================================= - fn node_type_shape(n: Node, source_indices: Map) -> String { let __is_leaf = n.connective == NoConnective && n.children |> count == 0 && n.properties |> count == 0 let n_name = authored_name_at(source_indices: source_indices, node: n) @@ -749,12 +665,7 @@ fn node_type_compatible(left: Node, right: Node, source_indices: Map count == 1)) && (right.children |> count == 1) && (is_declared_container_alias_spelling(name: authored_name_at(source_indices: source_indices, node: left)) || is_declared_container_alias_spelling(name: authored_name_at(source_indices: source_indices, node: right))) { - // Aliased sequence spellings — e.g. List (Container) vs FreeMonoid - // (Node), which collection.dag declares the SAME type (List = FreeMonoid). - // They canonicalize to the same template but carry different surface - // names. Compare element types across the two reps. Fires - // ONLY when the names DIFFER but canonicalize equal, so same-named and - // genuinely-distinct types are untouched (minimal blast radius). + match left.children |> first { Present { value: left_ch } => match right.children |> first { @@ -934,10 +845,6 @@ fn node_type_equals_core(left: Node, right: Node, source_indices: Map) -> List { let n_is_type_var = if n.inferred != none { is_type_variable(inferred: n.inferred.value) } else { false } if n_is_type_var { return [] } @@ -976,10 +883,6 @@ fn node_type_deps(n: Node, source_indices: Map) -> List Node { match lit { LitStr { value: _ } => string_type @@ -987,15 +890,11 @@ fn infer_literal_node(lit: LiteralValue) -> Node { LitFloat { value: _ } => float_type LitBool { value: _ } => bool_type LitNull => with_optional_cardinality(n: unit_type) - // Symbol is operationally String at runtime; `^IDENT` is an opaque spelling literal. + LitSymbol { value: _ } => string_type } } -// ========================================================================= -// Element and unwrap utilities -// ========================================================================= - fn method_receiver_element_node(receiver_type: Node, source_indices: Map) -> Node { let normed = normalize_access_type_node(n: receiver_type) let maybe_element = if node_is_keyed_collection(n: normed, source_indices: source_indices) { @@ -1022,9 +921,7 @@ fn extract_optional_inner_node(n: Node) -> Node { if is_optional { with_required_cardinality(n: n) } else if n.name == "Optional" && n.children |> count == 1 { - // Applied generic Optional: inner is the type argument when the coproduct - // Disj structure is not carried on the instantiated node (substitute_generics - // rebuild). Present/Absent synthesis still needs T for the value field. + match n.children |> first { Present { value: inner } => inner Absent => n @@ -1032,10 +929,6 @@ fn extract_optional_inner_node(n: Node) -> Node { } else { n } } -// Convert AlgebraFieldKind to the field name string for child lookup on types. -// Authority: algebra_field_entries in std/syntax.dag — this match must stay -// in sync. NOTE: child lookup still goes through find_child_named (string). -// The full structural fix requires typed child identifiers on algebra Nodes. fn algebra_field_kind_name(kind: AlgebraFieldKind) -> String { match kind { AlgAdd => "add" diff --git a/src/v1/05_emit.dag b/src/v1/05_emit.dag index af6e4d28c3a..29c6c7aee96 100644 --- a/src/v1/05_emit.dag +++ b/src/v1/05_emit.dag @@ -1,20 +1,3 @@ -// v2 emitter core -- target-agnostic dispatch and shared helpers. -// -// No I/O -- file writing is handled by the pipeline. -// This module provides: -// - The `emit` entry point that dispatches to per-target renderers -// - Scope management helpers (module_emit_scope, empty_emit_scope) -// - Item registry lookup (lookup_item, lookup_func_sig_in_scope) -// - Service call detection (re-exported from reconcile) -// - Name conversion utilities (to_snake, to_screaming_snake, etc.) -// - Indentation and string formatting utilities -// -// Target-specific rendering lives in 05_emit_rust.dag, 05_emit_python.dag. -// -// Invariant 6: "DAG nodes are facts, rendering is separate." -// This module contains no target-specific syntax. Adding a new target -// means creating a new renderer module and adding one match arm to `emit`. - module v1.compiler.emit import v1.std.core { @@ -117,16 +100,9 @@ import v1.compiler.languages { service_self_param, service_receiver_str, service_method_depth, service_methods_inside_class, service_return_str } -// Single authority for Rust algebra-method emit templates: the -// SimpleMethodSpec rows in extdeps own template + wraps_result together -// (derived map below). The former local `rust_method_template_emit` fork -// is dissolved into this import. + import extdeps.languages.rust.emit { rust_method_templates } -// Python/Go draw the same way from their own SimpleMethodSpec authority: the -// flat (`{recv}`/`{arg}`) templatable methods. map/fold/concat are absent from -// these derived maps and route through the runtime bridge (it forwards every -// arg — required for binary+ `fold`). The former local `python_method_template_emit` -// / `go_method_template_emit` forks are dissolved into these imports. + import extdeps.languages.python.emit { python_method_templates_flat } import extdeps.languages.go.emit { go_method_templates_flat } @@ -154,11 +130,6 @@ type TcoReassignInput { depth: Int } - -// ========================================================================= -// Backend capability contract -- what each backend declares it can handle. -// ========================================================================= - type BackendCapability = CapServiceEmit | CapAsyncTransport @@ -171,10 +142,6 @@ type BackendInfo { capabilities: List } -// ========================================================================= -// Import derivation -- pre-scan typed items to determine required imports. -// ========================================================================= - fn derive_module_imports(items: List, import_rules: List, target: RenderTarget, source_indices: Map) -> List { let has_async = items |> any(item => item.uses |> count > 0) let type_names = collect_type_names_from_items(items: items, source_indices: source_indices) @@ -221,13 +188,7 @@ fn emit_simple_expr(expr: Node, target: RenderTarget, source_indices: Map concat(emit_simple_expr(expr: b, target: target, source_indices: source_indices), ".", emit_ident(name: f, target: target)) } } else { - // Data declarations emit as functions in Rust; field access on a - // data reference needs base().field, not base.field. - // In transport contexts (the primary consumer of emit_simple_expr), - // there are no local let-bindings, so LocalValueBinding is a data - // reference. FunctionValueBinding and None (uninferred) are also - // module-level references. Only MatchBoundBinding and - // VariantValueBinding should NOT get (). + let base_is_fn = match b.expr_data { ExprVar { binding_kind: bk } => match bk { Present { value: MatchBoundBinding } => false @@ -238,8 +199,7 @@ fn emit_simple_expr(expr: Node, target: RenderTarget, source_indices: Map and - // field access on Rc needs .clone() for non-Copy types. + let field_str = concat(base_call, ".", emit_ident(name: f, target: target)) let needs_clone = base_is_fn && match target { Rust => true _ => false } if needs_clone { concat(field_str, ".clone()") } else { field_str } @@ -306,10 +266,6 @@ fn emit_simple_string_interp(parts: List, target: RenderTarget, sour } } -// ========================================================================= -// Scope helpers -// ========================================================================= - fn empty_emit_scope() -> InferScope { InferScope { type_env: TypeEnv { bindings: empty_map(), recursive_types: [], recursive_type_set: empty_map(), inductive_fields: empty_map(), source_indices: empty_map(), intern_table: empty_intern_table() }, @@ -336,8 +292,6 @@ fn module_emit_scope(typed_module: TypedModule) -> InferScope { } } -// scope_after_expr: extend scope with let-binding type for bodyless lets. -// After Expr->Node dissolution, resolved type is on Node.inferred. fn scope_after_expr(texpr: Node, scope: InferScope) -> InferScope { match texpr.expr_data { ExprLet => @@ -405,10 +359,6 @@ fn order_typed_call_args(args: List, func: String, scope: InferScope) -> L } } -// ========================================================================= -// Nested record detection -- for data definition edge cases -// ========================================================================= - fn has_nested_records_node(n: Node, source_indices: Map) -> Bool { let is_product = n.connective == Conj let is_coproduct = n.connective == Disj @@ -437,10 +387,6 @@ fn has_nested_records_node(n: Node, source_indices: Map) - } } -// ========================================================================= -// JSON emission for data values -- used with serde_json::json!() macro -// ========================================================================= - fn emit_data_value_json(value: Node, source_indices: Map) -> String { match value.expr_data { ExprLiteral { value: v } => match v { @@ -467,11 +413,6 @@ fn emit_data_value_json(value: Node, source_indices: Map) } } - -// ========================================================================= -// Identifier conversion utilities -// ========================================================================= - fn to_camel(name: String) -> String { let parts = name |> split(delimiter: "_") if parts |> count == 0 { name } @@ -510,10 +451,6 @@ fn test_file_path(module_name: String, target: RenderTarget) -> String { concat(file_dir, conventions.file_prefix, filename, conventions.file_suffix, language_spec(target: target).scaffold.source_file_extension) } -// ========================================================================= -// Template application helpers -- for language spec template strings. -// ========================================================================= - fn reserved_prefix(target: RenderTarget) -> String { match language_spec(target: target).reserved_words.strategy { PrefixEscape { prefix: prefix } => prefix @@ -528,15 +465,6 @@ fn reserved_suffix(target: RenderTarget) -> String { } } -// ========================================================================= -// Language data -- inline copies of per-language facts. -// ========================================================================= - - -// ========================================================================= -// Shared leaf functions -- parameterized by language data -// ========================================================================= - fn escape_rust_interp_text(s: String) -> String { let escaped = escape_string_literal_body(s: s) |> split(delimiter: "{") |> join(separator: "{{") @@ -571,10 +499,6 @@ fn is_null_coalesce(op: BinOp) -> Bool { } } -// ========================================================================= -// Per-language functions -- centralized data, inline lookup. -// ========================================================================= - fn rust_literal_for_pattern(value: LiteralValue) -> String { match value { LitStr { value: s } => emit_string_literal(s: s, suffix: "") @@ -586,14 +510,6 @@ fn rust_literal_for_pattern(value: LiteralValue) -> String { } } -// ========================================================================= -// P4.1: Target-dispatched rendering — unified by RenderTarget -// -// These functions replace per-target triples (rust_X, go_X, python_X) -// with a single function dispatched by RenderTarget. Backends should -// migrate to calling these instead of the per-target versions. -// ========================================================================= - fn emit_keyword(key: String, target: RenderTarget) -> String { target_keyword(target: target, key: key) } @@ -601,9 +517,7 @@ fn emit_keyword(key: String, target: RenderTarget) -> String { fn emit_literal(value: LiteralValue, target: RenderTarget) -> String { match value { LitStr { value: s } => - // String literal suffix from TypeCheckpoint (e.g. ".to_string()" for Rust). - // Fail-closed: missing TypeCheckpoint emits error marker. - // All targets (including Dag) must declare String in their TypeCheckpoints. + let suffix = match literal_suffix(target: target, dag_name: "String") { Present { value: sfx } => sfx Absent => emit_error_expr(message: "missing TypeCheckpoint for String literal suffix", target: target) @@ -652,10 +566,6 @@ fn emit_map_type(key_type: String, val_type: String, target: RenderTarget) -> St } } -// ========================================================================= -// Type rendering: all targets. Routes through build_type_rendering + render_type. -// ========================================================================= - fn emit_node_type(n: Node, target: RenderTarget, source_indices: Map) -> String { render_node_type(n: n, target: target, shared_types: empty_set(), source_indices: source_indices) } @@ -689,10 +599,6 @@ fn render_named_type_base(n: Node, target: RenderTarget, source_indices: Map, source_indices: Map) -> String { let tn = authored_name_at(source_indices: source_indices, node: n) let n_is_error = if n.inferred != none { is_compiler_error(inferred: n.inferred.value) } else { false } @@ -722,8 +628,7 @@ fn render_node_type(n: Node, target: RenderTarget, shared_types: Set, so _ => language_spec(target: target).void_type } let spec = language_spec(target: target) - // Single authority: callable_type_template overrides CallableRepr entirely. - // When null, CallableRepr is the sole authority (including return_separator). + let result = match spec.callable_type_template { Present { value: t } => replace(replace(t, "\{params\}", param_str), "\{return\}", ret_str) @@ -894,20 +799,12 @@ fn render_tuple_parts(parts: List, target: RenderTarget) -> String { } } -// ========================================================================= -// Shared item-level helpers -- identical across all backends -// ========================================================================= - fn has_service_items(typed: ResolvedGraph) -> Bool { typed.modules |> any(tm => tm.items |> any(item => is_service_item(item: item)) ) } -// ========================================================================= -// Shared service/transport graph queries -- target-agnostic facts about -// ========================================================================= - fn service_fallback_transport(item: Node) -> Node { if item.transport == none { local_transport_node(span: item.span) } else { item.transport.value } } @@ -982,7 +879,6 @@ fn compute_service_fields(fallback_transport: Node, op_children: List, sou } } - fn service_field_decls(fs: ServiceFieldSet, t: ServiceFieldTemplates) -> List { let result = [] let result = if fs.has_rest { concat(result, [t.rest_decl]) } else { result } @@ -1001,12 +897,6 @@ fn service_field_ctors(fs: ServiceFieldSet, t: ServiceFieldTemplates) -> List) -> List { else { stmts |> take(n: (stmts |> count) - 1) } } -// ========================================================================= -// Shared TCO eligibility check -- identical across all backends. -// ========================================================================= - fn is_tco_eligible(name: String, body: Node, registry: Map, source_indices: Map) -> Bool { match lookup_item(registry: registry, name: name) { Present { value: info } => info.is_self_recursive && info.has_non_tail_self_call == false @@ -1209,10 +1091,6 @@ fn is_self_recursive(name: String, body: Node, registry: Map, } } -// ========================================================================= -// Shared TCO reassignment core -- build temp-then-assign lines. -// ========================================================================= - fn tco_reassign_core( ordered_args: List, param_names: List, @@ -1274,10 +1152,6 @@ fn emit_shared_tco_expr( } } -// ========================================================================= -// Shared TCO handlers — consume BlockSyntax + TcoSyntax + callbacks. -// ========================================================================= - fn shared_tco_body(inner: String, depth: Int, spec: LanguageSpec) -> String { let syntax = spec.block_syntax let tco = spec.tco @@ -1424,10 +1298,6 @@ fn shared_tco_reassign( all_lines |> join(separator: "\n") } -// ========================================================================= -// Unified TCO entry points — Python and Go call these directly. -// ========================================================================= - fn unified_tco_recurse(expr: Node, scope: InferScope, depth: Int, fn_name: String, params: List, target: RenderTarget, registry: Map, recurse_expr: fn(Node, InferScope, Int) -> String, @@ -1505,13 +1375,6 @@ fn emit_unified_tco_body( shared_tco_body(inner: inner, depth: depth, spec: spec) } -// ========================================================================= -// Phase 3: Fully unified TCO + block-stmt entry points. -// -// These replace per-language wrappers in emit_python.dag / emit_go.dag. -// Only callback remaining: render_pattern (per-language pattern rendering). -// ========================================================================= - fn emit_unified_init_block_stmts( remaining: List, text: List, scope: InferScope, depth: Int, @@ -1685,10 +1548,6 @@ fn is_tco_candidate(texpr: Node, func_name: String, source_indices: Map) -> Bool { let suffix_len = string_length(s: suffix) let name_len = string_length(s: name) @@ -1778,17 +1637,10 @@ fn emit_let_binding_annotated(name: String, type_str: String, value: String, tar apply_type_template3(template: spec.annotations.let_binding_annotated, arg0: emit_ident(name: name, target: target), arg1: type_str, arg2: value) } - fn emit_return(value: String, target: RenderTarget) -> String { concat("return ", value) } -// ========================================================================= -// P4.2: Shared per-variant expression renderers. -// Pattern: backend dispatches ExprData, recurses on subexpressions, -// passes pre-rendered strings to shared renderer for target syntax. -// ========================================================================= - fn emit_unary_op(op: UnaryOpKind, operand_str: String, target: RenderTarget) -> String { match op { Not => concat(emit_keyword(key: "not", target: target), operand_str) @@ -1796,7 +1648,6 @@ fn emit_unary_op(op: UnaryOpKind, operand_str: String, target: RenderTarget) -> } } -// Only ExprMatch is statement-shaped in Go lambdas (per-arm `return` via emit_match_arm_body_stmt). fn go_lambda_emits_statement_body(body: Node) -> Bool { match body.expr_data { ExprMatch => true @@ -1838,11 +1689,6 @@ fn emit_null_coalesce(l_str: String, r_str: String, target: RenderTarget) -> Str apply_type_template2(template: spec.null_coalesce_template, arg0: l_str, arg1: r_str) } - -// ========================================================================= -// Shared expression handler helpers (CG-3: 3→1 homomorphism) -// ========================================================================= - fn emit_expr_var_shared(expr: Node, target: RenderTarget, source_indices: Map) -> String { let n = expr_var_name_at(texpr: expr, source_indices: source_indices) if n == "none" { emit_keyword(key: "null", target: target) } @@ -1868,14 +1714,6 @@ fn extract_string_interp_parts(expr: Node) -> List { }) } -// Shared ExprCast — data-driven, target-parameterized. -// Cast syntax derives from CastSyntax in extdeps/languages/*/types.dag. -// Rust: `expr as type`, Python/Go: `type(expr)`. -// -// Numeric casts are validated early in infer (dag_can_cast). Emit retains -// the per-target can_cast guard as a fail-closed safety net for casts -// outside the numeric domain (String, user types, etc.) until structural -// cast modeling replaces the string-keyed tables. fn emit_typed_cast_shared(expr: Node, cast_target_node: Node, target: RenderTarget, recurse: fn(Node) -> String, source_indices: Map) -> String { let expr_str = recurse(expr) let ty_str = emit_node_type(n: cast_target_node, target: target, source_indices: source_indices) @@ -1944,15 +1782,6 @@ fn emit_typed_slice_shared(base: Node, start: Node, end: Node, target: RenderTar } } -// ========================================================================= -// emit_shared_expr — full shared ExprData dispatcher (P4.2 step 5) -// -// The shared layer owns the whole-tree `ExprData` match. Backends provide: -// - `wrap_result` for target-specific result framing (Go indentation) -// - `recurse` for recursive sub-expression emission -// - per-variant hooks for the target-specific lowering cases -// ========================================================================= - fn emit_shared_expr( texpr: Node, target: RenderTarget, @@ -2048,10 +1877,6 @@ fn emit_default_bin_op(texpr: Node, target: RenderTarget, source_indices: Map, text: List, scope: InferScope, depth: Int, @@ -2188,12 +2013,6 @@ fn emit_typed_block_join( state.text |> join(separator: "\n") } -// Per-target algebra method emit templates — \{recv\} = receiver, \{arg\} = first argument. -// All three targets draw from their extdeps SimpleMethodSpec authority (the -// `*_method_templates*()` derived maps imported above). map/fold/concat are -// intentionally absent from every target's flat map — they are binary+ and -// route through the runtime bridge in emit_algebra_method_call_unified, which -// forwards every argument. fn method_template_emit_for_target(target: RenderTarget) -> Map? { match target { Rust => Present { value: rust_method_templates() } @@ -2216,7 +2035,6 @@ fn emit_algebra_method_template(method_name: String, recv_str: String, } } -// Shared typed first arg — renders first method argument or fails closed. fn emit_typed_first_arg_shared(args: List, target: RenderTarget, recurse: fn(Node) -> String) -> String { match args |> first { Present { value: a } => recurse(arg_value(n: a)) @@ -2224,15 +2042,6 @@ fn emit_typed_first_arg_shared(args: List, target: RenderTarget, recurse: } } -// ========================================================================= -// Unified typed expression handlers (Phase 2) -// -// These replace per-language typed_* functions. Each reads LanguageSpec -// data instead of branching on target. The `recurse` callback is the -// caller's expression entry point (emit_py_typed_expr / emit_go_typed_expr -// during transition; emit_unified_typed_expr once fully unified). -// ========================================================================= - fn emit_typed_string_interp_unified(parts: List, target: RenderTarget, recurse: fn(Node) -> String) -> String { let spec = language_spec(target: target) let interp = spec.string_interp @@ -2397,14 +2206,6 @@ fn emit_typed_method_call_unified(receiver: Node, method: String, args: List) -> String { match pattern { Bind { name: n } => emit_ident(name: n, target: target) @@ -2448,8 +2249,6 @@ fn emit_unified_variant_pattern(name: String, parent_enum: String?, field_bindin } } -// Match arm bodies are expression-shaped in the DAG; prepend return only when the target's -// match_value_form requires statement arms (Python match / Go switch), not expression arms (Rust). fn emit_match_arm_body_stmt(target: RenderTarget, body_str: String) -> String { match language_spec(target: target).expression_semantics.match_value_form { MatchStatementArmReturn => concat("return ", body_str) @@ -2457,11 +2256,6 @@ fn emit_match_arm_body_stmt(target: RenderTarget, body_str: String) -> String { } } - -// Shared match-arm line rendering — single authority for case keyword, guard, indent. -// Both emit_typed_match_unified and emit_unified_tco_match_arm delegate here. -// guard_prefix comes from ExpressionSemantics; fails closed if guard is present -// but target has no guard form. fn emit_match_arm_line( arm: Node, target: RenderTarget, case_depth: Int, body_depth: Int, body_str: String, guard_str: String, @@ -2481,8 +2275,6 @@ fn emit_match_arm_line( make_indent(level: body_depth), body_str) } -// Render guard expression from arm. Reads guard_prefix from ExpressionSemantics. -// Fails closed if a guard is present but the target has no guard syntax. fn emit_arm_guard(arm: Node, target: RenderTarget, render_guard_expr: fn(Node) -> String) -> String { let es = language_spec(target: target).expression_semantics match arm_guard(n: arm) { @@ -2551,17 +2343,6 @@ fn emit_field_access_unified(base_str: String, field: String, summary: FieldSumm } } -// ========================================================================= -// Unified typed expression entry point (Phase 2) -// -// Single dispatcher for Python and Go expression rendering. Reads all -// language facts from LanguageSpec. Takes one callback: render_pattern -// (per-language pattern rendering, not yet unified — Phase 5). -// -// Rust uses its own entry point (05_emit_rust.dag) due to ownership -// logic. When Phase 6 unifies Rust, it can call this function. -// ========================================================================= - fn emit_unified_typed_expr(texpr: Node, target: RenderTarget, registry: Map, scope: InferScope, depth: Int, fuel: Int, render_pattern: fn(MatchPattern) -> String) -> String { let si = scope.type_env.source_indices let spec = language_spec(target: target) @@ -2675,8 +2456,6 @@ fn emit_typed_tco_reassign_shared(args: List, params: List, shared_tco_reassign(ordered_args: ordered_args, param_names: param_names, spec: language_spec(target: target)) } -// Shared typed seed for template binding maps. Used by all backend emitters -// to create initial Map bindings without BRIDGE fabrication. fn seed_bindings(key: String, value: String) -> Map { map_insert(empty_map(), key, value) } diff --git a/src/v1/05_emit_core_support.dag b/src/v1/05_emit_core_support.dag index 88144caf1c9..6b89749ab61 100644 --- a/src/v1/05_emit_core_support.dag +++ b/src/v1/05_emit_core_support.dag @@ -1,9 +1,3 @@ -// v2 emit core support -- target-independent backend helper surface. -// -// This module owns leaf helpers used by multiple emit backends. It is the -// .dag-authored successor to the temporary Rust-only -// `v1_compiler_emit_core_support` facade. - module v1.compiler.emit_core_support import v1.std.core { @@ -20,10 +14,6 @@ import v1.compiler.languages { language_spec_for_target, test_conventions_for_target } -// ========================================================================= -// Backend result/test carriers shared by all emit backends. -// ========================================================================= - type EmitResult { files: List diagnostics: List diff --git a/src/v1/05_emit_go.dag b/src/v1/05_emit_go.dag index 179c1df82b5..670489026fc 100644 --- a/src/v1/05_emit_go.dag +++ b/src/v1/05_emit_go.dag @@ -1,24 +1,3 @@ -// v2 Go renderer -- emits Go source files from a typed module graph. -// -// This module contains all Go-specific rendering logic: struct types, -// interface satisfaction (structural typing), multi-return error handling, -// package-level organization, fmt.Errorf wrapping, goroutine-based -// concurrency, Go-idiomatic naming (PascalCase exports, camelCase locals). -// -// Target-agnostic helpers (scope management, item registry, name -// conversion, indent, service call collection) live in 05_emit.dag. -// -// Invariant 6: "DAG nodes are facts, rendering is separate." -// This file IS the Go rendering. No IR changes needed. -// -// Go-specific semantics modeled here: -// - No Result type: errors are second return values -// - Structural typing: interfaces are satisfied implicitly -// - Package-level organization: one package per module -// - No exceptions: multi-return (value, error) pattern -// - Pointer receivers for mutating methods -// - No sum types: tagged unions use interface + type switch - module v1.compiler.emit_go import v1.std.core { @@ -136,21 +115,13 @@ import v1.compiler.emit_core_support { is_function_item, is_data_def_item, is_service_def_item, is_resource_def_item, extract_test_projections } -// ========================================================================= -// Go language facts -- from std.languages spec (go_language, go_literals) -// ========================================================================= -// Root module path for emitted go.mod and all generated/... import paths. fn go_emit_module_root() -> String { "generated" } fn go_v2rt_import_path() -> String { concat(go_emit_module_root(), "/v2rt") } -// ========================================================================= -// Entry point -- Go target -// ========================================================================= - fn emit_go(typed: ResolvedGraph) -> EmitResult { let registry = typed.item_registry let test_projections = extract_test_projections(typed: typed) @@ -173,9 +144,6 @@ fn emit_go_v2rt_module() -> TextFile { } } -// ========================================================================= -// ========================================================================= - fn emit_go_mod(module_name: String) -> TextFile { let manifest_path = match scaffold_for_target(target: Go).manifest_file { Present { value: path } => path @@ -256,10 +224,6 @@ fn emit_go_mock_prop_setup(mock_prop: Node, depth: Int, source_indices: Map) -> TextFile { let m = typed_module.module let scope = module_emit_scope(typed_module: typed_module) @@ -292,10 +256,6 @@ fn go_package_name(module_name: String) -> String { } } -// ========================================================================= -// Import emission -// ========================================================================= - fn emit_go_imports(items: List, imports: List, source_indices: Map) -> String { let has_services = items |> any(item => is_service_item(item: item)) let has_types = items |> any(item => is_type_def_item(item: item)) @@ -331,10 +291,6 @@ fn collect_go_std_imports(has_services: Bool, has_types: Bool, has_functions: Bo concat(fmt_import, rt_import, net_imports) } -// ========================================================================= -// Item dispatch -// ========================================================================= - fn emit_go_typed_item(item: Node, registry: Map, scope: InferScope) -> String { let env = scope.type_env let item_text = authored_name(env: env, node: item) @@ -361,10 +317,6 @@ fn emit_go_typed_item(item: Node, registry: Map, scope: InferS } } -// ========================================================================= -// Type definitions -// ========================================================================= - fn emit_go_type_def_from_connective(item: Node, env: TypeEnv) -> String { let item_text = authored_name(env: env, node: item) let is_product = item.connective == Conj @@ -375,7 +327,6 @@ fn emit_go_type_def_from_connective(item: Node, env: TypeEnv) -> String { } } - fn emit_go_struct_from_children(name: String, children: List, env: TypeEnv) -> String { if children |> count == 0 { concat(language_spec(target: Go).items.type_alias_keyword, " ", name, " ", language_spec(target: Go).items.struct_keyword, "{}") @@ -432,15 +383,6 @@ fn emit_go_type_alias(name: String, base: Node, source_indices: Map String { let item_text = authored_name(env: env, node: item) - // After dissolution, capability children are identified by having params + let cap_children = item.children |> filter(c => c.params |> count > 0) let methods = cap_children |> map(c => emit_go_capability_method(cap_node: c, depth: 1, env: env)) let methods_str = methods |> join(separator: "\n") @@ -624,32 +554,19 @@ fn emit_go_capability_method(cap_node: Node, depth: Int, env: TypeEnv) -> String concat(emit_ident(name: param_node_name_at(n: p, source_indices: env.source_indices), target: Go), " ", emit_node_type(n: param_node_type_expr(n: p), target: Go, source_indices: env.source_indices)) ) let params_str = input_params |> join(separator: ", ") - // After dissolution, inferred on the child node IS the output type + let ret = emit_node_type(n: resolved_type(n: cap_node), target: Go, source_indices: env.source_indices) concat(make_indent(level: depth), go_export_ident(name: authored_name(env: env, node: cap_node)), "(", params_str, ") (", ret, ", error)") } -// ========================================================================= -// Data definition emission -// ========================================================================= - fn emit_go_data_def(name: String, type_node: Node, value: Node, registry: Map, scope: InferScope, depth: Int) -> String { let ty_str = emit_node_type(n: type_node, target: Go, source_indices: scope.type_env.source_indices) let val_str = emit_go_typed_expr(texpr: value, registry: registry, scope: scope, depth: depth, fuel: 1024) concat("var ", go_export_ident(name: name), " ", ty_str, " = ", val_str) } -// ========================================================================= -// Identifier conversion -- Go target -// ========================================================================= - fn go_export_ident(name: String) -> String { - // Go uses case-based visibility: PascalCase = exported. - // Ref: Go Spec "Exported identifiers". - // Matches on VisibilitySpec coproduct — CaseVisibility carries - // the export naming case, applied via shared apply_naming_case. - // M5: Go requires CaseVisibility. Any other variant is a data bug. - // Fail closed with error marker instead of silently dropping export semantics. + match language_spec(target: Go).visibility { CaseVisibility { export_case: ec } => { let result = apply_naming_case(name: name, case_style: ec) @@ -662,8 +579,3 @@ fn go_export_ident(name: String) -> String { _ => "__GO_VISIBILITY_SPEC_MISMATCH__" } } - - -// ========================================================================= -// Utility: check if a TypedGraph has service items -// ========================================================================= diff --git a/src/v1/05_emit_python.dag b/src/v1/05_emit_python.dag index a63710b3b0c..0869b53f68e 100644 --- a/src/v1/05_emit_python.dag +++ b/src/v1/05_emit_python.dag @@ -1,15 +1,3 @@ -// v2 Python renderer -- emits Python source files from a typed module graph. -// -// This module contains all Python-specific rendering logic: dataclasses, -// type hints, match/case (PEP 634), f-strings, aiohttp for services, -// Python-idiomatic naming (snake_case functions, PascalCase classes). -// -// Target-agnostic helpers (scope management, item registry, name -// conversion, indent, service call collection) live in 05_emit.dag. -// -// Invariant 6: "DAG nodes are facts, rendering is separate." -// This file IS the Python rendering. No IR changes needed. - module v1.compiler.emit_python import v1.std.core { @@ -126,16 +114,6 @@ import v1.compiler.emit_core_support { is_resource_def_item, extract_test_projections } -// ========================================================================= -// Python language facts -- from std.languages spec (python_language, python_literals) -// ========================================================================= - - - -// ========================================================================= -// Entry point -- Python target -// ========================================================================= - fn emit_python(typed: ResolvedGraph) -> EmitResult { let registry = typed.item_registry let test_projections = extract_test_projections(typed: typed) @@ -151,10 +129,6 @@ fn emit_python(typed: ResolvedGraph) -> EmitResult { EmitResult { files: files, diagnostics: [] } } -// ========================================================================= -// __init__.py generation -// ========================================================================= - fn py_derive_attribute() -> String { match serialization_for_target(target: Python).derive_attribute { Present { value: attr } => attr @@ -169,8 +143,6 @@ fn py_default_value() -> String { } } - - fn emit_init_py(modules: List) -> TextFile { let import_lines = modules |> map(tm => let mod_name = module_to_filename(name: authored_name_at(source_indices: tm.type_env.source_indices, node: tm.module)) @@ -236,10 +208,6 @@ fn emit_py_mock_prop_setup(mock_prop: Node, depth: Int, source_indices: Map) -> TextFile { let m = typed_module.module let scope = module_emit_scope(typed_module: typed_module) @@ -259,10 +227,6 @@ fn emit_py_module(typed_module: TypedModule, registry: Map) -> TextFile { path: concat(filename, scaffold_for_target(target: Python).source_file_extension), content: content } } -// ========================================================================= -// Import emission -// ========================================================================= - fn emit_py_imports(imports: List, source_indices: Map) -> String { if imports |> count == 0 { "" @@ -286,10 +250,6 @@ fn emit_py_imports(imports: List, source_indices: Map String { let items = typed_module.items let has_structs = items |> any(item => is_type_def_item(item: item) && item.connective == Conj) @@ -305,10 +265,6 @@ fn emit_py_prelude(typed_module: TypedModule) -> String { concat(future_import, dc_import, enum_import, typing_import, async_import) } -// ========================================================================= -// Item dispatch -// ========================================================================= - fn emit_py_typed_item(item: Node, registry: Map, scope: InferScope) -> String { let env = scope.type_env let item_text = authored_name(env: env, node: item) @@ -335,10 +291,6 @@ fn emit_py_typed_item(item: Node, registry: Map, scope: InferS } } -// ========================================================================= -// Connective-based type emission -- dispatches on Node children -// ========================================================================= - fn emit_py_type_def_from_connective(item: Node, env: TypeEnv) -> String { let item_text = authored_name(env: env, node: item) let is_product = item.connective == Conj @@ -349,7 +301,6 @@ fn emit_py_type_def_from_connective(item: Node, env: TypeEnv) -> String { } } - fn emit_py_dataclass_from_children(name: String, children: List, env: TypeEnv) -> String { if children |> count == 0 { concat(py_derive_attribute(), "\nclass ", name, ":\n pass") @@ -399,15 +350,6 @@ fn emit_py_variant_class_from_child(parent_name: String, child: Node, env: TypeE } } -// ========================================================================= -// Node-based type emission -// ========================================================================= - - -// ========================================================================= -// Pure function (fn) emission -// ========================================================================= - fn emit_py_fn_def(name: String, params: List, inferred: Node, body: Node, registry: Map, scope: InferScope) -> String { let depth = 0 let si = scope.type_env.source_indices @@ -430,10 +372,6 @@ fn emit_py_fn_def(name: String, params: List, inferred: Node, body: Node, } } -// ========================================================================= -// Workflow function (func) emission -- async def -// ========================================================================= - fn emit_py_func_def( name: String, params: List, @@ -478,10 +416,6 @@ fn emit_py_typed_expr(texpr: Node, registry: Map, scope: Infer render_pattern: pat => emit_unified_pattern(pattern: pat, target: Python, source_indices: scope.type_env.source_indices)) } -// ========================================================================= -// Service definition — Python -// ========================================================================= - fn emit_py_transport_body(transport: Node, op_name: String, source_indices: Map, depth: Int) -> String { emit_unified_transport_dispatch( transport: transport, op_name: op_name, source_indices: source_indices, depth: depth, target: Python, @@ -577,10 +511,6 @@ fn emit_py_local_call(op_name: String) -> String { "return ", emit_ident(name: op_name, target: Python), "()") } -// ========================================================================= -// Resource definition emission -// ========================================================================= - fn emit_py_resource_def(item: Node, env: TypeEnv) -> String { let item_text = authored_name(env: env, node: item) let depth = 0 @@ -608,10 +538,6 @@ fn emit_py_capability_method(cap_node: Node, env: TypeEnv) -> String { " ...") } -// ========================================================================= -// Data definition emission -// ========================================================================= - fn emit_py_data_def(name: String, type_node: Node, value: Node, registry: Map, scope: InferScope) -> String { let ty_str = emit_node_type(n: type_node, target: Python, source_indices: scope.type_env.source_indices) let upper_name = to_screaming_snake(name: name) @@ -619,14 +545,6 @@ fn emit_py_data_def(name: String, type_node: Node, value: Node, registry: Map Bool { string_length(s: type_name) >= 3 && substring(s: type_name, start: 0, end: 3) == "Rc<" } -// §3 faithful DAG String carrier for v2 Rust self-host (String = FreeMonoid, the -// declared std alias — std/string_type.dag: "String is the free monoid over Char", Char = -// Int where unicode_scalar, brand("Char")). The element is Char, NOT Nat: an earlier -// "Char = Nat" shortcut here silently reduced Char→Nat (an unauthorized emitter-side -// grounding), contradicting the declared alias and double-applying as FreeMonoid. -// SCAFFOLD (§3 second-representation of string_type.dag, time-bounded, low staleness — -// String=FreeMonoid is foundational/stable): the leaf base carries a Char element -// LITERAL because the leaf type-node has no resolved type-arg to read; the applied path is -// literal-free (bare FreeMonoid base + the resolved Char child drives the single application). -// DISSOLVE-ON: when resolve attaches the element (Char) to String LEAF nodes uniformly -// (resolve-side, not this PR), both the leaf literal AND the String special-case in -// rust_named_type_base/render_rust_text_carrier/rust_applied_type_base delete — String then -// expands as an ordinary closed alias through the applied path. -// §7: carrier is v2-self-host target only — v1 seed/bootstrap keeps host String. -// The corpus-representation choice is now a single corpus-global authority: -// `rust_corpus_repr` (04_infer.dag) computes RustCorpusRepr ONCE over all modules and -// threads it as `corpus_repr` to every renderer/emitter (EmitGraphInfo.corpus_repr). -// The old per-module gates (rust_corpus_includes_v1_compiler / rust_emit_faithful_text_carrier) -// were deleted: a per-module gate let a dsl module in a v1-bundled crate emit the faithful -// carrier while its siblings emit host — divergence was WRITABLE (a §5 dual-authority breach). -// §6-transitional: when src/v1 is deleted (seed→zero) no module has_seed, so corpus_repr -// resolves uniformly to FaithfulFreeMonoid — the pure-v2 faithful target; HostNative dissolves with the seed. -// Shape-reading predicates over the single corpus_repr authority (match, not `==`: -// a bare-variant `==` adjacent to a block `{` collides with record-literal syntax). fn corpus_repr_is_faithful(corpus_repr: RustCorpusRepr) -> Bool { match corpus_repr { FaithfulFreeMonoid => true HostNative => false } } @@ -244,8 +206,6 @@ fn render_rust_text_carrier(shared_types: Set) -> String { render_rust_shared_type_if_needed(type_name: "FreeMonoid", rendered: "FreeMonoid", shared_types: shared_types) } -// §2 — route ALL host→DAG String boundary bridges through HostStringText seam (one authority). -// §7: seam wrappers are v2-self-host only; v1 seed keeps host String (no v2_std_text module). fn emit_rust_host_to_dag_string_via_seam(host_expr: String, corpus_repr: RustCorpusRepr) -> String { if corpus_repr_is_faithful(corpus_repr: corpus_repr) { concat("crate::v2_std_text::host_string_text_from_rust_host(", host_expr, ")") @@ -289,17 +249,6 @@ fn rust_opaque_kernel_alias_carrier(name: String) -> String? { } } -// SEED host-numeric carrier: in the v1-compiler seed corpus (NOT the v2 faithful -// target), the algebraic numeric tower (Nat = CommutativeSemiring, -// Int = AbelianGroup>) maps to the host primitive i64 -- the -// numeric analogue of String -> host String in the seed branch. Gated by the SAME -// corpus-global `corpus_repr` (RustCorpusRepr) authority as the text carrier (single -// lever, no parallel mechanism). The v2-target faithful numeric representation is escalated to -// operator + non-blocking, so the faithful branch leaves the algebraic alias intact. -// The seed numeric ops (nat_compare/nat_max/nat_min) use /.clone(), all native to -// i64, so the alias-RHS override alone clears them and the Nat-as-number consumers -// (E0277/E0369). DISSOLUTION: remove when the numeric tower gains a faithful -// host-realization seam mirroring the text carrier (emit_rust_host_to_dag_string_via_seam). fn rust_seed_host_numeric_alias(name: String, corpus_repr: RustCorpusRepr) -> String? { if (name == "Nat" || name == "Int") && corpus_repr_is_host(corpus_repr: corpus_repr) { Present { value: "i64" } @@ -308,13 +257,6 @@ fn rust_seed_host_numeric_alias(name: String, corpus_repr: RustCorpusRepr) -> St } } -// SEED carrier (container analogue of String->host / Nat->i64): in the v1-seed -// branch the List nominal renders as the HOST CONTAINER the v1_rt Vec ops already -// use, so the nominal resolves (E0425/E0432) in the type-rendering paths that -// preserve nominals (render_rust_applied_type fn-sigs, render_rust_alias_rhs_type). -// Carrier-specific (List only) and GUARDED — does NOT touch the general -// preserve-nominal alias-RHS policy for non-container nominals (FreeMonoid/Box) or -// the v2-TARGET faithful FreeMonoid representation. List ops stay Vec-based. fn rust_seed_host_container_base(name: String, corpus_repr: RustCorpusRepr) -> String? { if name == "List" && corpus_repr_is_host(corpus_repr: corpus_repr) { Present { value: "Vec" } @@ -323,9 +265,6 @@ fn rust_seed_host_container_base(name: String, corpus_repr: RustCorpusRepr) -> S } } -// LEAF base. Faithful String renders its DECLARED element (String=FreeMonoid); the -// Char literal here is the time-bounded scaffold (see the carrier comment above) — a leaf -// type-node carries no resolved type-arg, so the element cannot be read from the node. fn rust_named_type_base(name: String, corpus_repr: RustCorpusRepr) -> String { if name == "Witness" || name == "witness" { "Witness" @@ -336,11 +275,6 @@ fn rust_named_type_base(name: String, corpus_repr: RustCorpusRepr) -> String { } } -// APPLIED base. When a faithful String node has a resolved type-arg child, the element is -// READ from that child (single application), so the base must be the BARE container — -// otherwise concat(base, "<", child, ">") double-applies (FreeMonoid). Literal- -// free for String (the element comes from the resolved child, not from here). All non-String -// types delegate to the leaf base unchanged. fn rust_applied_type_base(name: String, corpus_repr: RustCorpusRepr) -> String { if name == "String" && corpus_repr_is_faithful(corpus_repr: corpus_repr) { "FreeMonoid" @@ -363,7 +297,6 @@ fn rust_normalize_partial_function_field_type_text(rendered: String) -> String { ) } -// MachineWidth<8> etc.: phantom width-nat args ground to () in Rust, not empty <> (E0107). fn is_parametric_opaque_type_by_name(env: TypeEnv, type_name: String) -> Bool { match lookup_type_by_name(env: env, name: type_name) { Present { value: item } => is_parametric_opaque_type_decl_item(item: item, source_indices: env.source_indices) @@ -404,13 +337,13 @@ fn render_rust_applied_type_arg(n: Node, generic_param_names: List, shar fn render_rust_applied_type(n: Node, generic_param_names: List, shared_types: Set, corpus_repr: RustCorpusRepr, source_indices: Map, variant_to_enum: Map, env: TypeEnv) -> String { let base_name = authored_name_at(source_indices: source_indices, node: n) if n.children |> count == 0 { - // leaf: full leaf base (faithful String → FreeMonoid, element from the scaffold literal) + match rust_seed_host_container_base(name: base_name, corpus_repr: corpus_repr) { Present { value: host } => host Absent => rust_named_type_base(name: base_name, corpus_repr: corpus_repr) } } else { - // applied: bare container base so the resolved type-arg child drives the single application + let base = match rust_seed_host_container_base(name: base_name, corpus_repr: corpus_repr) { Present { value: host } => host Absent => rust_applied_type_base(name: base_name, corpus_repr: corpus_repr) @@ -433,15 +366,12 @@ fn render_rust_shared_type_if_needed(type_name: String, rendered: String, shared } else { rendered } } -// Single authority: shared_types decides Rc<> on rendered Rust type shells. fn render_rust_applied_type_shared(n: Node, generic_param_names: List, shared_types: Set, corpus_repr: RustCorpusRepr, source_indices: Map, variant_to_enum: Map, env: TypeEnv) -> String { let rendered = render_rust_applied_type(n: n, generic_param_names: generic_param_names, shared_types: shared_types, corpus_repr: corpus_repr, source_indices: source_indices, variant_to_enum: variant_to_enum, env: env) let type_name = authored_name_at(source_indices: source_indices, node: n) render_rust_shared_type_if_needed(type_name: type_name, rendered: rendered, shared_types: shared_types) } -// Generic type-param leaves compose with the canonical renderer (else arm); -// direct self-recursion on type-arg children keeps regen_stage0 stacker guard. fn render_rust_decl_type(n: Node, generic_param_names: List, shared_types: Set, corpus_repr: RustCorpusRepr, source_indices: Map, variant_to_enum: Map, env: TypeEnv) -> String { let applied_prop = find_property(props: n.properties, prop_name: "__applied_type_args", source_indices: source_indices) let applied_overlay = match applied_prop { @@ -483,12 +413,6 @@ fn render_rust_decl_type(n: Node, generic_param_names: List, shared_type } } -// Compose generic-arg preservation with canonical rendering — do not replace the -// applied-binding path when the enclosing item has type params. -// RC3: Closed type aliases (e.g. String = FreeMonoid, NanosecondDuration = -// Measure<...>) expand to applied shells in inference; fn signatures and alias RHS -// must emit the alias name only. Alias RHS may be brand-preserved (children > 0); -// peel on declared param count, not structural child count. fn rust_fn_sig_peel_closed_alias(env: TypeEnv, n: Node) -> Bool { let name = authored_name_at(source_indices: env.source_indices, node: n) if name == "String" { @@ -536,8 +460,6 @@ fn render_rust_fn_sig_type_applied_binding(n: Node, shared_types: Set, c } } -// Alias RHS: preserve nominal DAG type names (FreeMonoid, Box) — no container coercion. -// Qualify bases from defining modules when RHS type lives outside the alias module. fn render_rust_alias_rhs_type(n: Node, generic_param_names: List, shared_types: Set, corpus_repr: RustCorpusRepr, source_indices: Map, scope: InferScope, imports: List, registry: Map, module_name: String, export_sets: Map>, typed_modules: List, module_index: ModuleIndex, variant_to_enum: Map) -> String { let name = authored_name_at(source_indices: source_indices, node: n) if n.connective == NoConnective && n.children |> count == 0 && (generic_param_names |> any(g => g == name)) { @@ -599,7 +521,6 @@ fn is_type_variable(inferred: InferredNode) -> Bool { } } - fn is_rust_value_type(n: Node, source_indices: Map) -> Bool { let normed = normalize_access_type_node(n: n) match is_copy(target: Rust, dag_name: authored_name_at(source_indices: source_indices, node: normed)) { @@ -613,7 +534,6 @@ fn is_rust_string_like(n: Node, source_indices: Map) -> Bo is_string_like(target: Rust, name: authored_name_at(source_indices: source_indices, node: normed)) } - fn rust_source_root() -> String { match scaffold_for_target(target: Rust).source_dir { Present { value: dir } => dir @@ -626,8 +546,7 @@ fn rust_source_ext() -> String { } fn rust_visibility_prefix() -> String { - // M5: Rust uses KeywordVisibility. Any other variant is a data bug. - // Fail closed with error marker instead of fabricating "". + match visibility_for_target(target: Rust) { KeywordVisibility { prefix: p } => p _ => "__VISIBILITY_SPEC_MISMATCH__" @@ -666,9 +585,6 @@ fn rust_nominal_identity_carrier_def(name: String) -> String { concat(rust_ord_derives_text(), "\n", rust_visibility_prefix(), rust_items().struct_keyword, " ", name, "(pub String);") } -// 🟡 gated — nominal Ord carrier whitelist for M3 Symbol/DiffId only. -// Next nominal carrier must move this to a declared type property instead of -// extending the string predicates below. fn rust_nominal_identity_carrier_type_eligible(type_name: String) -> Bool { type_name == "Symbol" } @@ -698,9 +614,6 @@ fn rust_nominal_ord_derives_for_shape(name: String, children: List, source } } -// Bare `Set` / `Set` element nodes are name-only references (no -// children); parameterized spellings like `Symbol` carry a type-arg child -// and must fail-closed here instead of inheriting the whitelist by name alone. fn rust_nominal_ord_type_ref_eligible(elem_node: Node, source_indices: Map) -> Bool { let type_name = authored_name_at(source_indices: source_indices, node: elem_node) (elem_node.children |> count) == 0 @@ -961,13 +874,6 @@ fn item_binding_is_named(env: TypeEnv, node: Node, name: String) -> Bool { authored_name(env: env, node: node) == name || node.name == name } -// `wire_contract_item` from emit_module_full is the `data wire_contract = …` node. -// The data item's `body` is the initializer expression; when it aliases an imported -// `VariantEncoding` constant (e.g. `llm_snake_wire_contract`), follow the authored -// data binding through the data-item index before consulting inference. Ambiguous -// data names and alias cycles fail closed instead of falling back to default serde. -// Typed `data wire_contract: VariantEncoding = StringVariant { … }` bodies keep -// `ExprRecordLit` while `inferred` may point at the type expression — prefer the literal. fn resolve_wire_serde_policy_for_coproduct(wire_contract_item: Node?, source_indices: Map, data_items: Map>) -> RustEnumWireSerde { resolve_wire_serde_policy_for_coproduct_seen( wire_contract_item: wire_contract_item, @@ -1281,12 +1187,6 @@ fn rust_async_test_decorator() -> String { } } - -// ========================================================================= -// SG-9: Tail-recursive block statement emitters. -// Standalone list parameters avoid Rc refcount>1 deep clone on list_push. -// ========================================================================= - fn emit_rust_block_stmts(remaining: List, text: List, scope: InferScope, registry: Map, depth: Int, shared_types: Set, emit_info: EmitGraphInfo) -> BlockEmitState { match remaining |> first { @@ -1323,10 +1223,6 @@ fn emit_rust_init_block_stmts(remaining: List, text: List, } } -// ========================================================================= -// RC1: Variant-to-enum resolution helpers -// ========================================================================= - fn rust_qualify_type_leaf_name(name: String, variant_to_enum: Map) -> String { match map_get(variant_to_enum, name) { Present { value: parent } => @@ -1346,11 +1242,6 @@ fn rust_render_type_leaf_name(name: String, variant_to_enum: Map } } -// Phantom ZST type-tag markers (enum variants used as type-level kinds, e.g. the -// Quantity/Scale tags Memory/One/Power in Measure) must derive the -// same trait set as the carriers that mention them (Measure derives Debug/Clone/ -// PartialEq + serde), or those derives fail closed (E0277 Memory: !Debug, E0369 == on -// Measure, E0392 unused param). Single authority for the marker definition. fn rust_phantom_zst_marker_def(vname: String) -> String { concat( "#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]\n", @@ -1398,19 +1289,6 @@ fn has_complex_variants(item: Node) -> Bool { } } -// ========================================================================= -// RC2: Rc type wrapping helpers -// -// Non-Copy types (structs, complex enums) are wrapped in Rc in the -// emitted Rust code to match the DAG language's value semantics. -// Simple enums (all-unit variants) and a small exclusion list are exempt. -// -// shared_types is still derived inside the Rust emitter entry points instead of -// via a sibling helper that returns Set. The bootstrap -// interpreter mis-handles empty map returns as "no outputs", which breaks -// modules with no Rc-wrapped user types. -// ========================================================================= - fn is_simple_disj(item: Node) -> Bool { let complex = item.children |> filter(v => v.children |> count > 0) complex |> count == 0 @@ -1440,11 +1318,6 @@ fn is_type_constant(summary: TypeSummary, recursive_type_set: Set) -> Bo } } -// Build the shared_types map: the SINGLE authority for sharing (Rc) decisions. -// For languages with needs_sharing=false (GC languages), returns empty map — -// all Rc wrapping and cloning become no-ops via identity SharingStrategy templates. -// For Rust: includes user-defined types (structs, data enums) and built-in -// collection types. Constant types (fixed-width carriers) skip sharing. fn maybe_mark_shared_type(acc: Set, summary: TypeSummary, recursive_type_set: Set, target_needs_sharing: Bool) -> Set { let needs_sharing = target_needs_sharing && match summary.repr { StructRepr => true @@ -1458,16 +1331,12 @@ fn maybe_mark_shared_type(acc: Set, summary: TypeSummary, recursive_type } fn build_shared_types(type_summaries: Map, recursive_type_set: Set, target: RenderTarget) -> Set { - // needs_sharing: Rust=true (ownership), Python/Go=false (GC). - // When false, both folds produce empty results → identity SharingStrategy - // templates produce no-op wrapping/cloning. + let sharing = sharing_for_target(target: target) let user_shared = map_values(type_summaries) |> fold(init: empty_set(), f: (acc, summary) => maybe_mark_shared_type(acc: acc, summary: summary, recursive_type_set: recursive_type_set, target_needs_sharing: sharing.needs_sharing) ) - // Collection types: any template key whose bare type has O(n) clone cost. - // "optional" and "boolean_algebra" excluded. GC languages: sharing.needs_sharing - // is false so the filter produces an empty list. + let collection_keys = map_keys(rust_container_templates) |> filter(k => sharing.needs_sharing && k != "optional" && k != "boolean_algebra") collection_keys |> fold(init: user_shared, f: (acc, key) => @@ -1476,10 +1345,6 @@ fn build_shared_types(type_summaries: Map, recursive_type_s ) } -// ========================================================================= -// Ownership index -- fan-out=1 bindings that can be moved instead of cloned -// ========================================================================= - type OwnershipProofEntry { name: String proof: OwnershipProof @@ -1518,10 +1383,6 @@ fn build_ownership_results(modules: List) -> OwnershipBuildResult { ) } -// ========================================================================= -// Entry point -- Rust target -// ========================================================================= - fn emit_rust(typed: ResolvedGraph) -> EmitResult { let base_info = build_emit_graph_info(modules: typed.modules) let ownership = build_ownership_results(modules: typed.modules) @@ -1577,17 +1438,13 @@ fn emit_rust(typed: ResolvedGraph) -> EmitResult { } else { [] } - // Invariant: module declarations derived from emitted files, not hardcoded. + let all_mod_files = concat(module_files, [rt_file], dry_run_file) let lib_file = emit_lib_rs_from_files(all_module_files: all_mod_files, has_compiler_tests: has_pipeline) let files = concat([cargo, lib_file, main_file], all_mod_files, compiler_tests_file, test_files) EmitResult { files: files, diagnostics: [] } } -// ========================================================================= -// The v2 emitter prefixes runtime intrinsic calls with `v1_rt::`. -// ========================================================================= - fn emit_v2_rt_module() -> TextFile { TextFile { path: concat(rust_source_root(), "v1_rt", rust_source_ext()), content: rust_runtime_source() } } @@ -1596,9 +1453,6 @@ fn emit_compiler_tests_module() -> TextFile { TextFile { path: concat(rust_source_root(), "compiler_tests", rust_source_ext()), content: compiler_tests_source() } } -// ========================================================================= -// ========================================================================= - fn emit_lib_rs_from_files(all_module_files: List, has_compiler_tests: Bool) -> TextFile { let src_prefix_len = string_length(s: rust_source_root()) let ext_len = string_length(s: rust_source_ext()) @@ -1607,10 +1461,7 @@ fn emit_lib_rs_from_files(all_module_files: List, has_compiler_tests: let mod_name = substring(s: f.path, start: src_prefix_len, end: path_len - ext_len) concat(rust_visibility_prefix(), rust_items().module_keyword, " ", mod_name, ";") ) - // Hand-maintained modules: interpreter, CLI run handler, REST transport - // facts (bootstrap-only; dissolution when facts are graph-exported — see - // rest_transport_facts.rs bounded substrate seed). - // These are not generated — they survive regen as non-generated files. + let hand_maintained_mods = if has_compiler_tests { "\npub mod v1_interpreter;\npub mod cli_run;\npub mod rest_transport_facts;\npub mod wire_value_serialize;\npub mod coproduct_reflection;\npub mod resolved_graph_cache;\npub mod recorded_fixture;\npub mod cache_purity_oracle;\npub mod doc_reachability_project;\npub mod medium_structure_project;\npub mod extdeps_shape_transport_policy_project;\npub mod fact_cardinality_census;\npub mod import_resolution_project;\npub mod languages_consumer_census;\npub mod layering_imports_project;\npub mod module_path_index;\npub mod transport_script_position_project;" } else { @@ -1624,9 +1475,7 @@ fn emit_lib_rs_from_files(all_module_files: List, has_compiler_tests: let shared_types = emit_non_empty_wrappers() let content = concat("// Generated by v1 compiler -- do not edit.\n\n", "#![allow(unused_imports, unused_variables, unused_mut, unused_parens, dead_code, unreachable_patterns, non_shorthand_field_patterns, suspicious_double_ref_op, clippy::all)]\n", - // serde_json::json! expands recursively (json_internal! TT-munching); larger emitted - // test literals exceed rustc's default macro recursion_limit of 128. Raise it crate-wide - // -- serde_json's own documented fix, a crate-config attribute, not a per-literal change. + "#![recursion_limit = \"256\"]\n\n", mod_decls |> join(separator: "\n"), hand_maintained_mods, @@ -1635,12 +1484,6 @@ fn emit_lib_rs_from_files(all_module_files: List, has_compiler_tests: TextFile { path: concat(rust_source_root(), "lib", rust_source_ext()), content: concat(content, "\n") } } -// ========================================================================= -// Module emission -// ========================================================================= - -// Single-module harness entry: export_sets/typed_modules are one-element, so -// cross-module parametric alias emission only works via emit_rust (full graph). fn emit_module(typed_module: TypedModule, registry: Map) -> TextFile { let base_info = build_emit_graph_info(modules: [typed_module]) let shared = build_shared_types(type_summaries: base_info.type_summaries, recursive_type_set: base_info.recursive_type_set, target: Rust) @@ -1740,10 +1583,6 @@ fn emit_module_full(typed_module: TypedModule, registry: Map, TextFile { path: concat(rust_source_root(), filename, rust_source_ext()), content: content } } -// ========================================================================= -// Import emission -// ========================================================================= - fn emit_import_name(n: String, registry: Map) -> String { let is_data = match map_get(registry, n) { Present { value: info } => info.kind == DataItem @@ -1752,8 +1591,6 @@ fn emit_import_name(n: String, registry: Map) -> String { if is_data { to_snake(n) } else { n } } -// SG-8: graph type imports proven from the declared import module's physical defs -// and export surface — never from bare-name registry lookup (global homonyms). fn is_import_graph_type_name(name: String, import_module: String, typed_modules: List, registry: Map, export_sets: Map>, type_summaries: Map, source_indices: Map, module_index: ModuleIndex) -> Bool { if has_physical_type_def_in_module_filename(rhs_name: name, mod_filename: module_to_filename(name: import_module), typed_modules: typed_modules, source_indices: source_indices, module_index: module_index) { true @@ -1784,18 +1621,6 @@ fn item_defining_module_filename(name: String, registry: Map, } } -// ========================================================================= -// ModuleIndex: O(1) module lookup precomputed once per emit, threaded through -// the import + alias-resolution subtree. Hoists the former O(modules*items) -// linear scans (typed_module_by_name / module_name_from_filename / -// has_physical_type_def_in_module_filename) that ran per imported name per -// module — the per-module-growth O(n^2) in v2 emit. -// -// Built by iterating `typed_modules` in list order so insert-if-absent (by_name) -// and append (by_filename) reproduce the old `|> filter |> first` / `|> any` -// winners exactly: module names are unique, and by_filename keeps the full -// ordered list so `any`/`first` semantics over filename collisions are identical. -// ========================================================================= type ModuleIndex { by_name: Map by_filename: Map> @@ -1873,8 +1698,6 @@ fn type_name_is_export_source_in_module(name: String, module_name: String, typed } } -// Rust import emission must match alias emission: only names with nominal-shell authority -// (struct/enum/alias/identity carrier/emittable parametric alias), not opaque carriers. fn type_name_is_rust_importable_in_module(name: String, module_name: String, typed_modules: List, registry: Map, export_sets: Map>, source_indices: Map, module_index: ModuleIndex) -> Bool { match typed_module_by_name(module_name: module_name, typed_modules: typed_modules, source_indices: source_indices, module_index: module_index) { Absent => false @@ -1904,8 +1727,6 @@ fn type_name_is_rust_importable_in_module(name: String, module_name: String, typ } } -// Exported type names without Rust nominal-shell authority must not emit `use` lines -// (E0432 when the defining module intentionally emits no symbol). fn imported_name_is_non_emittable_type(name: String, import_module: String, typed_modules: List, registry: Map, export_sets: Map>, source_indices: Map, module_index: ModuleIndex) -> Bool { let defining_module = match reexport_source_module_name(name: name, import_module: import_module, typed_modules: typed_modules, export_sets: export_sets, source_indices: source_indices, module_index: module_index) { Present { value: src } => src @@ -1915,7 +1736,6 @@ fn imported_name_is_non_emittable_type(name: String, import_module: String, type && type_name_is_rust_importable_in_module(name: name, module_name: defining_module, typed_modules: typed_modules, registry: registry, export_sets: export_sets, source_indices: source_indices, module_index: module_index) == false } -// Walk explicit re-export chains (resolve get_exported_names surfaces) to the defining module. fn name_in_transitive_export_surface(name: String, module_name: String, visited: List, export_sets: Map>, typed_modules: List, source_indices: Map, module_index: ModuleIndex) -> Bool { if visited |> any(v => v == module_name) { false @@ -2054,9 +1874,6 @@ fn enum_physically_defined_in_module(enum_name: String, module_name: String, typ } } -// Recursively collect names reachable through wildcard import pools. -// export_sets[src] only records specific re-exports; wildcard-pooled names -// live on deeper modules in the chain (proxy2 -> proxy1 -> def). fn wildcard_import_pool_surface_names(module_name: String, visited: List, export_sets: Map>, typed_modules: List, source_indices: Map, module_index: ModuleIndex) -> List { if visited |> any(v => v == module_name) { [] @@ -2087,10 +1904,6 @@ fn wildcard_import_pool_surface_names(module_name: String, visited: List } } -// Wildcard import of a proxy module must still emit defining-module lines for -// re-exported names (variants/types) that `use proxy::*` cannot surface in Rust. -// export_sets[import_module] omits wildcard-pooled names (get_exported_names -// only records specific imports), so also scan wildcard source modules' surfaces. fn wildcard_reexport_surface_names(import_module: String, export_sets: Map>, typed_modules: List, source_indices: Map, module_index: ModuleIndex) -> List { let local_candidates = match map_get(export_sets, import_module) { Present { value: exported } => map_keys(exported) @@ -2251,8 +2064,6 @@ fn alias_rhs_rust_qualify_module_filename(name: String, alias_module: String, im } } -// Parametric alias RHS bases must not use bare global registry homonym lookup when the -// spelling is declared via import — import_module is the authority (boundary discipline, DESIGN.md §3). fn alias_rhs_base_module_filename(name: String, alias_module: String, imports: List, source_indices: Map, scope: InferScope, registry: Map, export_sets: Map>, typed_modules: List, module_index: ModuleIndex) -> String { let local_mod = module_to_filename(name: alias_module) match lookup_item(registry: scope.item_registry, name: name) { @@ -2304,8 +2115,6 @@ fn type_item_by_name_in_module_filename(rhs_name: String, mod_filename: String, } } -// Nominal shells referenced in alias RHS must map to types that actually emit Rust -// (struct/enum/alias/identity carrier/parametric alias), not opaque carriers. fn type_item_has_rust_nominal_shell_authority(item: Node, item_text: String, module_name: String, imports: List, scope: InferScope, registry: Map, export_sets: Map>, typed_modules: List, source_indices: Map, module_index: ModuleIndex) -> Bool { if is_type_def_item(item: item) { true @@ -2356,10 +2165,6 @@ fn type_item_has_rust_nominal_shell_authority(item: Node, item_text: String, mod } } -// G1 phantom type-arg tags (Quantity/Scale unit variants in Measure) are valid in -// alias RHS shells but are not standalone type declarations — short-circuit before the -// export/reexport authority walk, which otherwise loops when the variant name is on the -// export surface but has no physical type item (measure.dag MoneyAmount = Measure). fn is_phantom_unit_variant_type_arg(env: TypeEnv, variant_name: String) -> Bool { match lookup_unit_variant_phantom_type(env: env, variant_name: variant_name) { Present { value: _ } => true @@ -2367,9 +2172,6 @@ fn is_phantom_unit_variant_type_arg(env: TypeEnv, variant_name: String) -> Bool } } -// Parametric alias emission (F3/F4): only real `type Foo = Bar` shapes — not opaque -// carriers (`type GroupCompletion`) or container-lowering stubs as invalid Rust aliases. -// Authority is module-scoped: bare type_summaries collide on homonyms (fail-closed). fn rhs_base_has_rust_type_authority_in_module(rhs_name: String, def_mod_filename: String, module_name: String, imports: List, scope: InferScope, registry: Map, typed_modules: List, export_sets: Map>, source_indices: Map, module_index: ModuleIndex) -> Bool { if is_phantom_unit_variant_type_arg(env: scope.type_env, variant_name: rhs_name) { true @@ -2425,8 +2227,6 @@ fn rhs_base_has_rust_type_authority_in_module(rhs_name: String, def_mod_filename } } -// Every applied nominal shell in alias RHS must have Rust type authority in its -// defining module — matches recursive render_rust_alias_rhs_type emission. fn alias_rhs_nominal_shell_has_rust_authority(n: Node, module_name: String, imports: List, scope: InferScope, registry: Map, export_sets: Map>, typed_modules: List, source_indices: Map, module_index: ModuleIndex) -> Bool { if n.connective == NoConnective && n.children |> count > 0 { let name = authored_name_at(source_indices: source_indices, node: n) @@ -2606,8 +2406,7 @@ fn emit_specific_import_block(import_module: String, mod_name: String, filtered_ } else { true } ) let direct_names = unique_strings(items: non_variant_top) - // Same-module variant parents belong in the direct braced use (legacy top_with_parents - // merge). Only cross-module parents get a separate defining-module pub use (SG-8). + let local_parent_names = parent_list |> filter(p => deduped_names |> any(n => match find_variant_parent_in_module( @@ -2923,10 +2722,6 @@ fn emit_imports(imports: List, emit_info: EmitGraphInfo, registry: Map String { let use_line = concat( "use std::collections::BTreeSet;\n", @@ -2981,10 +2776,6 @@ fn emit_non_empty_wrappers() -> String { concat(vec_wrapper, "\n\n", set_wrapper) } -// ========================================================================= -// Item dispatch -// ========================================================================= - fn emit_typed_item(item: Node, module_name: String, registry: Map, scope: InferScope, shared_types: Set, emit_info: EmitGraphInfo, wire_contract_item: Node?, data_items: Map>, module_items: List, imports: List, export_sets: Map>, typed_modules: List, module_index: ModuleIndex) -> String { let env = scope.type_env let qualified_name = concat(module_name, ".", authored_name(env: env, node: item)) @@ -2992,8 +2783,7 @@ fn emit_typed_item(item: Node, module_name: String, registry: Map count) == 0 && rust_nominal_identity_carrier_type_eligible(type_name: item_text) { rust_nominal_identity_carrier_def(name: item_text) } else if is_zero_param_self_referential_opaque_decl(item: item, source_indices: env.source_indices) { @@ -3023,16 +2813,7 @@ fn emit_typed_item(item: Node, module_name: String, registry: Map count) == 0 && rust_nominal_identity_carrier_type_eligible(type_name: item_text) { rust_nominal_identity_carrier_def(name: item_text) } else { - // Seed corpus: a canonical container decl (`type List = - // FreeMonoid`) is NOT emittable-parametric (its FreeMonoid RHS - // shell has no Rust nominal authority), so it would skip to `""` and leave - // `List` undefined while the seed still references it (`list_length`'s - // `Rc>` sig, consumers' `use crate::std_types::List`). Emit it as a - // host-container alias (`pub type List = Vec`) so every - // `List` reference + import resolves. Corpus-gated via - // `rust_seed_host_container_base` (List-only, v1-compiler seed only); the - // faithful v2-target emit keeps the FreeMonoid algebra (Absent → existing - // path). Dissolves with the seed. + match rust_seed_host_container_base(name: item_text, corpus_repr: emit_info.corpus_repr) { Present { value: host } => let type_params = emit_type_params(params: item.params, source_indices: env.source_indices) @@ -3109,8 +2890,7 @@ fn emit_typed_item(item: Node, module_name: String, registry: Map info.service_names |> count > 0 || info.resource_names |> count > 0 Absent => false @@ -3131,13 +2911,6 @@ fn emit_typed_item(item: Node, module_name: String, registry: Map also provides heap indirection, so skip Box for Rc-wrapped types. fn needs_box_wrapping(n: Node, recursive_types: Set, shared_types: Set, source_indices: Map) -> Bool { if n.children |> count == 0 { let name = authored_name_at(source_indices: source_indices, node: n) @@ -3162,33 +2935,6 @@ fn emit_type_params(params: List, source_indices: Map Bool { let type_expr = param_node_type_expr(n: param) let type_expr_is_var = match type_expr.inferred { @@ -3196,14 +2942,7 @@ fn is_function_type_param(param: Node) -> Bool { Absent => false } let same_source_span = param.span.file == type_expr.span.file && param.span.start == type_expr.span.start && param.span.end == type_expr.span.end - // Name match via Node.name — stable across both source spans and the - // kernel spans installed by resolve_item_types's TypeVariable bindings. - // The param.name != "" guard is belt-and-suspenders: the primary filter - // for parse_param's recovery dummy (name: "", type_expr.inferred: none) - // is type_expr_is_var above. The empty-string guard only fires if some - // future pipeline change populates TypeVariable on an unnamed node — - // in which case falling through "" == "" would silently classify it as - // a type param. Cheap to keep until ParamKind dissolves the classifier. + param.name != "" && param.name == type_expr.name && (type_expr_is_var || same_source_span) } @@ -3215,26 +2954,6 @@ fn function_value_params(params: List) -> List { params |> filter(p => !is_function_type_param(param: p)) } -// Detect the ambiguity `fn f(T: T)` where a value param's name collides -// with a declared type param. Both params satisfy is_function_type_param -// (name == type_expr.name AND type_expr.inferred is TypeVariable, because -// resolve_item_types registered T as a TypeVariable binding). The emit-time -// splitter can't tell them apart — that's the shape flagged by the -// dissolution target (partition Node.params into type/value, or add -// ParamKind). -// -// Primary fail-closed for this ambiguity already lives upstream as a typed -// gunbc Diagnostic at resolve_item_types (04_resolve.dag:923, using -// has_duplicate_type_param_name). This emit-time `compile_error!` is kept -// as belt-and-suspenders against pipelines that bypass resolve (direct -// emit_fn_def calls in tests, partial pipelines) and would otherwise emit -// silent ``. -// -// Dissolution trigger for deleting THIS emit-time guard: when the -// ParamKind / params-slot partition dissolution lands, the classifier and -// this collision detector both evaporate — the boundary becomes -// structural instead of heuristic. (The upstream resolve-layer diagnostic -// would likely also change shape or dissolve at the same time.) fn function_type_params_have_collision(type_params: List) -> Bool { let names = type_params |> map(p => p.name) (names |> count) > (unique_strings(items: names) |> count) @@ -3283,9 +3002,6 @@ fn emit_type_def_from_connective(item: Node, recursive_types: Set, share } } - -// True iff the type node `n` (or any nested child) names `target` -- the -// structural "does this field type mention this generic param" test. fn type_node_mentions_name(n: Node, target: String, source_indices: Map) -> Bool { if authored_name_at(source_indices: source_indices, node: n) == target { true @@ -3294,25 +3010,14 @@ fn type_node_mentions_name(n: Node, target: String, source_indices: Map` (only `M` reaches the `count` field; `Q`/`S` tag the -// dimension) are the canonical case. Such params must be carried in a -// `PhantomData` field for the emitted struct to be well-formed, and every -// construction must then populate that field (see emit_typed_record_lit). fn struct_unused_param_names(generic_param_names: List, children: List, source_indices: Map) -> List { generic_param_names |> filter(p => !(children |> any(child => type_node_mentions_name(n: field_node_type_expr(n: child), target: p, source_indices: source_indices))) ) } -// The `<...>` arg of the synthetic `PhantomData` carrier for a struct's unused -// params: a single param bare (`PhantomData`), several as a tuple -// (`PhantomData<(Q, S)>`) so one field "uses" them all. fn rust_phantom_marker_inner(unused: List) -> String { - // Single unused param -> bare `PhantomData`; several -> `PhantomData<(Q, S)>`. - // `join` of a one-element list is that element, so the single case needs no - // Optional peel (avoids a `match` whose `Absent` arm is unreachable here). + if unused |> count == 1 { unused |> join(separator: ", ") } else { @@ -3320,8 +3025,6 @@ fn rust_phantom_marker_inner(unused: List) -> String { } } -// The unused-param `PhantomData` field name -- single authority shared by the -// struct definition and every construction so they always agree. fn rust_phantom_field_name() -> String { "_phantom" } fn emit_struct_from_children(name: String, type_params: String, generic_param_names: List, children: List, recursive_types: Set, shared_types: Set, env: TypeEnv, emit_info: EmitGraphInfo) -> String { @@ -3342,11 +3045,7 @@ fn emit_struct_from_children(name: String, type_params: String, generic_param_na concat(derives, "\n", rust_visibility_prefix(), rust_items().struct_keyword, " ", name, type_params, ";") } else { let field_lines = children |> map(child => emit_struct_field_from_child(struct_name: name, child: child, generic_param_names: generic_param_names, recursive_types: recursive_types, shared_types: shared_types, env: env, emit_info: emit_info)) - // E0392: carry any generic param unused by the fields in a PhantomData - // field (`Measure`'s phantom `Q`/`S`). The `#[derive]` on the - // struct adds the standard `Q: Trait` bounds, satisfied by the concrete - // marker tags at every instantiation; serde special-cases PhantomData and - // adds no Serialize/Deserialize bound. + let unused_params = struct_unused_param_names(generic_param_names: generic_param_names, children: children, source_indices: env.source_indices) let all_field_lines = if unused_params |> count > 0 { concat(field_lines, [concat(" ", rust_visibility_prefix(), rust_phantom_field_name(), ": std::marker::PhantomData<", rust_phantom_marker_inner(unused: unused_params), ">,")]) @@ -3770,15 +3469,6 @@ fn emit_variant_from_child(child: Node, enum_name: String, recursive_types: Set< } } -// ========================================================================= -// Node-based type emission -// ========================================================================= - - -// ========================================================================= -// Pure function (fn) emission -// ========================================================================= - fn emit_fn_def(name: String, params: List, inferred: Node, body: Node, registry: Map, scope: InferScope, shared_types: Set, emit_info: EmitGraphInfo) -> String { let si = scope.type_env.source_indices match rust_host_string_seam_fn_emit(name: name) { @@ -3787,10 +3477,7 @@ fn emit_fn_def(name: String, params: List, inferred: Node, body: Node, reg Absent => { let type_params = function_type_params(params: params) let value_params = function_value_params(params: params) - // Fail closed on the `fn f(T: T)` name-shadowing ambiguity — the - // emit-time splitter can't distinguish the declared type param from the - // value param. Emit a target-compile-time error rather than silent - // ``. Dissolution: partition Node.params or add ParamKind. + if function_type_params_have_collision(type_params: type_params) { return concat("compile_error!(\"type param name collides with a value param in fn '", name, "' — a value param shares its name with a declared type param; rename the value param or dissolve via ParamKind/params-slot partition\");\n") } @@ -3831,10 +3518,6 @@ fn emit_fn_def_non_tco(name: String, type_params_str: String, params_str: String } } -// ========================================================================= -// Workflow function (func) emission -// ========================================================================= - fn emit_func_def( name: String, params: List, @@ -3846,13 +3529,7 @@ fn emit_func_def( shared_types: Set, emit_info: EmitGraphInfo ) -> String { - // Note: `func`/`pattern`/`interface` items parse through - // parse_block_body_from_prefix, which stores `prefix.params` directly and - // drops `prefix.type_params`. Unlike `fn` (parse_fn_body_from_prefix), the - // `params` slot here carries value params only, so no type/value split is - // needed. Fail closed if that invariant ever breaks — e.g., a future - // parser change that prepends type params into this slot would silently - // emit them as value params without the guard below. + if function_type_params(params: params) |> count > 0 { return concat("compile_error!(\"emit_func_def saw type params in `", name, "` — func/pattern/interface don't route through the generic-fn split today; the parser invariant at parse_block_body_from_prefix has changed. Plumb type_params through this path or dissolve via ParamKind/params-slot partition.\");\n") } @@ -3915,10 +3592,6 @@ fn emit_func_body(body: Node, registry: Map, scope: InferScope } } -// ========================================================================= -// Tail-Call Optimization (TCO) -- Rust rendering -// ========================================================================= - fn emit_tco_params(params: List, generic_param_names: List, shared_types: Set, corpus_repr: RustCorpusRepr, source_indices: Map, variant_to_enum: Map, env: TypeEnv) -> String { let strs = params |> map(p => emit_tco_param(param: p, generic_param_names: generic_param_names, shared_types: shared_types, corpus_repr: corpus_repr, source_indices: source_indices, variant_to_enum: variant_to_enum, env: env)) strs |> join(separator: ", ") @@ -3950,28 +3623,11 @@ fn emit_func_inferred(inferred: Node, shared_types: Set, corpus_repr: Ru concat(" -> Result<", render_rust_type(n: inferred, shared_types: shared_types, corpus_repr: corpus_repr, source_indices: source_indices), ", Box>") } -// ========================================================================= -// Parameter emission -// ========================================================================= - fn emit_params(params: List, generic_param_names: List, shared_types: Set, corpus_repr: RustCorpusRepr, source_indices: Map, read_only_params: Set, variant_to_enum: Map, env: TypeEnv) -> String { let strs = params |> map(p => emit_param(param: p, generic_param_names: generic_param_names, shared_types: shared_types, corpus_repr: corpus_repr, source_indices: source_indices, read_only_params: read_only_params, variant_to_enum: variant_to_enum, env: env)) strs |> join(separator: ", ") } -// Callable-shaped parameters (first-class `fn(A)->B` in source) lower to -// `impl Fn(A) -> B + Clone` in Rust parameter position. -// -// Emitter-fidelity / #650 boundary: -// - Source types do not spell a Clone obligation; `+ Clone` is an explicit -// Rust target contract so callables may be invoked from multiple use sites -// via `.clone()` when `emit_var_ref` decides the binding is not movable. -// Emitted call sites are often plain `f(arg)` (no spelled `Fn::call`); some -// bodies still omit a visible `f.clone()` while relying on this bound elsewhere. -// - `emit_info.movable` remains the sole authority for move vs `.clone()` on -// ordinary variable references (`emit_var_ref`). Do not remove `+ Clone` -// here and "compensate" only in the emitter: that splits clone authority -// between the type signature and ownership analysis and breaks fixed-point. fn render_rust_param_sig_type(param: Node, generic_param_names: List, shared_types: Set, corpus_repr: RustCorpusRepr, source_indices: Map, variant_to_enum: Map, env: TypeEnv) -> String { let type_node = resolved_type(n: param) render_rust_fn_sig_type(n: type_node, generic_param_names: generic_param_names, shared_types: shared_types, corpus_repr: corpus_repr, source_indices: source_indices, variant_to_enum: variant_to_enum, env: env) @@ -4016,10 +3672,6 @@ fn needs_reference_node(n: Node, source_indices: Map) -> B } } -// ========================================================================= -// Pattern emission -- shared by ExprData match arms -// ========================================================================= - fn is_string_lit_pattern(p: MatchPattern) -> Bool { match p { LitPattern { value: v } => @@ -4083,8 +3735,7 @@ fn collect_pattern_string_guards(pattern: MatchPattern, path_prefix: List "" } } else { - // This path segment follows the emitted Rust Option pattern (`Some(...)`), - // not the canonical DAG Optional constructor (`Present`). + collect_pattern_string_guards(pattern: fb_pat, path_prefix: list_push(path_prefix, "Some"), source_indices: source_indices) } Absent => "" @@ -4318,15 +3969,6 @@ fn emit_variant_pattern(name: String, parent_enum: String?, field_bindings: List } } -// ========================================================================= -// Rc-aware pattern matching -// -// When a variant field holds an Rc-wrapped type (e.g. ExprData::ExprLiteral.value -// is Rc), nested pattern matching fails in Rust. These helpers -// detect such fields, emit `ref field_name` in the pattern, and generate -// a let-else prelude that destructures via .as_ref(). -// ========================================================================= - type RcPatternAnalysis { matches_rc_variant: Bool matches_option_rc_variant: Bool @@ -4412,10 +4054,7 @@ fn analyze_rc_match(scrutinee: Node, arms: List, scrut_type: String, share } let arms_want_option = arm_analyses |> any(a => a.matches_option_rc_variant) let arms_want_deref = arm_analyses |> any(a => a.matches_rc_variant) - // Optional> matches directly — no deref needed. The Some(x) binding - // gives x: Rc which is correct. Only use .as_deref().cloned() when arms - // destructure the inner value (matches_option_rc_variant from arm analysis). - // Never use (*...).clone() on Optional — it can't deref Option. + RcMatchAnalysis { needs_option_deref: arms_want_option, needs_deref: if scrutinee_is_optional { false } else { scrutinee_is_rc_wrapped || arms_want_deref } @@ -4587,10 +4226,6 @@ fn rc_pattern_preludes(pattern: MatchPattern, rc_analysis: RcPatternAnalysis, sh } } -// ========================================================================= -// Record literal helpers -- shared by ExprRecordLit emission -// ========================================================================= - fn explicit_record_struct_name(type_name: String?, inferred_node: Node, shared_types: Set, source_indices: Map) -> String? { let is_optional = inferred_node.return_cardinality == CardOptional if is_optional { @@ -4623,10 +4258,6 @@ fn explicit_record_struct_name(type_name: String?, inferred_node: Node, shared_t fn is_simple_type_node(n: Node, source_indices: Map) -> Bool { is_rust_value_type(n: n, source_indices: source_indices) } -// ========================================================================= -// Node expression emission -- reads inferred directly -// ========================================================================= - fn variant_parent_from_binding_kind(binding_kind: VarBindingKind?) -> String? { match binding_kind { Present { value: VariantValueBinding { parent_enum: parent_enum } } => Present { value: parent_enum } @@ -4634,7 +4265,6 @@ fn variant_parent_from_binding_kind(binding_kind: VarBindingKind?) -> String? { } } - fn apply_clone(expr: String) -> String { apply_type_template1(template: sharing_for_target(target: Rust).clone_value, arg0: expr) } @@ -4676,9 +4306,7 @@ fn emit_var_ref(name: String, binding_kind: VarBindingKind?, resolved_type: Infe if name == "none" || name == "None" { emit_keyword(key: "null", target: Rust) } else if name == "true" || name == "false" { emit_keyword(key: name, target: Rust) } else { - // `movable` is the sole authority for emitting plain variable references - // by value. `owned_bindings` is a different concept: bindings already - // unwrapped from Rc to owned T for field projection. + let moves_by_value = set_contains(emit_info.movable, name) let sharing = language_spec(target: Rust).sharing let variant_parent = effective_variant_parent(name: name, binding_kind: binding_kind, resolved_type: resolved_type, emit_info: emit_info, source_indices: source_indices) @@ -4762,14 +4390,6 @@ fn emit_typed_expr_base(texpr: Node, registry: Map, scope: Inf } } -// True iff the dot-accessed `field` on `base` is stored as `Box<..>` -- the -// struct-def emitter boxes a field whose type `needs_box_wrapping` (recursive -// types, and generic-param magnitudes like `Measure.count: Box`). Reading -// such a field as a plain `base.field.clone()` is an E0308 (`Box` vs `M`); -// it must deref through the Box. Reuses the SAME `needs_box_wrapping` the def -// uses, so the read always agrees with how the field was stored. The base type -// is alias-chain-expanded first so a cross-module alias (`Gibibyte` -> -// `Measure { count }`) resolves to the struct carrying the field. fn field_access_field_is_boxed(base: Node, field: String, scope: InferScope, shared_types: Set, emit_info: EmitGraphInfo) -> Bool { let base_struct = expand_type_for_field_access(n: resolved_type(n: base), env: scope.type_env, module_name: scope.module_name) match find_child_named(n: base_struct, name: field, source_indices: scope.type_env.source_indices) { @@ -4818,11 +4438,7 @@ fn emit_typed_field_access(base: Node, field: String, summary: FieldSummary?, re } let field_is_boxed = field_access_field_is_boxed(base: base, field: field, scope: scope, shared_types: shared_types, emit_info: emit_info) if field_is_boxed { - // Box stored field: deref through the Box to the magnitude, then - // clone. The parens group the deref before `.clone()` (unary `*` - // binds looser than `.`), so they are NOT redundant and stay - // warning-clean under -D warnings in sole-return, chained, and - // receiver positions alike. + concat("(*", base_str, ".", emit_ident(name: field, target: Rust), ").clone()") } else if base_is_owned { concat(base_str, ".", emit_ident(name: field, target: Rust)) @@ -4903,9 +4519,6 @@ fn type_node_child_is_type_variable(c: Node) -> Bool { if ch.inferred != none { is_type_variable(inferred: ch.inferred.value) } else { false } } -// BTreeSet requires T: Ord. Whitelist kernel types with known Ord Rust carriers -// plus nominal carriers whose emitted Rust shape can derive Ord. -// Float (no total Ord), Json (serde_json::Value), and keyed maps fail-closed. fn rust_btree_set_element_ord_eligible(elem_node: Node, source_indices: Map) -> Bool { let elem_name = authored_name_at(source_indices: source_indices, node: elem_node) elem_name == "String" || elem_name == "Int" || elem_name == "Bool" || elem_name == "Unit" @@ -4950,20 +4563,14 @@ fn emit_rust_empty_set_expr(set_type: Node, shared_types: Set, source_in } } -// rust_runtime_bridge_wraps_result_in_rc: DELETED. -// With shared_types as single authority, map values are already Rc-wrapped -// for non-Copy types. lookup/map_get return the correct type directly. - fn rust_runtime_bridge_wraps_collection_result_in_rc(function_name: String) -> Bool { - // Two derived maps, single authority each: RuntimeFunction.wraps_result - // for runtime functions, SimpleMethodSpec.wraps_result for method templates. + map_contains_key(rt_wraps_result(), function_name) || map_contains_key(rust_method_wraps_result(), function_name) } fn rust_runtime_bridge_collection_result_needs_rc_elements(function_name: String, result_type: InferredNode?) -> Bool { - // With Rc-baked container templates, map values are already Rc - // for user types. Adding .map(Rc::new) would double-wrap. + false } @@ -5063,31 +4670,12 @@ fn emit_rust_expr_record_lit(expr: Node, registry: Map, scope: ExprRecordLit { parent_enum: parent_enum } => let tn = record_lit_type_name_at(texpr: expr, source_indices: scope.type_env.source_indices) let fs = expr.children - // Peel a struct-alias literal to its underlying concrete struct before - // emission. `Gibibyte { count: x }` (where `type Gibibyte = - // Measure`) leaves `resolved_type` a bare alias leaf, - // so the emitter would name the alias (E0560: `Rc` has no field - // `count`), skip `Box`-wrapping the field, and skip the `Rc::new` share - // wrap. Expanding the alias chain to the `Measure { count }` struct - // (G3, same machinery the field-access TYPE lookup uses) makes the struct - // NAME, the field `Box` boxing, and the shared-type `Rc::new` all - // resolve exactly as the direct `Measure { .. }` literal already does. - // For a non-alias literal `resolved_type` is already the concrete record, - // and the expansion returns it unchanged. + let si = scope.type_env.source_indices let resolved_rt = resolved_type(n: expr) let expanded_rt = expand_type_for_field_access(n: resolved_rt, env: scope.type_env, module_name: scope.module_name) let variant_name = match tn { Present { value: n } => n Absent => "" } - // The struct-literal NAME passed to emit_typed_record_lit must be the - // CONCRETE struct (`Measure`), not the authored alias (`Gibibyte` = - // `Rc>`, not brace-constructible). `record_lit_type_name_at` - // gives the alias; `expanded_rt` is the peeled struct decl. Pass its name - // so the literal name, the `Box` field boxing (keyed on the type_name), - // and the `PhantomData` population (E0392) all resolve on the real struct - // — every one of these keys on this name, so they must agree (cursor - // review 31502). Only override for a plain struct-alias literal (peeled - // type is a named product, not an enum variant); variants / non-aliases - // keep their authored name. + let peeled_type_name = if parent_enum == none && expanded_rt.connective == Conj && expanded_rt.ident_span != none { let concrete = authored_name_at(source_indices: si, node: expanded_rt) if concrete != "" && concrete != variant_name { Present { value: concrete } } else { tn } @@ -5099,9 +4687,7 @@ fn emit_rust_expr_record_lit(expr: Node, registry: Map, scope: match contextual_variant_parent(variant_name: variant_name, parent_enum: parent_enum, resolved_type: expanded_rt, emit_info: emit_info, source_indices: si) { Present { value: p } => p Absent => - // Share-wrap on the peeled concrete struct (`Measure`) when the - // literal was written through an alias whose own name is not a - // shared type; falls back to the written name otherwise. + let expanded_name = authored_name_at(source_indices: si, node: expanded_rt) if expanded_rt.ident_span != none && expanded_name != "" && expanded_name != variant_name && set_contains(shared_types, expanded_name) { expanded_name @@ -5241,11 +4827,6 @@ fn is_map_typed_expr(texpr: Node, source_indices: Map) -> } } -// `discriminant(x)` lowering — Rust emission of the constructor-name intrinsic. -// Surgical (option 1): a `match` is synthesized only at the call site, so enums that are never -// discriminant'd emit nothing extra (no per-enum method, no stage0 bloat). Each arm maps a -// variant to its own authored name string — the same value the interpreter returns from -// `Value::Variant { variant_name }`, keeping run and emit paths consistent. fn emit_discriminant_call_lowering(value_arg: Node, registry: Map, scope: InferScope, depth: Int, shared_types: Set, emit_info: EmitGraphInfo) -> String { let arg = arg_value(n: value_arg) let scrutinee = emit_typed_expr(texpr: arg, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info, fuel: 1024) @@ -5264,8 +4845,7 @@ fn emit_discriminant_call_lowering(value_arg: Node, registry: Map ", emit_rust_host_to_dag_string_via_seam(host_expr: concat("\"", child_text, "\""), corpus_repr: emit_info.corpus_repr), ",") ) - // Borrow the scrutinee so the arms (which never bind fields) don't move it. Shared types - // are Rc-wrapped, so deref through the Rc with `&*` to reach `&T`; owned values use `&`. + let scrut_ref = if set_contains(shared_types, ty_name) { concat("&*(", scrutinee, ")") } else { @@ -5273,7 +4853,7 @@ fn emit_discriminant_call_lowering(value_arg: Node, registry: Map join(separator: "\n"), "\n })") } else { - // Product/record type: the discriminant is the type's own name (interpreter returns type_name). + emit_rust_host_to_dag_string_via_seam(host_expr: concat("\"", ty_name, "\""), corpus_repr: emit_info.corpus_repr) } Absent => emit_rust_host_to_dag_string_via_seam(host_expr: concat("\"", ty_name, "\""), corpus_repr: emit_info.corpus_repr) @@ -5301,11 +4881,7 @@ fn emit_typed_call_expr(func: String, args: List, inferred: InferredNode?, } else { emit_typed_call(func: func, args: args, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info) } - // No lookup Rc-wrapping needed: shared_types is the single authority for - // Rc decisions. Map values are already Rc-wrapped if they're non-Copy - // types, so lookup/map_get return the correct type directly. - // Runtime functions that return bare collections (map_keys, map_values) - // need Rc wrapping so their result is Rc>, matching List. + if rust_runtime_bridge_wraps_collection_result_in_rc(function_name: func) { concat("Rc::new(", call_str, ")") } else { call_str } @@ -5363,8 +4939,7 @@ fn emit_typed_call(func: String, args: List, registry: Map first { @@ -5433,8 +5008,7 @@ fn emit_typed_call(func: String, args: List, registry: Map join(separator: ", ") let runtime_name = rust_runtime_bridge_name(function_name: func) let si = scope.type_env.source_indices - // Callable param invoked while also passed as an argument (e.g. nat_cata succ - // recursion) borrows the impl Fn binding; clone at the call site per + Clone. + let callee_self_capture = if is_rt { false } else { filled_args |> any(a => expr_references_var(node: arg_value(n: a), var_name: func, source_indices: si)) } @@ -5622,7 +5196,6 @@ fn lambda_param_type_strs(params: List, param_nodes: List, fallbac ) } - fn emit_typed_collection_lambda(lambda_expr: Node, elem_type_str: String, registry: Map, scope: InferScope, depth: Int, shared_types: Set, emit_info: EmitGraphInfo) -> String { match lambda_expr.expr_data { ExprLambda => @@ -5638,19 +5211,13 @@ fn emit_typed_collection_lambda(lambda_expr: Node, elem_type_str: String, regist } } -// Emit a fold lambda with typed accumulator and element parameters. -// The accumulator type is always emitted as `_` because the reconciler's inferred -// BRIDGE: emitter compensation — uses acc_type_str as fallback for fold lambda -// accumulator annotation. Blocked on inference fold accuracy (CX-C). -// The inferred type for fold accumulators is often wrong (e.g. bare Map -// instead of Map>). Rust can infer the correct type from init and body. fn emit_typed_fold_lambda(lambda_expr: Node, acc_type_str: String, elem_type_str: String, registry: Map, scope: InferScope, depth: Int, shared_types: Set, emit_info: EmitGraphInfo) -> String { match lambda_expr.expr_data { ExprLambda => let ps = lambda_param_names_at(texpr: lambda_expr, source_indices: scope.type_env.source_indices) let bd = lambda_body(texpr: lambda_expr) let pn = lambda_expr.children |> skip(1) - // BRIDGE: Known unresolved acc type patterns → emit "_" to let Rust infer + let safe_acc_type = if acc_type_str == "Rc>" || acc_type_str == "Vec<()>" || acc_type_str == "Option<()>" { "_" } else { acc_type_str } let fallback_types = ps |> enumerate |> map(pair => if pair.first == 0 { safe_acc_type } else { elem_type_str } @@ -5659,9 +5226,7 @@ fn emit_typed_fold_lambda(lambda_expr: Node, acc_type_str: String, elem_type_str let params_str = param_strs |> join(separator: ", ") let lambda_scope = lambda_scope_from_children(scope: scope, params: ps, param_nodes: pn) let body_str = emit_typed_expr(texpr: bd, registry: registry, scope: lambda_scope, depth: depth, shared_types: shared_types, emit_info: emit_info, fuel: 1024) - // If the accumulator is in owned_bindings, unwrap Rc at iteration start. - // This rebinds the acc from Rc to owned T so field accesses move - // instead of clone, keeping Rc refcounts at 1 for in-place mutation. + let acc_name = match ps |> first { Present { value: n } => n Absent => "" } let needs_unwrap = acc_name != "" && set_contains(emit_info.owned_bindings, acc_name) if needs_unwrap { @@ -5829,10 +5394,7 @@ fn emit_rust_sort_by_method_call(receiver: Node, args: List, registry: Map emit_typed_collection_lambda(lambda_expr: arg_value(n: a), elem_type_str: elem_type_str, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info) Absent => "compile_error!(\"missing sort_by key function argument\")" } - // Use iter().cloned().collect() which works for both Rc> (auto-deref) - // and bare Vec. Avoids the (*recv).clone() issue with slices. - // When elem_type_str is "_", omit type annotations and use a.clone() directly - // as sort key — avoids the (|name: _| ...)(a.clone()) wrapper that Rust can't infer. + let sharing = language_spec(target: Rust).sharing let iter_str = apply_type_template1(template: sharing.iter_owned, arg0: recv_str) let a_clone = apply_type_template1(template: sharing.clone_value, arg0: "a") @@ -6037,9 +5599,7 @@ fn emit_typed_method_call(method_call_node: Node, receiver: Node, method: String concat(var_name, ".", emit_ident(name: method, target: Rust), "(", args_str, ").await?") } AlgebraMethodSemantics { method_def: method_def, fold_accumulator_type: fold_accumulator_type, size_effect: _, cost_shape: _, algebra_template: _ } => { - // Read method identity from the definition node. - // BRIDGE: reads method_def name until Lane C provides per-method - // rendering from language extdeps keyed by the definition node. + let method_name = authored_name_at(source_indices: scope.type_env.source_indices, node: method_def) if method_name == "fold" { emit_rust_fold_method_call(method_call_node: method_call_node, fold_accumulator_type: fold_accumulator_type, result_type: result_type, receiver: receiver, args: args, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info) @@ -6115,10 +5675,7 @@ fn emit_typed_match(scrutinee: Node, arms: List, registry: Map map(arm => emit_typed_match_arm(arm: arm, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info, scrut_type: scrut_type, match_result_type: match_result_type, string_from_mode: false)) let arms_str = arm_strs |> join(separator: "\n") - // RC5: If scrutinee is Option>, use .as_deref().cloned() to get Option. - // If scrutinee is Rc, use (*s).clone() to get owned T. - // When all arms are string literal patterns, convert scrutinee to &str - // so Rust can match &str patterns against String values. + let needs_as_str = all_arms_are_string_lit(arms: arms) && arms |> count > 0 let needs_string_from = has_string_lit_with_bind(arms: arms) if rc_match.needs_option_deref { @@ -6200,7 +5757,6 @@ fn emit_typed_let(name: String, value: Node, body: Node?, registry: Map Bool { match lookup_emit_type_summary(emit_info: emit_info, type_name: struct_name) { Present { value: summary } => @@ -6473,14 +6029,7 @@ fn emit_typed_record_lit(type_name: String?, fields: List, parent_enum: St Absent => let rt_is_type_var = if resolved_type.inferred != none { is_type_variable(inferred: resolved_type.inferred.value) } else { false } let rt_name = authored_name_at(source_indices: si, node: resolved_type) - // Only treat `rt_name` as an enum PARENT (prefixing `display_tn` as - // `Enum::Variant`) when `tn` is genuinely a variant of it -- the - // guard the sibling sites already use. Without it, a struct-alias - // literal whose expanded `resolved_type` names a different concrete - // struct than the written name would wrongly emit `Measure::Gibibyte` - // (cursor review 31502). For a plain struct it is not a variant, so - // the fallback yields `parent_enum` and `display_tn` stays the bare - // struct name. + if resolved_type.ident_span != none && rt_name != tn && !rt_is_type_var && rt_name != "Error" && variant_belongs_to_enum(type_summaries: emit_info.type_summaries, variant_name: tn, enum_name: rt_name) { Present { value: rt_name } @@ -6581,13 +6130,7 @@ fn emit_typed_record_lit(type_name: String?, fields: List, parent_enum: St } else { [] } Absent => [] } - // Populate the synthetic PhantomData field (E0392) whenever the target - // struct carries unused generic params -- the construction counterpart - // of the phantom field appended in emit_struct_from_children. `tn` here - // is the CONCRETE struct name (the caller passes the peeled - // `expanded_rt` name as type_name, not the alias), so this lookup hits - // the `Measure` decl (connective==Conj) and fires for Measure/its - // aliases, staying silent for every fully-used-param struct and variant. + let phantom_strs = match lookup_type_by_name(env: scope.type_env, name: tn) { Present { value: struct_decl } => let is_struct = struct_decl.connective == Conj @@ -6669,7 +6212,6 @@ fn is_string_typed_expr(e: Node, source_indices: Map) -> B } } - fn emit_typed_string_interp(parts: List, registry: Map, scope: InferScope, depth: Int, shared_types: Set, emit_info: EmitGraphInfo) -> String { let has_interpolations = parts |> any(p => match p { Interpolation { expr: _ } => true _ => false }) let fmt_parts = parts |> map(p => typed_interp_format_part(part: p, registry: registry, scope: scope, depth: depth, shared_types: shared_types, emit_info: emit_info, has_interpolations: has_interpolations)) @@ -6701,10 +6243,6 @@ fn emit_typed_block(stmts: List, registry: Map, scope: I concat("{\n", make_indent(level: depth + 1),(state.text |> join(separator: "\n")), "\n}") } -// ========================================================================= -// Node TCO -- Rust rendering -// ========================================================================= - fn emit_tco_init_block_stmts(remaining: List, text: List, scope: InferScope, registry: Map, depth: Int, shared_types: Set, emit_info: EmitGraphInfo, params: List) -> BlockEmitState { match remaining |> first { @@ -6935,10 +6473,6 @@ fn emit_typed_tco_reassign(args: List, params: List, registry: Map join(separator: "\n")), "\n}") } -// ========================================================================= -// Service definition emission -// ========================================================================= - fn emit_service_def( item: Node, registry: Map, @@ -7160,11 +6694,7 @@ fn emit_dry_run_branch_from_props(op_name: String, inferred: Node, mock_props: L Absent => concat(log_line, "\ncompile_error!(\"mock property list was non-empty but first() returned None\")") } } else { - // Missing dry-run fixture is a RUNTIME condition (is_dry_run() && no mock - // data), not a compile-time one — emitting compile_error! here gated a - // runtime state at compile time (§5 category error), making the whole - // emitted crate fail to build whenever any service op lacks a fixture. - // Fail closed at runtime instead: a loud panic on the unprovisioned path. + concat(log_line, "\npanic!(\"no mock data available for dry-run operation: ", op_name, "\")") } } @@ -7197,8 +6727,6 @@ fn emit_rest_call(op_name: String, transport: Node, registry: Map join(separator: "\n") } -// RE-1a: HTTP method dispatch — reads transport method field. -// Fail-closed: unknown method or non-identifier expression → compile_error! fn emit_rest_http_method(transport: Node, source_indices: Map) -> String { match transport_method(t: transport, source_indices: source_indices) { Present { value: m } => match m.inferred { @@ -7206,7 +6734,7 @@ fn emit_rest_http_method(transport: Node, source_indices: Map chars |> map(ch => to_lower_char(ch: ch)) |> join(separator: "") _ => - // Fallback for uninferred transport (fail-closed) + concat("compile_error!(\"transport method type not resolved\")") } Absent => "compile_error!(\"transport method not specified\")" @@ -7259,13 +6787,7 @@ fn emit_rest_auth_line(transport: Node, service_item: Node, http_method: String, let auth_input = service_config_auth_input(n: service_item, source_indices: source_indices) match auth_scheme { Present { value: auth } => { - // Single authority: when auth_source is declared, the credential lives - // in self.auth_token (acquired at construction time) and auth_input is - // not consulted. When auth_source is absent, auth_input names the - // per-call parameter. The operation input parameter (auth_token: Secret) - // still appears in the .dag definition for both cases — filtering it - // from the emitted signature when auth_source is present requires - // operation parameter pruning at emit time (tracked as RE follow-up #10). + let has_auth_source = match service_config_auth_source(n: service_item, source_indices: source_indices) { Present { value: _ } => true Absent => false } let token_param = if has_auth_source { "self.auth_token.clone()" } else { @@ -7287,7 +6809,7 @@ fn emit_rest_auth_line(transport: Node, service_item: Node, http_method: String, } concat("let request = client.", http_method, "(&url)\n .header(\"", header_name, "\", ", token_param, ");") _ => - // Fail-closed: unknown auth shape + concat("compile_error!(\"unrecognized auth scheme expression shape\"); let request = client.", http_method, "(&url);") } } @@ -7326,11 +6848,6 @@ fn emit_rest_query_line(transport: Node, corpus_repr: RustCorpusRepr, source_ind } } -// RE-1 POST body: emit .json() with serde_json::json! macro for request body. -// Body must be ExprRecordLit (field-init children). Fail-closed: any other -// expression shape produces compile_error! — no silent fabrication. -// Optional-typed fields are emitted as conditional insertions so None values -// are omitted from the wire body rather than serialized as null. fn emit_rest_body_line(transport: Node, source_indices: Map) -> String { match transport_request_body(t: transport, source_indices: source_indices) { Present { value: b } => match b.expr_data { @@ -7390,8 +6907,6 @@ fn has_response_200_property(op_node: Node, source_indices: Map any(p => field_init_node_name_at(n: p, source_indices: source_indices) == "response_200") } -// Returns none when the property is absent or when it is present but the body type -// cannot be resolved. Emitters must combine with has_response_200_property for fail-closed behavior. fn operation_response_200_resolved_type(op_node: Node, source_indices: Map, env: TypeEnv) -> Node? { match op_node.properties |> filter(p => field_init_node_name_at(n: p, source_indices: source_indices) == "response_200") |> first { Present { value: p } => { @@ -7844,10 +7359,6 @@ fn emit_local_call(op_name: String) -> String { "Ok(", emit_ident(name: op_name, target: Rust), "())") } -// ========================================================================= -// Resource definition emission -// ========================================================================= - fn emit_resource_def(item: Node, shared_types: Set, corpus_repr: RustCorpusRepr, env: TypeEnv) -> String { let item_text = authored_name(env: env, node: item) let cap_children = item.children @@ -7877,10 +7388,6 @@ fn emit_capability_method(cap_node: Node, shared_types: Set, corpus_repr concat(items.async_prefix, items.func_keyword, " ", emit_ident(name: authored_name(env: env, node: cap_node), target: Rust), "(", all_params, ") -> Result<", ret, ", Box>;") } -// ========================================================================= -// Data definition emission -// ========================================================================= - fn data_value_has_cross_refs(value: Node) -> Bool { match value.expr_data { ExprVar { binding_kind: _ } => true @@ -7914,7 +7421,7 @@ fn emit_data_def(name: String, type_node: Node, value: Node, registry: Map when body stores Rc::new(...) (e.g. TestClaim via call). + let ty_str = if needs_rc && !rust_type_is_rc_wrapped(type_name: ty_str) { wrap_shared_type(target: Rust, inner: ty_str) } else { @@ -8032,10 +7539,6 @@ fn emit_data_def_body(type_node: Node, value: Node, registry: Map, corpus_repr: RustCorpusRepr) -> TextFile { let depth = 0 let test_fns = projections @@ -8105,15 +7608,6 @@ fn emit_mock_prop_setup(mock_prop: Node, depth: Int, source_indices: Map) -> String { if features |> count > 0 { let feat_strs = features |> map(f => concat("\"", f, "\"")) @@ -8144,11 +7638,6 @@ fn emit_cargo_toml(crate_name: String, has_services: Bool) -> TextFile { TextFile { path: "Cargo.toml", content: concat(header, workspace, "\n[dependencies]\n", all_deps |> join(separator: "")) } } - -// ========================================================================= -// Workflow function collection -// ========================================================================= - type WorkflowFunc { name: String module_name: String @@ -8172,10 +7661,6 @@ fn extract_literal_string(expr: Node) -> String? { } } -// Resolve a param's default expression to a literal string value. -// Direct literals are extracted immediately. ExprVar references are -// resolved through the registry (for DataItem identity) and module -// items (for body content) — single authority chain, no scanning. fn resolve_param_default(param: Node, registry: Map, module_items: List, source_indices: Map) -> String? { match param_node_default_value(n: param) { Present { value: dv } => match dv.expr_data { @@ -8231,18 +7716,11 @@ fn to_workflow_func(item: Node, module_name: String, registry: Map, source_indices: Map) -> Bool { if item.body == none { false } else if item.uses |> count > 0 { true } else { - // Registry is total for body-bearing items that went through inference. - // A missing entry is a compiler invariant violation — the item should - // have been registered during inference. Fail-closed: include the item - // so the diagnostic surfaces rather than silently dropping a subcommand. + match lookup_item(registry: registry, name: authored_name_at(source_indices: source_indices, node: item)) { Present { value: info } => info.service_names |> count > 0 || info.resource_names |> count > 0 Absent => true @@ -8296,10 +7774,6 @@ fn validate_workflow_param_defaults(workflow_funcs: List) -> List< ) } -// ========================================================================= -// Generates a CLI entry point using clap with one subcommand per workflow -// ========================================================================= - fn find_resource_module(resource_name: String, modules: List) -> String { let matching = modules |> filter(m => m.items |> any(item => authored_name_at(source_indices: m.type_env.source_indices, node: item) == resource_name)) let result = match matching |> first { @@ -8538,8 +8012,6 @@ fn emit_main_fn(workflow_funcs: List, has_services: Bool, has_pipe concat(async_attr, "{\n", make_indent(level: depth + 1),body_lines, "\n}") } -// A5: Emit the Compile match arm for the bootstrap compile subcommand. -// FF-9: import-driven source resolution from source roots with transitive imports. fn emit_compile_match_arm(crate_name: String) -> String { let pipeline_mod = module_to_filename(name: "v1.compiler.compile") concat( @@ -8660,12 +8132,6 @@ fn emit_run_match_arm(crate_name: String) -> String { " },") } -// ========================================================================= -// Pipeline helper functions emitted at module level in main.rs -// FF-9: import-driven source resolution + diagnostic rendering -// ========================================================================= - -// Emit module-level functions for FF-9 import resolution. fn emit_main_pipeline_fns(crate_name: String) -> String { let pipeline_mod = module_to_filename(name: "v1.compiler.compile") let artifact_mod = module_to_filename(name: "v1.compiler.artifact") @@ -8997,10 +8463,6 @@ fn emit_main_service_arg_list(wf: WorkflowFunc, has_services: Bool) -> List TextFile { let content = concat( "// Generated by v1 compiler -- do not edit.\n", diff --git a/src/v1/artifact.dag b/src/v1/artifact.dag index a032b7c4db0..bbc5f7cdfdf 100644 --- a/src/v1/artifact.dag +++ b/src/v1/artifact.dag @@ -1,29 +1,15 @@ -// v2 artifact model -- explicit representation of buildable/deployable units. - module v1.compiler.artifact import v1.std.core { TextFile, SourceSpan } -// ========================================================================= -// Render target -- which language the emitter produces. -// ========================================================================= - type RenderTarget = Rust | Python | Go | Dag -// ========================================================================= -// Artifact kinds -// ========================================================================= - type ArtifactKind = ServiceBinary | Library | Frontend | GeneratedSupport -// ========================================================================= -// Artifact -// ========================================================================= - type Artifact { name: String kind: ArtifactKind @@ -32,10 +18,6 @@ type Artifact { dependencies: List } -// ========================================================================= -// Boundary -- how artifacts communicate -// ========================================================================= - type BoundaryKind = DirectCall | HttpJson @@ -50,19 +32,11 @@ type Boundary { contract: String } -// ========================================================================= -// Artifact plan -- what the compiler will build -// ========================================================================= - type ArtifactPlan { artifacts: List boundaries: List } -// ========================================================================= -// Explicit partition rules -- authoritative today -// ========================================================================= - type PartitionRule = Explicit { artifacts: List } @@ -73,21 +47,11 @@ fn plan_artifacts(rule: PartitionRule) -> ArtifactPlan { } } -// ========================================================================= -// Artifact output -- per-artifact emission result -// ========================================================================= - type ArtifactOutput { artifact: Artifact files: List } -// ========================================================================= -// Default plan -- compatibility wrapper -// ========================================================================= - -// Create a default single-artifact plan from the current pipeline state. -// This is the compatibility wrapper -- current behavior becomes an explicit plan. fn default_artifact_plan(root_modules: List, target: RenderTarget) -> ArtifactPlan { plan_artifacts(rule: Explicit { artifacts: [Artifact { name: "default", @@ -98,27 +62,19 @@ fn default_artifact_plan(root_modules: List, target: RenderTarget) -> Ar }] }) } -// ========================================================================= -// DAG artifact substrate (post-infer typed graph JSON, --target dag) -// ========================================================================= - -// 🟡 needs-more-work — feature:v2-dag-node-content-hash — dissolve-on: v2 Node/content_hash is DagNodeId authority (audit v2-dag-artifact-zip-fold-hang-2026-05-21.md). type DagNodeId = String -// 🟡 needs-more-work — feature:v2-dag-typed-inferred-record — dissolve-on: emit_dag_artifact constructs DagInferredRecord instead of inferred JSON strings. type DagInferredRecord = ResolvedRef { node: DagNodeId } | TypeVariableRef { id: String } | CompilerErrorRecord { message: String, span: SourceSpan } -// 🟡 needs-more-work — feature:v2-dag-typed-module-ref — dissolve-on: serialize_typed_module returns DagModuleRef (modules[] stops using raw JSON strings). type DagModuleRef { module: DagNodeId items: List item_registry_keys: List } -// 🟡 needs-more-work — feature:v2-dag-typed-diagnostic-record — dissolve-on: typed ErrorNode projection populates DagDiagnosticRecord in DagArtifact. type DagDiagnosticRecord { severity: String message: String @@ -127,7 +83,6 @@ type DagDiagnosticRecord { category: String? } -// 🟡 needs-more-work — feature:v2-dag-typed-artifact-emit — dissolve-on: emit_dag_artifact returns DagArtifact (JSON encoder walks carrier, not string concat). type DagArtifact { version: String nodes: Map diff --git a/src/v1/coercion.dag b/src/v1/coercion.dag index 39b1c605468..93b8407454a 100644 --- a/src/v1/coercion.dag +++ b/src/v1/coercion.dag @@ -1,5 +1,3 @@ -// Type realization: data-driven type coercion from .dag algebra declarations. - module v1.compiler.coercion import v1.compiler.artifact { RenderTarget, Rust, Python, Go, Dag } @@ -19,10 +17,6 @@ import extdeps.languages.go.types { } import extdeps.languages.dag.types { dag_type_checkpoints } -// ========================================================================= -// Per-target dispatch -// ========================================================================= - fn target_checkpoints(target: RenderTarget) -> List { match target { Rust => rust_type_checkpoints @@ -64,10 +58,6 @@ fn target_optional_template(target: RenderTarget) -> String { } } -// ========================================================================= -// Level 0: Cast syntax lookup -// ========================================================================= - fn target_cast_syntax(target: RenderTarget) -> CastSyntax? { match target { Rust => Present { value: rust_cast_syntax } @@ -92,16 +82,10 @@ fn render_cast(expr_str: String, type_str: String, target: RenderTarget) -> Stri replace(replace(syntax.template, "\{expr\}", expr_str), "\{type\}", type_str) } -// ========================================================================= -// Level 1: Checkpoint lookup (primitive scalar coercion) -// ========================================================================= - fn lookup_checkpoint(target: RenderTarget, dag_name: String) -> TypeCheckpoint? { target_checkpoints(target: target) |> filter(cp => cp.dag_name == dag_name) |> first } -// Transitional: fail-open (returns dag_name on miss). M5-full should -// fail-closed once all surface types have checkpoint declarations. fn coerce_primitive_type(target: RenderTarget, dag_name: String) -> String { match lookup_checkpoint(target: target, dag_name: dag_name) { Present { value: cp } => cp.target_type @@ -126,10 +110,6 @@ fn literal_suffix(target: RenderTarget, dag_name: String) -> String? { } } -// ========================================================================= -// Level 2: Algebra inhabitant lookup (container coercion) -// ========================================================================= - fn lookup_inhabitant(target: RenderTarget, algebra: String) -> InhabitantDecl? { target_inhabitants(target: target) |> filter(inh => inh.algebra == algebra) |> first } @@ -144,10 +124,6 @@ fn coerce_container_template(target: RenderTarget, container_name: String) -> St } } -// ========================================================================= -// Template application utilities -// ========================================================================= - fn apply_inhabitant_template1(template: String, inner: String) -> String { replace(template, "\{0\}", inner) } @@ -156,10 +132,6 @@ fn apply_inhabitant_template2(template: String, first: String, second: String) - replace(replace(template, "\{0\}", first), "\{1\}", second) } -// ========================================================================= -// Structural test extraction -- auto-generate coercion tests from data -// ========================================================================= - type CoercionAssertion = CheckpointAssertion { target: RenderTarget, dag_name: String, expected_type: String } | ContainerAssertion { target: RenderTarget, container_name: String, expected_template: String } @@ -193,8 +165,6 @@ fn checkpoint_tests(target: RenderTarget) -> List { } } -// Generate container test: coerce_container_template(target, name) == Some(template) -// Derived from canonical_container_names (std/types.dag) — single authority. fn inhabitant_test_names() -> List { canonical_container_names() } @@ -265,4 +235,3 @@ fn extract_coercion_tests() -> List { copy_tests(), template_application_tests()) } - diff --git a/src/v1/compile.dag b/src/v1/compile.dag index cc9a0844046..5ff2a44b2a8 100644 --- a/src/v1/compile.dag +++ b/src/v1/compile.dag @@ -1,5 +1,3 @@ -// v2 compiler pipeline -- the full .dag-to-files transform. - module v1.compiler.compile import v1.std.core { @@ -41,10 +39,6 @@ import std.termination { import v1.compiler.ownership { OwnershipProof, OwnershipDecision, SharedError, analyze_ownership } import v1.compiler.artifact { ArtifactPlan, Artifact, RenderTarget, Dag, default_artifact_plan } -// ========================================================================= -// Types -// ========================================================================= - type SourceFile { path: String content: String @@ -77,10 +71,6 @@ type FrontendPrepared { newline_index: NewlineIndex } -// ========================================================================= -// Function extraction for pipeline analyses -// ========================================================================= - fn extract_func_entries(typed: ResolvedGraph) -> List { typed.modules |> filter(m => m.items |> any(item => item.body != Absent)) @@ -123,22 +113,6 @@ fn ownership_diagnostics(proofs: List) -> List { ) } -// ========================================================================= -// Complexity analysis (opt-in; v2 emission gate unchanged) -// -// When enabled, classify_recursion_pattern / build_complexity_report can -// surface ComplexityUnknown for non-descending self-calls (fn spin(n: n)). -// -// v2 does NOT fail-closed on those diagnostics today — emission gates on -// typed_diags only (see type_errors filter below); main.rs and dag run -// carve out ComplexityUnknown (00_core.dag is_interpreter_blocking_diagnostic, -// cx-design.md C6). Tier-1 CX enforcement is v3 Pure Bootstrap scope. -// -// Default-off skips build_complexity_report (~356s full-src compile) because -// the report is not consumed by emission. Callers that need the report or -// CX diagnostics pass CompilePipelineOptions { analyze_complexity: true }. -// ========================================================================= - type CompilePipelineOptions { analyze_complexity: Bool } @@ -159,10 +133,6 @@ fn complexity_diagnostics(complexity: ComplexityReport) -> List { ) } -// ========================================================================= -// Diagnostic collection -// ========================================================================= - fn empty_artifact_plan() -> ArtifactPlan { ArtifactPlan { artifacts: [], boundaries: [] } } @@ -195,11 +165,6 @@ fn json_optional_string(value: String?) -> String { } } -// ------------------------------------------------------------------------- -// DAG artifact v0.2.0: collect each Node once, emit NodeId ($ref) elsewhere. -// See v1.compiler.artifact { DagArtifact, DagNodeId } and audit doc. -// ------------------------------------------------------------------------- - type DagCollectAcc { seen: Map order: List @@ -212,8 +177,6 @@ type DagCollectPending { fp: String } -// Provisional location key until v2 content_hash (see DagNodeId 🟡 gate). Collection -// fail-closes when two distinct nodes share this key (dag_node_fingerprint mismatch). fn dag_node_key(node: Node) -> String { let anchor = dag_node_collection_anchor(node: node) concat( @@ -224,18 +187,12 @@ fn dag_node_key(node: Node) -> String { }) } -// Resolved on expression nodes is ordinary type inference (ExprVar, calls, etc.). -// Only NoExprData identity shells (type/import back-links) share the declaration anchor. -// 🟡 predicate dissolution — feature:T-37-dag-collect-identity-anchor — bind T-37 — -// dissolve-on: DagNode exposes a modeled identity-shell fact (or anchor walk is structurally -// total on shells) so collection does not match expr_data × InferredNode here; -// forbidden: permanent cross-pass is_* over Node storage shape outside canonical query surface. fn dag_node_is_resolved_identity_shell(node: Node) -> Bool { match node.expr_data { NoExprData => match node.inferred { Present { value: Resolved { node: _ } } => - // Type/import back-links only — never peel nodes that carry structure. + node.body == none && node.transport == none && (node.children |> count) == 0 && (node.params |> count) == 0 _ => false @@ -441,7 +398,7 @@ fn dag_collect_insert(node: Node, acc: DagCollectAcc) -> DagCollectAcc { collision_errors: list_push(acc.collision_errors, dag_node_key_collision_error(key: key, span: anchor.span)) } } else { - // Authored span: provisional key may alias; first canonical record wins until v2 content_hash. + acc } Absent => { @@ -462,9 +419,6 @@ fn dag_collect_insert(node: Node, acc: DagCollectAcc) -> DagCollectAcc { } } -// Import statements (module.params) are not collected: module shell skips param walk, -// imports serialize inline via imports[] (string names), and ref validation skips -// module-shell params + import-statement children (T-37). fn dag_collect_from_module(module: TypedModule, acc: DagCollectAcc) -> DagCollectAcc { let acc = dag_collect_insert(node: module.module, acc: acc) module.items |> fold(init: acc, f: (a, item) => dag_collect_insert(node: item, acc: a)) @@ -485,7 +439,6 @@ fn dag_graph_source_indices(typed: ResolvedGraph) -> Map { typed.modules |> fold(init: empty_map(), f: (acc, m) => map_merge(acc, m.type_env.source_indices)) } -// Only called after dag_emit_ref_errors passes (fail-closed preflight). fn serialize_node_ref(node: Node, key_to_id: Map) -> String { match map_get(key_to_id, dag_node_key(node: node)) { Present { value: id } => concat("{\"$ref\": ", json_quote(s: id), "}") @@ -551,9 +504,6 @@ fn field_value_shape_name(value: FieldValueShape) -> String { } } -// (P5.3: severity_name and error_category_name deleted -- severity and category -// are now strings stored in diagnostic Node properties, not enum variants.) - fn var_binding_kind_name(value: VarBindingKind) -> String { match value { LocalValueBinding => "LocalValueBinding" @@ -981,24 +931,17 @@ fn serialize_param(param: Node, source_indices: Map, key_t "}") } -// 🟡 predicate dissolution — feature:T-37-dag-collect-identity-anchor — bind T-37 — -// Shape-filter on module shell (all params are import slots); not a semantic Module tag. fn is_module_shell_node(n: Node) -> Bool { n.inferred == none && n.expr_data == NoExprData && n.connective == NoConnective && n.body == none && n.transport == none && n.uses |> count == 0 && n.params |> all(p => is_import_slot_node(p: p)) } -// 🟡 predicate dissolution — feature:T-37-dag-collect-identity-anchor — bind T-37 — fn is_import_slot_node(n: Node) -> Bool { import_is_all(n: n) || ( n.params |> count == 0 && n.ident_span != none && n.body == none && n.expr_data == NoExprData) } -// 🟡 predicate dissolution — feature:T-37-dag-collect-identity-anchor — bind T-37 — -// Shape-filter for import statements (import_is_all OR specific-import name children), -// NOT a general semantic discriminator — safe here because callers gate on traversal -// context (dag_emit_check_node_refs on collected order nodes; module.params slots). fn is_import_statement_node(n: Node) -> Bool { import_is_all(n: n) || ( (n.children |> count) > 0 && n.body == none && n.expr_data == NoExprData @@ -1184,10 +1127,6 @@ fn front_end_sources(sources: List) -> FrontendResult { } } -// ========================================================================= -// Full pipeline -// ========================================================================= - fn resolve_sources(sources: List) -> CompileResult { let frontend = front_end_sources(sources: sources) CompileResult { files: [], diagnostics: frontend.diagnostics } @@ -1224,9 +1163,6 @@ fn compile_to_resolved(sources: List) -> ResolvedPipelineResult { ) } -// Discovery-corpus merged resolve: same pipeline as `compile_to_resolved`, but -// advisory-demoted typecheck diagnostics do not nil the typed graph (SCAFFOLD in -// `00_core.dag`; consumed by `discover_owned_data_decls`). fn compile_to_resolved_discovery_corpus_advisory(sources: List) -> ResolvedPipelineResult { compile_to_resolved_with_options_gated( sources: sources, diff --git a/src/v1/compiler_tests_rust.dag b/src/v1/compiler_tests_rust.dag index f312d3e51e4..20e5e0d3fcb 100644 --- a/src/v1/compiler_tests_rust.dag +++ b/src/v1/compiler_tests_rust.dag @@ -1,10 +1,3 @@ -// v2 compiler test harness template for Rust stage0. -// -// Defines the Rust source text for the compiler_tests module. -// This is the single authority for the stage0 test harness. -// The emit layer calls compiler_tests_source() and writes the result -// as src/compiler_tests.rs in the emitted crate. - module v1.compiler.compiler_tests_rust import v1.compiler.artifact { RenderTarget, Rust, Python, Go } diff --git a/src/v1/complexity.dag b/src/v1/complexity.dag index edbfa26d434..f8aa78678e9 100644 --- a/src/v1/complexity.dag +++ b/src/v1/complexity.dag @@ -1,17 +1,3 @@ -// v2 complexity analysis -- cost algebra and proof vocabulary. -// -// Defines the symbolic types for runtime complexity analysis of DAG -// execution structure. The analyzer walks authoritative program structure -// and produces symbolic cost terms. Program IR and cost IR are separate: -// these types are proof objects, not program nodes. -// -// Two metrics are first-class: -// work -- total operations performed -// span -- critical path length under DAG dependency structure -// -// D2: get_or_compute_summary -- lazy cached summary computation -// D4: build_complexity_report -- collect summaries (no violations — all costs concrete) - module v1.compiler.complexity import std.types { SourceSpan } @@ -88,10 +74,6 @@ import std.graph { is_lexicographic_descent, is_valid_proof } -// ========================================================================= -// Size expressions -- symbolic sizes for analysis -// ========================================================================= - type SizeExpr = SizeConst { value: Int } | SizeVar { name: String } @@ -99,10 +81,6 @@ type SizeExpr | SizeAdd { left: SizeExpr, right: SizeExpr } | SizeMax { left: SizeExpr, right: SizeExpr } -// ========================================================================= -// Cost expressions -- symbolic costs -// ========================================================================= - type CostExpr = CostConst { value: Int } | CostAdd { left: CostExpr, right: CostExpr } @@ -113,37 +91,8 @@ type CostExpr | CostExtern { name: String } | CostUnknown { reason: String } -// ========================================================================= -// Certainty of a proof -// ========================================================================= - type Certainty = Proven | Conservative -// ========================================================================= -// Emission cost shape -- single authority on method cost structure -// -// Every IntrinsicMethod has exactly one CostShape, computed by -// intrinsic_method_cost_shape. Adding a new IntrinsicMethod variant -// without a CostShape causes an exhaustive match failure. -// -// Shapes: -// ShapeConstant -- O(1) regardless of input size -// ShapeLinearScan -- O(|receiver|) with O(1) per element (count, join) -// ShapeIterateBody -- O(|receiver| * body_cost) (map, filter, fold) -// ShapeSortBody -- O(|receiver| * body_cost), Conservative (sort_by) -// produces_collection -- derived at consumer from expression return type -// ========================================================================= - - -// Child accessor identity is now derived from the IR child layout model -// in 00_core.dag (expr_child_roles / wrapper_child_roles). The hardcoded -// child_accessor_table has been deleted — use is_child_accessor_in_model() -// imported from v1.std.core instead. - -// ========================================================================= -// Complexity summary -- the main output type -// ========================================================================= - type ComplexitySummary { work: CostExpr span: CostExpr @@ -151,26 +100,10 @@ type ComplexitySummary { certainty: Certainty } -// ========================================================================= -// D2: Cost intern table -- lazy + cached complexity analysis -// -// CostExpr trees can grow exponentially during composition. The intern -// table deduplicates structurally identical subtrees via hash-consing. -// Summaries are cached per function name for lazy on-demand computation. -// -// Usage: thread CostInternTable through analysis calls. Each call may -// return an updated table with new cache entries. -// ========================================================================= - type CostInternTable { summaries: Map } -// Distinct concept from v1.std.core.empty_intern_table (the string InternTable -// constructor): this builds the complexity-summary CostInternTable. The names -// must not collide — §3 single authority — so this module-local helper is named -// for the type it constructs. (The full compile→emit closure pulls in BOTH -// modules, so a shared `empty_intern_table` name was a real cross-module fork.) fn empty_cost_intern_table() -> CostInternTable { CostInternTable { summaries: empty_map() } } @@ -185,10 +118,6 @@ fn lookup_summary(table: CostInternTable, func_name: String) -> ComplexitySummar map_get(table.summaries, func_name) } -// Evict a summary from the intern table, dropping its CostExpr trees. -// Replaces the full CostExpr tree with a trivial constant so the Rc -// nodes of the original tree are freed. Safe only when no future caller -// will read this summary (fan-in has reached 0). fn evict_summary(table: CostInternTable, func_name: String) -> CostInternTable { cache_summary(table: table, func_name: func_name, summary: ComplexitySummary { work: CostConst { value: 0 }, @@ -198,18 +127,6 @@ fn evict_summary(table: CostInternTable, func_name: String) -> CostInternTable { }) } -// ========================================================================= -// D3: DAG composition rules -// -// Compose complexity summaries over call graph structure. -// Each rule corresponds to a DAG pattern: -// sequential → cost_add -// parallel → cost_max (for span) -// loop → cost_mul(iterations, body) -// conditional → condition + cost_max(branches) -// recursive → pattern-match on SCC structure -// ========================================================================= - type CallEdge { caller: String callee: String @@ -238,7 +155,6 @@ type ParserStateParam { index: Int } - type ParserResultSource = ParserResultAdvance { input: DescentEvidence } | ParserResultExpect { input: DescentEvidence } @@ -278,9 +194,6 @@ type SccEdgeBlockAcc { vars: Map } -// CX-C: Check if a method has algebra iteration semantics (fold, map, etc.) -// by reading the structural MethodSemantics fact carried on ExprMethodCall, -// rather than string-matching method names. fn is_algebra_iteration_method(method_semantics: MethodSemantics?) -> Bool { match method_semantics { Present { value: AlgebraMethodSemantics { method_def: _, fold_accumulator_type: _, size_effect: _, cost_shape: _, algebra_template: _ } } => true @@ -306,12 +219,6 @@ fn method_callback_element_position(method_semantics: MethodSemantics?) -> Int? } } -// Extract the element parameter name from a lambda argument using the -// algebra template's callback_element_position. The template declares -// which CallableOf param receives IteratedSubValue of the receiver — -// this function reads that declared position and indexes into the -// lambda's params via skip(position). Fail-closed: returns none when -// no template declares a position (no heuristic guessing). fn iteration_element_name(method_semantics: MethodSemantics?, lambda: Node, si: Map) -> String? { let params = lambda_param_names_at(texpr: lambda, source_indices: si) match method_callback_element_position(method_semantics: method_semantics) { @@ -320,11 +227,6 @@ fn iteration_element_name(method_semantics: MethodSemantics?, lambda: Node, si: } } - -// Thin wrapper: is_valid_proof returns true when proof is valid (no cycles). -// This function returns true when there IS a cycle (inverted sense for callers). -// Delegates to std.termination.is_valid_proof as the single authority. -// CX-P: accepts the actual proof so is_valid_proof can validate dimensions. fn proof_has_non_descending_cycle(proof: TerminationProof, members: List, edges: List) -> Bool { is_valid_proof(proof: proof, edges: edges) == false } @@ -333,16 +235,6 @@ fn set_has(m: Map, key: String) -> Bool { map_get(m, key) != none } -// `descent_evidence_bounded_lattice` / lifted helpers live in `std.termination` -// (single authority for the `DescentEvidence` lattice). Parser-progress paths in this module use -// `strict_after_parser_witness` (below) — they do not import `promote_to_strict` anymore. -// -// P5: `promote_to_strict` remains in `std.termination` for bootstrap / mirror parity and -// lattice receipts; rename to `evidence_passthrough_preserving_strict` is still queued there -// (see `std.termination` header), not on `complexity.dag` import surface. - -// Concrete parser witnesses advance a state that is already known not to be -// ahead of the original parser state. They do not rescue unknown provenance. fn strict_after_parser_witness(input: DescentEvidence) -> DescentEvidence { match input { DescentUnknown => DescentUnknown @@ -1142,13 +1034,10 @@ fn infer_all_parser_always_advancing(func_index: Map, si: Map infer_parser_always_advancing_members(members: members, func_index: func_index, si: si) } -// Typed empty map helpers — eliminates BRIDGE fabrications for standalone -// empty_map() calls where the emitter can't infer the value type. fn seed_string_map(key: String, value: String) -> Map { map_insert(empty_map(), key, value) } - fn seed_bool_map(key: String) -> Map { map_insert(empty_map(), key, true) } @@ -1161,7 +1050,6 @@ fn seed_func_entry_map(key: String, value: FuncEntry) -> Map map_insert(empty_map(), key, value) } - fn build_call_graph_from_parser_edges(names: List, edges: List) -> CallGraph { CallGraph { edges: edges @@ -1195,10 +1083,7 @@ fn collect_parser_edges_for_scc(members: List, func_index: Map flat_map(name => match map_get(func_index, name) { @@ -1252,14 +1137,6 @@ fn count_self_calls(body: Node, func_name: String, si: Map own + from_children } -// Count maximum self-calls on any single execution path through an -// expression. Match/if arms are mutually exclusive (max, not sum). -// Self-calls inside fold/forEach bodies are already bounded by the -// iteration and do not contribute to the recursion branching factor. -// -// This distinguishes tree traversal (O(n)) from branching recursion -// (O(2^n)). A function with 15 self-call sites across 10 match arms -// has max_path_calls = max(arm calls), not sum(arm calls). fn max_path_self_calls(body: Node, func_name: String, si: Map) -> Int { max_path_self_calls_with_cont(body: body, func_name: func_name, continue_calls: 0, si: si) } @@ -1297,16 +1174,7 @@ fn max_path_self_calls_with_cont(body: Node, func_name: String, continue_calls: cond_calls + branch_max ExprForEach => - // Self-calls inside a forEach body are bounded by the iteration. - // They do NOT contribute to the recursion branching factor. - // Only count calls in the collection expression (not the body). - // - // Soundness note: this exclusion is correct for path-counting (the - // branching factor). However, self_calls_have_descending_witness - // DOES recurse into the forEach body via the `_ => children` arm, - // so recursive self-calls inside forEach bodies are still checked - // for descent witnesses. A negative test should verify this - // (ROADMAP regression test: forEach body containing self-calls). + max_path_self_calls_with_cont(body: foreach_collection(texpr: body), func_name: func_name, continue_calls: 0, si: si) + continue_calls ExprLet => @@ -1503,33 +1371,11 @@ fn expr_descending_witness_source(expr: Node, descending_witness_names: Map expr_descending_witness_source(expr: binop_left(texpr: expr), descending_witness_names: descending_witness_names, si: si) _ => none } - // ExprFieldAccess and ExprMethodCall are NOT treated as descent - // witnesses without resolved recursive-field facts. Although .dag - // values are acyclically constructed (Decidability Invariant Part 1), - // field access on `x` is only a valid RECURSION descent witness when - // the accessed field is a proven recursive child position of x's type. - // `x.metadata` (Int field) or `x.sibling_payload` (non-recursive - // field) are substructures of x but do not prove that the recursion - // measure decreases. Treating arbitrary field access as descent would - // accept unsound programs like `f(match x { A(child) => child, B(_) => x })`. - // - // Container-child descent (CX-1) handles Node tree walkers via - // accessor identity. User-defined recursive unions deferred until - // resolve/infer carry descent witnesses downstream. + _ => none } } -// Soundness note on witness propagation through control flow: -// -// This function propagates descent witness names through let-bindings. -// It does NOT propagate through ExprIf or ExprMatch branches because -// "either branch yields a witness" is unsound: `if cond { n - 1 } else { n }` -// would be accepted as descent when only some execution paths actually descend. -// Similarly, `match x { A(child) => child, B(_) => x }` has a non-descending -// arm. For ExprIf/ExprMatch to propagate witnesses, ALL branches must yield -// descent from the same parameter — a requirement that is not yet enforced. -// This is tracked in ROADMAP §I1. fn condition_param_names(expr: Node, param_set: Map, si: Map) -> Map { match expr.expr_data { ExprVar { binding_kind: _ } => @@ -1614,22 +1460,6 @@ fn has_arithmetic_descent(body: Node, func_name: String, si: Map first, recurses on -// list |> skip(1). List length strictly decreases. -// ========================================================================= - -// Check if an expression is `accessor(param)` where accessor is a known -// child-position accessor from 00_core.dag. -// CX-L: recognize accessor(param) AND accessor(descent_var) as child descent. -// If a is a descent var (sub-tree of param), then accessor(a) extracts a child -// of that sub-tree — strictly smaller than param. fn is_accessor_of_param(expr: Node, param_name: String, vars: Map, si: Map) -> Bool { match expr.expr_data { ExprCall { call_semantics: _, descent_evidence: _ } => @@ -1657,23 +1487,18 @@ fn is_children_of_param(expr: Node, param_name: String, vars: Map, ExprVar { binding_kind: _ } => expr_var_name_at(texpr: base, source_indices: si) == param_name _ => false } - // CX-K: a variable previously bound to param.children is also children. + ExprVar { binding_kind: _ } => set_has(m: vars, key: expr_var_name_at(texpr: expr, source_indices: si)) ExprMethodCall { method_semantics: ms } => match method_size_effect(method_semantics: ms) { Present { value: ShrinkEffect } => is_children_of_param(expr: method_receiver(texpr: expr), param_name: param_name, vars: vars, si: si) _ => false } - // CX-J: skip(param.children, N) function-call form is normalized to - // ExprMethodCall by inference (04_infer.dag Tier 2 bridge). Dead post-inference. + _ => false } } -// CX-C: Check if an expression is structural children of the parameter -// or a known descent variable. Handles: -// - param.children / descent_var.children -// - param.children |> skip(N) / descent_var.children |> skip(N) fn is_structural_children(expr: Node, param_name: String, vars: Map, si: Map) -> Bool { is_children_of_param(expr: expr, param_name: param_name, vars: vars, si: si) || match expr.expr_data { @@ -1691,8 +1516,7 @@ fn is_structural_children(expr: Node, param_name: String, vars: Map is_structural_children(expr: method_receiver(texpr: expr), param_name: param_name, vars: vars, si: si) _ => false } - // CX-J: skip(descent_var.children, N) function-call form is normalized to - // ExprMethodCall by inference (04_infer.dag Tier 2 bridge). Dead post-inference. + _ => false } } @@ -1711,10 +1535,6 @@ fn is_child_descent_expr(expr: Node, param_name: String, vars: Map } } -// CX-D: Tree-size-preserving wrapper transparency. -// Some functions return a Node with identical children/structure but different -// surface properties (e.g., cardinality). The complexity analyzer can see -// through these wrappers: wrapper(n: arg) has the same tree size as arg. fn is_tree_size_preserving_wrapper(expr: Node, si: Map) -> Bool { match expr.expr_data { ExprCall { call_semantics: _, descent_evidence: _ } => @@ -1731,11 +1551,6 @@ fn wrapper_inner_arg(expr: Node) -> Node { } } -// CX-D: Sub-value extraction. Some functions return a proper sub-value of -// their Node argument (tree_size(result) < tree_size(arg)). Unlike wrappers -// which preserve tree size, extractors strictly reduce it. -// rt_type extracts n.inferred.Resolved.node; param_node_type_expr extracts -// n.children |> first. Both produce a strict sub-value of the input Node. fn is_sub_value_extractor(expr: Node, si: Map) -> Bool { match expr.expr_data { ExprCall { call_semantics: _, descent_evidence: _ } => @@ -1764,8 +1579,7 @@ fn is_list_shrink_expr(expr: Node, param_name: String, si: Map expr_var_name_at(texpr: method_receiver(texpr: expr), source_indices: si) == param_name _ => false } - // Verify skip amount is a positive integer literal (fail-closed: skip(0) or - // non-literal skip amounts are not accepted as descent evidence). + && match method_arg_nodes(texpr: expr) |> first { Present { value: arg_node } => match arg_value(n: arg_node).expr_data { @@ -1778,10 +1592,6 @@ fn is_list_shrink_expr(expr: Node, param_name: String, si: Map skip(N) with N > 0. -// Unlike is_list_shrink_expr (which requires the receiver to be the measure -// param variable), this permits arbitrary receivers — callers classify the -// receiver's evidence separately and elevate NonIncreasing to Strict. fn is_generalized_shrink(expr: Node, si: Map) -> Bool { match expr.expr_data { ExprMethodCall { method_semantics: ms } => @@ -1802,9 +1612,6 @@ fn is_generalized_shrink(expr: Node, si: Map) -> Bool { } } -// CX-R: Check if an expression is a list-preserving pass-through call -// and extract the inner tokens argument. Uses parser_passthrough_state_expr -// (single authority in 02_parse.dag) to identify skip_newlines/skip_continuation_newlines. fn list_passthrough_inner(expr: Node, si: Map) -> Node? { parser_passthrough_state_expr(expr: expr, source_indices: si) } @@ -1817,17 +1624,6 @@ fn list_passthrough_inner_arg(expr: Node, si: Map) -> Node list_passthrough_inner(expr: expr, si: si) } -// CX-R: Check if a call is to a tokens-consuming helper. -// Parser convention: any function whose call site uses a named "tokens:" -// argument, where the argument traces back to the caller's measure param, -// is treated as consuming. This generalizes advance/expect/expect_name and -// covers parse_* helpers that form a structural subset of the parser. -// -// Optimistic soundness: parser code always error-checks before using the -// result for recursion (if has_err { return }; ... r.tokens ...), so on -// the path where recursion happens, the helper has successfully consumed. -// The check is scoped by the presence of a "tokens:" named argument at -// the call site — non-parser code does not use this convention. fn is_tokens_consuming_call(expr: Node, param_name: String, vars: Map, si: Map) -> Bool { match expr.expr_data { ExprCall { call_semantics: _, descent_evidence: _ } => @@ -1839,7 +1635,6 @@ fn is_tokens_consuming_call(expr: Node, param_name: String, vars: Map) -> Node? { expr.children |> fold(init: none, f: (acc, child) => match acc { @@ -1855,9 +1650,6 @@ fn consuming_tokens_arg(expr: Node, si: Map) -> Node? { ) } -// CX-R: Check if an expression carries the tokens dimension — either the -// measure param itself, a descent var, or a NonIncreasing alias (field-name -// provenance, list shrink, or pass-through wrapper of any of these). fn is_tokens_input_expr(expr: Node, param_name: String, vars: Map, si: Map) -> Bool { match expr.expr_data { ExprVar { binding_kind: _ } => @@ -1925,14 +1717,10 @@ fn expr_contains_descent(expr: Node, param_name: String, vars: Map Present { value: last_stmt } => expr_contains_descent(expr: last_stmt, param_name: param_name, vars: vars, check_child: check_child, check_list: check_list, si: si) Absent => false } - // CX-Q: bare param is NOT descent — only proper sub-values are. - // param.field IS descent (field access reduces tree), but param alone - // is Same/NonIncreasing. Conflating them caused is_if_option_descent - // to promote `if cond { param } else { none }` as descent. + ExprVar { binding_kind: _ } => set_has(m: vars, key: expr_var_name_at(texpr: expr, source_indices: si)) - // CX-E: param.field or descent_var.field is a sub-value (acyclic construction), - // but only if the field is a known sub-value field (structural witness). + ExprFieldAccess => let field = field_access_field_at(texpr: expr, source_indices: si) let base = field_access_base(texpr: expr) @@ -1943,8 +1731,7 @@ fn expr_contains_descent(expr: Node, param_name: String, vars: Map bname == param_name || set_has(m: vars, key: bname) _ => false } - // CX-M: methods with a declared size effect (projection, shrink, identity) - // preserve or extract descent. If the receiver is descent, the result is descent. + ExprMethodCall { method_semantics: ms } => let receiver = method_receiver(texpr: expr) match method_size_effect(method_semantics: ms) { @@ -1958,9 +1745,6 @@ fn expr_contains_descent(expr: Node, param_name: String, vars: Map } } -// CX-F: Extract variable bindings from nested match patterns. -// Handles arbitrarily deep patterns like Some { value: Resolved { node: x } } -// by recursing into VariantPattern field bindings. fn collect_field_binding_names(fb: Node, vars: Map) -> Map { match field_binding_pattern(n: fb) { Bind { name: binding_name } => map_insert(vars, binding_name, true) @@ -1972,21 +1756,6 @@ fn collect_field_binding_names(fb: Node, vars: Map) -> Map ... }`, -// `let x = param.children |> first`, etc. -// -// Soundness: for let-bindings, ONLY accept direct descent expressions -// (is_child_descent_expr / is_list_shrink_expr / known descent var). -// Do NOT look through if/match wrappers — `let x = if c { desc } else { n }` -// is not guaranteed descent (W-3 witness propagation class). -// For match scrutinees, expr_contains_descent IS safe because the match -// extracts the descending component (W-4 is safe when gated by match). -// RC-2: Check if an if-expression produces Option where the descent branch -// returns Some(child) and the non-descent branch returns none. -// This is safe because the subsequent match on Some { value: x } guarantees -// we only recurse when the descent branch was taken. -// NOT a general relaxation of W-3 — only applies when else-branch is none. fn is_if_option_descent(val: Node, param_name: String, vars: Map, check_child: Bool, check_list: Bool, si: Map) -> Bool { match val.expr_data { ExprIf => @@ -2006,19 +1775,6 @@ fn is_if_option_descent(val: Node, param_name: String, vars: Map, } } -// CX-J: Match-option descent for let-bindings. -// Recognizes `match descent_expr { Some { value: v } => v, None => default }` -// where the scrutinee contains descent and a variant arm extracts the value. -// Requires: at least one arm extracts a variant binding, AND all non-extracting -// arms are safe (don't return param_name directly — that would pass through -// the original value without descent). -// -// This function is generic on VariantPattern structure — it does not hardcode -// Option/Result type names. Any sum type with a variant that carries a sub-value -// is recognized. A full coproduct projection metadata registry (like -// function_size_effects for functions) would let the analyzer distinguish -// "projection extracts a strict sub-value" from "projection extracts an -// unrelated value". Deferred to CX-D (coproduct metadata in std/). fn is_match_option_descent(val: Node, param_name: String, vars: Map, check_child: Bool, check_list: Bool, si: Map) -> Bool { match val.expr_data { ExprMatch => @@ -2039,11 +1795,7 @@ fn is_match_option_descent(val: Node, param_name: String, vars: Map false } ) - // All arms must not return the original param directly. - // VariantPattern arms that extract bindings and return one are fine - // (checked by has_extraction). But variant arms that return the - // param itself (e.g. None => n) would make the overall match - // non-descending — same check as non-variant arms. + let all_safe = match_arm_nodes(texpr: val) |> all(arm_node => let arm_b = arm_body(n: arm_node) match arm_b.expr_data { @@ -2067,11 +1819,11 @@ fn collect_descent_vars(body: Node, param_name: String, vars: Map, ExprVar { binding_kind: _ } => set_has(m: vars, key: expr_var_name_at(texpr: val, source_indices: si)) _ => false } - // RC-2: if cond { descent } else { none } → conditional descent via Option + let is_option_descent = is_if_option_descent(val: val, param_name: param_name, vars: vars, check_child: check_child, check_list: check_list, si: si) - // CX-J: match descent_expr { Some { value: v } => v, None => default } + let is_match_descent = is_match_option_descent(val: val, param_name: param_name, vars: vars, check_child: check_child, check_list: check_list, si: si) - // CX-D: wrapper(descent_expr_or_var) preserves descent status + let is_wrapped_descent = if is_tree_size_preserving_wrapper(expr: val, si: si) { let inner = wrapper_inner_arg(expr: val) let inner_is_descent = (check_child && is_child_descent_expr(expr: inner, param_name: param_name, vars: vars, si: si)) @@ -2082,7 +1834,7 @@ fn collect_descent_vars(body: Node, param_name: String, vars: Map, } inner_is_descent || inner_is_var } else { false } - // CX-D: extractor(descent_or_param) produces a strict sub-value + let is_extraction_descent = if is_sub_value_extractor(expr: val, si: si) { let inner = extractor_inner_arg(expr: val) let inner_is_descent = (check_child && is_child_descent_expr(expr: inner, param_name: param_name, vars: vars, si: si)) @@ -2097,8 +1849,7 @@ fn collect_descent_vars(body: Node, param_name: String, vars: Map, } inner_is_descent || inner_is_var || inner_is_param } else { false } - // CX-K: param.children / descent_var.children is a children-list alias. - // Adding to vars allows `ss |> first/last` to be recognized as child descent. + let is_children_list = match val.expr_data { ExprFieldAccess => let base = field_access_base(texpr: val) @@ -2112,7 +1863,7 @@ fn collect_descent_vars(body: Node, param_name: String, vars: Map, } _ => false } - // CX-R: pass-through call wrapping a descent expression + let is_passthrough_descent = if is_list_passthrough_call(expr: val, si: si) { match list_passthrough_inner_arg(expr: val, si: si) { Present { value: inner } => @@ -2125,27 +1876,19 @@ fn collect_descent_vars(body: Node, param_name: String, vars: Map, Absent => false } } else { false } - // CX-R: tokens-consuming call (parser convention). - // Any call with a named `tokens:` arg where the input traces back to - // the measure param is treated as consuming. The result carries - // strictly smaller tokens, so the binding is added to descent_vars. + let is_consuming_helper = check_list && is_tokens_consuming_call(expr: val, param_name: param_name, vars: vars, si: si) let val_is_descent = is_direct_descent || is_descent_var || is_option_descent || is_match_descent || is_wrapped_descent || is_extraction_descent || is_children_list || is_passthrough_descent || is_consuming_helper let next_vars = if val_is_descent { map_insert(vars, let_binding_name_at(texpr: body, source_indices: si), true) } else { vars } - // The let-binding name is added to next_vars when the value is a - // descent expression. Arm-local bindings from match arms inside the - // let-value must NOT leak into the let-body scope — they are only - // valid within their respective arms. + match let_body(texpr: body) { Present { value: b } => collect_descent_vars(body: b, param_name: param_name, vars: next_vars, check_child: check_child, check_list: check_list, si: si) Absent => next_vars } ExprMatch => - // If scrutinee is a descent expr, a descent var, or the parameter itself, - // then variant field bindings are structural parts (descent). - // CX-B: generalized from Some-only to all variant patterns. + let scrut = match_scrutinee(texpr: body) let scrut_is_descent = expr_contains_descent(expr: scrut, param_name: param_name, vars: vars, check_child: check_child, check_list: check_list, si: si) let scrut_is_param = match scrut.expr_data { @@ -2167,16 +1910,13 @@ fn collect_descent_vars(body: Node, param_name: String, vars: Map, } ) } else { vars } - // Recurse into each arm body independently from with_patterns. - // Arm-body bindings are arm-local — they must NOT propagate to sibling arms. - // Results are merged (union) for SCC precomputation use only. + match_arm_nodes(texpr: body) |> fold(init: with_patterns, f: (acc, arm_node) => let arm_result = collect_descent_vars(body: arm_body(n: arm_node), param_name: param_name, vars: with_patterns, check_child: check_child, check_list: check_list, si: si) map_merge(acc, arm_result) ) ExprIf => - // Both branches start from the SAME input vars — then-branch bindings - // must NOT contaminate the else-branch (they are branch-local). + let then_vars = collect_descent_vars(body: if_then_branch(texpr: body), param_name: param_name, vars: vars, check_child: check_child, check_list: check_list, si: si) let else_vars = match if_else_branch(texpr: body) { Present { value: eb } => collect_descent_vars(body: eb, param_name: param_name, vars: vars, check_child: check_child, check_list: check_list, si: si) @@ -2187,8 +1927,7 @@ fn collect_descent_vars(body: Node, param_name: String, vars: Map, body.children |> fold(init: vars, f: (acc, stmt) => collect_descent_vars(body: stmt, param_name: param_name, vars: acc, check_child: check_child, check_list: check_list, si: si) ) - // CX-H: ExprReturn wraps an inner expression — recurse into it so that - // descent vars from match arms inside `return match ... { }` are visible. + ExprReturn => match body.children |> first { Present { value: inner } => collect_descent_vars(body: inner, param_name: param_name, vars: vars, check_child: check_child, check_list: check_list, si: si) @@ -2198,22 +1937,13 @@ fn collect_descent_vars(body: Node, param_name: String, vars: Map, } } -// Verify every self-call in `body` passes a child-descending or -// list-shrinking expression as the argument at `param_name`. -// -// INCREMENTAL: threads descent_vars through the body in evaluation order. -// A binding is only visible AFTER it is defined — later bindings never -// retroactively justify earlier self-calls (fixes precomputed-vars soundness bug). -// Fail-closed: any unrecognizable self-call argument → false. fn all_self_calls_descend(body: Node, func_name: String, param_name: String, check_child: Bool, check_list: Bool, si: Map) -> Bool { all_self_calls_descend_inc(body: body, func_name: func_name, param_name: param_name, vars: empty_map(), check_child: check_child, check_list: check_list, si: si) } fn all_self_calls_descend_inc(body: Node, func_name: String, param_name: String, vars: Map, check_child: Bool, check_list: Bool, si: Map) -> Bool { match body.expr_data { - // Lambda bodies are iteration-bounded (fold/map/any callbacks). - // Self-calls inside lambdas don't contribute to branching factor. - // Skip: callback-local evidence must not justify outer recursion. + ExprLambda => true ExprLet => let val = let_value(texpr: body) @@ -2225,7 +1955,7 @@ fn all_self_calls_descend_inc(body: Node, func_name: String, param_name: String, _ => false } let is_option = is_if_option_descent(val: val, param_name: param_name, vars: vars, check_child: check_child, check_list: check_list, si: si) - // CX-K: param.children / descent_var.children is a children-list alias + let is_children_list = match val.expr_data { ExprFieldAccess => let base = field_access_base(texpr: val) @@ -2251,8 +1981,7 @@ fn all_self_calls_descend_inc(body: Node, func_name: String, param_name: String, let scrut = match_scrutinee(texpr: body) let scrut_ok = all_self_calls_descend_inc(body: scrut, func_name: func_name, param_name: param_name, vars: vars, check_child: check_child, check_list: check_list, si: si) let scrut_is_descent = expr_contains_descent(expr: scrut, param_name: param_name, vars: vars, check_child: check_child, check_list: check_list, si: si) - // CX-B: scrutinee IS the parameter or a known descent var — - // variant field bindings are structural parts (standard induction). + let scrut_is_param = match scrut.expr_data { ExprVar { binding_kind: _ } => let sname = expr_var_name_at(texpr: scrut, source_indices: si) @@ -2308,8 +2037,7 @@ fn all_self_calls_descend_inc(body: Node, func_name: String, param_name: String, all_self_calls_descend_inc(body: child, func_name: func_name, param_name: param_name, vars: vars, check_child: check_child, check_list: check_list, si: si) )) ExprMethodCall { method_semantics: ms } => - // CX-C: if iterating over structural children, the iteration - // parameter is a descent variable (standard structural induction). + let receiver = method_receiver(texpr: body) let is_iter = is_algebra_iteration_method(method_semantics: ms) let is_struct = is_structural_children(expr: receiver, param_name: param_name, vars: vars, si: si) @@ -2377,11 +2105,6 @@ fn matches_on_sub_value_field(body: Node, param_name: String, si: Map first, or a variable bound to -// such an expression. Accessors always return children[N] (proven by -// the IR model), so the argument is strictly smaller than the param -// regardless of which ExprData variant the param carries. fn is_container_child_descent(body: Node, func_name: String, params: List, si: Map) -> Bool { params |> any(p => let pname = param_node_name_at(n: p, source_indices: si) @@ -2390,8 +2113,6 @@ fn is_container_child_descent(body: Node, func_name: String, params: List, ) } -// CX-1b: List-shrinkage descent. Every self-call passes param |> skip(N). -// Covers emit_*_block_stmts, kahn_drain with fuel, init_block_stmts. fn is_list_shrinkage_descent(body: Node, func_name: String, params: List, si: Map) -> Bool { params |> any(p => let pname = param_node_name_at(n: p, source_indices: si) @@ -2399,10 +2120,6 @@ fn is_list_shrinkage_descent(body: Node, func_name: String, params: List, ) } -// ========================================================================= -// T2: Unified proof constructor -// ========================================================================= - fn is_arithmetic_descent_expr(expr: Node, param_name: String, si: Map) -> Bool { match expr.expr_data { ExprBinOp { op: Sub, algebra_field: _ } => @@ -2432,27 +2149,26 @@ fn classify_self_call_evidence(arg_expr: Node, param_name: String, descent_vars: if set_has(m: descent_vars, key: vname) { Strict } else if vname == param_name { NonIncreasing } else { DescentUnknown } - // CX-D: wrapper transparency — unwrap and re-classify inner argument + ExprCall { call_semantics: _, descent_evidence: _ } => if is_tree_size_preserving_wrapper(expr: arg_expr, si: si) { classify_self_call_evidence(arg_expr: wrapper_inner_arg(expr: arg_expr), param_name: param_name, descent_vars: descent_vars, check_child: check_child, check_list: check_list, si: si) } else if is_sub_value_extractor(expr: arg_expr, si: si) { - // CX-D: sub-value extraction strictly reduces tree size. - // Elevate Same→Strict because extractor(param) < param. + let inner_ev = classify_self_call_evidence(arg_expr: extractor_inner_arg(expr: arg_expr), param_name: param_name, descent_vars: descent_vars, check_child: check_child, check_list: check_list, si: si) match inner_ev { NonIncreasing => Strict _ => inner_ev } } else if is_list_passthrough_call(expr: arg_expr, si: si) { - // CX-R: List-preserving pass-through (skip_newlines, etc.). + match list_passthrough_inner_arg(expr: arg_expr, si: si) { Present { value: inner } => classify_self_call_evidence(arg_expr: inner, param_name: param_name, descent_vars: descent_vars, check_child: check_child, check_list: check_list, si: si) Absent => DescentUnknown } } else { DescentUnknown } - // CX-E: param.field or descent_var.field is a strict sub-value + ExprFieldAccess => let base = field_access_base(texpr: arg_expr) match base.expr_data { @@ -2460,14 +2176,14 @@ fn classify_self_call_evidence(arg_expr: Node, param_name: String, descent_vars: let bname = expr_var_name_at(texpr: base, source_indices: si) if set_has(m: descent_vars, key: bname) || bname == param_name { Strict } else { - // CX-R: Field-name provenance — r.tokens carries NonIncreasing evidence. + let field = field_access_field_at(texpr: arg_expr, source_indices: si) if field == param_name { NonIncreasing } else { DescentUnknown } } _ => DescentUnknown } - // CX-R: Generalized shrink — ANY_EXPR |> skip(N) strictly reduces receiver. + ExprMethodCall { method_semantics: ms } => if is_generalized_shrink(expr: arg_expr, si: si) { let recv = method_receiver(texpr: arg_expr) @@ -2488,9 +2204,7 @@ fn try_dimension_for_param(body: Node, func_name: String, param_name: String, ch } fn try_branching_dimension_for_param(body: Node, func_name: String, param_name: String, check_child: Bool, check_list: Bool, si: Map) -> DescentEvidence? { - // CX-N: Like try_dimension_for_param but only collects evidence from - // branches that contribute the maximum self-call count, filtering out - // dominated branches whose DescentUnknown would poison the proof. + collect_evidence_incremental(body: body, func_name: func_name, param_name: param_name, vars: empty_map(), check_child: check_child, check_list: check_list, branching_only: true, si: si) } @@ -2498,12 +2212,9 @@ fn merge_optional_evidence(a: DescentEvidence?, b: DescentEvidence?) -> DescentE optional_evidence_meet(a: a, b: b) } -// Collect the WORST evidence across all self-calls for a given param/dimension. -// INCREMENTAL: threads descent_vars through the body in evaluation order. -// Lambda bodies are skipped (callback-local evidence must not justify outer recursion). fn collect_evidence_incremental(body: Node, func_name: String, param_name: String, vars: Map, check_child: Bool, check_list: Bool, branching_only: Bool, si: Map) -> DescentEvidence? { match body.expr_data { - // Skip lambda bodies — callback-local evidence must not justify outer recursion + ExprLambda => none ExprLet => let val = let_value(texpr: body) @@ -2515,9 +2226,9 @@ fn collect_evidence_incremental(body: Node, func_name: String, param_name: Strin _ => false } let is_option = is_if_option_descent(val: val, param_name: param_name, vars: vars, check_child: check_child, check_list: check_list, si: si) - // CX-J: match descent_expr { Some { value: v } => v, None => default } + let is_match_descent = is_match_option_descent(val: val, param_name: param_name, vars: vars, check_child: check_child, check_list: check_list, si: si) - // CX-D: wrapper(descent_expr_or_var) preserves descent status + let is_wrapped = if is_tree_size_preserving_wrapper(expr: val, si: si) { let inner = wrapper_inner_arg(expr: val) let inner_d = (check_child && is_child_descent_expr(expr: inner, param_name: param_name, vars: vars, si: si)) @@ -2528,7 +2239,7 @@ fn collect_evidence_incremental(body: Node, func_name: String, param_name: Strin } inner_d || inner_v } else { false } - // CX-D: extractor(descent_or_param) produces a strict sub-value + let is_extraction = if is_sub_value_extractor(expr: val, si: si) { let inner = extractor_inner_arg(expr: val) let inner_d = (check_child && is_child_descent_expr(expr: inner, param_name: param_name, vars: vars, si: si)) @@ -2543,7 +2254,7 @@ fn collect_evidence_incremental(body: Node, func_name: String, param_name: Strin } inner_d || inner_v || inner_p } else { false } - // CX-K: param.children / descent_var.children is a children-list alias + let is_children_list = match val.expr_data { ExprFieldAccess => let base = field_access_base(texpr: val) @@ -2557,7 +2268,7 @@ fn collect_evidence_incremental(body: Node, func_name: String, param_name: Strin } _ => false } - // CX-R: pass-through call wrapping a descent expression + let is_passthrough = if is_list_passthrough_call(expr: val, si: si) { match list_passthrough_inner_arg(expr: val, si: si) { Present { value: inner } => @@ -2570,7 +2281,7 @@ fn collect_evidence_incremental(body: Node, func_name: String, param_name: Strin Absent => false } } else { false } - // CX-R: tokens-consuming call — result carries strict tokens + let is_consuming_helper = check_list && is_tokens_consuming_call(expr: val, param_name: param_name, vars: vars, si: si) let next_vars = if is_direct || is_var || is_option || is_match_descent || is_wrapped || is_extraction || is_children_list || is_passthrough || is_consuming_helper { map_insert(vars, let_binding_name_at(texpr: body, source_indices: si), true) @@ -2584,17 +2295,14 @@ fn collect_evidence_incremental(body: Node, func_name: String, param_name: Strin let scrut = match_scrutinee(texpr: body) let scrut_ev = collect_evidence_incremental(body: scrut, func_name: func_name, param_name: param_name, vars: vars, check_child: check_child, check_list: check_list, branching_only: branching_only, si: si) let scrut_is_descent = expr_contains_descent(expr: scrut, param_name: param_name, vars: vars, check_child: check_child, check_list: check_list, si: si) - // CX-B: scrutinee IS the parameter or a known descent var + let scrut_is_param = match scrut.expr_data { ExprVar { binding_kind: _ } => let sname = expr_var_name_at(texpr: scrut, source_indices: si) sname == param_name || set_has(m: vars, key: sname) _ => false } - // CX-N: when branching_only, skip base-case arms (0 self-calls). - // Arms with ≥1 self-call MUST still provide descent evidence — a light - // arm that calls self(param) without descent would diverge. - // Each arm gets INDEPENDENT vars — arm-local bindings don't leak + let arms_ev = match_arm_nodes(texpr: body) |> fold(init: none, f: (acc, arm_node) => let is_base_case = if branching_only { max_path_self_calls(body: arm_body(n: arm_node), func_name: func_name, si: si) == 0 @@ -2619,8 +2327,7 @@ fn collect_evidence_incremental(body: Node, func_name: String, param_name: Strin merge_optional_evidence(a: scrut_ev, b: arms_ev) ExprIf => let cond_ev = collect_evidence_incremental(body: if_condition(texpr: body), func_name: func_name, param_name: param_name, vars: vars, check_child: check_child, check_list: check_list, branching_only: branching_only, si: si) - // CX-N: when branching_only, skip base-case branches (0 self-calls). - // Branches with ≥1 self-call must still provide descent evidence. + let then_branch = if_then_branch(texpr: body) let else_opt = if_else_branch(texpr: body) let then_path = if branching_only { max_path_self_calls(body: then_branch, func_name: func_name, si: si) } else { 0 } @@ -2661,8 +2368,7 @@ fn collect_evidence_incremental(body: Node, func_name: String, param_name: Strin ) result.evidence ExprMethodCall { method_semantics: ms } => - // CX-C: if iterating over structural children, the iteration - // parameter is a descent variable — enter lambda body with evidence. + let receiver = method_receiver(texpr: body) let is_iter = is_algebra_iteration_method(method_semantics: ms) let is_struct = is_structural_children(expr: receiver, param_name: param_name, vars: vars, si: si) @@ -2781,9 +2487,7 @@ fn construct_termination_proof(func_name: String, body: Node, params: List } fn construct_branching_termination_proof(func_name: String, body: Node, params: List, si: Map) -> TerminationProof? { - // CX-N: Like construct_termination_proof but uses branching_only evidence. - // Type-directed: use parameter's algebra profile to select dimension. - // ArithmeticRepeat is skipped (unsafe for branching — exponential blowup). + params |> fold(init: none, f: (best, p) => match best { Present { value: _ } => best @@ -2801,7 +2505,7 @@ fn construct_branching_termination_proof(func_name: String, body: Node, params: Present { value: Strict } => Present { value: TerminationProof { dimensions: [ListLength { param: pname }] } } _ => none } - Present { value: ArithmeticRepeat } => none // unsafe for branching + Present { value: ArithmeticRepeat } => none Absent => let tree_ev = try_branching_dimension_for_param(body: body, func_name: func_name, param_name: pname, check_child: true, check_list: false, si: si) match tree_ev { @@ -2818,9 +2522,6 @@ fn construct_branching_termination_proof(func_name: String, body: Node, params: ) } -// Bridge: extract a CallPattern from a TerminationProof's primary ranking dimension. -// The first dimension determines the iteration primitive; remaining dimensions -// are for lexicographic composition (handled by the proof validator). fn proof_to_call_pattern(proof: TerminationProof) -> CallPattern { match proof.dimensions |> first { Present { value: dim } => match dim { @@ -2843,18 +2544,10 @@ fn classify_recursion_pattern(func_name: String, body: Node, params: List, } lower_call_pattern(pattern: FoldBodyCall { outer_collection: outer }) } else { - // ── Step 1: Try descent_evidence (single authority) ────────────── - // Read the evidence that CX-L2 annotated on ExprCall nodes. - // This is the same source of truth that analyze_structural_bounds uses. - // If ALL self-calls have structural descent evidence (StrictSubValue - // or IteratedSubValue), convert to CallPattern directly. This unifies - // both channels — no dual representation. + let all_evidence = collect_self_call_evidence(body: body, fn_name: func_name, si: si) let has_evidence = all_evidence |> count > 0 - // Two conditions for structural proof: - // Check if ALL self-calls have structural evidence on at least one param. - // PreservedValue is NOT accepted here — a preserved branch can loop - // back to itself indefinitely (soundness: conditional_descent test). + let all_structural = has_evidence && all_evidence |> all(call_ev => call_ev |> any(rel => match rel { @@ -2866,14 +2559,7 @@ fn classify_recursion_pattern(func_name: String, body: Node, params: List, ) ) if all_structural { - // Per-call best evidence. Do NOT flatten across calls — different - // calls may descend on different params. If any call's best is - // arithmetic-only (no tree), the overall must be arithmetic so - // the branching guard applies correctly. - // ALL calls must be arithmetic-only to trigger the branching guard. - // A single PropertyContraction call (ArithmeticDescent, no tree) among - // tree-walking calls should NOT force arithmetic mode — the tree evidence - // from other calls correctly bounds the recursion. + let all_calls_arithmetic_only = all_evidence |> all(call_ev => let has_tree = call_ev |> any(rel => match rel { StrictSubValue { field: _, factor: _ } => true IteratedSubValue { field: _ } => true _ => false } @@ -2898,9 +2584,7 @@ fn classify_recursion_pattern(func_name: String, body: Node, params: List, ) |> first } } - // Guard: ArithmeticDescent with branching (multiple self-calls per path) - // is UNSOUND as O(n). Example: fib(n-1) + fib(n-2) = O(2^n), not O(n). - // Tree descent with branching IS sound (bounded by tree size). + let is_arithmetic_branching = match first_call { Present { value: rel } => match rel { ArithmeticDescent { param: _, factor: _ } => path_calls > 1 _ => false } @@ -3062,8 +2746,6 @@ fn scc_label(members: List) -> String { } } -// CX-3: Classify a call to an SCC peer as Strict (accessor/descent), -// Same (unchanged param), or Unknown (fail-closed). fn classify_scc_call_progress(arg_expr: Node, param_name: String, descent_vars: Map, si: Map) -> DescentEvidence { if is_child_descent_expr(expr: arg_expr, param_name: param_name, vars: descent_vars, si: si) { Strict @@ -3076,28 +2758,26 @@ fn classify_scc_call_progress(arg_expr: Node, param_name: String, descent_vars: if set_has(m: descent_vars, key: vname) { Strict } else if vname == param_name { NonIncreasing } else { DescentUnknown } - // CX-D: wrapper transparency — unwrap and re-classify inner argument + ExprCall { call_semantics: _, descent_evidence: _ } => if is_tree_size_preserving_wrapper(expr: arg_expr, si: si) { classify_scc_call_progress(arg_expr: wrapper_inner_arg(expr: arg_expr), param_name: param_name, descent_vars: descent_vars, si: si) } else if is_sub_value_extractor(expr: arg_expr, si: si) { - // CX-D: sub-value extraction strictly reduces tree size. - // Elevate Same→Strict because extractor(param) < param. + let inner_progress = classify_scc_call_progress(arg_expr: extractor_inner_arg(expr: arg_expr), param_name: param_name, descent_vars: descent_vars, si: si) match inner_progress { NonIncreasing => Strict _ => inner_progress } } else if is_list_passthrough_call(expr: arg_expr, si: si) { - // CX-R: List-preserving pass-through (skip_newlines, etc.). - // The call returns ≤ its input — reclassify the inner tokens argument. + match list_passthrough_inner_arg(expr: arg_expr, si: si) { Present { value: inner } => classify_scc_call_progress(arg_expr: inner, param_name: param_name, descent_vars: descent_vars, si: si) Absent => DescentUnknown } } else { DescentUnknown } - // CX-E: param.field or descent_var.field is a strict sub-value + ExprFieldAccess => let base = field_access_base(texpr: arg_expr) match base.expr_data { @@ -3105,18 +2785,14 @@ fn classify_scc_call_progress(arg_expr: Node, param_name: String, descent_vars: let bname = expr_var_name_at(texpr: base, source_indices: si) if set_has(m: descent_vars, key: bname) || bname == param_name { Strict } else { - // CX-R: Field-name provenance — if the extracted field has the same - // name as the measure param, it carries NonIncreasing evidence. - // Covers: let r = helper(tokens: tokens); scc_call(tokens: r.tokens) + let field = field_access_field_at(texpr: arg_expr, source_indices: si) if field == param_name { NonIncreasing } else { DescentUnknown } } _ => DescentUnknown } - // CX-R: Generalized shrink — ANY_EXPR |> skip(N) strictly reduces any - // NonIncreasing or Strict receiver. Covers `r.tokens |> skip(1)` and - // similar patterns where the receiver is not directly the param variable. + ExprMethodCall { method_semantics: ms } => if is_generalized_shrink(expr: arg_expr, si: si) { let recv = method_receiver(texpr: arg_expr) @@ -3132,31 +2808,13 @@ fn classify_scc_call_progress(arg_expr: Node, param_name: String, descent_vars: } } -// CX-3: Walk a function body and collect progress edges for all calls -// to SCC peers. For each call, check each named argument to see if -// it passes a descent expression for the caller's param. -// -// CX-N: Extends descent_vars through ExprLet/ExprMatch/ExprBlock/ExprIf, -// mirroring collect_evidence_incremental's var threading. fn collect_scc_child_edges(body: Node, caller: String, param_name: String, descent_vars: Map, target_set: Map, check_child: Bool, check_list: Bool, scc_measure_params: Map>, si: Map) -> List { match body.expr_data { ExprLambda => [] ExprCall { call_semantics: _, descent_evidence: _ } => let callee = expr_call_func_at(texpr: body, source_indices: si) let own_edges = if set_has(m: target_set, key: callee) { - // CX-P: Check arguments targeting the CALLEE's measure parameters. - // The old check (aname == param_name) failed when caller and callee - // used different parameter names. Checking all args is unsound — - // descent on a non-measure callee param doesn't prove the measure - // dimension decreases. Instead, look up the callee's measure params - // and only classify arguments that target one of them. - // - // Known limitation: if a callee has MULTIPLE measure params, the - // fold takes the best progress across all of them. This can mix - // evidence from different callee params (e.g., Strict on param B - // but Same on param A, where A drives recursion). The sound fix - // is per-callee-param edge tracking — deferred because all current - // .dag functions have at most one Node param driving recursion. + let callee_params = match map_get(scc_measure_params, callee) { Present { value: p } => p Absent => empty_map() @@ -3164,9 +2822,7 @@ fn collect_scc_child_edges(body: Node, caller: String, param_name: String, desce let progress = body.children |> fold(init: DescentUnknown, f: (acc, arg_node) => let targets_measure = match arg_name_at(n: arg_node, source_indices: si) { Present { value: aname } => - // Single authority: callee's measure params determine which - // argument carries the ranking dimension. No fallback to - // caller's param name (dual authority is unsound). + map_get(callee_params, aname) != none Absent => false } @@ -3186,8 +2842,7 @@ fn collect_scc_child_edges(body: Node, caller: String, param_name: String, desce ) concat(own_edges, child_edges) ExprMethodCall { method_semantics: ms } => - // CX-C: if iterating over structural children, the iteration - // parameter is a descent variable for SCC edge classification. + let receiver = method_receiver(texpr: body) let is_iter = is_algebra_iteration_method(method_semantics: ms) let is_struct = is_structural_children(expr: receiver, param_name: param_name, vars: descent_vars, si: si) @@ -3212,7 +2867,7 @@ fn collect_scc_child_edges(body: Node, caller: String, param_name: String, desce collect_scc_child_edges(body: child, caller: caller, param_name: param_name, descent_vars: descent_vars, target_set: target_set, check_child: check_child, check_list: check_list, scc_measure_params: scc_measure_params, si: si) ) } - // CX-N: Thread descent vars through let-bindings. + ExprLet => let val = let_value(texpr: body) let val_edges = collect_scc_child_edges(body: val, caller: caller, param_name: param_name, descent_vars: descent_vars, target_set: target_set, check_child: check_child, check_list: check_list, scc_measure_params: scc_measure_params, si: si) @@ -3261,7 +2916,7 @@ fn collect_scc_child_edges(body: Node, caller: String, param_name: String, desce } _ => false } - // CX-R: pass-through call wrapping a descent expression + let is_passthrough = if is_list_passthrough_call(expr: val, si: si) { match list_passthrough_inner_arg(expr: val, si: si) { Present { value: inner } => @@ -3274,7 +2929,7 @@ fn collect_scc_child_edges(body: Node, caller: String, param_name: String, desce Absent => false } } else { false } - // CX-R: tokens-consuming call — result carries strict tokens + let is_consuming_helper = check_list && is_tokens_consuming_call(expr: val, param_name: param_name, vars: descent_vars, si: si) let next_vars = if is_direct || is_var || is_option || is_match_descent || is_wrapped || is_extraction || is_children_list || is_passthrough || is_consuming_helper { map_insert(descent_vars, let_binding_name_at(texpr: body, source_indices: si), true) @@ -3284,11 +2939,7 @@ fn collect_scc_child_edges(body: Node, caller: String, param_name: String, desce Absent => [] } concat(val_edges, body_edges) - // Walk match arms. Pattern-binding promotion is NOT done here — it would - // unsoundly promote all variant bindings as descent vars without verifying - // they are recursive child positions. The pre-computed descent_vars - // (from collect_descent_vars) already contains bindings from the function - // body; that is the single authority for descent variable discovery. + ExprMatch => let scrut = match_scrutinee(texpr: body) let scrut_edges = collect_scc_child_edges(body: scrut, caller: caller, param_name: param_name, descent_vars: descent_vars, target_set: target_set, check_child: check_child, check_list: check_list, scc_measure_params: scc_measure_params, si: si) @@ -3296,8 +2947,7 @@ fn collect_scc_child_edges(body: Node, caller: String, param_name: String, desce collect_scc_child_edges(body: arm_body(n: arm_node), caller: caller, param_name: param_name, descent_vars: descent_vars, target_set: target_set, check_child: check_child, check_list: check_list, scc_measure_params: scc_measure_params, si: si) ) concat(scrut_edges, arms_edges) - // CX-N: Thread descent vars through block statements sequentially. - // Uses collect_descent_vars as the single source of truth for var threading. + ExprBlock => let result = body.children |> fold(init: SccEdgeBlockAcc { edges: [], vars: descent_vars }, f: (acc, stmt) => let stmt_edges = collect_scc_child_edges(body: stmt, caller: caller, param_name: param_name, descent_vars: acc.vars, target_set: target_set, check_child: check_child, check_list: check_list, scc_measure_params: scc_measure_params, si: si) @@ -3322,10 +2972,6 @@ fn collect_scc_child_edges(body: Node, caller: String, param_name: String, desce } } -// CX-3: SCC container-child descent proof. Every cycle through the SCC -// must have at least one edge where the callee receives an accessor -// child (Strict). Fail-closed: any DescentUnknown edge or -// any all-Same cycle means SameArgumentCall (bounded by Forever). fn is_scc_container_child_descent(members: List, func_index: Map, scc_name_set: Map, si: Map) -> Bool { let measure_params = build_scc_measure_params(members: members, func_index: func_index, si: si) let edges = members |> flat_map(name => @@ -3335,10 +2981,7 @@ fn is_scc_container_child_descent(members: List, func_index: Map count == 0 || unknown_count < best_unknown { @@ -3355,10 +2998,6 @@ fn is_scc_container_child_descent(members: List, func_index: Map ProofEdge { ProofEdge { caller: pe.caller, callee: pe.callee, evidence: [pe.progress] } } @@ -3407,11 +3046,6 @@ fn count_known_in_edge_map(m: Map) -> Int { ) |> count } -// Pick the param edge map with better coverage (more known-evidence edges). -// Soundness: all edges from a member must use a single param's measure. -// A cross-param JOIN (best-of per edge) is unsound because edge A→B may -// descend on param x while edge A→C descends on param z — no single -// ranking function decreases on both edges. fn pick_best_param_edges(current: Map, candidate: Map) -> Map { if count_known_in_edge_map(m: candidate) > count_known_in_edge_map(m: current) { candidate } else { current } @@ -3436,9 +3070,7 @@ fn collect_scc_independent_dim_edges(members: List, func_index: Map fold(init: empty_map(), f: (bm, pe) => merge_edge_evidence(acc_map: bm, pe: pe)) pick_best_param_edges(current: best_map, candidate: param_map) ) - // Merge into 2D proof edges — map keys are callee names. - // Note: map_keys order is nondeterministic (HashMap-backed), but consumers - // (all(), proof_has_non_descending_cycle) are order-independent. + let all_keys = concat(map_keys(tree_edge_map), map_keys(list_edge_map)) let unique_keys = all_keys |> fold(init: empty_map(), f: (acc, k) => map_insert(acc, k, true)) map_keys(unique_keys) |> map(callee => @@ -3451,18 +3083,11 @@ fn collect_scc_independent_dim_edges(members: List, func_index: Map, func_index: Map, scc_name_set: Map, si: Map) -> List { let edges = collect_parser_edges_for_scc(members: members, func_index: func_index, scc_name_set: scc_name_set, si: si) edges |> map(pe => progress_edge_to_proof_edge(pe: pe)) } -// CX-A: For each SCC call edge, independently find the best evidence for -// TreeSize and TokenPosition. TreeSize uses the existing child-descent -// infrastructure; TokenPosition uses collect_parser_edges_for_scc (single -// authority). Produces 2-slot ProofEdge values: [tree_ev, parser_ev]. fn collect_scc_tree_parser_dim_edges(members: List, func_index: Map, scc_name_set: Map, si: Map) -> List { let all_parser_edges = collect_parser_edges_for_scc(members: members, func_index: func_index, scc_name_set: scc_name_set, si: si) if (all_parser_edges |> count) == 0 { @@ -3481,9 +3106,7 @@ fn collect_scc_tree_parser_dim_edges(members: List, func_index: Map filter(pe => pe.caller == name) let parser_edge_map = my_parser_edges |> fold(init: empty_map(), f: (bm, pe) => merge_edge_evidence(acc_map: bm, pe: pe)) - // Merge into 2D proof edges — map keys are callee names. - // Note: map_keys order is nondeterministic (HashMap-backed), but consumers - // (all(), proof_has_non_descending_cycle) are order-independent. + let all_keys = concat(map_keys(tree_edge_map), map_keys(parser_edge_map)) let unique_keys = all_keys |> fold(init: empty_map(), f: (acc, k) => map_insert(acc, k, true)) map_keys(unique_keys) |> map(callee => @@ -3497,15 +3120,12 @@ fn collect_scc_tree_parser_dim_edges(members: List, func_index: Map, func_index: Map, scc_name_set: Map, si: Map) -> List { let measure_params = build_scc_measure_params(members: members, func_index: func_index, si: si) members |> flat_map(name => match map_get(func_index, name) { Present { value: entry } => - // Self-edge: read CX-L2 descent_evidence from annotated self-calls + let self_evidence = collect_self_call_evidence(body: entry.body, fn_name: name, si: si) let self_has_calls = self_evidence |> count > 0 let self_all_structural = self_has_calls && self_evidence |> all(call_ev => @@ -3547,8 +3167,7 @@ fn collect_scc_cx_l2_tree_edges(members: List, func_index: Map filter(m => m != name) let cross_edges = other_members |> flat_map(callee => let callee_evidence = collect_callee_evidence(body: entry.body, callee_name: callee, si: si) @@ -3563,7 +3182,6 @@ fn collect_scc_cx_l2_tree_edges(members: List, func_index: Map, func_index: Map, si: Map) -> String { members |> fold(init: "", f: (found, name) => if found != "" { found } @@ -3593,7 +3211,7 @@ fn scc_descending_param(members: List, func_index: Map, func_index: Map, scc_name_set: Map, si: Map) -> TerminationProof? { - // CX-L2 hybrid: self-edges from descent_evidence, cross-edges from old system + let cx_l2_edges = collect_scc_cx_l2_tree_edges(members: members, func_index: func_index, scc_name_set: scc_name_set, si: si) let cx_l2_all_known = cx_l2_edges |> all(e => match e.evidence |> first { @@ -3628,7 +3246,7 @@ fn construct_scc_termination_proof(members: List, func_index: Map count) > 0 && proof_has_non_descending_cycle(proof: list_proof, members: members, edges: list_edges) == false { Present { value: list_proof } } else { - // CX-A: Try TokenPosition (parser state advance) + let parser_edges = collect_scc_parser_proof_edges(members: members, func_index: func_index, scc_name_set: scc_name_set, si: si) let parser_all_known = parser_edges |> all(e => match e.evidence |> first { @@ -3641,9 +3259,7 @@ fn construct_scc_termination_proof(members: List, func_index: Map all(e => match e.evidence |> first { Present { value: ev } => match ev { DescentUnknown => false _ => true } @@ -3654,7 +3270,7 @@ fn construct_scc_termination_proof(members: List, func_index: Map count) > 0 && proof_has_non_descending_cycle(proof: lex_proof, members: members, edges: lex_edges) == false { Present { value: lex_proof } } else { - // CX-A: Lexicographic [TreeSize, TokenPosition] + let tree_parser_edges = collect_scc_tree_parser_dim_edges(members: members, func_index: func_index, scc_name_set: scc_name_set, si: si) let tp_all_known = tree_parser_edges |> all(e => match e.evidence |> first { @@ -3799,19 +3415,6 @@ fn scalar_output() -> Map { empty_map() } - -// ========================================================================= -// Size variable extraction -// ========================================================================= - -// Derive size preservation from algebra facts: a method preserves the receiver -// size variable if its declared size_effect is ShrinkEffect or IdentityEffect. -// These are exact structural witnesses from the algebra template. -// ProjectionEffect (first/last) does NOT preserve collection size — it -// projects a single element from the collection. -// NOTE: produces_collection is NOT used here — "returns a collection" and -// "preserves receiver size" are different facts (append returns a collection -// but grows it). Only size_effect carries the size-relation contract. fn method_preserves_collection_size(method_semantics: MethodSemantics?) -> Bool { if method_semantics == none { false } else { @@ -3853,10 +3456,6 @@ fn size_binder_name(size: SizeExpr) -> String { } } -// ========================================================================= -// CostShape dispatch -- compute cost from reconcile-provided shape -// ========================================================================= - fn resolve_lambda_arg(mc_arg_nodes: List, si: Map) -> Node? { let f_arg = mc_arg_nodes |> filter(a => authored_name_at(source_indices: si, node: a) == "f") |> first match f_arg { @@ -3975,10 +3574,6 @@ fn cost_of_method_by_shape(shape: CostShape, produces_collection: Bool, recv_r: } } -// ========================================================================= -// D4: Complexity reporting -// ========================================================================= - type ComplexityViolation { func_name: String reason: String @@ -4007,10 +3602,6 @@ fn empty_complexity_report() -> ComplexityReport { } } -// ========================================================================= -// Cost simplification -// ========================================================================= - fn simplify_cost(expr: CostExpr) -> CostExpr { match expr { CostConst { value: _ } => expr @@ -4071,15 +3662,6 @@ fn simplify_cost(expr: CostExpr) -> CostExpr { } } -// ========================================================================= -// Asymptotic normalization and formatting -// -// CostExpr is the single authority for complexity. Classification is -// asymptotic normalization: collapse all positive constants to 1 and -// re-simplify so identity elements absorb. The normalized CostExpr IS -// the complexity class. Formatting reads CostExpr directly. -// ========================================================================= - fn format_size(size: SizeExpr) -> String { match size { SizeLen { collection: c } => concat("|", c, "|") @@ -4355,19 +3937,6 @@ fn deduplicate(items: List) -> List { result.out } - -// ========================================================================= -// D2: Lazy cached summary computation -// -// get_or_compute_summary checks the cache first. On miss, walks the -// function body to build a CostExpr for work/span/output_size, interns -// the result, and caches the summary. -// -// FuncEntry is a lightweight representation of a function for the -// complexity analyzer: name + body Node. Threaded alongside the -// CostInternTable so recursive callee lookups can find function bodies. -// ========================================================================= - type FuncEntry { name: String body: Node @@ -4376,11 +3945,6 @@ type FuncEntry { is_tail_recursive: Bool } -// Module-owned recursion facts: shared authority for all functions. -// Built once from the merged TypeEnv across all compiled modules, -// not copied per-function. -// Note: recursive_type_set was removed (unused downstream — the complexity -// analyzer operates on Node structure, not type-name lookups). type RecursionContext { } @@ -4389,17 +3953,12 @@ type SummaryResult { table: CostInternTable } -// Accumulator for topological-order processing with transit eviction. -// Threads the intern table, classified results, fan-in counters, and -// fail-closed violations through the fold over topological order. type TopoBuildAcc { table: CostInternTable classes: Map violations: List fan_in: Map - // Functions explicitly processed by the topological fold. - // Prevents evicting SCC co-members computed as side effects - // before they reach their own fold iteration. + processed: Map } @@ -4806,14 +4365,10 @@ fn get_or_compute_summary(func_name: String, func_index: Map, let final_table = cache_summary(table: result.table, func_name: func_name, summary: simplified) SummaryResult { summary: simplified, table: final_table } Absent => - // Function not in func_index. Distinguish genuine externals - // (Rust stdlib, runtime) from unexpected misses (internal function - // lost from index — a bug that should not fail open). + let is_unexpected_miss = map_get(scc_index, func_name) != none if is_unexpected_miss { - // Internal function in SCC index but missing from func_index. - // Fail closed — this is a bug in extract_func_entries or the - // import closure, not a genuine external. + let miss_summary = ComplexitySummary { work: CostUnknown { reason: concat("internal function missing from index: ", func_name) }, span: CostUnknown { reason: concat("internal function missing from index: ", func_name) }, @@ -4834,14 +4389,6 @@ fn get_or_compute_summary(func_name: String, func_index: Map, } } -// ========================================================================= -// CX-L3: Structural descent analyzer -// -// Reads SubValueRelation witnesses from typed ExprCall nodes. -// Produces CostBound for self-recursive functions via the induction model. -// NEVER pattern-matches AST — reads only descent_evidence fields. -// ========================================================================= - fn collect_call_evidence(body: Node, target_set: Map, si: Map) -> List> { match body.expr_data { ExprCall { call_semantics: _, descent_evidence: de } => @@ -4897,8 +4444,6 @@ fn merge_param_evidence(all_calls: List>, param_index: In ) } -// Extract the ShrinkFactor for a parameter across all self-call sites. -// Fail-closed: mixed factors → none (no structural bound produced). fn extract_shrink_factor(all_calls: List>, param_index: Int) -> ShrinkFactor? { all_calls |> fold(init: Present { value: UnitShrink }, f: (acc, call_evidence) => match acc { @@ -5038,17 +4583,6 @@ fn analyze_structural_bounds(func_entries: List, si: Map, recursion_ctx: RecursionContext, source_indices: Map) -> ComplexityReport { let si = source_indices let func_index = func_entries |> fold(init: empty_map(), f: (acc, entry) => @@ -5076,8 +4610,7 @@ fn build_complexity_report(func_entries: List, recursion_ctx: Recursi } else { acc } } ) - // Compute fan-in: for each callee, how many unique callers reference it? - // Used to know when a summary can be evicted (all callers processed). + let call_forward = forward_adjacency(names: func_entries |> map(entry => entry.name), graph: scc_result.call_graph) let fan_in = func_entries |> fold(init: empty_map(), f: (acc, entry) => let callees = match map_get(call_forward, entry.name) { @@ -5090,11 +4623,7 @@ fn build_complexity_report(func_entries: List, recursion_ctx: Recursi map_insert(inner, callee, current + 1) ) ) - // Process in topological order (callees before callers). After each - // function is analyzed, classify it eagerly (storing the string) and - // decrement fan-in for its callees. When a callee's fan-in reaches 0, - // evict its CostExpr tree — the working set is bounded by the maximum - // frontier width of the call graph, not the total function count. + let result = scc_result.processing_order |> fold(init: TopoBuildAcc { table: empty_cost_intern_table(), classes: empty_map(), @@ -5108,10 +4637,7 @@ fn build_complexity_report(func_entries: List, recursion_ctx: Recursi let class_str = classify_complexity(expr: sr.summary.work) let new_classes = map_insert(acc.classes, func_name, class_str) let new_processed = map_insert(acc.processed, func_name, true) - // Detect violations: any function whose cost contains CostUnknown. - // CX-P: reason carries the structural root cause from CostUnknown, - // not the asymptotic class string (which is display output). - // CX-P: span propagated from FuncEntry for diagnostic source location. + let new_violations = if is_unknown_cost(expr: sr.summary.work) { concat(acc.violations, [ComplexityViolation { func_name: func_name, @@ -5129,18 +4655,14 @@ fn build_complexity_report(func_entries: List, recursion_ctx: Recursi let current = match map_get(fi, callee) { Present { value: v } => v, Absent => 0 } map_insert(fi, callee, current - 1) ) - // Evict summaries whose fan-in has reached 0, but ONLY if the - // function was explicitly processed by this fold. SCC co-members - // may be computed as side effects of get_or_compute_summary — - // evicting them before their fold iteration would replace their - // real summary with a fabricated placeholder. + let evicted_table = unique_callees |> fold(init: sr.table, f: (t, callee) => let remaining = match map_get(new_fan_in, callee) { Present { value: v } => v, Absent => 0 } if remaining <= 0 && set_has(m: new_processed, key: callee) { evict_summary(table: t, func_name: callee) } else { t } ) - // Also evict this function if it has no callers (fan-in was 0 from the start) + let self_fan_in = match map_get(new_fan_in, func_name) { Present { value: v } => v, Absent => 0 } let final_table = if self_fan_in <= 0 { evict_summary(table: evicted_table, func_name: func_name) @@ -5149,9 +4671,7 @@ fn build_complexity_report(func_entries: List, recursion_ctx: Recursi Absent => acc } }) - // intern_table is transit-only — not exposed in the report. - // Evicted entries contain fabricated placeholders that would mislead consumers. - // CX-L3: structural descent analysis — reads descent_evidence, not AST. + let structural_bounds = analyze_structural_bounds(func_entries: func_entries, si: si) ComplexityReport { function_classes: result.classes, diff --git a/src/v1/effect_derivation.dag b/src/v1/effect_derivation.dag index b28094cd98e..ec935b6be37 100644 --- a/src/v1/effect_derivation.dag +++ b/src/v1/effect_derivation.dag @@ -1,5 +1,3 @@ -// v2 effect derivation bridge. - module v1.compiler.effect_derivation import std.types { HttpMethod, GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS } diff --git a/src/v1/languages.dag b/src/v1/languages.dag index 8fc1a173829..86c72030f11 100644 --- a/src/v1/languages.dag +++ b/src/v1/languages.dag @@ -1,12 +1,3 @@ -// Compiler-local language-spec projection. -// -// Imports shared types from `dsl/std/languages.dag` (the single authority for -// language facts) and adds compiler-specific types (SerializationSpec, -// TestConventions, SharingStrategy, IndexingSemantics, AnnotationRequirements) -// that the emitter needs but that are not (yet) in std. -// -// Accessor functions route RenderTarget → concrete extdep data. - module v1.compiler.languages import v1.compiler.artifact { RenderTarget, Rust, Python, Go, Dag } @@ -22,10 +13,6 @@ import std.syntax { BodyKind, ExprBody, BlockBody, TypeBody, ValueBody, NoBody, ServiceBody, ResourceBody } -// NamingCase: mirrors std.languages.NamingCase (single authority). -// Cannot import directly — std/languages.dag contains CommentSyntax -// with String? fields the bootstrap compiler doesn't yet emit correctly. -// Import from std.languages once bootstrap handles optional string fields. import extdeps.languages.rust.syntax { rust_operators, rust_item_forms } import extdeps.languages.python.syntax { python_operators, python_item_forms } @@ -144,7 +131,6 @@ type TestConventions { async_decorator: String? } - type ImportTrigger = TypeUsageTrigger { type_name: String } | TraitImplTrigger { trait_name: String } @@ -158,18 +144,16 @@ type ImportRule { } type SharingStrategy { - needs_sharing: Bool // Rust: true (ownership), Python/Go: false (GC) + needs_sharing: Bool wrap_template: String - // Clone templates: {0}=value, {1}=field (for field_clone). - // In GC'd languages these are identity — no ownership transfer needed. + clone_value: String deref_clone: String field_clone: String iter_owned: String clone_suffix: String - // Borrow templates: {0}=type (param) or {0}=expr (arg). - // Rust: "&{0}" wraps both types and expressions. GC languages: identity (no borrows needed). - borrow_param_template: String // wraps param type: Rust "&Rc", Go/Python identity + + borrow_param_template: String borrow_arg_template: String } @@ -199,14 +183,6 @@ type ServiceFieldTemplates { file_ctor: String } - -// -- Block syntax: structural formatting facts per target language ----------- -// Two authorities: -// Language spec — block delimiters, statement terminators, significant -// whitespace. These are correctness requirements (Python). -// Style spec — indent unit, readability conventions. These are readability -// requirements (correctness during bootstrap; optional post-bootstrap -// via gofmt/rustfmt). type BlockSyntax { block_open: String block_close: String @@ -280,8 +256,6 @@ fn service_self_param(spec: LanguageSpec) -> String { } } -// TODO: receiver_template on LanguageSpec — Go receiver syntax "(c *Name) " -// is hardcoded here. Phase 5: move to a data-driven rendering template. fn service_receiver_str(spec: LanguageSpec, service_name: String) -> String { match spec.service_method { SelfInParams { self_param: _ } => "" @@ -303,8 +277,6 @@ fn service_methods_inside_class(spec: LanguageSpec) -> Bool { } } -// TODO: return_template on LanguageSpec — error tuple format "(T, error)" -// is hardcoded here. Phase 5: move to fully data-driven rendering. fn service_return_str(spec: LanguageSpec, ret_type: String) -> String { match spec.service_return { ArrowReturn => concat(spec.items.return_arrow, ret_type) @@ -312,15 +284,6 @@ fn service_return_str(spec: LanguageSpec, ret_type: String) -> String { } } -// Item definition facts — individual tokens the emitter composes. -// Not templates: each field is one language fact. -// -// RELATION TO SyntaxSpec: SyntaxSpec.ItemForm.keyword is the PARSING -// keyword (what to recognize). ItemKeywords.*_keyword is the EMISSION -// keyword (what to produce). For same-language compilation (Rust→Rust) -// these are identical. For cross-language (.dag→Rust) they differ -// (parse "fn", emit "fn"/"def"/"func"). When each target language has -// a full SyntaxSpec, ItemKeywords should derive from it (single authority). type ItemKeywords { func_keyword: String async_prefix: String @@ -335,10 +298,6 @@ type ItemKeywords { import_from_keyword: String } -// Look up an emission keyword by item kind from a SyntaxSpec's item_forms. -// Fails closed with an error marker if no matching ItemForm exists. -// M5: "No fallbacks that fabricate" — missing item form is a data bug -// that should cause visible compilation failure, not silent "" emission. fn item_keyword_for_kind(forms: List, kind: ItemFormKind) -> String { let matching = forms |> filter(f => f.kind == kind) match matching |> first { @@ -374,8 +333,7 @@ type LanguageSpec { void_type: String tuple_syntax: TupleSyntax string_interp: StringInterpSyntax - // Callable type rendering: non-null overrides CallableRepr.template entirely. - // Rust: "Rc {return}>". Others: null (use CallableRepr). + callable_type_template: String? naming_case: NamingCase async_call_prefix: String @@ -408,11 +366,6 @@ type RecordLitSyntax { anon_field_indent: String } -// String interpolation — three structurally different models: -// FormatArgs: format function + positional args (Rust format!, Go fmt.Sprintf) -// InlineExpr: inline interpolation (Python f-strings) -// C-1: dissolves `match target` in emit_simple_string_interp. -// InterpStyle: FormatArgs carries its placeholder; InlineExpr has none. type InterpStyle = FormatArgs { placeholder: String } | InlineExpr @@ -429,11 +382,6 @@ type StringInterpSyntax { escape_pairs: List } -// ========================================================================= -// SyntaxSpec — unified with std.syntax (single authority) -// ItemForm, OperatorSpec, SyntaxSpec, BodyKind imported above. -// ========================================================================= - fn rust_spec() -> LanguageSpec { LanguageSpec { target_name: "rust", @@ -1091,8 +1039,6 @@ fn expression_semantics_for_target(target: RenderTarget) -> ExpressionSemantics language_spec_for_target(target: target).expression_semantics } -// Apply sharing wrap template to a type string. -// Rust: "Rc<{0}>" → "Rc", Python/Go: "{0}" → "MyType" fn wrap_shared_type(target: RenderTarget, inner: String) -> String { let tmpl = sharing_for_target(target: target).wrap_template replace(tmpl, "{0}", inner) diff --git a/src/v1/ownership.dag b/src/v1/ownership.dag index 29adf82f417..1f7b861900b 100644 --- a/src/v1/ownership.dag +++ b/src/v1/ownership.dag @@ -1,32 +1,3 @@ -// v2 ownership analysis -- static proof that try_unwrap fallbacks are unnecessary. -// -// D-ownership: Classify every use of every binding by how it consumes the value. -// Compute semantic_consumer_count after removing administrative edges (TCO -// threading, pass-through reads). At try_unwrap sites: -// -// semantic_consumers == 1 -> Rc::into_inner().expect("sole owner") -// semantic_consumers > 1 -> compile error: cannot guarantee O(1) mutation -// unclassified -> completeness bug in this analysis -// -// One pass over each function body. No heuristics, no fallbacks. -// -// Clone elision (FF-1 exploitation): -// -// The .dag language guarantees fan-out is a syntactic property (facts flow -// forward). This analysis computes binding_fan_out and the emitter -// reads it: fan-out=0 → dead code, fan-out=1 → move, fan-out>1 → Rc + clone. -// -// VarBindingKind flows from inference through the ownership proof to emission. -// BindingUsage carries binding_kind so the proof is self-contained: -// -// LocalValueBinding (params, let-bindings) → Rc owned → movable if fan-out=1 -// MatchBoundBinding (match pattern vars) → &T reference → never movable -// FunctionValueBinding, VariantValueBinding → not values → never movable -// -// Lambda/foreach bodies are walked in a fresh accumulator; all discovered names -// are recorded twice in the outer accumulator (fan-out >= 2), preventing name -// collisions from incorrectly marking outer bindings as movable. - module v1.compiler.ownership import v1.std.core { @@ -50,16 +21,6 @@ import v1.std.core { } import v1.compiler.emit { to_string } -// ========================================================================= -// Edge classification -// -// Each use of a binding is classified by how it affects the value: -// Consumed -- value is moved/destructured (ownership transferred) -// Read -- value is read but not consumed (borrow) -// Threaded -- administrative: TCO accumulator threading -// Projected -- field projection (partial read) -// ========================================================================= - type EdgeKind = Consumed | Read | Threaded | Projected type EdgeClassification { @@ -68,10 +29,6 @@ type EdgeClassification { span_start: Int } -// ========================================================================= -// Binding usage -- per-binding analysis result -// ========================================================================= - type BindingUsage { name: String binding_kind: VarBindingKind? @@ -82,19 +39,10 @@ fn semantic_consumer_count(usage: BindingUsage) -> Int { usage.consumers |> filter(c => match c.kind { Consumed => true, _ => false }) |> count } -// Fan-out: total reference count across all edge kinds. -// This is the syntactic fan-out of a binding — how many times it is referenced. -// The emit rule: fan-out=0 → dead code, fan-out=1 → move, fan-out>1 → Rc + clone. -// Threaded edges (fold accumulators) are excluded — they are linearity-preserving -// by construction and should not inflate fan-out. fn binding_fan_out(usage: BindingUsage) -> Int { usage.consumers |> filter(c => match c.kind { Threaded => false, _ => true }) |> count } -// ========================================================================= -// Ownership proof -- per-function analysis output -// ========================================================================= - type OwnershipDecision = SoleOwner { binding: String, site: String } | SharedError { binding: String, consumer_count: Int, sites: List } @@ -123,10 +71,6 @@ type OwnershipProof { fold_acc_unwrap: List } -// ========================================================================= -// Internal accumulator for collecting edge classifications -// ========================================================================= - type UsageAccum { bindings: Map fold_call_nodes: List @@ -151,10 +95,6 @@ fn record_use(accum: UsageAccum, name: String, kind: EdgeKind, site: String, bin UsageAccum { bindings: map_insert(accum.bindings, name, updated), fold_call_nodes: accum.fold_call_nodes } } -// ========================================================================= -// Branch-aware merge: mutually exclusive branches (match arms, if/else) -// ========================================================================= - fn max_usage_by_fan_out(a: BindingUsage, b: BindingUsage) -> BindingUsage { if binding_fan_out(usage: b) > binding_fan_out(usage: a) { b } else { a } } @@ -177,10 +117,6 @@ fn merge_branch_usages(base: UsageAccum, branches: List) -> UsageAcc UsageAccum { bindings: binding_merged, fold_call_nodes: base.fold_call_nodes |> concat(branch_fold_nodes) } } -// ========================================================================= -// AST walker -- classifies each variable reference by position -// ========================================================================= - fn walk_expr(accum: UsageAccum, texpr: Node, in_tail: Bool, si: Map) -> UsageAccum { match texpr.expr_data { ExprVar { binding_kind: bk } => @@ -335,10 +271,6 @@ fn walk_expr(accum: UsageAccum, texpr: Node, in_tail: Bool, si: Map OwnershipDecision { let sc = semantic_consumer_count(usage: usage) if sc == 1 { @@ -361,15 +293,6 @@ fn make_decision(usage: BindingUsage) -> OwnershipDecision { } } -// ========================================================================= -// Movable set -- emit-facing projection of ownership analysis -// -// A binding is "movable" when its fan-out is exactly 1 AND it is an owned -// local (parameter or let-binding). Match-bound variables produce &T -// references in Rust (from .as_ref() destructuring) and must always clone. -// The binding_kind field on BindingUsage carries this fact from inference. -// ========================================================================= - fn is_owned_local(kind: VarBindingKind?) -> Bool { match kind { Present { value: LocalValueBinding } => true @@ -383,12 +306,6 @@ fn build_movable_set(proof: OwnershipProof) -> Set { |> fold(init: empty_set(), f: (acc, usage) => set_insert(acc, usage.name)) } -// Read-only params: function parameters where all usage edges are -// Read/Projected/Threaded (no Consumed). These can be passed by borrow -// (&Rc) instead of owned (Rc), eliminating clone at each call site. -// Fan-out=1 params are excluded — they're already movable (zero cost). -// param_names restricts to actual function parameters (VarBindingKind -// doesn't distinguish params from let-bindings). fn build_read_only_params(proof: OwnershipProof, param_names: Set) -> Set { proof.bindings |> map_values |> filter(usage => @@ -400,10 +317,6 @@ fn build_read_only_params(proof: OwnershipProof, param_names: Set) -> Se |> fold(init: empty_set(), f: (acc, usage) => set_insert(acc, usage.name)) } -// ========================================================================= -// Callable reference collection — which functions are passed as values? -// ========================================================================= - fn collect_callable_refs(texpr: Node, si: Map) -> Set { match texpr.expr_data { ExprVar { binding_kind: bk } => @@ -466,10 +379,6 @@ fn collect_callable_refs(texpr: Node, si: Map) -> Set Node { match body.expr_data { ExprLet => match let_body(texpr: body) { @@ -631,10 +540,6 @@ fn analyze_single_fold(method_call: Node, si: Map) -> Fold } } -// ========================================================================= -// Analysis entry point -// ========================================================================= - fn analyze_ownership(func_name: String, params: List, body: Node, si: Map) -> OwnershipProof { let initial = params |> fold(init: empty_usage_accum(), f: (acc, p) => let p_name = authored_name_at(source_indices: si, node: p) diff --git a/src/v1/probe_emit_interp.dag b/src/v1/probe_emit_interp.dag index 07fbaf9bfd4..178dd93a633 100644 --- a/src/v1/probe_emit_interp.dag +++ b/src/v1/probe_emit_interp.dag @@ -1,31 +1,8 @@ -// EMISSION VERIFICATION PATH probe (not floor-enrolled) — answers the parent's -// ONE FACT: can the v2 interpreter RUN 05_emit_rust.dag's full compile→emit fold -// so that a .dag emitter change verifies BY INTERPRETATION, with no seed-mirror -// regen + cargo round trip? -// -// `gunbc run` resolves the live `src/v1` + `dsl` closure and INTERPRETS it. These -// probes drive `compile_sources(.., target: Rust)` — the same fold the seed -// emitter compiles — over an inline source and assert a property of the emitted -// Rust. Green here means: the emit fold is interpretation-runnable end to end -// (lex → parse → resolve → infer → emit), so every future emitter fix is -// checkable by running this path, not by hand-syncing the 22.7k-line Rust seed. -// -// Run (each returns Bool, ^claim-run prints the witness): -// gunbc run --source-root src/v1 --source-root dsl \ -// --entry src/v1/probe_emit_interp.dag \ -// --function probe_emit_fold_runs --claim-run -// -// The asserted properties are faithful to the CURRENT emitter (clean main) — they -// do not depend on any in-flight seed-emit change — so the probe isolates the -// interpreter-runs-the-fold fact from any particular emitter edit. module v1.probe.emit_interp import v1.compiler.compile { compile_sources, SourceFile } import v1.compiler.artifact { RenderTarget, Rust } -// A minimal, self-contained source: one pure function over Int. Exercises the -// whole pipeline (tokenize → parse → resolve → infer → emit) yet stays small -// enough that the emitted signature is stable to assert against. fn probe_source() -> SourceFile { SourceFile { path: "src/v1/probe_add.dag", @@ -38,21 +15,14 @@ fn probe_emitted() -> String { result.files |> map(f => f.content) |> join(separator: "\n") } -// FACT 1 — the emit fold produces a non-empty Rust artifact through the -// interpreter (it ran to completion, not a partial/aborted fold). fn probe_emit_fold_runs() -> Bool { (probe_emitted() |> length) > 0 } -// FACT 2 — the emitted artifact carries the authored function as a Rust `fn add` -// signature. Discriminating: a wrong/partial emit (or a fold that bailed before -// the function emitter) makes this go RED. fn probe_emits_fn_add() -> Bool { contains(probe_emitted(), "fn add") } -// FACT 3 — the pipeline reports zero diagnostics on a well-formed source, so a -// green emit is a true success, not a fail-open fabrication (DESIGN §5). fn probe_clean_source_has_no_diagnostics() -> Bool { let result = compile_sources(sources: [probe_source()], target: Rust) (result.diagnostics |> count) == 0 diff --git a/src/v1/runtime_go.dag b/src/v1/runtime_go.dag index 7d798cb73f7..698a1a38733 100644 --- a/src/v1/runtime_go.dag +++ b/src/v1/runtime_go.dag @@ -1,8 +1,3 @@ -// v2 runtime template for Go targets. -// -// Defines the v2rt package source text for Go emission. The emit layer writes -// generated/v2rt/v2rt.go into the output tree. - module v1.compiler.runtime_go fn go_rt_header() -> String { diff --git a/src/v1/runtime_rust.dag b/src/v1/runtime_rust.dag index f4d6b6dca0d..94656788230 100644 --- a/src/v1/runtime_rust.dag +++ b/src/v1/runtime_rust.dag @@ -1,13 +1,3 @@ -// v2 runtime template for Rust targets. -// -// Defines the Rust source text for the v1_rt runtime shim module. -// This is the single authority for runtime intrinsic implementations. -// The v1 V2_RUNTIME_SOURCE const in v1_runtime_shim.rs is bootstrap-only -// legacy and should not be extended. -// -// The emit layer calls rust_runtime_source() and writes the result as -// src/v1_rt.rs in the emitted crate. - module v1.compiler.runtime_rust fn rt_header() -> String { diff --git a/src/v1/stage0_crates.dag b/src/v1/stage0_crates.dag index c30c73f42bf..ecf9fe94503 100644 --- a/src/v1/stage0_crates.dag +++ b/src/v1/stage0_crates.dag @@ -1,22 +1,3 @@ -// v2 stage0 crate-boundary authority -- .dag model of the split crates. -// -// This module is the .dag-authored successor to the hand-written -// `render_stage0_*` constants and renderers in -// `src/v1/stage0/src/bin/regen_stage0.rs`. It declares, as data, which -// generated stage0 modules form each split support crate, that crate's -// Cargo dependencies, and whether it carries the root `NonEmpty*` wrappers -- -// then emits the exact `Cargo.toml` and `lib.rs` wrapper files `regen_stage0` -// writes and verifies. The wrapper bodies themselves are emitted from the -// existing single source `v1.compiler.emit_rust.emit_non_empty_wrappers`. -// -// Authority migration only: the emitted bytes are byte-for-byte identical to -// the files PR #4429 rendered from Rust. `regen_stage0` keeps its -// writer/verifier role but sources the wrapper contents from here. -// -// The semantic module bodies still live in the generated stage0 seed; this -// module never copies them -- it only references them by `#[path]` include or -// `pub use` re-export. - module v1.compiler.stage0_crates import v1.std.core { TextFile } @@ -24,23 +5,10 @@ import v1.compiler.emit_rust { emit_non_empty_wrappers, emit_cargo_dep } import extdeps.cargo { CargoDependency, CargoFeature, RegistryDep, LocalPathDep } import extdeps.cargo_version { render_cargo_package_header_prefix } -// ========================================================================= -// Crate-boundary model -// ========================================================================= - -// Which split crate, and therefore which lib.rs shape, a spec describes. type Stage0CrateKind = CoreCrate | EmitCoreCrate -// Dependencies reuse the canonical `extdeps.cargo.CargoDependency` model -// (name + source: registry version or local path) -- no local dep type. - -// One split support crate. -// -// `modules` are generated stage0 module stems (file name without `.rs`). -// For a CoreCrate they become `#[path]` includes; for an EmitCoreCrate they -// become re-export wrapper modules sourced from `v1-stage0-core`. type Stage0CrateSpec { package_name: String crate_dir: String @@ -48,13 +16,9 @@ type Stage0CrateSpec { header_doc: String modules: List dependencies: List - // Optional `[features]` entries for split crates whose generated modules use - // `#[cfg(feature = ...)]` (e.g. v1_rt's text_lookup_work_counter gate). + features: List - // Whether this crate defines the `NonEmpty*` newtype wrappers at its lib - // root. The wrapper bodies are NOT modeled here -- they are emitted from the - // single source `v1.compiler.emit_rust.emit_non_empty_wrappers`, the same - // function that produces them for the monolithic stage0 lib. + carries_non_empty_wrappers: Bool } @@ -62,18 +26,6 @@ type Stage0CratePlan { crates: List } -// ========================================================================= -// Shared literal fragments -// ========================================================================= - -// The crate-level `#![allow(...)]` block both wrapper libs open with. -// -// This is the same lint set `emit_lib_rs_from_files` puts on the monolith lib, -// but it is NOT shared with that inline literal: the monolith's output is -// rustfmt'd before its freshness compare, so it can emit the single-line form; -// the split-crate wrappers are compared raw by `verify_stage0_split_crate_boundaries`, -// so this must be the rustfmt-stable multi-line form. One helper cannot satisfy -// both verify paths byte-for-byte. fn stage0_crate_allow_block() -> String { [ "#![allow(", @@ -116,13 +68,6 @@ fn stage0_emit_core_header_doc() -> String { ] |> join(separator: "\n") } -// ========================================================================= -// Manifest emission -// ========================================================================= - -// Registry deps delegate to the existing single source `emit_cargo_dep` -// (same renderer the compiler uses for its own Cargo manifests). The path-dep -// form, which `emit_cargo_dep` does not cover, is rendered here. fn render_stage0_crate_dep(dep: CargoDependency) -> String { match dep.source { RegistryDep { version: version, features: features } => @@ -165,12 +110,6 @@ fn emit_stage0_crate_manifest(spec: Stage0CrateSpec) -> TextFile { TextFile { path: concat(spec.crate_dir, "/Cargo.toml"), content: content } } -// ========================================================================= -// lib.rs emission -// ========================================================================= - -// CoreCrate: `#[path]`-include each module, then carry the root wrappers. -// The wrapper bodies come from emit_rust's single source, not copied here. fn render_stage0_core_lib(spec: Stage0CrateSpec) -> String { let includes = spec.modules |> map(m => concat("#[path = \"../../stage0/src/", m, ".rs\"]\npub mod ", m, ";")) @@ -187,8 +126,6 @@ fn render_stage0_core_lib(spec: Stage0CrateSpec) -> String { } } -// EmitCoreCrate: re-export each module from v1-stage0-core, import the root -// shims, then include and glob-re-export the generated emit-core support. fn render_stage0_emit_core_lib(spec: Stage0CrateSpec) -> String { let reexports = spec.modules |> map(m => concat("pub mod ", m, " {\n pub use v1_stage0_core::", m, "::*;\n}")) @@ -211,10 +148,6 @@ fn emit_stage0_crate_lib(spec: Stage0CrateSpec) -> TextFile { TextFile { path: concat(spec.crate_dir, "/src/lib.rs"), content: content } } -// ========================================================================= -// Plan -- the existing #4429 crates, modeled as data -// ========================================================================= - fn stage0_crate_plan() -> Stage0CratePlan { Stage0CratePlan { crates: [ @@ -297,10 +230,6 @@ fn stage0_crate_plan() -> Stage0CratePlan { } } -// ========================================================================= -// Boundary file set -- what regen_stage0 writes and verifies -// ========================================================================= - fn emit_stage0_crate_boundary_files(plan: Stage0CratePlan) -> List { plan.crates |> flat_map(spec => [ emit_stage0_crate_manifest(spec), @@ -308,7 +237,6 @@ fn emit_stage0_crate_boundary_files(plan: Stage0CratePlan) -> List { ]) } -// Nullary entry point regen_stage0 calls. fn stage0_crate_boundary_files() -> List { emit_stage0_crate_boundary_files(stage0_crate_plan()) } diff --git a/src/v1/trace.dag b/src/v1/trace.dag index 63c017b4f8d..334527fbc99 100644 --- a/src/v1/trace.dag +++ b/src/v1/trace.dag @@ -1,22 +1,12 @@ -// v2 trace model -- normalized execution traces, repro cases, source mapping. - module v1.compiler.trace import v1.std.core { SourceSpan } -// ========================================================================= -// F1: Span mapping -- generated code line → source .dag span -// ========================================================================= - type SpanMapping { generated_line: Int source_span: SourceSpan } -// ========================================================================= -// F2: Trace events -- normalized from emitted runtimes or host adapters -// ========================================================================= - type TraceEvent = TraceEnter { node_id: String, span: SourceSpan, inputs: Map } | TraceExit { node_id: String, span: SourceSpan, output: String } @@ -41,7 +31,6 @@ fn trace_push_event(trace: Trace, event: TraceEvent) -> Trace { Trace { events: list_push(trace.events, event), stack: trace.stack } } -// Push a frame onto the trace stack (function entry). O(1) with Rc. fn trace_push_frame(trace: Trace, frame: TraceFrame) -> Trace { Trace { events: trace.events, stack: list_push(trace.stack, frame) } } @@ -55,10 +44,6 @@ fn trace_pop_frame(trace: Trace) -> Trace { } } -// ========================================================================= -// F2: Trace helpers -- extract fields from TraceEvent variants -// ========================================================================= - fn event_span(event: TraceEvent) -> SourceSpan { match event { TraceEnter { node_id: _, span: s, inputs: _ } => s @@ -75,10 +60,6 @@ fn event_node_id(event: TraceEvent) -> String { } } -// ========================================================================= -// F2: Trace filtering and replay -// ========================================================================= - type TraceFilter = FilterByFunc { func_name: String } | FilterBySpan { start: Int, end: Int } @@ -103,10 +84,6 @@ fn replay_trace(trace: Trace, filter: TraceFilter) -> List { } } -// ========================================================================= -// F2: Trace formatting -// ========================================================================= - fn format_span(sp: SourceSpan) -> String { concat("[", to_string(sp.start), "..", to_string(sp.end), ")") } @@ -126,10 +103,6 @@ fn format_trace(trace: Trace) -> List { trace.events |> map(e => format_trace_event(event: e)) } -// ========================================================================= -// F3: Hermetic reproduction -// ========================================================================= - type ReproCase { func_name: String inputs: Map @@ -146,10 +119,6 @@ fn capture_repro(func_name: String, inputs: Map, trace: Trace) - } } -// ========================================================================= -// F4: Cross-language source mapping -// ========================================================================= - type SourceMap { generated_file: String mappings: List diff --git a/src/v1/workspace_members.dag b/src/v1/workspace_members.dag index 9ef3ee53c70..b834eae0079 100644 --- a/src/v1/workspace_members.dag +++ b/src/v1/workspace_members.dag @@ -1,34 +1,7 @@ -// v2 workspace-membership authority -- .dag model of the generated stage0 -// crate entries inside the top-level `[workspace] members` array. -// -// Open Question 4 of the crate-boundary doc: `[workspace] members` was a -// hand-edited literal, so adding a split crate meant editing both the -// `stage0_crate_plan` AND `Cargo.toml`. This module closes that: the member -// entry for each generated stage0 crate is *derived* from the same -// `stage0_crate_plan` that emits the crate's `Cargo.toml`/`lib.rs`. Adding a -// `Stage0CrateSpec` now contributes its `[workspace] members` line for free -- -// regen_stage0 rewrites the bracketed region of the top-level manifest. -// -// Scope is deliberately narrow. Only the generated stage0 crate entries are -// modeled; the hand-authored members (the regen bin, tests, v3 crates, tools) -// and all other workspace-global config (`[workspace.dependencies]`, profiles, -// metadata) stay hand-edited in `Cargo.toml`. We own one bracketed region of -// that file, never the whole manifest. - module v1.compiler.workspace_members import v1.compiler.stage0_crates { stage0_crate_plan, Stage0CrateSpec } -// ========================================================================= -// Managed-region markers -// ========================================================================= - -// Sentinel comment lines that bracket the generated stage0 crate entries -// inside the top-level `Cargo.toml` `[workspace] members` array. regen_stage0 -// locates these (sourced from here, not hardcoded in Rust) and replaces the -// lines between them with `stage0_workspace_member_region`. The leading -// indentation matches the array's element indentation so the committed file is -// rustfmt/taplo-neutral. fn workspace_members_region_begin_marker() -> String { " # BEGIN generated stage0 crate members -- regen_stage0 writes this region (authority: v1.compiler.workspace_members)" } @@ -37,19 +10,10 @@ fn workspace_members_region_end_marker() -> String { " # END generated stage0 crate members" } -// ========================================================================= -// Region body -- one members entry per generated stage0 crate -// ========================================================================= - -// A single TOML array element line for one generated stage0 crate, using the -// crate's own `crate_dir` from the boundary plan -- the same field that names -// where its `Cargo.toml`/`lib.rs` are written. fn stage0_member_entry(spec: Stage0CrateSpec) -> String { concat(" \"", spec.crate_dir, "\",") } -// The full set of generated stage0 member lines, derived from the boundary -// plan. This is exactly the text regen_stage0 splices between the markers. fn stage0_workspace_member_region() -> String { stage0_crate_plan().crates |> map(stage0_member_entry) diff --git a/src/v2/lens/affected_set/boundary_prune_receipt.dag b/src/v2/lens/affected_set/boundary_prune_receipt.dag index aaa4a124b38..7024c912c85 100644 --- a/src/v2/lens/affected_set/boundary_prune_receipt.dag +++ b/src/v2/lens/affected_set/boundary_prune_receipt.dag @@ -1,4 +1,3 @@ - module v2.test.lens_affected_set.boundary_prune_receipt import v2.lens.affected_set { diff --git a/src/v2/lens/affected_set/closure_deep_chain.dag b/src/v2/lens/affected_set/closure_deep_chain.dag index 11388a95f19..c9cded4f871 100644 --- a/src/v2/lens/affected_set/closure_deep_chain.dag +++ b/src/v2/lens/affected_set/closure_deep_chain.dag @@ -1,7 +1,3 @@ -// Scope: D1a regression — root-seeded affected_set_closure must reach the |V|-deep leaf. -// Status: by-execution under-closure guard; substrate closure superset-safe; dimension + -// real-diff selectivity pending receipt + provenance producer. - module v2.test.lens_affected_set.closure_deep_chain import v2.lens.affected_set { affected_set_closure } @@ -12,7 +8,6 @@ import v2.std.logic { Bool } import v2.std.node { Atom, Edge, Named, Node, Symbol, TypeNode, SyntheticOccurrence } import v2.test.lens_common.infer_fixture { claim_atom_node } -// |V| = 8 linear chain: root -> ... -> leaf (depth |V|−1 Contains edges). data closure_deep_chain_leaf: Node = claim_atom_node(s: ^closure_deep_chain_leaf_symbol) data closure_deep_chain_n06: Node = Node { @@ -75,7 +70,6 @@ data closure_deep_chain_vertex_count: Int = 8 data closure_deep_chain_dependency_reading: List = dependency_lens(root: closure_deep_chain_root) -// Reverse pass order stresses one-hop-per-round propagation (needs iterate-to-stable fixpoint). data closure_deep_chain_reverse_dependency_reading: List = [ DependencyView { source: closure_deep_chain_n06, diff --git a/src/v2/lens/affected_set/closure_superset_safe.dag b/src/v2/lens/affected_set/closure_superset_safe.dag index 988e0a3daf5..78e0bde11b9 100644 --- a/src/v2/lens/affected_set/closure_superset_safe.dag +++ b/src/v2/lens/affected_set/closure_superset_safe.dag @@ -1,8 +1,3 @@ -// Scope: D1a receipt — substrate closure superset-safe; dimension + real-diff selectivity -// pending receipt + provenance producer. -// Status: scoped to affected_set_closure(dependencies, edit_locus) only; does NOT claim -// dimension-aware selectivity or real-git-diff coverage (D1b: canonical_changed_dimension_receipt). - module v2.test.lens_affected_set.closure_superset_safe import v2.lens.affected_set { AffectedSetClosureAcc, affected_set_closure } @@ -31,7 +26,6 @@ fn node_in_closure_frontier(closure: AffectedSetClosureAcc, node: Node) -> Bool ) } -// Prefix of the deep-chain substrate: three hops from root (strict subset of full reading). data closure_superset_safe_prefix_dependencies: List = [ DependencyView { source: closure_deep_chain_root, diff --git a/src/v2/lens/affected_set/data_dep_not_in_frontier.dag b/src/v2/lens/affected_set/data_dep_not_in_frontier.dag index 571dc599add..7ed89844d3f 100644 --- a/src/v2/lens/affected_set/data_dep_not_in_frontier.dag +++ b/src/v2/lens/affected_set/data_dep_not_in_frontier.dag @@ -1,4 +1,3 @@ - module v2.test.lens_affected_set.data_dep_not_in_frontier import v2.lens.affected_set { dependency_in_lens_frontier } diff --git a/src/v2/lens/affected_set/dimension_seed_rerun.dag b/src/v2/lens/affected_set/dimension_seed_rerun.dag index c56acd0aeb9..2a29930684a 100644 --- a/src/v2/lens/affected_set/dimension_seed_rerun.dag +++ b/src/v2/lens/affected_set/dimension_seed_rerun.dag @@ -1,7 +1,3 @@ -// Observation: ReExecFrontier.decisions is a std Map = PartialFunction with no decidable -// structural `==`; the receipt observes the frontier by application (frontier_decision_matches) -// and compares the resolved FrontierDecision, never whole-frontier `==`. - module v2.test.lens_affected_set.dimension_seed_rerun import v2.lens.affected_set { @@ -44,9 +40,6 @@ fn dimension_seed_rerun_claim_holds() -> Bool { ) } -// Discrimination receipt (perturb-red, committed): the seeded decision is Rerun, which -// must NOT match a wrong Excluded decision. Holds == true proves the comparison -// discriminates on the FrontierDecision arm, not a tautology that ignores it. data dimension_seed_wrong_decision: FrontierDecision = Excluded { first: DimensionNotRead { usage_site: affected_set_example_node, diff --git a/src/v2/lens/affected_set/edit_locus_resolver_test.dag b/src/v2/lens/affected_set/edit_locus_resolver_test.dag index 115d4c4fb6f..a434257a656 100644 --- a/src/v2/lens/affected_set/edit_locus_resolver_test.dag +++ b/src/v2/lens/affected_set/edit_locus_resolver_test.dag @@ -1,5 +1,3 @@ -// Scope: git diff → edit_locus Node resolver receipts (path-regex bindings). - module v2.test.lens_affected_set.edit_locus_resolver import extdeps.communication.medium { Lossless, Medium } @@ -40,7 +38,6 @@ import v2.test.lens_common.infer_fixture { claim_inferred_tree } - import v2.std.verification { BoolWitness, BoolWitnessClaim, @@ -172,7 +169,6 @@ fn edit_locus_occurrence_textual_file_eq( } } -// 🟡 gated — feature:source-authority-span-lane — bind fn edit_locus_source_provenance_producer_holds() -> Bool { match source_ir_artifact_provenance( source_read: edit_locus_source_producer_fixture_source_read @@ -193,7 +189,6 @@ fn edit_locus_source_provenance_producer_holds() -> Bool { } } -// occurrence_id excluded from structural equality (design-node-identity-channels.md). fn edit_locus_source_provenance_node_eq(left: Node, right: Node) -> Bool { exact_structural_equality_zip_fold(source_facts: left, candidate: right) } @@ -250,7 +245,6 @@ fn edit_locus_root_from_source_locus_holds( } } -// 🟡 gated — feature:source-authority-span-lane — bind fn edit_locus_source_provenance_parse_root_span_holds() -> Bool { match source_ir_artifact_provenance( source_read: edit_locus_source_producer_fixture_source_read @@ -282,15 +276,8 @@ data edit_locus_affected_set_wire_fail_closed_claim_passes: Bool = edit_locus_af data edit_locus_source_provenance_affected_set_wire_claim_passes: Bool = edit_locus_source_provenance_affected_set_wire_holds() data edit_locus_source_provenance_producer_rejects_malformed_source_claim_passes: Bool = edit_locus_source_provenance_producer_rejects_malformed_source_holds() -// Transport Symbol pin — independent authority for substrate-equivalence field-preservation laws. data edit_locus_narrow_resolution_holds_symbol: Symbol = edit_locus_narrow_resolution_holds -// Unified bool-witness claim for the narrow-resolution witness. Single authority -// for the claim consumed by v2.test.workflow.unified_test_claim_substrate_equivalence -// and the v2.workflow.glob_discovery type-head perturb fixture (which expects a -// discovered bool-witness decl named `unified_claim_edit_locus_narrow`). Was a -// dangling cross-module import until now — declared nowhere despite both consumers -// importing it (surfaced by the whole-tree resolve, invisible to the dsl-first gate). data unified_claim_edit_locus_narrow: UnifiedTestClaim = BoolWitnessClaim { witness: BoolWitness { entry: "src/v2/lens/affected_set/edit_locus_resolver_test.dag", diff --git a/src/v2/lens/affected_set/excluded_propagation_proof.dag b/src/v2/lens/affected_set/excluded_propagation_proof.dag index 5c8439f3e70..75a44235b0f 100644 --- a/src/v2/lens/affected_set/excluded_propagation_proof.dag +++ b/src/v2/lens/affected_set/excluded_propagation_proof.dag @@ -1,7 +1,3 @@ -// Observation: ReExecFrontier.decisions is a std Map = PartialFunction with no decidable -// structural `==`; the receipt observes the frontier by application (frontier_decision_matches) -// and compares the resolved FrontierDecision, never whole-frontier `==`. - module v2.test.lens_affected_set.excluded_propagation_proof import v2.lens.affected_set { @@ -65,10 +61,6 @@ fn excluded_propagation_proof_claim_holds() -> Bool { ) } -// Discrimination receipt (perturb-red, committed): one mark_excluded yields rest:[] -// (single proof), which must NOT match the accumulated expected decision rest:[proof]. -// Holds == true proves frontier_decision_matches discriminates on Excluded.rest contents -// — i.e. it is a real structural comparison, not a tautology that ignores accumulation. fn excluded_propagation_proof_discriminates() -> Bool { frontier_decision_matches( frontier: mark_excluded( diff --git a/src/v2/lens/affected_set/fail_closed_pending_escalation.dag b/src/v2/lens/affected_set/fail_closed_pending_escalation.dag index 24d38dd217d..8a2af8f71f5 100644 --- a/src/v2/lens/affected_set/fail_closed_pending_escalation.dag +++ b/src/v2/lens/affected_set/fail_closed_pending_escalation.dag @@ -1,4 +1,3 @@ - module v2.test.lens_affected_set.fail_closed_pending_escalation import v2.lens.affected_set { diff --git a/src/v2/lens/affected_set/frontier_observation.dag b/src/v2/lens/affected_set/frontier_observation.dag index c9958bc92ee..e551d3bb2d0 100644 --- a/src/v2/lens/affected_set/frontier_observation.dag +++ b/src/v2/lens/affected_set/frontier_observation.dag @@ -1,14 +1,3 @@ -// Scope: shared observation helper for affected_set frontier receipts. -// A ReExecFrontier carries `decisions: Map`, and -// std Map IS a PartialFunction (v2.std.collection: `lookup: fn(K) -> Witness`). -// Over structural (non-String) keys the v2 runtime represents that Map in its -// closure/record form, which has NO decidable structural `==` — two structurally -// identical maps compare false (even a value vs. itself). So a frontier receipt -// must OBSERVE the map by application (look the key up and compare the resolved -// FrontierDecision), never assert whole-frontier `==`. FrontierDecision is a -// coproduct over lists/records, which DO compare structurally, so the resolved -// decision is a sound equality target. - module v2.test.lens_affected_set.frontier_observation import v2.lens.affected_set { ChangedSubgraphFrontier, ExclusionKey, FailClosed, FrontierDecision, ReExecFrontier } diff --git a/src/v2/lens/affected_set/irt1_boundary_prune_receipt.dag b/src/v2/lens/affected_set/irt1_boundary_prune_receipt.dag index e5cd9b3c159..7e17a6ca384 100644 --- a/src/v2/lens/affected_set/irt1_boundary_prune_receipt.dag +++ b/src/v2/lens/affected_set/irt1_boundary_prune_receipt.dag @@ -1,5 +1,3 @@ -// Scope: IRT-1 leaf — purity-aware boundary prune receipt. - module v2.test.lens_affected_set.irt1_boundary_prune_receipt import v2.lens.affected_set { diff --git a/src/v2/lens/affected_set/irt1_dimension_seed_receipt.dag b/src/v2/lens/affected_set/irt1_dimension_seed_receipt.dag index 6f898a7178f..9cf7d897ee7 100644 --- a/src/v2/lens/affected_set/irt1_dimension_seed_receipt.dag +++ b/src/v2/lens/affected_set/irt1_dimension_seed_receipt.dag @@ -1,5 +1,3 @@ -// Scope: IRT-1 leaf — dimension seed rerun receipt. - module v2.test.lens_affected_set.irt1_dimension_seed_receipt import v2.lens.affected_set { DimensionNotRead, Excluded, ExclusionKey, FrontierDecision, Rerun, empty_frontier, mark_rerun } @@ -35,9 +33,6 @@ fn irt1_dimension_seed_receipt_claim_holds() -> Bool { ) } -// Discrimination receipt (perturb-red, committed): seeded decision is Rerun, which must -// NOT match a wrong Excluded decision. Holds == true proves the comparison discriminates -// on the FrontierDecision arm, not a tautology. data irt1_dimension_seed_wrong_decision: FrontierDecision = Excluded { first: DimensionNotRead { usage_site: affected_set_example_node, diff --git a/src/v2/lens/affected_set/irt1_empty_diff_frontier_receipt.dag b/src/v2/lens/affected_set/irt1_empty_diff_frontier_receipt.dag index e692e912660..4172dd95445 100644 --- a/src/v2/lens/affected_set/irt1_empty_diff_frontier_receipt.dag +++ b/src/v2/lens/affected_set/irt1_empty_diff_frontier_receipt.dag @@ -1,5 +1,3 @@ -// Scope: IRT-1 leaf — empty accumulator yields empty frontier. - module v2.test.lens_affected_set.irt1_empty_diff_frontier_receipt import v2.lens.affected_set { diff --git a/src/v2/lens/affected_set/irt1_excluded_propagation_receipt.dag b/src/v2/lens/affected_set/irt1_excluded_propagation_receipt.dag index 10b3656ec26..53dd29dfdd9 100644 --- a/src/v2/lens/affected_set/irt1_excluded_propagation_receipt.dag +++ b/src/v2/lens/affected_set/irt1_excluded_propagation_receipt.dag @@ -1,8 +1,3 @@ -// Scope: IRT-1 leaf — excluded-dimension propagation receipt. -// Observation: ReExecFrontier.decisions is a std Map = PartialFunction with no decidable -// structural `==`; the receipt observes the frontier by application (frontier_decision_matches) -// and compares the resolved FrontierDecision, never whole-frontier `==`. - module v2.test.lens_affected_set.irt1_excluded_propagation_receipt import v2.lens.affected_set { DimensionNotRead, Excluded, ExclusionKey, FrontierDecision, PropagationExclusionProof, empty_frontier, mark_excluded } @@ -54,9 +49,6 @@ fn irt1_excluded_propagation_receipt_claim_holds() -> Bool { ) } -// Discrimination receipt (perturb-red, committed): one mark_excluded yields rest:[], -// which must NOT match the accumulated expected decision rest:[proof]. Holds == true -// proves the comparison discriminates on Excluded.rest, not a tautology. fn irt1_excluded_propagation_receipt_discriminates() -> Bool { frontier_decision_matches( frontier: mark_excluded( diff --git a/src/v2/lens/affected_set/irt1_fail_closed_absorption_receipt.dag b/src/v2/lens/affected_set/irt1_fail_closed_absorption_receipt.dag index 91aa9bc60c9..ebe80e80976 100644 --- a/src/v2/lens/affected_set/irt1_fail_closed_absorption_receipt.dag +++ b/src/v2/lens/affected_set/irt1_fail_closed_absorption_receipt.dag @@ -1,5 +1,3 @@ -// Scope: IRT-1 leaf — fail-closed fold absorbs later edits via seed_edit_fold_acc and re_exec_frontier_from_diff. - module v2.test.lens_affected_set.irt1_fail_closed_absorption_receipt import v2.lens.affected_set { diff --git a/src/v2/lens/affected_set/irt1_leaf_claim_suite.dag b/src/v2/lens/affected_set/irt1_leaf_claim_suite.dag index ffed0c9b5fb..4c0b3b444a2 100644 --- a/src/v2/lens/affected_set/irt1_leaf_claim_suite.dag +++ b/src/v2/lens/affected_set/irt1_leaf_claim_suite.dag @@ -1,5 +1,3 @@ -// Scope: IRT-1 aggregate TestClaim for affected_set_irt1_subsumption MechanicalReverification row. - module v2.test.lens_affected_set.irt1_leaf_claim_suite import v2.lens.affected_set_examples { diff --git a/src/v2/lens/affected_set/node_precision.dag b/src/v2/lens/affected_set/node_precision.dag index 172641642ad..f214bcfbc7d 100644 --- a/src/v2/lens/affected_set/node_precision.dag +++ b/src/v2/lens/affected_set/node_precision.dag @@ -1,6 +1,3 @@ -// Scope: affected-set reading claim for Node-level edit/rebuild precision. - - module v2.test.lens_affected_set.node_precision import v2.compiler.infer { InferredFacts, InferredTree } diff --git a/src/v2/lens/affected_set/projection_dep_in_frontier.dag b/src/v2/lens/affected_set/projection_dep_in_frontier.dag index b8282760b93..edb617de199 100644 --- a/src/v2/lens/affected_set/projection_dep_in_frontier.dag +++ b/src/v2/lens/affected_set/projection_dep_in_frontier.dag @@ -1,4 +1,3 @@ - module v2.test.lens_affected_set.projection_dep_in_frontier import v2.lens.affected_set { dependency_in_lens_frontier } diff --git a/src/v2/lens/affected_set/provenance_producer.dag b/src/v2/lens/affected_set/provenance_producer.dag index 582dc0b6a96..5ca832d12a2 100644 --- a/src/v2/lens/affected_set/provenance_producer.dag +++ b/src/v2/lens/affected_set/provenance_producer.dag @@ -1,7 +1,3 @@ -// Scope: D1b-PR2 — whole-ingest NodeArtifactProvenance producer + git-diff→reading wire. -// Status: by-execution receipts for node_artifact_provenance_from_source_root; selectivity -// superset on the provenance→edit_locus→affected_set_reading path. - module v2.test.lens_affected_set.provenance_producer import extdeps.communication.medium { Lossless, Medium } @@ -39,7 +35,6 @@ import v2.test.lens_common.infer_fixture { claim_inferred_tree } - data provenance_producer_path_fixture: RepoPath = "src/v2/lens/affected_set/source_producer_fixture.dag" data provenance_producer_path_mid: RepoPath = "src/v2/lens/affected_set.dag" data provenance_producer_path_root: RepoPath = "src/v2/lens/affected_set_examples.dag" @@ -47,7 +42,7 @@ data provenance_producer_path_unknown: RepoPath = "src/v2/lens/unknown_path.dag" data provenance_producer_fixture_source: Medium = Medium { carried: dag_mvp1_source_text, fidelity: Lossless } data provenance_producer_malformed_source: Medium = Medium { carried: "fn add(x:Int, y:Int) -> Int { x + ", fidelity: Lossless } -// Valid wave-1 module surface — outside mvp1 fn-only parse coverage (fn-surface under-read axis). + data provenance_producer_module_surface_source: Medium = Medium { carried: "module edit.locus\nfn add(x:Int, y:Int) -> Int { x + y }", fidelity: Lossless } data provenance_producer_fixture_artifact: Artifact = Artifact { kind: SourceFile, diff --git a/src/v2/lens/affected_set/sg_claims_test.dag b/src/v2/lens/affected_set/sg_claims_test.dag index 5a59b7d76e1..13a5af58a8b 100644 --- a/src/v2/lens/affected_set/sg_claims_test.dag +++ b/src/v2/lens/affected_set/sg_claims_test.dag @@ -1,9 +1,5 @@ -// Scope: foldable-now claim-run witnesses for IRT-1 affected_set leaf receipts. -// Excluded (A-blocked-v2-crash, not in roster): dimension_seed, excluded_propagation, leaf_suite. - module v2.test.lens_affected_set.sg_claims - import v2.std.logic { Bool } import v2.test.lens_affected_set.fail_closed_pending_escalation { fail_closed_pending_escalation_claim_holds @@ -38,8 +34,6 @@ import v2.test.lens_affected_set.provenance_producer { provenance_producer_unknown_diff_fail_closed_holds } - - import v2.std.verification { BoolWitness, BoolWitnessClaim, @@ -49,93 +43,70 @@ test fn lens_affected_set_irt1_boundary_prune_holds() -> Bool { irt1_boundary_prune_receipt_claim_holds() } - test fn lens_affected_set_irt1_fail_closed_pending_escalation_holds() -> Bool { fail_closed_pending_escalation_claim_holds() } - test fn lens_affected_set_irt1_fail_closed_absorption_holds() -> Bool { irt1_fail_closed_absorption_receipt_claim_holds() } - test fn lens_affected_set_irt1_empty_diff_frontier_holds() -> Bool { irt1_empty_diff_frontier_receipt_claim_holds() } - test fn lens_affected_set_closure_deep_chain_holds() -> Bool { closure_deep_chain_claim_holds() } - test fn lens_affected_set_closure_superset_safe_holds() -> Bool { closure_superset_safe_claim_holds() } - test fn lens_affected_set_changed_dimension_receipt_classifies_union_holds() -> Bool { irt1_public_changed_dimension_receipt_classifies_union() } - test fn lens_affected_set_dimension_classification_conservative_discriminates_holds() -> Bool { irt1_dimension_classification_conservative_discriminates() } - test fn lens_affected_set_changed_edit_yields_changed_subgraph_holds() -> Bool { irt1_public_affected_set_changed_edit_yields_changed_subgraph() } - test fn lens_affected_set_provenance_producer_single_read_emits_row_holds() -> Bool { provenance_producer_single_read_emits_row_holds() } - test fn lens_affected_set_provenance_producer_rejects_malformed_ingest_holds() -> Bool { provenance_producer_rejects_malformed_ingest_holds() } - test fn lens_affected_set_provenance_producer_rejects_beyond_mvp1_surface_ingest_holds() -> Bool { provenance_producer_rejects_beyond_mvp1_surface_ingest_holds() } - test fn lens_affected_set_provenance_producer_parse_coverage_known_diff_fail_closed_holds() -> Bool { provenance_producer_parse_coverage_known_diff_fail_closed_holds() } - test fn lens_affected_set_provenance_producer_coverage_receipt_holds() -> Bool { provenance_producer_coverage_receipt_holds() } - test fn lens_affected_set_provenance_producer_git_diff_wire_holds() -> Bool { provenance_producer_git_diff_wire_holds() } - test fn lens_affected_set_provenance_producer_unknown_diff_fail_closed_holds() -> Bool { provenance_producer_unknown_diff_fail_closed_holds() } - test fn lens_affected_set_provenance_producer_selectivity_superset_holds() -> Bool { provenance_producer_selectivity_superset_holds() } - test fn lens_affected_set_provenance_producer_selectivity_superset_discriminates_holds() -> Bool { provenance_producer_selectivity_superset_discriminates_holds() } - -// D1b gap-1: diff→dimension classification witnesses (CI-enrolled via affected_set_ci_runner gate rows). -// Provenance-producer claims: structurally enrolled so discovery (discover_owned_data -// + owned_data_bool_witness_transport_tsv) projects them into the affected-set claim-run -// roster directly — replaces the hand-typed AffectedSetNodeFrontierClaimRunRow rows that -// formerly re-enrolled these by name in affected_set_ci_runner.dag. \ No newline at end of file diff --git a/src/v2/lens/affected_set/verified_by_in_frontier.dag b/src/v2/lens/affected_set/verified_by_in_frontier.dag index 24761c2387e..96520e2d32e 100644 --- a/src/v2/lens/affected_set/verified_by_in_frontier.dag +++ b/src/v2/lens/affected_set/verified_by_in_frontier.dag @@ -1,4 +1,3 @@ - module v2.test.lens_affected_set.verified_by_in_frontier import v2.lens.affected_set { dependency_in_lens_frontier } diff --git a/src/v2/lens/application/application_subterm_at_empty_path.dag b/src/v2/lens/application/application_subterm_at_empty_path.dag index ae57120049d..d12eb4100ce 100644 --- a/src/v2/lens/application/application_subterm_at_empty_path.dag +++ b/src/v2/lens/application/application_subterm_at_empty_path.dag @@ -1,5 +1,3 @@ -// Status: compile-only ratchet for application.dag consumer edge. - module v2.test.lens_application.application_subterm_at_empty_path import v2.lens.application { subterm_at } diff --git a/src/v2/lens/application/apply_lens_enforce_uses_projection.dag b/src/v2/lens/application/apply_lens_enforce_uses_projection.dag index 0e02c38445f..bd1a3df9c97 100644 --- a/src/v2/lens/application/apply_lens_enforce_uses_projection.dag +++ b/src/v2/lens/application/apply_lens_enforce_uses_projection.dag @@ -1,5 +1,3 @@ -// Status: compile-only ratchet for LensEnforcement projection + output-shaped violation relation. - module v2.test.lens_application.apply_lens_enforce_uses_projection import v2.lens.application { diff --git a/src/v2/lens/application/apply_lens_introspect_rejection_is_advisory.dag b/src/v2/lens/application/apply_lens_introspect_rejection_is_advisory.dag index 9f0e0cbd004..a9260cc8830 100644 --- a/src/v2/lens/application/apply_lens_introspect_rejection_is_advisory.dag +++ b/src/v2/lens/application/apply_lens_introspect_rejection_is_advisory.dag @@ -1,5 +1,3 @@ -// Status: compile-only ratchet — CompileLensIntrospect advisory demotion at compile boundary. - module v2.test.lens_application.apply_lens_introspect_rejection_is_advisory import v2.compiler.compile { @@ -60,8 +58,6 @@ fn apply_lens_mode_gate_rejects_enforce() -> Bool { } } -// Pins: Introspect demotion produces Accepted(Violates) where Violates.diagnostic.reason matches -// the original gate rejection reason (not a fabricated diagnostic). fn introspect_witness_violates_reason_matches(w: Witness) -> Bool { match w { Violates { diagnostic: d } => d.reason == ^compile_lens_gate_rejected @@ -69,9 +65,6 @@ fn introspect_witness_violates_reason_matches(w: Witness) -> Bool { } } -// 🟡 gated — feature:v2-nested-match — same constraint as introspect_witness_violates_reason_matches. -// Pins: advisory Accepted.diagnostics carries Some (not None) and head.reason matches the original -// gate rejection reason. Does not pin locus, tail, or correction — only head.reason identity. fn introspect_diagnostics_head_reason_matches(od: Diagnostics) -> Bool { match od { Some { diagnostics: r } => r.head.reason == ^compile_lens_gate_rejected diff --git a/src/v2/lens/application/empty_required_lenses_skip_gate.dag b/src/v2/lens/application/empty_required_lenses_skip_gate.dag index 6a11fb814b5..19cfb68bac4 100644 --- a/src/v2/lens/application/empty_required_lenses_skip_gate.dag +++ b/src/v2/lens/application/empty_required_lenses_skip_gate.dag @@ -1,4 +1,3 @@ - module v2.test.lens_application.empty_required_lenses_skip_gate import v2.std.host_transport { target_emit_host_runtime_row_unconfigured } diff --git a/src/v2/lens/application/enforce_rejecting_gate_fires.dag b/src/v2/lens/application/enforce_rejecting_gate_fires.dag index 4f5e993d458..009d36612b0 100644 --- a/src/v2/lens/application/enforce_rejecting_gate_fires.dag +++ b/src/v2/lens/application/enforce_rejecting_gate_fires.dag @@ -1,4 +1,3 @@ - module v2.test.lens_application.enforce_rejecting_gate_fires import v2.compiler.compile { diff --git a/src/v2/lens/application/introspect_clean_tree_passes.dag b/src/v2/lens/application/introspect_clean_tree_passes.dag index f7e7a7ee9f8..1ef10c6fa18 100644 --- a/src/v2/lens/application/introspect_clean_tree_passes.dag +++ b/src/v2/lens/application/introspect_clean_tree_passes.dag @@ -1,4 +1,3 @@ - module v2.test.lens_application.introspect_clean_tree_passes import v2.compiler.compile { CompileLens, CompileLensIntrospect, apply_compile_lens } diff --git a/src/v2/lens/application/rejecting_lens_blocks_before_compile.dag b/src/v2/lens/application/rejecting_lens_blocks_before_compile.dag index 91580a6d730..8551cbb7fca 100644 --- a/src/v2/lens/application/rejecting_lens_blocks_before_compile.dag +++ b/src/v2/lens/application/rejecting_lens_blocks_before_compile.dag @@ -1,4 +1,3 @@ - module v2.test.lens_application.rejecting_lens_blocks_before_compile import v2.std.host_transport { target_emit_host_runtime_row_unconfigured } diff --git a/src/v2/lens/application/serialize_applied_diff_swap_operands_test.dag b/src/v2/lens/application/serialize_applied_diff_swap_operands_test.dag index 8b373f0a8bf..2fba5cc88f0 100644 --- a/src/v2/lens/application/serialize_applied_diff_swap_operands_test.dag +++ b/src/v2/lens/application/serialize_applied_diff_swap_operands_test.dag @@ -1,5 +1,3 @@ -// Scope: by-execution witness — apply_diff + serialize_diff_as_source_patch → DagSourcePatch. - module v2.test.lens_application.serialize_applied_diff_swap_operands import extdeps.communication.medium { Lossless, Medium } diff --git a/src/v2/lens/application/sg_claims_test.dag b/src/v2/lens/application/sg_claims_test.dag index 429a4dc5270..7283e9e5dab 100644 --- a/src/v2/lens/application/sg_claims_test.dag +++ b/src/v2/lens/application/sg_claims_test.dag @@ -1,9 +1,5 @@ -// Scope: discriminating claim-run witnesses for lens application + synthesis -// Status: execute-the-carrier pos+neg arms; routed through v2 `dag run --claim-run`. - module v2.test.lens_application.sg_claims - import v2.lens.application { DeclarationId, DeclarationScope, @@ -23,8 +19,6 @@ import v2.test.lens_synthesis.synthesis_gap_polynomial { synthesis_gap_poly_lens_non_empty } - - import v2.std.verification { BoolWitness, BoolWitnessClaim, @@ -36,12 +30,10 @@ data lens_application_synthesis_root_declaration: DeclarationId = DeclarationId path: lens_application_synthesis_root_path } - test fn lens_application_introspect_advisory_holds() -> Bool { apply_lens_introspect_rejection_is_advisory_claim_holds() } - fn lens_application_synthesis_gap_enforce_rejects() -> Bool { match apply_advisory_lens( lens: decision_tree_synthesis_advisory_lens, @@ -60,12 +52,10 @@ fn lens_application_synthesis_gap_enforce_rejects() -> Bool { } } - test fn lens_application_synthesis_gap_polynomial_holds() -> Bool { synthesis_gap_poly_lens_non_empty() } - test fn lens_application_synthesis_gap_enforce_rejection_holds() -> Bool { lens_application_synthesis_gap_enforce_rejects() } diff --git a/src/v2/lens/application/substitute_at_ambiguous_duplicate_name.dag b/src/v2/lens/application/substitute_at_ambiguous_duplicate_name.dag index 0176b33b910..f9345c98dbd 100644 --- a/src/v2/lens/application/substitute_at_ambiguous_duplicate_name.dag +++ b/src/v2/lens/application/substitute_at_ambiguous_duplicate_name.dag @@ -1,5 +1,3 @@ -// Status: compile-only ratchet for D1 ambiguous substitution rejection. - module v2.test.lens_application.substitute_at_ambiguous_duplicate_name import v2.lens.application { substitute_at } diff --git a/src/v2/lens/application/substitute_at_depth_three.dag b/src/v2/lens/application/substitute_at_depth_three.dag index 15bd63d434f..73fb9351412 100644 --- a/src/v2/lens/application/substitute_at_depth_three.dag +++ b/src/v2/lens/application/substitute_at_depth_three.dag @@ -1,5 +1,3 @@ -// Status: compile-only ratchet for D1 ancestor threading order (depth > 2). - module v2.test.lens_application.substitute_at_depth_three import v2.lens.application { substitute_at } diff --git a/src/v2/lens/application/substitute_at_depth_two.dag b/src/v2/lens/application/substitute_at_depth_two.dag index 2cb94e206d1..52834bd4d0b 100644 --- a/src/v2/lens/application/substitute_at_depth_two.dag +++ b/src/v2/lens/application/substitute_at_depth_two.dag @@ -1,5 +1,3 @@ -// Status: compile-only ratchet for D1 t[s]p ancestor rebuild. - module v2.test.lens_application.substitute_at_depth_two import v2.lens.application { substitute_at } diff --git a/src/v2/lens/application/subterm_at_ambiguous_duplicate_name.dag b/src/v2/lens/application/subterm_at_ambiguous_duplicate_name.dag index 400adf48372..57d3b6e276d 100644 --- a/src/v2/lens/application/subterm_at_ambiguous_duplicate_name.dag +++ b/src/v2/lens/application/subterm_at_ambiguous_duplicate_name.dag @@ -1,5 +1,3 @@ -// Status: compile-only ratchet for D1 ambiguous path rejection. - module v2.test.lens_application.subterm_at_ambiguous_duplicate_name import v2.lens.application { subterm_at } diff --git a/src/v2/lens/common/construction_justification.dag b/src/v2/lens/common/construction_justification.dag index 2bf9675b066..01569b85d8a 100644 --- a/src/v2/lens/common/construction_justification.dag +++ b/src/v2/lens/common/construction_justification.dag @@ -1,59 +1,15 @@ -// src/v2/lens/common/construction_justification.dag -// Single authority (§3) for the AUTHORING-TIME construction-justification rule (DESIGN §5/§6, -// ROADMAP §0). DESIGN §6: "construction first" — before adding ANY lens you must justify why -// the bad-state class cannot be made unwritable by CONSTRUCTION (single authority / realization -// derived from model); convert what can be converted, and reserve a lens for the genuinely -// unstructurable residue (§6: a lens is *validation* — it concedes the bad state is writable). -// -// This module models the §5 decidability trichotomy ONCE so every lens records its judgment on -// its own carrier (no parallel ledger — DESIGN §3/§6: the mark on the carrier is the authority). -// The three classes are DESIGN §5's, which docs/plans/expressibility-frontier.md §2 generalizes -// into a frontier of regions ①Wall / ②Lens-residue / ③Inexpressible. We reuse those names — this -// is NOT a parallel taxonomy: -// - WallNow ≡ frontier ① Wall (decidable AND grounded: a single authority -// already makes the bad state unwritable / the value total-by-construction -// — a thing in this class should be CONSTRUCTION, not a validation lens). -// - WallAfterGrounding ⊆ frontier ② Lens-residue (decidable, but not yet unwritable — waiting on -// its single authority; an interim lens that NAMES the authority that will -// turn it into a wall and the trigger that dissolves it). -// - RatchetForever ≡ frontier ③ Inexpressible (undecidable — by Rice or by missing domain -// knowledge; a permanent residue mechanism / honest review, never a wall — -// the §5 "never" trap is pricing this region as ①). -// -// LAYERING (DESIGN §6, the §7 recursion — do NOT confuse the two checks): -// - The #5433 inert-lens backstop (discover_floor_corpus_rows) is the EXECUTABLE corpus check: -// every lens is wired-or-deleted. It runs over the corpus and is the floor guarantee. -// - THIS rule is the AUTHORING-TIME judgment layered ON TOP of it: the *correctness* of a class -// + rationale is the unstructurable human residue, but the REQUIREMENT to have RECORDED a -// justification is itself structurable — so a fail-closed presence check (sibling to the -// #5433 backstop) makes the missing-justification state unwritable. The judgment does not, and -// cannot, replace the corpus check; it reinforces it. - module v2.lens.common.construction_justification import v2.std.text { String } -// DESIGN §5 decidability trichotomy / expressibility-frontier §2 regions. The class a lens's -// target bad-state belongs to — the answer to "why is this a lens (validation) and not -// construction?". type ConstructionClass - // ① Wall — decidable and grounded NOW. A lens here is a category error: the bad state is (or - // can be) made unwritable by a single authority, or the value is total-by-construction. - // `construction` names that authority / total-computation. Recorded honestly when a module - // filed under `v2.lens.*` is actually a construction, not a validation (a §3 home question). + = WallNow { construction: String } - // ② Lens-residue — decidable, but the construction is not built yet. `grounding_authority` - // names the single authority that will turn this into a wall; `dissolve_on` is the trigger that - // deletes the lens. An interim, SHRINKING residue. + | WallAfterGrounding { grounding_authority: String, dissolve_on: String } - // ③ Inexpressible — undecidable (Rice) or needs domain knowledge. `undecidable_because` states - // which. A permanent residue / honest review — never a wall. + | RatchetForever { undecidable_because: String } -// A lens's recorded construction-justification. `class` is the structurable §5 bucket; -// `rationale` is the human, unstructurable judgment (DESIGN §6 residue) — why construction is not -// (yet) available for this lens's bad-state class. The presence-check enforces that this decl -// exists and is well-typed; it does NOT (cannot) verify the rationale is correct. type ConstructionJustification { class: ConstructionClass rationale: String diff --git a/src/v2/lens/common/infer_fixture.dag b/src/v2/lens/common/infer_fixture.dag index 7dc8d45a704..a86013196c5 100644 --- a/src/v2/lens/common/infer_fixture.dag +++ b/src/v2/lens/common/infer_fixture.dag @@ -1,5 +1,3 @@ -// Status: introduced as Phase-1 ratchet test-claim template (crisp-boar-896). - module v2.test.lens_common.infer_fixture import v2.compiler.infer { InferredFacts, InferredTree } diff --git a/src/v2/lens/complexity/algebra_receipts_test.dag b/src/v2/lens/complexity/algebra_receipts_test.dag index c74d9573a96..9b88c13639a 100644 --- a/src/v2/lens/complexity/algebra_receipts_test.dag +++ b/src/v2/lens/complexity/algebra_receipts_test.dag @@ -1,4 +1,3 @@ - module v2.test.lens_complexity.algebra_receipts import v2.lens.complexity { @@ -111,16 +110,10 @@ fn variable_sensitive_dominance_actual() -> Bool { ) == variable_sensitive_dominance_claim.expected_dominates } -// By-execution gate: the product of two degree-2 polynomials over distinct -// variables projects to the declared degree-4 multidimensional bound — the -// degree-addition and variable-merge algebra. A degree miscount goes RED. test fn algebra_receipts_polynomial_product_projection_holds() -> Bool { polynomial_product_projected_complexity() == polynomial_product_degree_claim.expected_complexity } -// By-execution gate: dominance is variable-sensitive — two linear bounds over -// DIFFERENT size variables are incomparable (neither dominates). A name-blind -// comparator that returned dominance would go RED. test fn algebra_receipts_variable_sensitive_dominance_holds() -> Bool { variable_sensitive_dominance_actual() } diff --git a/src/v2/lens/complexity/map_id_test.dag b/src/v2/lens/complexity/map_id_test.dag index 82a2d0436c6..39b0237ade2 100644 --- a/src/v2/lens/complexity/map_id_test.dag +++ b/src/v2/lens/complexity/map_id_test.dag @@ -1,4 +1,3 @@ - module v2.test.lens_complexity.map_id import v2.lens.complexity { ComplexityBound, complexity_lens, linear_complexity } @@ -71,8 +70,6 @@ fn map_id_actual_complexity() -> Witness { complexity_lens(n: map_id_input) } -// By-execution gate: cost_lens projects the iterative Cardinality body to the -// declared LinearCost; a wrong fold (e.g. constant or quadratic) goes RED. test fn map_id_cost_projection_holds() -> Bool { match map_id_actual_cost() { Holds { value: actual } => actual == map_id_claim.expected_cost @@ -80,8 +77,6 @@ test fn map_id_cost_projection_holds() -> Bool { } } -// By-execution gate: the complexity lens (asymptotic projection of cost_lens) -// yields the declared linear bound. Discriminates a mis-projected class/variable. test fn map_id_complexity_projection_holds() -> Bool { match map_id_actual_complexity() { Holds { value: actual } => actual == map_id_claim.expected_complexity diff --git a/src/v2/lens/complexity/nested_test.dag b/src/v2/lens/complexity/nested_test.dag index fec2dd768cb..dcbe5542ff5 100644 --- a/src/v2/lens/complexity/nested_test.dag +++ b/src/v2/lens/complexity/nested_test.dag @@ -1,4 +1,3 @@ - module v2.test.lens_complexity.nested import v2.lens.complexity { @@ -103,8 +102,6 @@ fn nested_actual_complexity() -> Witness { complexity_lens(n: nested_input) } -// By-execution gate: nested Cardinality folds to a product cost over the two -// distinct size variables. A flattened single-variable fold goes RED. test fn nested_cost_projection_holds() -> Bool { match nested_actual_cost() { Holds { value: actual } => actual == nested_claim.expected_cost @@ -112,8 +109,6 @@ test fn nested_cost_projection_holds() -> Bool { } } -// By-execution gate: the asymptotic projection of the product cost is the -// declared multidimensional quadratic bound (degree 2, both variables). test fn nested_complexity_projection_holds() -> Bool { match nested_actual_complexity() { Holds { value: actual } => actual == nested_claim.expected_complexity diff --git a/src/v2/lens/complexity/typescript_wave2a_type_alias_task.dag b/src/v2/lens/complexity/typescript_wave2a_type_alias_task.dag index e53a33cf5db..d09316db795 100644 --- a/src/v2/lens/complexity/typescript_wave2a_type_alias_task.dag +++ b/src/v2/lens/complexity/typescript_wave2a_type_alias_task.dag @@ -1,4 +1,3 @@ - module v2.test.lens_complexity.typescript_wave2a_type_alias_task import v2.extdeps.languages.typescript { ts_wave2a_task_type_alias_emitted } diff --git a/src/v2/lens/complexity/while_external_condition_test.dag b/src/v2/lens/complexity/while_external_condition_test.dag index 9421c1f9f58..d1d84c346ce 100644 --- a/src/v2/lens/complexity/while_external_condition_test.dag +++ b/src/v2/lens/complexity/while_external_condition_test.dag @@ -1,4 +1,3 @@ - module v2.test.lens_complexity.while_external_condition import v2.lens.complexity { @@ -72,9 +71,6 @@ fn while_external_condition_actual_complexity() -> Witness { complexity_lens(n: while_external_condition_input) } -// By-execution gate (§5 fail-closed): a Loop with an undeclared bound has no -// modeled cost, so cost_lens must Violate — never fabricate a finite bound. A -// fail-open lens that returned Holds would go RED here. test fn while_external_condition_cost_fails_closed_holds() -> Bool { match while_external_condition_actual_cost() { Holds { value: _ } => false @@ -82,8 +78,6 @@ test fn while_external_condition_cost_fails_closed_holds() -> Bool { } } -// By-execution gate (§5 fail-closed): the asymptotic projection propagates the -// cost Violation rather than inventing an UnknownComplexity Holds. test fn while_external_condition_complexity_fails_closed_holds() -> Bool { match while_external_condition_actual_complexity() { Holds { value: _ } => false diff --git a/src/v2/lens/cost/atom_zero_test.dag b/src/v2/lens/cost/atom_zero_test.dag index 2a388ba032b..766cb3b19fa 100644 --- a/src/v2/lens/cost/atom_zero_test.dag +++ b/src/v2/lens/cost/atom_zero_test.dag @@ -1,4 +1,3 @@ - module v2.test.lens_cost.atom_zero import v2.lens.cost { cost_lens, zero_cost } diff --git a/src/v2/lens/cost/disj_alternative_floor_test.dag b/src/v2/lens/cost/disj_alternative_floor_test.dag index 0f148f0ab4c..76fb28b6112 100644 --- a/src/v2/lens/cost/disj_alternative_floor_test.dag +++ b/src/v2/lens/cost/disj_alternative_floor_test.dag @@ -1,14 +1,3 @@ -// Status: EXECUTED Bool witness — all-zero Disj arms still cost at least O(1). -// -// Pins the cost-lens AlternativeCost floor (cost.dag `compose_child_cost` AlternativeCost arm). -// Two checks together pin the CORRECT fix uniquely: -// (1) bug-fix: cost_lens(Disj([Atom, Atom])) projects unit_cost(), NOT zero_cost() — -// selecting between alternatives has a non-trivial cost even when every branch body is free. -// (2) control: symbolic_max's zero-absorption is PRESERVED — symbolic_max(0, 0) == 0. -// (2) is what distinguishes the right fix (symbolic_max(child, unit) floor at the -// AlternativeCost layer) from the wrong one (removing zero-absorption inside symbolic_max): -// BOTH make (1) green, but the wrong fix makes (2) RED. - module v2.test.lens_cost.disj_alternative_floor import v2.lens.cost { @@ -29,7 +18,6 @@ import v2.std.node { } import v2.std.witness { Holds } -// Two zero-cost Disj arms (Atom base cost = zero_cost). data disj_floor_arm_a: Node = Node { kind: TypeNode { connective: Atom { identity: ^disj_floor_arm_a_atom } }, children: [], @@ -42,7 +30,6 @@ data disj_floor_arm_b: Node = Node { occurrence_id: SyntheticOccurrence } -// Disj with two zero-cost arms — the structure cost_lens folds. data disj_floor_input: Node = Node { kind: TypeNode { connective: Disj }, children: [ @@ -52,7 +39,6 @@ data disj_floor_input: Node = Node { occurrence_id: SyntheticOccurrence } -// (1) Bug-fix: a Disj over zero-cost branches projects unit_cost(), not zero_cost(). fn disj_floor_projects_unit() -> Bool { match cost_lens(n: disj_floor_input) { Holds { value: actual } => actual == unit_cost() @@ -60,8 +46,6 @@ fn disj_floor_projects_unit() -> Bool { } } -// (2) Control: symbolic_max keeps absorbing zero — max(0, 0) == 0. The wrong fix -// (removing zero-absorption inside symbolic_max) makes this return unit_cost(), going RED here. fn symbolic_max_absorbs_zero() -> Bool { symbolic_max(a: zero_cost(), b: zero_cost()) == zero_cost() } diff --git a/src/v2/lens/cost/disj_max.dag b/src/v2/lens/cost/disj_max.dag index e2bcc570ced..e6e3aac0fc1 100644 --- a/src/v2/lens/cost/disj_max.dag +++ b/src/v2/lens/cost/disj_max.dag @@ -1,4 +1,3 @@ - module v2.test.lens_cost.disj_max import v2.lens.cost { LinearCost, SizeVariable, cost_lens } diff --git a/src/v2/lens/cost/loop_illegal_named.dag b/src/v2/lens/cost/loop_illegal_named.dag index 770dcff35af..29db16ff78c 100644 --- a/src/v2/lens/cost/loop_illegal_named.dag +++ b/src/v2/lens/cost/loop_illegal_named.dag @@ -1,4 +1,3 @@ - module v2.test.lens_cost.loop_illegal_named import v2.lens.cost { cost_lens } diff --git a/src/v2/lens/cost/loop_iteration_floor_test.dag b/src/v2/lens/cost/loop_iteration_floor_test.dag index 628c35f936b..1b2543daad2 100644 --- a/src/v2/lens/cost/loop_iteration_floor_test.dag +++ b/src/v2/lens/cost/loop_iteration_floor_test.dag @@ -1,14 +1,3 @@ -// Status: EXECUTED Bool witness — an empty/zero-cost loop body still costs O(n). -// -// Pins the cost-lens iteration floor (cost.dag `compose_child_cost` IterativeCost arm). -// Two checks together pin the CORRECT fix uniquely: -// (1) bug-fix: cost_lens(zero-body loop) projects LinearCost in the bound measure, NOT -// ConstantCost — the loop's iteration count survives a zero-cost body. -// (2) control: symbolic_product's absorbing zero is PRESERVED — symbolic_product(linear, 0) == 0. -// (2) is what distinguishes the right fix (symbolic_max(child, unit) floor at the -// IterativeCost layer) from the wrong one (making 0 the multiplicative identity -// inside symbolic_product): BOTH make (1) green, but the identity hack makes (2) RED. - module v2.test.lens_cost.loop_iteration_floor import v2.lens.cost { @@ -32,21 +21,18 @@ import v2.std.node { } import v2.std.witness { Holds } -// A zero-cost loop body: a bare Atom (base_cost_for_connective Atom => zero_cost). data loop_iteration_floor_body: Node = Node { kind: TypeNode { connective: Atom { identity: ^loop_iteration_floor_body_atom } }, children: [], occurrence_id: SyntheticOccurrence } -// The loop-bound measure the cardinality is linear in. data loop_iteration_floor_measure: Node = Node { kind: TypeNode { connective: Atom { identity: ^loop_iteration_floor_measure_atom } }, children: [], occurrence_id: SyntheticOccurrence } -// Loop with a zero-cost body and a bound edge — the structure cost_lens folds. data loop_iteration_floor_input: Node = Node { kind: ComputationNode { behavior: Loop }, children: [ @@ -56,7 +42,6 @@ data loop_iteration_floor_input: Node = Node { occurrence_id: SyntheticOccurrence } -// (1) Bug-fix: an empty loop projects LinearCost in the bound measure, not ConstantCost. fn loop_iteration_floor_projects_linear() -> Bool { match cost_lens(n: loop_iteration_floor_input) { Holds { value: actual } => @@ -68,8 +53,6 @@ fn loop_iteration_floor_projects_linear() -> Bool { } } -// (2) Control: symbolic_product keeps absorbing zero — linear * 0 == 0. The wrong fix -// (0 as the product identity) makes this return linear, going RED here. fn symbolic_product_absorbs_zero() -> Bool { symbolic_product( a: linear_in_node(n: loop_iteration_floor_measure), diff --git a/src/v2/lens/cost/loop_linear.dag b/src/v2/lens/cost/loop_linear.dag index a2d910dac12..b7a00b80660 100644 --- a/src/v2/lens/cost/loop_linear.dag +++ b/src/v2/lens/cost/loop_linear.dag @@ -1,4 +1,3 @@ - module v2.test.lens_cost.loop_linear import v2.lens.cost { LinearCost, cost_lens } diff --git a/src/v2/lens/cost/loop_unknown.dag b/src/v2/lens/cost/loop_unknown.dag index 4cb899d177c..998c713e698 100644 --- a/src/v2/lens/cost/loop_unknown.dag +++ b/src/v2/lens/cost/loop_unknown.dag @@ -1,4 +1,3 @@ - module v2.test.lens_cost.loop_unknown import v2.lens.cost { cost_lens } diff --git a/src/v2/lens/cost/map_linear.dag b/src/v2/lens/cost/map_linear.dag index d16c3982400..a0059172206 100644 --- a/src/v2/lens/cost/map_linear.dag +++ b/src/v2/lens/cost/map_linear.dag @@ -1,4 +1,3 @@ - module v2.test.lens_cost.map_linear import v2.lens.cost { LinearCost, SizeVariable, cost_lens } diff --git a/src/v2/lens/cost/nested_product.dag b/src/v2/lens/cost/nested_product.dag index 5e0813281e3..40ea3ccd034 100644 --- a/src/v2/lens/cost/nested_product.dag +++ b/src/v2/lens/cost/nested_product.dag @@ -1,4 +1,3 @@ - module v2.test.lens_cost.nested_product import v2.lens.cost { LinearCost, ProductCost, SizeVariable, cost_lens } diff --git a/src/v2/lens/cost/p9_llvm_instruction_cost_registry_owner.dag b/src/v2/lens/cost/p9_llvm_instruction_cost_registry_owner.dag index 4de48028ed6..4df1ab11361 100644 --- a/src/v2/lens/cost/p9_llvm_instruction_cost_registry_owner.dag +++ b/src/v2/lens/cost/p9_llvm_instruction_cost_registry_owner.dag @@ -1,7 +1,3 @@ -// Scope: B1 structural receipt — exactly one LensOwnedFnV0 registry row equals the canonical -// llvm_instruction_cost authority (fn_name + owner_module_path on the same row). -// Corpus scan for shadow fn defs: v2_p9_llvm_instruction_cost_single_owner_test.rs (P9 receipt); - module v2.test.lens_cost.p9_llvm_instruction_cost_registry_owner import v2.lens.registry { diff --git a/src/v2/lens/coverage/discriminant_predicate_defect_key.dag b/src/v2/lens/coverage/discriminant_predicate_defect_key.dag index db632d9a6bd..e574545e441 100644 --- a/src/v2/lens/coverage/discriminant_predicate_defect_key.dag +++ b/src/v2/lens/coverage/discriminant_predicate_defect_key.dag @@ -1,4 +1,3 @@ - module v2.test.lens_coverage.discriminant_predicate_defect_key import v2.lens.coverage { diff --git a/src/v2/lens/coverage/hollow_type_defect_key_test.dag b/src/v2/lens/coverage/hollow_type_defect_key_test.dag index bc2f8df398d..0fcaa3490a8 100644 --- a/src/v2/lens/coverage/hollow_type_defect_key_test.dag +++ b/src/v2/lens/coverage/hollow_type_defect_key_test.dag @@ -1,4 +1,3 @@ - module v2.test.lens_coverage.hollow_type_defect_key import v2.lens.coverage { diff --git a/src/v2/lens/coverage/near_miss_vacuous_not_parallel.dag b/src/v2/lens/coverage/near_miss_vacuous_not_parallel.dag index 8e3c1e3fdf4..543d5c228ed 100644 --- a/src/v2/lens/coverage/near_miss_vacuous_not_parallel.dag +++ b/src/v2/lens/coverage/near_miss_vacuous_not_parallel.dag @@ -1,4 +1,3 @@ - module v2.test.lens_coverage.near_miss_vacuous_not_parallel import v2.lens.coverage { diff --git a/src/v2/lens/coverage/parallel_authority_defect_key.dag b/src/v2/lens/coverage/parallel_authority_defect_key.dag index 92f42dd962f..9afa72306f2 100644 --- a/src/v2/lens/coverage/parallel_authority_defect_key.dag +++ b/src/v2/lens/coverage/parallel_authority_defect_key.dag @@ -1,4 +1,3 @@ - module v2.test.lens_coverage.parallel_authority_defect_key import v2.lens.coverage { diff --git a/src/v2/lens/coverage/sibling_degenerate_not_hollow.dag b/src/v2/lens/coverage/sibling_degenerate_not_hollow.dag index 192c01cff9d..b944ba8d939 100644 --- a/src/v2/lens/coverage/sibling_degenerate_not_hollow.dag +++ b/src/v2/lens/coverage/sibling_degenerate_not_hollow.dag @@ -1,4 +1,3 @@ - module v2.test.lens_coverage.sibling_degenerate_not_hollow import v2.lens.coverage { diff --git a/src/v2/lens/effect/effect_depends_on.dag b/src/v2/lens/effect/effect_depends_on.dag index 0bec3246dc5..fb424f3a36d 100644 --- a/src/v2/lens/effect/effect_depends_on.dag +++ b/src/v2/lens/effect/effect_depends_on.dag @@ -1,5 +1,3 @@ - - module v2.test.lens_effect.effect_depends_on import v2.compiler.infer { InferredFacts, InferredTree } diff --git a/src/v2/lens/extdeps_external_authority/module_refs.dag b/src/v2/lens/extdeps_external_authority/module_refs.dag index b0d1c151f0b..ce186f8a5e5 100644 --- a/src/v2/lens/extdeps_external_authority/module_refs.dag +++ b/src/v2/lens/extdeps_external_authority/module_refs.dag @@ -1,5 +1,3 @@ -// Scope: canonical QualifiedName identities for extdeps_external_authority witnesses. - module v2.lens.extdeps_external_authority.module_refs import v2.std.collection { List } @@ -55,13 +53,11 @@ data extdeps_fixture_file_anchor_module: QualifiedName = QnCons { } } -// Anchor-machinery modules: RFC/header-grounded (uri) or self-anchoring (external_authority). data extdeps_external_authority_anchor_machinery_exempt_modules: List = [ extdeps_uri_module, extdeps_external_authority_module ] -// RED perturb fixtures excluded from live clean-tree roster (would false-RED the gate). data extdeps_external_authority_clean_tree_roster_exclusions: List = [ extdeps_fixture_bogus_scheme_module, extdeps_fixture_missing_anchor_module, diff --git a/src/v2/lens/extdeps_shape_transport_policy/module_refs.dag b/src/v2/lens/extdeps_shape_transport_policy/module_refs.dag index 2f60e407aa8..0de83328cf1 100644 --- a/src/v2/lens/extdeps_shape_transport_policy/module_refs.dag +++ b/src/v2/lens/extdeps_shape_transport_policy/module_refs.dag @@ -1,6 +1,3 @@ -// Scope: canonical QualifiedName module identities for extdeps_shape_transport_policy witnesses. -// Single authority — module path from `module` declarations, not filepath nicknames. - module v2.lens.extdeps_shape_transport_policy.module_refs import v2.std.qualified_name { QnCons, QnEmpty, QualifiedName } diff --git a/src/v2/lens/fact_density/computation_not_type_carrier.dag b/src/v2/lens/fact_density/computation_not_type_carrier.dag index f745fa4b2e1..6475367ecf4 100644 --- a/src/v2/lens/fact_density/computation_not_type_carrier.dag +++ b/src/v2/lens/fact_density/computation_not_type_carrier.dag @@ -1,4 +1,3 @@ - module v2.test.lens_fact_density.computation_not_type_carrier import v2.lens.fact_density { NotATypeCarrier, carrier_spec_fact } diff --git a/src/v2/lens/fact_density/conj_positional_only_no_fact.dag b/src/v2/lens/fact_density/conj_positional_only_no_fact.dag index ba9791e3a8f..035b061eec3 100644 --- a/src/v2/lens/fact_density/conj_positional_only_no_fact.dag +++ b/src/v2/lens/fact_density/conj_positional_only_no_fact.dag @@ -1,4 +1,3 @@ - module v2.test.lens_fact_density.conj_positional_only_no_fact import v2.lens.fact_density { NoFact, carrier_spec_fact } diff --git a/src/v2/lens/fact_density/fact_bundle_compile_lens_passes.dag b/src/v2/lens/fact_density/fact_bundle_compile_lens_passes.dag index eaabc88a9b3..315eb7a4a37 100644 --- a/src/v2/lens/fact_density/fact_bundle_compile_lens_passes.dag +++ b/src/v2/lens/fact_density/fact_bundle_compile_lens_passes.dag @@ -1,4 +1,3 @@ - module v2.test.lens_fact_density.fact_bundle_compile_lens_passes import v2.compiler.compile { diff --git a/src/v2/lens/fact_density/fact_bundle_named.dag b/src/v2/lens/fact_density/fact_bundle_named.dag index 3ef12b1d31f..e2afe038217 100644 --- a/src/v2/lens/fact_density/fact_bundle_named.dag +++ b/src/v2/lens/fact_density/fact_bundle_named.dag @@ -1,4 +1,3 @@ - module v2.test.lens_fact_density.fact_bundle_named import v2.lens.fact_density { NamedFieldFacts, carrier_spec_fact } diff --git a/src/v2/lens/fact_density/hollow_alias_blocked_in_run_gates.dag b/src/v2/lens/fact_density/hollow_alias_blocked_in_run_gates.dag index 6894275ada8..3b8130eb2bf 100644 --- a/src/v2/lens/fact_density/hollow_alias_blocked_in_run_gates.dag +++ b/src/v2/lens/fact_density/hollow_alias_blocked_in_run_gates.dag @@ -1,4 +1,3 @@ - module v2.test.lens_fact_density.hollow_alias_blocked_in_run_gates import v2.compiler.compile { diff --git a/src/v2/lens/fact_density/hollow_alias_blocked_via_always_required_lenses.dag b/src/v2/lens/fact_density/hollow_alias_blocked_via_always_required_lenses.dag index 00def07f4c8..beca6187542 100644 --- a/src/v2/lens/fact_density/hollow_alias_blocked_via_always_required_lenses.dag +++ b/src/v2/lens/fact_density/hollow_alias_blocked_via_always_required_lenses.dag @@ -1,4 +1,3 @@ - module v2.test.lens_fact_density.hollow_alias_blocked_via_always_required_lenses import v2.compiler.compile { diff --git a/src/v2/lens/fact_density/hollow_alias_compile_lens_rejects.dag b/src/v2/lens/fact_density/hollow_alias_compile_lens_rejects.dag index b84614af4a5..d09cb040dff 100644 --- a/src/v2/lens/fact_density/hollow_alias_compile_lens_rejects.dag +++ b/src/v2/lens/fact_density/hollow_alias_compile_lens_rejects.dag @@ -1,4 +1,3 @@ - module v2.test.lens_fact_density.hollow_alias_compile_lens_rejects import v2.compiler.compile { diff --git a/src/v2/lens/fact_density/hollow_alias_nested_rejected.dag b/src/v2/lens/fact_density/hollow_alias_nested_rejected.dag index 174a928fbd5..8b27c535811 100644 --- a/src/v2/lens/fact_density/hollow_alias_nested_rejected.dag +++ b/src/v2/lens/fact_density/hollow_alias_nested_rejected.dag @@ -1,4 +1,3 @@ - module v2.test.lens_fact_density.hollow_alias_nested_rejected import v2.compiler.compile { diff --git a/src/v2/lens/fact_density/hollow_alias_no_fact.dag b/src/v2/lens/fact_density/hollow_alias_no_fact.dag index c66c004d9bf..d8de0f19910 100644 --- a/src/v2/lens/fact_density/hollow_alias_no_fact.dag +++ b/src/v2/lens/fact_density/hollow_alias_no_fact.dag @@ -1,4 +1,3 @@ - module v2.test.lens_fact_density.hollow_alias_no_fact import v2.lens.fact_density { NoFact, carrier_spec_fact } diff --git a/src/v2/lens/fact_density/hollow_alias_vtc_empty_lenses_rejected.dag b/src/v2/lens/fact_density/hollow_alias_vtc_empty_lenses_rejected.dag index ce27640a373..55076b9cca8 100644 --- a/src/v2/lens/fact_density/hollow_alias_vtc_empty_lenses_rejected.dag +++ b/src/v2/lens/fact_density/hollow_alias_vtc_empty_lenses_rejected.dag @@ -1,4 +1,3 @@ - module v2.test.lens_fact_density.hollow_alias_vtc_empty_lenses_rejected import v2.compiler.compile { diff --git a/src/v2/lens/fact_density/kernel_ambient_bool.dag b/src/v2/lens/fact_density/kernel_ambient_bool.dag index 5011006bcb8..c399e885457 100644 --- a/src/v2/lens/fact_density/kernel_ambient_bool.dag +++ b/src/v2/lens/fact_density/kernel_ambient_bool.dag @@ -1,4 +1,3 @@ - module v2.test.lens_fact_density.kernel_ambient_bool import v2.lens.fact_density { diff --git a/src/v2/lens/idempotency/family_receipt.dag b/src/v2/lens/idempotency/family_receipt.dag index 27e65b67649..3f93c4b2a0a 100644 --- a/src/v2/lens/idempotency/family_receipt.dag +++ b/src/v2/lens/idempotency/family_receipt.dag @@ -1,7 +1,5 @@ - module v2.test.lens_idempotency.family_receipt - import v2.compiler.eval { TestClaimRun } import v2.std.collection { List } import v2.std.node { Node } @@ -10,7 +8,6 @@ import v2.test.lens_idempotency.write_effect { run_idempotency_write_effect_receipt } - data lens_idempotency_runtime_value_rows: List> = [ run_idempotency_write_effect_receipt ] diff --git a/src/v2/lens/idempotency/sg_claims_test.dag b/src/v2/lens/idempotency/sg_claims_test.dag index 8fb260cbf27..e45da5238b3 100644 --- a/src/v2/lens/idempotency/sg_claims_test.dag +++ b/src/v2/lens/idempotency/sg_claims_test.dag @@ -1,15 +1,10 @@ -// Status: execute-the-carrier; routed through v2 `dag run --claim-run`. - module v2.test.lens_idempotency.sg_claims - import v2.std.logic { Bool } import v2.test.lens_idempotency.write_effect { idempotency_write_effect_claim_holds } - - import v2.std.verification { BoolWitness, BoolWitnessClaim, diff --git a/src/v2/lens/idempotency/subject_roster.dag b/src/v2/lens/idempotency/subject_roster.dag index 49c89d3ba8c..73b48a6a4f9 100644 --- a/src/v2/lens/idempotency/subject_roster.dag +++ b/src/v2/lens/idempotency/subject_roster.dag @@ -1,8 +1,5 @@ -// Status: single-leaf family — expand when additional lens_idempotency claims land. - module v2.test.lens_idempotency.subject_roster - import v2.compiler.eval { TestClaimEvalSubject } import v2.std.collection { List } import v2.std.node { Node } @@ -10,7 +7,6 @@ import v2.test.lens_idempotency.write_effect { subject_idempotency_write_effect_receipt } - data lens_idempotency_subject_rows: List> = [ subject_idempotency_write_effect_receipt ] diff --git a/src/v2/lens/idempotency/write_effect.dag b/src/v2/lens/idempotency/write_effect.dag index 5e1c63034be..57628a27ffe 100644 --- a/src/v2/lens/idempotency/write_effect.dag +++ b/src/v2/lens/idempotency/write_effect.dag @@ -1,4 +1,3 @@ - module v2.test.lens_idempotency.write_effect import v2.compiler.infer { InferredFacts, InferredTree } diff --git a/src/v2/lens/identical_variant_payload/sg_claims_test.dag b/src/v2/lens/identical_variant_payload/sg_claims_test.dag index 2a4d46f91c8..f8faf99cb1e 100644 --- a/src/v2/lens/identical_variant_payload/sg_claims_test.dag +++ b/src/v2/lens/identical_variant_payload/sg_claims_test.dag @@ -1,10 +1,5 @@ -// Scope: discriminating claim-run witness for L1.4 IdenticalVariantPayload -// Status: executes identical_variant_payload_fact_for_type; pos expects Unrealized verdict, -// neg arm discriminates stored verdict identity (mutation-proven red-under-mutation). - module v2.test.lens_identical_variant_payload.sg_claims - import std.lens_verdict { Holds, LensVerdict, @@ -27,7 +22,6 @@ import v2.std.verification { data ivp_witness_type_node: Node = claim_atom_node(s: ^ivp_witness_type_symbol) data ivp_witness_module: String = "v2.lens.identical_variant_payload" - fn ivp_verdict_is_unrealized(verdict: LensVerdict) -> Bool { match verdict { Unrealized { diagnostic: _ } => true @@ -35,19 +29,16 @@ fn ivp_verdict_is_unrealized(verdict: LensVerdict) -> Bool { } } - fn witness_ivp_unrealized_green_on_fact() -> Bool { ivp_verdict_is_unrealized( verdict: identical_variant_payload_fact_for_type(type_node: ivp_witness_type_node).verdict ) } - fn ivp_sample_holds() -> LensVerdict { Holds } - fn witness_ivp_unrealized_red_on_holds() -> Bool { match ivp_sample_holds() { Unrealized { diagnostic: _ } => false @@ -55,7 +46,6 @@ fn witness_ivp_unrealized_red_on_holds() -> Bool { } } - fn witness_ivp_locus_green_on_fact() -> Bool { match identical_variant_payload_fact_for_type(type_node: ivp_witness_type_node).verdict { Unrealized { diagnostic: d } => match d.at { @@ -65,7 +55,6 @@ fn witness_ivp_locus_green_on_fact() -> Bool { } } - fn witness_ivp_locus_red_on_perturbed_module() -> Bool { let perturbed = Unrealized { diagnostic: LensVerdictDiagnostic { @@ -81,7 +70,6 @@ fn witness_ivp_locus_red_on_perturbed_module() -> Bool { } } - test fn lens_identical_variant_payload_unrealized_scaffold_holds() -> Bool { witness_ivp_unrealized_green_on_fact() && witness_ivp_unrealized_red_on_holds() diff --git a/src/v2/lens/ownership/resource_dependency.dag b/src/v2/lens/ownership/resource_dependency.dag index 8757dc0d7c9..2086a11e16c 100644 --- a/src/v2/lens/ownership/resource_dependency.dag +++ b/src/v2/lens/ownership/resource_dependency.dag @@ -1,5 +1,3 @@ - - module v2.test.lens_ownership.resource_dependency import v2.compiler.infer { InferredFacts, InferredTree } diff --git a/src/v2/lens/ownership/subject_roster.dag b/src/v2/lens/ownership/subject_roster.dag index 0283cbb9276..37c077ae797 100644 --- a/src/v2/lens/ownership/subject_roster.dag +++ b/src/v2/lens/ownership/subject_roster.dag @@ -1,8 +1,5 @@ -// Status: family receipt — claim + subject + claim-id frontier are co-located; workflow/lens_ownership_family_eval.dag routes rows through run_test_claim. - module v2.test.lens_ownership.subject_roster - import v2.compiler.eval { TestClaimEvalSubject, eval_context_v4_evaluator_wave1, @@ -32,7 +29,6 @@ fn ownership_claim_pass_node() -> Node { } } - fn ownership_claim_fail_node() -> Node { Node { kind: TypeNode { connective: Atom { identity: ^lens_ownership_claim_fail_symbol } }, @@ -41,7 +37,6 @@ fn ownership_claim_fail_node() -> Node { } } - fn ownership_claim_input(ok: Bool) -> Node { if ok { ownership_claim_pass_node() @@ -50,7 +45,6 @@ fn ownership_claim_input(ok: Bool) -> Node { } } - data claim_lens_ownership_resource_dependency: TestClaim = EqualsClaim { label: "lens_ownership resource dependency witness passes through run_test_claim", anchor: manual_claim_anchor(anchor: ManualAnchorAbsent), @@ -59,7 +53,6 @@ data claim_lens_ownership_resource_dependency: TestClaim = EqualsClaim { classification: TestClassification { tier: Tier1, layer: Unit } } - data subject_lens_ownership_resource_dependency: TestClaimEvalSubject = eval_test_claim_subject( claim: claim_lens_ownership_resource_dependency, context: eval_context_v4_evaluator_wave1(), @@ -67,15 +60,12 @@ data subject_lens_ownership_resource_dependency: TestClaimEvalSubject = ev evaluator_pin: ^lens_ownership_resource_dependency_pin ) - data lens_ownership_subject_rows: List> = [ subject_lens_ownership_resource_dependency ] - data lens_ownership_family_claim_ids: List = [ ^claim_lens_ownership_resource_dependency ] - data lens_ownership_node_subject_rows: List> = lens_ownership_subject_rows diff --git a/src/v2/lens/parallelism/data_dependency.dag b/src/v2/lens/parallelism/data_dependency.dag index dd40753bdcf..782af6c30d4 100644 --- a/src/v2/lens/parallelism/data_dependency.dag +++ b/src/v2/lens/parallelism/data_dependency.dag @@ -1,4 +1,3 @@ - module v2.test.lens_parallelism.data_dependency import v2.compiler.infer { InferredFacts, InferredTree } diff --git a/src/v2/lens/parallelism/family_receipt.dag b/src/v2/lens/parallelism/family_receipt.dag index 497301d53e0..de98c40b8f9 100644 --- a/src/v2/lens/parallelism/family_receipt.dag +++ b/src/v2/lens/parallelism/family_receipt.dag @@ -1,7 +1,5 @@ - module v2.test.lens_parallelism.family_receipt - import v2.compiler.eval { TestClaimRun } import v2.std.collection { List } import v2.std.node { Node } @@ -10,7 +8,6 @@ import v2.test.lens_parallelism.data_dependency { run_parallelism_data_dependency_receipt } - data lens_parallelism_runtime_value_rows: List> = [ run_parallelism_data_dependency_receipt ] diff --git a/src/v2/lens/registry/sg_claims_test.dag b/src/v2/lens/registry/sg_claims_test.dag index 0b8d2a2c2ef..95b4b46c7fb 100644 --- a/src/v2/lens/registry/sg_claims_test.dag +++ b/src/v2/lens/registry/sg_claims_test.dag @@ -1,9 +1,5 @@ -// Scope: discriminating claim-run witnesses for lens registry query -// Status: executes lens_registry_v0 query surface; pos+neg discriminates singleton resolution. - module v2.test.lens_registry.sg_claims - import v2.lens.registry { Complexity, Cost, @@ -23,8 +19,6 @@ import v2.lens.registry { import v2.std.collection { List } import v2.std.logic { Bool } - - import v2.std.verification { BoolWitness, BoolWitnessClaim, @@ -42,12 +36,10 @@ data lens_registry_v0_required_ids: List = [ TableDecisionTree ] - test fn lens_registry_required_ids_resolve_holds() -> Bool { lens_registry_ids_resolve(lens_ids: lens_registry_v0_required_ids) } - test fn lens_registry_singleton_row_counts_holds() -> Bool { lens_registry_row_count(lens_id: Complexity) == 1 && lens_registry_bound_row_count(lens_id: Complexity) == 1 diff --git a/src/v2/lens/structural_resolution/binds_to_resolved_test.dag b/src/v2/lens/structural_resolution/binds_to_resolved_test.dag index f69bd4112b5..df5ee9b20d1 100644 --- a/src/v2/lens/structural_resolution/binds_to_resolved_test.dag +++ b/src/v2/lens/structural_resolution/binds_to_resolved_test.dag @@ -1,4 +1,3 @@ - module v2.test.lens_structural_resolution.binds_to_resolved import v2.compiler.infer { InferredFacts, InferredTree } @@ -33,7 +32,6 @@ fn sr_claim_atom(s: Symbol) -> Node { } } -// Atom reference (NoEdges); declaration container carries the BindsTo edge (P6 / node discipline). data sr_use: Node = sr_claim_atom(s: ^sr_use_symbol) data sr_decl: Node = Node { diff --git a/src/v2/lens/structural_resolution/claim_carrier.dag b/src/v2/lens/structural_resolution/claim_carrier.dag index 61fb8c8e1cc..d4ff223ac2c 100644 --- a/src/v2/lens/structural_resolution/claim_carrier.dag +++ b/src/v2/lens/structural_resolution/claim_carrier.dag @@ -1,5 +1,3 @@ -// Scope: shared TestClaim row carrier for structural_resolution lens claims. - module v2.test.lens_structural_resolution.claim_carrier import v2.compiler.infer { InferredTree } diff --git a/src/v2/lens/structural_resolution/facts_lookup_miss.dag b/src/v2/lens/structural_resolution/facts_lookup_miss.dag index 2f59e78e998..dd03577888b 100644 --- a/src/v2/lens/structural_resolution/facts_lookup_miss.dag +++ b/src/v2/lens/structural_resolution/facts_lookup_miss.dag @@ -1,4 +1,3 @@ - module v2.test.lens_structural_resolution.facts_lookup_miss import v2.compiler.infer { diff --git a/src/v2/lens/structural_resolution/module_import_out_of_scope.dag b/src/v2/lens/structural_resolution/module_import_out_of_scope.dag index c1b300db314..6e3d8b9f8d9 100644 --- a/src/v2/lens/structural_resolution/module_import_out_of_scope.dag +++ b/src/v2/lens/structural_resolution/module_import_out_of_scope.dag @@ -1,4 +1,3 @@ - module v2.test.lens_structural_resolution.module_import_out_of_scope import v2.compiler.infer { InferredFacts, InferredTree } diff --git a/src/v2/lens/structural_resolution/unbound_symbol_at_use.dag b/src/v2/lens/structural_resolution/unbound_symbol_at_use.dag index 31f63e077a0..f7c37edbbbc 100644 --- a/src/v2/lens/structural_resolution/unbound_symbol_at_use.dag +++ b/src/v2/lens/structural_resolution/unbound_symbol_at_use.dag @@ -1,4 +1,3 @@ - module v2.test.lens_structural_resolution.unbound_symbol_at_use import v2.compiler.infer { InferredFacts, InferredTree } diff --git a/src/v2/lens/structural_resolution/unresolved_infer_witness.dag b/src/v2/lens/structural_resolution/unresolved_infer_witness.dag index 9fa2d28bc95..e6886519d47 100644 --- a/src/v2/lens/structural_resolution/unresolved_infer_witness.dag +++ b/src/v2/lens/structural_resolution/unresolved_infer_witness.dag @@ -1,4 +1,3 @@ - module v2.test.lens_structural_resolution.unresolved_infer_witness import v2.compiler.infer { diff --git a/src/v2/lens/structural_similarity/sg_claims_test.dag b/src/v2/lens/structural_similarity/sg_claims_test.dag index 286c6183881..e3dc518ffe0 100644 --- a/src/v2/lens/structural_similarity/sg_claims_test.dag +++ b/src/v2/lens/structural_similarity/sg_claims_test.dag @@ -1,10 +1,5 @@ -// Scope: discriminating claim-run witness for L1.12.b structural_similarity -// Status: executes structural_similarity_fact; pos expects Unrealized verdict, -// neg arm discriminates stored verdict identity (mutation-proven red-under-mutation). - module v2.test.lens_structural_similarity.sg_claims - import v2.lens.structural_similarity { StructuralSimilarityVerdict, Unrealized, @@ -13,8 +8,6 @@ import v2.lens.structural_similarity { import v2.std.logic { Bool } import v2.std.verification { BoolWitness, BoolWitnessClaim, UnifiedTestClaim } - -// treating this as a permanent variant-discriminant authority. fn ss_verdict_is_unrealized(verdict: StructuralSimilarityVerdict) -> Bool { match verdict { Unrealized => true @@ -22,10 +15,8 @@ fn ss_verdict_is_unrealized(verdict: StructuralSimilarityVerdict) -> Bool { } } - test fn lens_structural_similarity_unrealized_scaffold_holds() -> Bool { ss_verdict_is_unrealized( verdict: structural_similarity_fact().verdict ) } - diff --git a/src/v2/lens/synthesis/constant_not_dominated_by_linear.dag b/src/v2/lens/synthesis/constant_not_dominated_by_linear.dag index ccf8db136c1..3ff3da62a16 100644 --- a/src/v2/lens/synthesis/constant_not_dominated_by_linear.dag +++ b/src/v2/lens/synthesis/constant_not_dominated_by_linear.dag @@ -1,4 +1,3 @@ - module v2.test.lens_synthesis.constant_not_dominated_by_linear import v2.lens.cost { LinearCost, SizeVariable, symbolic_cost_dominates, unit_cost } diff --git a/src/v2/lens/synthesis/exponential_dominates_polynomial.dag b/src/v2/lens/synthesis/exponential_dominates_polynomial.dag index b5e6c4f0e0d..34a984c52c0 100644 --- a/src/v2/lens/synthesis/exponential_dominates_polynomial.dag +++ b/src/v2/lens/synthesis/exponential_dominates_polynomial.dag @@ -1,4 +1,3 @@ - module v2.test.lens_synthesis.exponential_dominates_polynomial import v2.lens.cost { diff --git a/src/v2/lens/synthesis/factorial_dominates_exponential.dag b/src/v2/lens/synthesis/factorial_dominates_exponential.dag index 8ea5f573aa6..f8cd9c7f39a 100644 --- a/src/v2/lens/synthesis/factorial_dominates_exponential.dag +++ b/src/v2/lens/synthesis/factorial_dominates_exponential.dag @@ -1,4 +1,3 @@ - module v2.test.lens_synthesis.factorial_dominates_exponential import v2.lens.cost { diff --git a/src/v2/lens/synthesis/linear_not_dominated_by_polynomial.dag b/src/v2/lens/synthesis/linear_not_dominated_by_polynomial.dag index 67d73eac4b1..ba243b40dc9 100644 --- a/src/v2/lens/synthesis/linear_not_dominated_by_polynomial.dag +++ b/src/v2/lens/synthesis/linear_not_dominated_by_polynomial.dag @@ -1,4 +1,3 @@ - module v2.test.lens_synthesis.linear_not_dominated_by_polynomial import v2.lens.cost { diff --git a/src/v2/lens/synthesis/polynomial_dominates_linear_test.dag b/src/v2/lens/synthesis/polynomial_dominates_linear_test.dag index bbb07a6c578..742f2df14f6 100644 --- a/src/v2/lens/synthesis/polynomial_dominates_linear_test.dag +++ b/src/v2/lens/synthesis/polynomial_dominates_linear_test.dag @@ -1,4 +1,3 @@ - module v2.test.lens_synthesis.polynomial_dominates_linear import v2.lens.cost { diff --git a/src/v2/lens/synthesis/synthesis_gap_information_theoretic.dag b/src/v2/lens/synthesis/synthesis_gap_information_theoretic.dag index 1a62756eb9f..c7c761b4d4c 100644 --- a/src/v2/lens/synthesis/synthesis_gap_information_theoretic.dag +++ b/src/v2/lens/synthesis/synthesis_gap_information_theoretic.dag @@ -1,4 +1,3 @@ - module v2.test.lens_synthesis.synthesis_gap_information_theoretic import v2.lens.complexity { ComplexityBound, complexity_lens } @@ -57,7 +56,6 @@ data synthesis_gap_it_inner: Node = Node { occurrence_id: SyntheticOccurrence } -// Nested Cardinality → O(n^2) > Ω(n); InformationTheoretic lower bound is Ω(n). data synthesis_gap_it_input: Node = Node { kind: TypeNode { connective: Cardinality }, children: [ @@ -92,9 +90,6 @@ data claim_synthesis_gap_information_theoretic: TestClaim = EqualsClaim { classification: TestClassification { tier: Tier1, layer: Unit } } -// Pins the published ReportReason to SynthesisGapInformationTheoretic. -// Bypasses List to avoid bootstrap generic-T field-access limitation (same as polynomial -// claim): calls assess_information_theoretic directly so r binds from a concrete coproduct field. fn synthesis_gap_it_first_reason_node() -> Node { match complexity_lens(n: synthesis_gap_it_input) { Violates { diagnostic: _ } => synthesis_gap_it_sentinel diff --git a/src/v2/lens/synthesis/synthesis_gap_polynomial.dag b/src/v2/lens/synthesis/synthesis_gap_polynomial.dag index f399c5ff9da..0242afd0260 100644 --- a/src/v2/lens/synthesis/synthesis_gap_polynomial.dag +++ b/src/v2/lens/synthesis/synthesis_gap_polynomial.dag @@ -1,4 +1,3 @@ - module v2.test.lens_synthesis.synthesis_gap_polynomial import v2.lens.complexity { ComplexityBound, complexity_lens } @@ -57,7 +56,6 @@ data synthesis_gap_poly_inner: Node = Node { occurrence_id: SyntheticOccurrence } -// Nested Cardinality → polynomial (O(n^2)) complexity; strictly dominates Θ(n log n) lower bound. data synthesis_gap_poly_input: Node = Node { kind: TypeNode { connective: Cardinality }, children: [ @@ -92,10 +90,6 @@ data claim_synthesis_gap_polynomial: TestClaim = EqualsClaim { classification: TestClassification { tier: Tier1, layer: Unit } } -// Pins the published ReportReason to SynthesisGapDecisionTree. -// Bypasses List to avoid bootstrap generic-T field-access limitation: r.reason fails when r -// is a generic lambda param T even after T=Report is established. Calls assess_decision_tree -// directly so r is bound from a concrete coproduct field (report: Report), not generic T. fn synthesis_gap_poly_first_reason_node() -> Node { match complexity_lens(n: synthesis_gap_poly_input) { Violates { diagnostic: _ } => synthesis_gap_poly_sentinel diff --git a/src/v2/lens/synthesis/synthesis_no_technique_diagnostic.dag b/src/v2/lens/synthesis/synthesis_no_technique_diagnostic.dag index 3395798492a..4531551a992 100644 --- a/src/v2/lens/synthesis/synthesis_no_technique_diagnostic.dag +++ b/src/v2/lens/synthesis/synthesis_no_technique_diagnostic.dag @@ -1,4 +1,3 @@ - module v2.test.lens_synthesis.synthesis_no_technique_diagnostic import v2.lens.synthesis { AlgebraicRank, synthesis_lens_diagnostics } @@ -47,7 +46,6 @@ data synthesis_no_tech_inner: Node = Node { occurrence_id: SyntheticOccurrence } -// Polynomial node: complexity_lens gives Holds; AlgebraicRank has no worked example. data synthesis_no_tech_input: Node = Node { kind: TypeNode { connective: Cardinality }, children: [ diff --git a/src/v2/lens/table_decision_tree/sg_claims_test.dag b/src/v2/lens/table_decision_tree/sg_claims_test.dag index 88fdbab35b7..5702c7e57e1 100644 --- a/src/v2/lens/table_decision_tree/sg_claims_test.dag +++ b/src/v2/lens/table_decision_tree/sg_claims_test.dag @@ -1,11 +1,5 @@ -// Scope: discriminating claim-run witness for L1.13.c table_decision_tree -// Status: executes table_decision_tree_fact_for_fn; pos expects Unrealized -// verdict, neg arm discriminates stored verdict identity (mutation-proven -// red-under-mutation). - module v2.test.lens_table_decision_tree.sg_claims - import std.lens_verdict { Holds, LensVerdict, @@ -21,11 +15,9 @@ import v2.std.node { Node, Symbol } import v2.std.verification { BoolWitness, BoolWitnessClaim, UnifiedTestClaim } import v2.test.lens_common.infer_fixture { claim_atom_node } - data tdt_witness_fn_node: Node = claim_atom_node(s: ^tdt_witness_fn_node_symbol) data tdt_witness_module: String = "v2.lens.table_decision_tree" - fn tdt_verdict_is_unrealized(verdict: LensVerdict) -> Bool { match verdict { Unrealized { diagnostic: _ } => true @@ -33,19 +25,16 @@ fn tdt_verdict_is_unrealized(verdict: LensVerdict) -> Bool { } } - fn witness_tdt_unrealized_green_on_fact() -> Bool { tdt_verdict_is_unrealized( verdict: table_decision_tree_fact_for_fn(fn_node: tdt_witness_fn_node).verdict ) } - fn tdt_sample_holds() -> LensVerdict { Holds } - fn witness_tdt_unrealized_red_on_holds() -> Bool { match tdt_sample_holds() { Unrealized { diagnostic: _ } => false @@ -53,7 +42,6 @@ fn witness_tdt_unrealized_red_on_holds() -> Bool { } } - fn witness_tdt_locus_green_on_fact() -> Bool { match table_decision_tree_fact_for_fn(fn_node: tdt_witness_fn_node).verdict { Unrealized { diagnostic: d } => match d.at { @@ -63,7 +51,6 @@ fn witness_tdt_locus_green_on_fact() -> Bool { } } - fn witness_tdt_locus_red_on_perturbed_module() -> Bool { let perturbed = Unrealized { diagnostic: LensVerdictDiagnostic { @@ -79,7 +66,6 @@ fn witness_tdt_locus_red_on_perturbed_module() -> Bool { } } - test fn lens_table_decision_tree_unrealized_scaffold_holds() -> Bool { witness_tdt_unrealized_green_on_fact() && witness_tdt_unrealized_red_on_holds() diff --git a/src/v2/lens/testgen/dag_input_surface_test.dag b/src/v2/lens/testgen/dag_input_surface_test.dag index 27da4d7ea8e..e9d861af93a 100644 --- a/src/v2/lens/testgen/dag_input_surface_test.dag +++ b/src/v2/lens/testgen/dag_input_surface_test.dag @@ -1,5 +1,3 @@ -// Status: activated — structural EqualsClaim + witness receipts prove scheduled/bootstrap .dag LBE input surface. - module v2.test.lens_testgen.dag_input_surface import v2.lens.testgen { diff --git a/src/v2/lens/testgen/generator_provenance_test.dag b/src/v2/lens/testgen/generator_provenance_test.dag index 0a126a55d94..1313dfcf1a4 100644 --- a/src/v2/lens/testgen/generator_provenance_test.dag +++ b/src/v2/lens/testgen/generator_provenance_test.dag @@ -1,5 +1,3 @@ -// Status: activated — structural EqualsClaim + green witness over the provenance integrity fn. - module v2.test.lens_testgen.generator_provenance import v2.lens.testgen { scheduled_generators_carry_provenance } @@ -25,7 +23,6 @@ fn lens_testgen_generator_provenance_marker_node(marker: Symbol) -> Node { } } - fn lens_testgen_generator_provenance_rhs() -> Node { if scheduled_generators_carry_provenance() { lens_testgen_generator_provenance_marker_node(marker: ^lens_testgen_generator_provenance_ok) @@ -34,7 +31,6 @@ fn lens_testgen_generator_provenance_rhs() -> Node { } } - data claim_lens_testgen_scheduled_generators_carry_provenance: TestClaim = EqualsClaim { label: "lens_testgen/generator_provenance: scheduled generators carry consistent provenance + profile metadata", anchor: manual_claim_anchor(anchor: ManualAnchorAbsent), @@ -43,5 +39,4 @@ data claim_lens_testgen_scheduled_generators_carry_provenance: TestClaim = Equal classification: TestClassification { tier: Tier1, layer: Unit } } - test data witness_lens_testgen_scheduled_generators_carry_provenance_green: Bool = scheduled_generators_carry_provenance() diff --git a/src/v2/lens/testgen/shadow_ci_receipt_test.dag b/src/v2/lens/testgen/shadow_ci_receipt_test.dag index f7c62440edf..78ddf45ccc6 100644 --- a/src/v2/lens/testgen/shadow_ci_receipt_test.dag +++ b/src/v2/lens/testgen/shadow_ci_receipt_test.dag @@ -1,4 +1,3 @@ - module v2.test.lens_testgen.shadow_ci_receipt import v2.lens.testgen { diff --git a/src/v2/lens/unit_modeling/bitwidth_flat_scalar_flagged_test.dag b/src/v2/lens/unit_modeling/bitwidth_flat_scalar_flagged_test.dag index 5979e352809..4e53df048fb 100644 --- a/src/v2/lens/unit_modeling/bitwidth_flat_scalar_flagged_test.dag +++ b/src/v2/lens/unit_modeling/bitwidth_flat_scalar_flagged_test.dag @@ -1,8 +1,3 @@ -// Scope: B1 unit-modeling lens — FAIL/flag witness for the Information/BitWidth -// token. A pointer/word-width field (`pointer_width`) typed as a bare -// kernel `Int` MUST be flagged: width is an Information quantity carried -// by BitWidth = Measure (measure.dag:192), and the - module v2.test.lens_unit_modeling.bitwidth_flat_scalar_flagged import v2.lens.unit_modeling { unit_modeling_gate } diff --git a/src/v2/lens/unit_modeling/bitwidth_modeled_passes_test.dag b/src/v2/lens/unit_modeling/bitwidth_modeled_passes_test.dag index e898b76076c..6c254048292 100644 --- a/src/v2/lens/unit_modeling/bitwidth_modeled_passes_test.dag +++ b/src/v2/lens/unit_modeling/bitwidth_modeled_passes_test.dag @@ -1,11 +1,3 @@ -// Scope: B1 unit-modeling lens — PASS/clean witness for the Information/BitWidth -// token. The SAME `pointer_width` field attached to the BitWidth carrier -// (Measure, measure.dag:192) — NOT a bare `Int` leaf — -// MUST pass (gate Accepted). Pairs with bitwidth_flat_scalar_flagged.dag; -// identical field name, only the leaf type differs, so the lens -// discriminates on the resolved type for the width tokens too. Also guards -// against conflating BitWidth (Information) with ByteSize (Memory). - module v2.test.lens_unit_modeling.bitwidth_modeled_passes import v2.lens.unit_modeling { unit_modeling_gate } @@ -31,9 +23,6 @@ import v2.std.verification { manual_claim_anchor } -// Target resolves to the BitWidth carrier (Measure), not the -// bare kernel Int — so `edge_field_type_name` returns `^BitWidth`, the field is -// correctly modeled, and it is not a violation. data bitwidth_carrier_target: Node = Node { kind: TypeNode { connective: Atom { identity: ^BitWidth } }, children: [], diff --git a/src/v2/lens/unit_modeling/coordinate_index_not_flagged_test.dag b/src/v2/lens/unit_modeling/coordinate_index_not_flagged_test.dag index f6dc4195cde..9970ece3590 100644 --- a/src/v2/lens/unit_modeling/coordinate_index_not_flagged_test.dag +++ b/src/v2/lens/unit_modeling/coordinate_index_not_flagged_test.dag @@ -1,14 +1,3 @@ -// Scope: B1 unit-modeling lens — COORDINATE exemption witness. A bare `Int` -// field named `index` is a COORDINATE — addition is meaningless (index 2 + -// index 3 is not position 5) — so it is NOT a unit-bearing quantity and is -// exempt under the inverted predicate. Proves the categorical exemption -// (one of the two closed non-additive kinds) does not over-flag a -// legitimate bare Int. -// Note: exact-Symbol exemption recognizes the BARE marker `index` — the correct -// fail-closed primitive (substring would unsoundly exempt reindex_count). -// COMPOUND forms (matrix_index) safely false-positive-flag and clear in -// phase-2 by acquiring the Index brand-type — see name_is_exempt. - module v2.test.lens_unit_modeling.coordinate_index_not_flagged import v2.lens.unit_modeling { unit_modeling_gate } diff --git a/src/v2/lens/unit_modeling/electrical_blocked_pending_flagged_test.dag b/src/v2/lens/unit_modeling/electrical_blocked_pending_flagged_test.dag index 68f066104b8..034594c30ce 100644 --- a/src/v2/lens/unit_modeling/electrical_blocked_pending_flagged_test.dag +++ b/src/v2/lens/unit_modeling/electrical_blocked_pending_flagged_test.dag @@ -1,13 +1,3 @@ -// Scope: B1 unit-modeling lens — electrical / two-class witness. Under the -// inverted predicate a GATE-N-pending electrical field (`tdp_watts`) typed -// as a bare kernel `Int` IS FLAGGED (a quantity is a quantity regardless of -// whether its carrier exists yet — exempting it would reintroduce the -// false-negative the inversion kills). Because Power has no carrier in the -// hint map, the flag is the BLOCKED-PENDING-CARRIER class — the honest -// reconciliation is to escalate the upstream carrier gate (M12 / Practice -// 13), not to suppress the flag. Proves the blocked-pending diagnostic -// class the §4 census drains-pending-substrate from. - module v2.test.lens_unit_modeling.electrical_blocked_pending_flagged import v2.lens.unit_modeling { unit_modeling_gate } diff --git a/src/v2/lens/unit_modeling/flat_scalar_unit_leaf_flagged_test.dag b/src/v2/lens/unit_modeling/flat_scalar_unit_leaf_flagged_test.dag index eee9c94ebd8..c51d8bef799 100644 --- a/src/v2/lens/unit_modeling/flat_scalar_unit_leaf_flagged_test.dag +++ b/src/v2/lens/unit_modeling/flat_scalar_unit_leaf_flagged_test.dag @@ -1,10 +1,3 @@ -// Scope: B1 unit-modeling lens — FAIL/flag witness. A Named unit-token field -// (`ram_bytes`) typed as a bare kernel `Int` leaf MUST be flagged -// (gate Rejected). Pairs with modeled_unit_field_passes.dag (same field -// name, attached to a carrier → Accepted) — the two differ ONLY in the -// leaf type, so together they prove the lens discriminates on the type, -// not the name alone. - module v2.test.lens_unit_modeling.flat_scalar_unit_leaf_flagged import v2.lens.unit_modeling { unit_modeling_gate } @@ -30,15 +23,12 @@ import v2.std.verification { manual_claim_anchor } -// A field target whose resolved type is the bare kernel `Int` binding. data int_leaf_target: Node = Node { kind: TypeNode { connective: Atom { identity: ^dag_binding_type_int } }, children: [], occurrence_id: SyntheticOccurrence } -// A carrier with one Named unit-token field (`ram_bytes`) typed as bare `Int` — -// the exact flat-scalar unit leaf the lens exists to catch. data flat_scalar_unit_leaf_input: Node = Node { kind: TypeNode { connective: Conj }, children: [ @@ -50,8 +40,6 @@ data flat_scalar_unit_leaf_input: Node = Node { occurrence_id: SyntheticOccurrence } -// ram_bytes has a KNOWN carrier (ByteSize) in the hint map, so the flag is the -// FIXABLE-NOW class — assert both the flag AND the class. test fn flat_scalar_unit_leaf_flagged_holds() -> Bool { match unit_modeling_gate(node: flat_scalar_unit_leaf_input) { Rejected { diagnostics: r } => r.head.reason == ^unit_modeling_flat_scalar_unit_leaf_fixable diff --git a/src/v2/lens/unit_modeling/label_version_not_flagged_test.dag b/src/v2/lens/unit_modeling/label_version_not_flagged_test.dag index 66f24aea674..4a92352a3e8 100644 --- a/src/v2/lens/unit_modeling/label_version_not_flagged_test.dag +++ b/src/v2/lens/unit_modeling/label_version_not_flagged_test.dag @@ -1,10 +1,3 @@ -// Scope: B1 unit-modeling lens — LABEL exemption witness. A bare `Int` field -// named `version` is a LABEL — addition is meaningless (version 2 + version -// 3 is nonsense) — so it is not a unit-bearing quantity and is exempt under -// the inverted predicate. Pairs with coordinate_index_not_flagged.dag to -// cover both closed non-additive kinds (LABEL + COORDINATE), proving the -// categorical exemption does not over-flag legitimate bare Ints. - module v2.test.lens_unit_modeling.label_version_not_flagged import v2.lens.unit_modeling { unit_modeling_gate } diff --git a/src/v2/lens/unit_modeling/modeled_unit_field_admitted_via_always_required_lenses_test.dag b/src/v2/lens/unit_modeling/modeled_unit_field_admitted_via_always_required_lenses_test.dag index fd0bb288ef7..4051c535075 100644 --- a/src/v2/lens/unit_modeling/modeled_unit_field_admitted_via_always_required_lenses_test.dag +++ b/src/v2/lens/unit_modeling/modeled_unit_field_admitted_via_always_required_lenses_test.dag @@ -1,11 +1,3 @@ -// Scope: REGISTRATION witness (PASS arm) — proves unit_modeling_lens, now in -// always_required_lenses(), does NOT false-positive a correctly-modeled field. -// The SAME `ram_bytes` field attached to a `ByteSize` carrier (not a bare Int leaf) -// is ADMITTED by the full roster (both fact_density and unit_modeling Accept). -// Pairs with unit_modeling_blocked_via_always_required_lenses.dag (FAIL arm) — the -// two differ ONLY in the field's resolved leaf type, so together they prove the -// registered lens discriminates on the modeled type, not the field name alone. - module v2.test.lens_unit_modeling.modeled_unit_field_admitted_via_always_required_lenses import v2.compiler.compile { @@ -40,16 +32,12 @@ import v2.test.lens_common.infer_fixture { claim_inferred_tree } -// Field target resolved to the `ByteSize` carrier — a correctly-modeled unit field, -// NOT the bare kernel `Int` binding. data unit_ar_pass_carrier_target: Node = Node { kind: TypeNode { connective: Atom { identity: ^ByteSize } }, children: [], occurrence_id: SyntheticOccurrence } -// Same `ram_bytes` field as the FAIL arm, attached to the ByteSize carrier — both -// fact_density (fact-bundle) and unit_modeling (modeled leaf) Accept. data unit_ar_pass_root: Node = Node { kind: TypeNode { connective: Conj }, children: [ diff --git a/src/v2/lens/unit_modeling/modeled_unit_field_passes_test.dag b/src/v2/lens/unit_modeling/modeled_unit_field_passes_test.dag index 796ede0c1f3..18ef05abdcf 100644 --- a/src/v2/lens/unit_modeling/modeled_unit_field_passes_test.dag +++ b/src/v2/lens/unit_modeling/modeled_unit_field_passes_test.dag @@ -1,10 +1,3 @@ -// Scope: B1 unit-modeling lens — PASS/clean witness. The SAME unit-token field -// (`ram_bytes`) attached to a carrier (`ByteSize`, i.e. NOT a bare kernel -// `Int` leaf) MUST pass (gate Accepted). Pairs with -// flat_scalar_unit_leaf_flagged.dag — identical field name, only the leaf -// type differs — proving the lens discriminates on the resolved type, so -// it neither rubber-stamps nor false-positives a correctly-modeled field. - module v2.test.lens_unit_modeling.modeled_unit_field_passes import v2.lens.unit_modeling { unit_modeling_gate } @@ -30,16 +23,12 @@ import v2.std.verification { manual_claim_anchor } -// A field target whose resolved type is the `ByteSize` carrier — NOT the bare -// kernel `Int` binding. `edge_field_type_name` returns `^ByteSize`, which is not -// `^dag_binding_type_int`, so the leaf is correctly modeled and not a violation. data bytesize_carrier_target: Node = Node { kind: TypeNode { connective: Atom { identity: ^ByteSize } }, children: [], occurrence_id: SyntheticOccurrence } -// Same `ram_bytes` field name as the flag witness — but attached to a carrier. data modeled_unit_field_input: Node = Node { kind: TypeNode { connective: Conj }, children: [ diff --git a/src/v2/lens/unit_modeling/novel_unit_flagged_test.dag b/src/v2/lens/unit_modeling/novel_unit_flagged_test.dag index 5dd7cd09c90..528d566c9b8 100644 --- a/src/v2/lens/unit_modeling/novel_unit_flagged_test.dag +++ b/src/v2/lens/unit_modeling/novel_unit_flagged_test.dag @@ -1,11 +1,3 @@ -// Scope: B1 unit-modeling lens — THE inversion witness. A NOVEL unit field with -// an UNRECOGNIZED name (`throughput`) typed as a bare kernel `Int` is NOW -// FLAGGED — the token-keyed predecessor would have silently MISSED it (the -// false-negative the flag-by-default inversion exists to kill). Since -// `throughput` has no known carrier in the hint map, the flag is the -// BLOCKED-PENDING-CARRIER class (escalate the upstream carrier gate; no -// fix-suggestion) — proving the two-class diagnostic for novel quantities. - module v2.test.lens_unit_modeling.novel_unit_flagged import v2.lens.unit_modeling { unit_modeling_gate } @@ -37,8 +29,6 @@ data int_leaf_target_novel: Node = Node { occurrence_id: SyntheticOccurrence } -// `throughput` is a genuine quantity but NOT in the carrier-hint map and NOT a -// label/coordinate — so default-flag fires, in the blocked-pending class. data novel_unit_input: Node = Node { kind: TypeNode { connective: Conj }, children: [ diff --git a/src/v2/lens/unit_modeling/unit_modeling_blocked_via_always_required_lenses_test.dag b/src/v2/lens/unit_modeling/unit_modeling_blocked_via_always_required_lenses_test.dag index 4f87dc60b5e..a2e2f8b3702 100644 --- a/src/v2/lens/unit_modeling/unit_modeling_blocked_via_always_required_lenses_test.dag +++ b/src/v2/lens/unit_modeling/unit_modeling_blocked_via_always_required_lenses_test.dag @@ -1,12 +1,3 @@ -// Scope: REGISTRATION witness (FAIL arm) — proves unit_modeling_lens is wired into -// always_required_lenses() (the roster validate_then_compile runs fail-closed). -// A fact-bearing carrier (so fact_density ACCEPTS it — not a hollow alias) whose -// `ram_bytes` field is a bare kernel `Int` leaf is REJECTED by the roster with the -// unit-modeling diagnostic. Because fact_density passes and the rejection reason is -// `^unit_modeling_flat_scalar_unit_leaf_fixable`, this discriminates that -// unit_modeling specifically ran (not fact_density) — i.e. the registration is live. -// Pairs with modeled_unit_field_admitted_via_always_required_lenses.dag (PASS arm). - module v2.test.lens_unit_modeling.unit_modeling_blocked_via_always_required_lenses import v2.compiler.compile { @@ -41,16 +32,12 @@ import v2.test.lens_common.infer_fixture { claim_inferred_tree } -// Field target resolved to the bare kernel `Int` binding — the flat-scalar unit leaf. data unit_ar_int_leaf: Node = Node { kind: TypeNode { connective: Atom { identity: ^dag_binding_type_int } }, children: [], occurrence_id: SyntheticOccurrence } -// Fact-bundle carrier (Conj + Named field) -> fact_density ACCEPTS (not a hollow alias); -// the `ram_bytes` field is a bare Int leaf -> unit_modeling REJECTS. Same node shape as -// flat_scalar_unit_leaf_flagged.dag, lifted through the always-required roster. data unit_ar_root: Node = Node { kind: TypeNode { connective: Conj }, children: [ diff --git a/src/v2/lens/unused_parameters/binds_to_edge.dag b/src/v2/lens/unused_parameters/binds_to_edge.dag index e827c576d25..fa6ac4aff0e 100644 --- a/src/v2/lens/unused_parameters/binds_to_edge.dag +++ b/src/v2/lens/unused_parameters/binds_to_edge.dag @@ -1,5 +1,3 @@ - - module v2.test.lens_unused_parameters.binds_to_edge import v2.compiler.infer { InferredFacts, InferredTree } diff --git a/src/v2/lens/unused_parameters/non_use_edges.dag b/src/v2/lens/unused_parameters/non_use_edges.dag index 774c1740ca3..197441f3db0 100644 --- a/src/v2/lens/unused_parameters/non_use_edges.dag +++ b/src/v2/lens/unused_parameters/non_use_edges.dag @@ -1,8 +1,3 @@ -// Contains ⇒ IsStructuralComposition -// DataDependsOn ⇒ IsNonUseDependency -// GeneratedFrom ⇒ RequiresResolutionWitness (fail-closed) - - module v2.test.lens_unused_parameters.non_use_edges import v2.compiler.infer { InferredFacts, InferredTree } diff --git a/src/v2/lens/unused_parameters/rollup_unused_declaration.dag b/src/v2/lens/unused_parameters/rollup_unused_declaration.dag index c0e45f395a1..63bc3c1ce03 100644 --- a/src/v2/lens/unused_parameters/rollup_unused_declaration.dag +++ b/src/v2/lens/unused_parameters/rollup_unused_declaration.dag @@ -1,6 +1,3 @@ -// Composition: `unused_parameters_fact = rollup_unused ∘ use_classification_fact`. - - module v2.test.lens_unused_parameters.rollup_unused_declaration import v2.compiler.infer { InferredFacts, InferredTree } @@ -32,9 +29,6 @@ data rollup_function: Node = Node { occurrence_id: SyntheticOccurrence } -// `Contains` edge positions `rollup_parameter` as a declaration candidate -// (function-parameter position, v3 lens contract). No BindsTo edge with -// `source == rollup_parameter` ⇒ roll-up reports the parameter as unused. data rollup_contains_dependency: DependencyView = DependencyView { source: rollup_function, dependent: rollup_parameter, diff --git a/src/v2/program.dag b/src/v2/program.dag index 7ce6f8a733b..0c99bf0331f 100644 --- a/src/v2/program.dag +++ b/src/v2/program.dag @@ -1,9 +1,3 @@ -// src/v2/program.dag -// Scope: §4 first runnable v2 program — pick_if Branch body composed with effect_io handlers. -// Status: runnable entry + Bool witness for `dag run --entry src/v2/program.dag --claim-run`. -// Spine: pick_if Branch then-arm yields ReadResource→WriteResource (handlers bound at run start). -// Run loop: runtime_run schedules kw_true → effect_io_roundtrip (wave-1 sequential, §4c). - module v2.program import v2.compiler.body_producer { produce_mvp1_pick_if_arrow_with_body } @@ -173,7 +167,6 @@ data program_effect_io_roundtrip: Node = effect_io_roundtrip_transform( payload: program_payload_literal ) -// pick_if-shaped Branch: true → effect_io roundtrip; false → literal two (discriminating else arm). data program_pick_if_branch_with_effect_io: Node = dag_mvp1_pick_if_body_from_arms( condition: program_kw_true(), then_arm: program_effect_io_roundtrip, @@ -376,7 +369,6 @@ fn program_eval_root(root: Node) -> Outcome { ) } -// Runnable entry: evaluate the pick_if Branch with effect_io in the then-arm. fn program_branch_effect_io_holds() -> Bool { match program_eval_root(root: program_pick_if_branch_with_effect_io) { Accepted { value: v, diagnostics: None } => v == program_unit_value() @@ -409,7 +401,6 @@ fn program_pick_if_arrow_tree(callee: Node) -> InferredTree { } } -// §2 body: source-bridged pick_if arrow executes under the same handler-bound program context. fn program_pick_if_arrow_holds() -> Bool { match produce_mvp1_pick_if_arrow_with_body() { Accepted { value: arrow, diagnostics: _ } => { @@ -427,7 +418,6 @@ fn program_pick_if_arrow_holds() -> Bool { } } -// §4c: runtime_run loop — scheduler orders kw_true then effect_io_roundtrip; IO step uses handler-bound context. data program_run_branch_gate: Node = program_kw_true() data program_run_io_step: Node = program_effect_io_roundtrip @@ -458,7 +448,6 @@ fn program_runtime_run_holds() -> Bool { } } -// Main witness: spine Branch+IO, §2 pick_if body, and runtime_run loop composition. fn program_runs_holds() -> Bool { program_branch_effect_io_holds() && program_pick_if_arrow_holds()