pycforge 0.15.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pycforge/__init__.py +8 -0
- pycforge/__main__.py +2 -0
- pycforge/_version.py +1 -0
- pycforge/converter/__init__.py +0 -0
- pycforge/converter/analysis/__init__.py +5 -0
- pycforge/converter/analysis/functions.py +723 -0
- pycforge/converter/analysis/model.py +158 -0
- pycforge/converter/analysis/planning.py +464 -0
- pycforge/converter/analysis/stage.py +1976 -0
- pycforge/converter/analysis/symbols.py +237 -0
- pycforge/converter/analysis/validation.py +2166 -0
- pycforge/converter/c_output/__init__.py +2 -0
- pycforge/converter/c_output/conformance.py +297 -0
- pycforge/converter/c_output/renderer.py +220 -0
- pycforge/converter/conditional_regions/__init__.py +71 -0
- pycforge/converter/conditional_regions/analysis.py +481 -0
- pycforge/converter/conditional_regions/lowering.py +579 -0
- pycforge/converter/conditional_regions/model.py +243 -0
- pycforge/converter/conditional_regions/validation.py +690 -0
- pycforge/converter/containers/__init__.py +19 -0
- pycforge/converter/containers/analysis.py +542 -0
- pycforge/converter/containers/lowering.py +290 -0
- pycforge/converter/containers/model.py +146 -0
- pycforge/converter/contracts/__init__.py +0 -0
- pycforge/converter/contracts/configuration.py +178 -0
- pycforge/converter/contracts/identifiers.py +68 -0
- pycforge/converter/contracts/versions.py +71 -0
- pycforge/converter/core/__init__.py +0 -0
- pycforge/converter/core/artifact_io.py +87 -0
- pycforge/converter/core/cancellation.py +7 -0
- pycforge/converter/core/canonicalization.py +254 -0
- pycforge/converter/core/context.py +13 -0
- pycforge/converter/core/diagnostics.py +95 -0
- pycforge/converter/core/enums.py +23 -0
- pycforge/converter/core/fingerprint.py +22 -0
- pycforge/converter/core/pipeline.py +8 -0
- pycforge/converter/core/progress.py +25 -0
- pycforge/converter/core/request.py +50 -0
- pycforge/converter/core/resource_policy.py +31 -0
- pycforge/converter/core/result.py +23 -0
- pycforge/converter/core/serialization.py +36 -0
- pycforge/converter/core/stage.py +10 -0
- pycforge/converter/core/stage_artifact.py +80 -0
- pycforge/converter/core/stage_outcome.py +15 -0
- pycforge/converter/decision_trace/__init__.py +0 -0
- pycforge/converter/decision_trace/recorder.py +52 -0
- pycforge/converter/facade.py +459 -0
- pycforge/converter/frontend/__init__.py +4 -0
- pycforge/converter/frontend/normalizer.py +59 -0
- pycforge/converter/frontend/parser.py +20 -0
- pycforge/converter/frontend/source_document.py +93 -0
- pycforge/converter/frontend/stages.py +518 -0
- pycforge/converter/frontend/validation.py +76 -0
- pycforge/converter/io/__init__.py +0 -0
- pycforge/converter/io/atomic_writer.py +33 -0
- pycforge/converter/ir/__init__.py +1 -0
- pycforge/converter/ir/c_ir/__init__.py +4 -0
- pycforge/converter/ir/c_ir/builder.py +26 -0
- pycforge/converter/ir/c_ir/model.py +318 -0
- pycforge/converter/ir/c_ir/serialization.py +88 -0
- pycforge/converter/ir/c_ir/validation.py +834 -0
- pycforge/converter/ir/python_ir/__init__.py +17 -0
- pycforge/converter/ir/python_ir/nodes.py +86 -0
- pycforge/converter/keyword_calls/__init__.py +68 -0
- pycforge/converter/keyword_calls/analysis.py +458 -0
- pycforge/converter/keyword_calls/lowering.py +271 -0
- pycforge/converter/keyword_calls/model.py +304 -0
- pycforge/converter/keyword_calls/validation.py +802 -0
- pycforge/converter/keyword_only_calls/__init__.py +68 -0
- pycforge/converter/keyword_only_calls/analysis.py +640 -0
- pycforge/converter/keyword_only_calls/lowering.py +376 -0
- pycforge/converter/keyword_only_calls/model.py +328 -0
- pycforge/converter/keyword_only_calls/validation.py +1357 -0
- pycforge/converter/lowering.py +1000 -0
- pycforge/converter/modules/__init__.py +13 -0
- pycforge/converter/modules/analysis.py +1250 -0
- pycforge/converter/modules/lowering.py +200 -0
- pycforge/converter/modules/model.py +73 -0
- pycforge/converter/modules/stage.py +192 -0
- pycforge/converter/numeric_semantics/__init__.py +23 -0
- pycforge/converter/numeric_semantics/analysis.py +420 -0
- pycforge/converter/numeric_semantics/lowering.py +153 -0
- pycforge/converter/numeric_semantics/model.py +123 -0
- pycforge/converter/records/__init__.py +34 -0
- pycforge/converter/records/analysis.py +1394 -0
- pycforge/converter/records/lowering.py +393 -0
- pycforge/converter/records/model.py +319 -0
- pycforge/converter/rules/__init__.py +3 -0
- pycforge/converter/support_templates/__init__.py +49 -0
- pycforge/converter/support_templates/assembly.py +106 -0
- pycforge/converter/support_templates/factories.py +348 -0
- pycforge/converter/support_templates/model.py +159 -0
- pycforge/converter/support_templates/registry.py +388 -0
- pycforge/converter/telemetry/__init__.py +0 -0
- pycforge/converter/telemetry/sink.py +39 -0
- pycforge/ide/__init__.py +19 -0
- pycforge/ide/__main__.py +5 -0
- pycforge/ide/_worker_event_codec.py +276 -0
- pycforge/ide/_worker_protocol_json.py +197 -0
- pycforge/ide/_worker_protocol_types.py +90 -0
- pycforge/ide/_worker_request_codec.py +200 -0
- pycforge/ide/_worker_request_data.py +260 -0
- pycforge/ide/_worker_result_data.py +343 -0
- pycforge/ide/action_contract.py +469 -0
- pycforge/ide/action_validation.py +229 -0
- pycforge/ide/command_palette.py +223 -0
- pycforge/ide/controller.py +904 -0
- pycforge/ide/controller_conversion.py +546 -0
- pycforge/ide/controller_io.py +205 -0
- pycforge/ide/editor.py +951 -0
- pycforge/ide/editor_commands_qt.py +274 -0
- pycforge/ide/editor_lexical.py +43 -0
- pycforge/ide/editor_sidebars.py +236 -0
- pycforge/ide/editor_syntax.py +243 -0
- pycforge/ide/find_replace.py +535 -0
- pycforge/ide/icons.py +91 -0
- pycforge/ide/io_service.py +196 -0
- pycforge/ide/model.py +164 -0
- pycforge/ide/panels.py +903 -0
- pycforge/ide/positions.py +220 -0
- pycforge/ide/process_worker.py +130 -0
- pycforge/ide/qt.py +230 -0
- pycforge/ide/qt_actions.py +418 -0
- pycforge/ide/qt_close.py +169 -0
- pycforge/ide/qt_command_palette.py +297 -0
- pycforge/ide/qt_contract.py +220 -0
- pycforge/ide/qt_documents.py +590 -0
- pycforge/ide/qt_editor_buffers.py +113 -0
- pycforge/ide/qt_editor_surfaces.py +112 -0
- pycforge/ide/qt_menus.py +324 -0
- pycforge/ide/qt_projection.py +500 -0
- pycforge/ide/qt_shell.py +348 -0
- pycforge/ide/qt_shell_interactions.py +320 -0
- pycforge/ide/qt_state.py +393 -0
- pycforge/ide/qt_workspace_features.py +398 -0
- pycforge/ide/qt_workspace_navigation.py +208 -0
- pycforge/ide/qt_workspace_observers.py +173 -0
- pycforge/ide/qt_workspace_panels.py +545 -0
- pycforge/ide/qt_workspace_widgets.py +228 -0
- pycforge/ide/resources/icons/about.svg +1 -0
- pycforge/ide/resources/icons/add-document.svg +1 -0
- pycforge/ide/resources/icons/brand-mark.svg +1 -0
- pycforge/ide/resources/icons/bundle-search.svg +1 -0
- pycforge/ide/resources/icons/cancel.svg +1 -0
- pycforge/ide/resources/icons/check.svg +1 -0
- pycforge/ide/resources/icons/chevron-right.svg +1 -0
- pycforge/ide/resources/icons/close.svg +1 -0
- pycforge/ide/resources/icons/collapse-all.svg +1 -0
- pycforge/ide/resources/icons/command-palette.svg +1 -0
- pycforge/ide/resources/icons/convert.svg +1 -0
- pycforge/ide/resources/icons/copy.svg +1 -0
- pycforge/ide/resources/icons/cut.svg +1 -0
- pycforge/ide/resources/icons/decision-trace.svg +1 -0
- pycforge/ide/resources/icons/details.svg +1 -0
- pycforge/ide/resources/icons/diagnostics.svg +1 -0
- pycforge/ide/resources/icons/duplicate-line.svg +1 -0
- pycforge/ide/resources/icons/exit.svg +1 -0
- pycforge/ide/resources/icons/expand-all.svg +1 -0
- pycforge/ide/resources/icons/export.svg +1 -0
- pycforge/ide/resources/icons/find.svg +1 -0
- pycforge/ide/resources/icons/go-to-line.svg +1 -0
- pycforge/ide/resources/icons/go-to-output.svg +1 -0
- pycforge/ide/resources/icons/go-to-source.svg +1 -0
- pycforge/ide/resources/icons/history.svg +1 -0
- pycforge/ide/resources/icons/indent.svg +1 -0
- pycforge/ide/resources/icons/link-c.svg +1 -0
- pycforge/ide/resources/icons/mappings.svg +1 -0
- pycforge/ide/resources/icons/module.svg +1 -0
- pycforge/ide/resources/icons/move-down.svg +1 -0
- pycforge/ide/resources/icons/move-line-down.svg +1 -0
- pycforge/ide/resources/icons/move-line-up.svg +1 -0
- pycforge/ide/resources/icons/move-up.svg +1 -0
- pycforge/ide/resources/icons/next.svg +1 -0
- pycforge/ide/resources/icons/open.svg +1 -0
- pycforge/ide/resources/icons/outdent.svg +1 -0
- pycforge/ide/resources/icons/outline.svg +1 -0
- pycforge/ide/resources/icons/paste.svg +1 -0
- pycforge/ide/resources/icons/previous.svg +1 -0
- pycforge/ide/resources/icons/primary-module.svg +1 -0
- pycforge/ide/resources/icons/redo.svg +1 -0
- pycforge/ide/resources/icons/remove-document.svg +1 -0
- pycforge/ide/resources/icons/replace.svg +1 -0
- pycforge/ide/resources/icons/save-as.svg +1 -0
- pycforge/ide/resources/icons/save.svg +1 -0
- pycforge/ide/resources/icons/select-all.svg +1 -0
- pycforge/ide/resources/icons/settings.svg +1 -0
- pycforge/ide/resources/icons/show-c.svg +1 -0
- pycforge/ide/resources/icons/split-view.svg +1 -0
- pycforge/ide/resources/icons/summary.svg +1 -0
- pycforge/ide/resources/icons/telemetry.svg +1 -0
- pycforge/ide/resources/icons/toggle-comment.svg +1 -0
- pycforge/ide/resources/icons/toggle-fold.svg +1 -0
- pycforge/ide/resources/icons/undo.svg +1 -0
- pycforge/ide/resources/icons/whitespace.svg +1 -0
- pycforge/ide/revisions.py +400 -0
- pycforge/ide/search_service.py +280 -0
- pycforge/ide/session_history.py +150 -0
- pycforge/ide/source_editing.py +417 -0
- pycforge/ide/source_structure.py +558 -0
- pycforge/ide/source_structure_async.py +191 -0
- pycforge/ide/supervisor.py +503 -0
- pycforge/ide/theme.py +119 -0
- pycforge/ide/theme_stylesheet.py +598 -0
- pycforge/ide/theme_workspace_stylesheet.py +155 -0
- pycforge/ide/visual_tokens.py +142 -0
- pycforge/ide/worker_protocol.py +80 -0
- pycforge/ide/workspace_search.py +462 -0
- pycforge/ide/workspace_session.py +256 -0
- pycforge/laboratory/__init__.py +6 -0
- pycforge/laboratory/audits.py +2244 -0
- pycforge/laboratory/checkpoint_e.py +2685 -0
- pycforge/laboratory/cli.py +119 -0
- pycforge/laboratory/exit_codes.py +11 -0
- pycforge/laboratory/keyword_audit.py +661 -0
- pycforge/laboratory/keyword_only_audit.py +481 -0
- pycforge-0.15.2.dist-info/METADATA +209 -0
- pycforge-0.15.2.dist-info/RECORD +221 -0
- pycforge-0.15.2.dist-info/WHEEL +5 -0
- pycforge-0.15.2.dist-info/entry_points.txt +5 -0
- pycforge-0.15.2.dist-info/licenses/LICENSE +674 -0
- pycforge-0.15.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
"""C IR lowering adapter for the Phase 13 immutable static-record profile.
|
|
2
|
+
|
|
3
|
+
The adapter consumes only published record facts. It does not rediscover
|
|
4
|
+
class semantics, allocate storage, introduce helpers, or own scalar expression
|
|
5
|
+
lowering. Those services are supplied by the cumulative lowerer so record
|
|
6
|
+
lowering remains a small, independently testable representation boundary.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import hashlib
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from typing import Any, Callable
|
|
14
|
+
|
|
15
|
+
from pycforge.converter.ir.c_ir import (
|
|
16
|
+
CIdentifier,
|
|
17
|
+
CIdentifierRef,
|
|
18
|
+
CMemberAccessExpr,
|
|
19
|
+
CMemberAccessMode,
|
|
20
|
+
CQualifier,
|
|
21
|
+
CRecordDefinition,
|
|
22
|
+
CRecordField,
|
|
23
|
+
CRecordInitializer,
|
|
24
|
+
CStorage,
|
|
25
|
+
CType,
|
|
26
|
+
CVariableDeclaration,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _sid(prefix: str, *parts: str) -> str:
|
|
31
|
+
return prefix + hashlib.sha256("\x1f".join(parts).encode("utf-8")).hexdigest()[:20]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(slots=True)
|
|
35
|
+
class RecordLoweringServices:
|
|
36
|
+
"""Services and proved fact indexes required by record lowering.
|
|
37
|
+
|
|
38
|
+
Each fact mapping may use the corresponding semantic ID or source-node ID
|
|
39
|
+
as its key. :class:`RecordCIRLowerer` indexes values by their published
|
|
40
|
+
identity fields, which lets it consume the canonical fact-table mappings
|
|
41
|
+
directly without depending on a particular caller-side index choice.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
nodes: dict[str, dict[str, Any]]
|
|
45
|
+
definitions: dict[str, dict[str, Any]]
|
|
46
|
+
fields: dict[str, dict[str, Any]]
|
|
47
|
+
initializers: dict[str, dict[str, Any]]
|
|
48
|
+
instances: dict[str, dict[str, Any]]
|
|
49
|
+
bindings: dict[str, dict[str, Any]]
|
|
50
|
+
accesses: dict[str, dict[str, Any]]
|
|
51
|
+
source_bindings_by_id: dict[str, dict[str, Any]]
|
|
52
|
+
generated_names: dict[str, str]
|
|
53
|
+
expression: Callable[..., tuple[tuple[Any, ...], Any]]
|
|
54
|
+
temporary: Callable[..., tuple[CVariableDeclaration, CIdentifierRef]]
|
|
55
|
+
category_type: Callable[[str], CType]
|
|
56
|
+
identifier: Callable[[dict[str, Any], dict[str, Any]], CIdentifier]
|
|
57
|
+
provenance: Callable[..., Any]
|
|
58
|
+
synthetic_provenance: Callable[..., Any]
|
|
59
|
+
reject: Callable[..., Any]
|
|
60
|
+
check_cancellation: Callable[[], None]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class RecordCIRLowerer:
|
|
64
|
+
"""Lower complete immutable-record proofs into structured C IR."""
|
|
65
|
+
|
|
66
|
+
def __init__(self, services: RecordLoweringServices) -> None:
|
|
67
|
+
self.services = services
|
|
68
|
+
self.definition_by_record = self._index(services.definitions, "record_id")
|
|
69
|
+
self.field_by_id = self._index(services.fields, "field_id")
|
|
70
|
+
self.initializer_by_id = self._index(services.initializers, "initializer_id")
|
|
71
|
+
self.instance_by_assignment = self._index(services.instances, "assignment_node_id")
|
|
72
|
+
self.binding_by_id = self._index(services.bindings, "binding_id")
|
|
73
|
+
self.access_by_node = self._index(services.accesses, "access_node_id")
|
|
74
|
+
self.constructed_bindings: dict[str, CIdentifier] = {}
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def _index(
|
|
78
|
+
values: dict[str, dict[str, Any]], identity_field: str
|
|
79
|
+
) -> dict[str, dict[str, Any]]:
|
|
80
|
+
return {
|
|
81
|
+
str(value[identity_field]): value
|
|
82
|
+
for value in values.values()
|
|
83
|
+
if isinstance(value, dict) and isinstance(value.get(identity_field), str)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
def definitions(self) -> tuple[CRecordDefinition, ...]:
|
|
87
|
+
"""Return record declarations in canonical fact-table order."""
|
|
88
|
+
|
|
89
|
+
self.services.check_cancellation()
|
|
90
|
+
declarations: list[CRecordDefinition] = []
|
|
91
|
+
for fact in self.services.definitions.values():
|
|
92
|
+
self.services.check_cancellation()
|
|
93
|
+
if not isinstance(fact, dict):
|
|
94
|
+
self.services.reject(
|
|
95
|
+
"PYC3601", "Static-record definition fact is malformed"
|
|
96
|
+
)
|
|
97
|
+
declarations.append(self._definition(fact))
|
|
98
|
+
return tuple(declarations)
|
|
99
|
+
|
|
100
|
+
def construction(self, node: dict[str, Any]) -> tuple[Any, ...]:
|
|
101
|
+
"""Lower one proved constructor assignment with explicit LTR staging."""
|
|
102
|
+
|
|
103
|
+
s = self.services
|
|
104
|
+
s.check_cancellation()
|
|
105
|
+
fact = self.instance_by_assignment.get(node.get("node_id"))
|
|
106
|
+
if fact is None:
|
|
107
|
+
s.reject(
|
|
108
|
+
"PYC3605",
|
|
109
|
+
"Static-record construction lacks a published instance fact",
|
|
110
|
+
node,
|
|
111
|
+
)
|
|
112
|
+
if fact.get("assignment_node_id") != node.get("node_id"):
|
|
113
|
+
s.reject("PYC3605", "Static-record assignment identity is inconsistent", node)
|
|
114
|
+
|
|
115
|
+
definition = self.definition_by_record.get(fact.get("record_id"))
|
|
116
|
+
if definition is None:
|
|
117
|
+
s.reject("PYC3605", "Static-record construction type cannot be resolved", node)
|
|
118
|
+
self._require_closed_definition(definition)
|
|
119
|
+
|
|
120
|
+
initializer = self.initializer_by_id.get(definition.get("initializer_id"))
|
|
121
|
+
if (
|
|
122
|
+
initializer is None
|
|
123
|
+
or initializer.get("record_id") != definition.get("record_id")
|
|
124
|
+
or tuple(initializer.get("field_ids", ()))
|
|
125
|
+
!= tuple(definition.get("field_ids", ()))
|
|
126
|
+
or initializer.get("receiver_model")
|
|
127
|
+
!= "direct-addressed-initialization-receiver"
|
|
128
|
+
or initializer.get("evaluation_order")
|
|
129
|
+
!= "field-declaration-order-left-to-right-once"
|
|
130
|
+
or initializer.get("initialization_completeness")
|
|
131
|
+
!= "all-fields-exactly-once"
|
|
132
|
+
):
|
|
133
|
+
s.reject(
|
|
134
|
+
"PYC3605",
|
|
135
|
+
"Static-record construction lacks its exact initializer proof",
|
|
136
|
+
node,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
if (
|
|
140
|
+
fact.get("class_node_id") != definition.get("class_node_id")
|
|
141
|
+
or fact.get("mutable") is not False
|
|
142
|
+
or fact.get("storage_model") != "automatic-inline-record"
|
|
143
|
+
or fact.get("ownership_model") != "unique-lexical-owner"
|
|
144
|
+
or fact.get("lifetime_model") != "enclosing-function-activation"
|
|
145
|
+
or fact.get("aliasing_model") != "forbidden"
|
|
146
|
+
or fact.get("cleanup_model") != "none"
|
|
147
|
+
or fact.get("nullability_model") != "non-null-by-construction"
|
|
148
|
+
or fact.get("allocation_model") != "none"
|
|
149
|
+
):
|
|
150
|
+
s.reject(
|
|
151
|
+
"PYC3606",
|
|
152
|
+
"Static-record instance lacks the automatic no-allocation proof",
|
|
153
|
+
node,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
binding_fact = self.binding_by_id.get(fact.get("binding_id"))
|
|
157
|
+
if (
|
|
158
|
+
binding_fact is None
|
|
159
|
+
or binding_fact.get("instance_id") != fact.get("instance_id")
|
|
160
|
+
or binding_fact.get("record_id") != fact.get("record_id")
|
|
161
|
+
or binding_fact.get("declaration_node_id") != fact.get("target_node_id")
|
|
162
|
+
or binding_fact.get("single_assignment") is not True
|
|
163
|
+
or binding_fact.get("noalias") is not True
|
|
164
|
+
or binding_fact.get("escapes") is not False
|
|
165
|
+
):
|
|
166
|
+
s.reject(
|
|
167
|
+
"PYC3606",
|
|
168
|
+
"Static-record construction lacks a unique, non-escaping owner proof",
|
|
169
|
+
node,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
target = s.nodes.get(fact.get("target_node_id"))
|
|
173
|
+
source_binding = s.source_bindings_by_id.get(fact.get("binding_id"))
|
|
174
|
+
construction = s.nodes.get(fact.get("construction_node_id"))
|
|
175
|
+
if target is None or source_binding is None or construction is None:
|
|
176
|
+
s.reject(
|
|
177
|
+
"PYC3605",
|
|
178
|
+
"Static-record construction source binding or node is absent",
|
|
179
|
+
node,
|
|
180
|
+
)
|
|
181
|
+
if target.get("kind") != "Name" or construction.get("kind") != "Call":
|
|
182
|
+
s.reject("PYC3605", "Static-record construction shape is malformed", node)
|
|
183
|
+
|
|
184
|
+
field_ids = tuple(definition.get("field_ids", ()))
|
|
185
|
+
argument_ids = tuple(fact.get("argument_node_ids", ()))
|
|
186
|
+
if not field_ids or len(argument_ids) != len(field_ids):
|
|
187
|
+
s.reject(
|
|
188
|
+
"PYC3605", "Static-record constructor arity proof is inconsistent", construction
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
prelude: list[Any] = []
|
|
192
|
+
staged_arguments: list[Any] = []
|
|
193
|
+
for ordinal, (field_id, argument_id) in enumerate(
|
|
194
|
+
zip(field_ids, argument_ids)
|
|
195
|
+
):
|
|
196
|
+
s.check_cancellation()
|
|
197
|
+
field = self.field_by_id.get(field_id)
|
|
198
|
+
argument = s.nodes.get(argument_id)
|
|
199
|
+
if (
|
|
200
|
+
field is None
|
|
201
|
+
or field.get("record_id") != definition.get("record_id")
|
|
202
|
+
or field.get("ordinal") != ordinal
|
|
203
|
+
or field.get("mutable") is not False
|
|
204
|
+
or argument is None
|
|
205
|
+
):
|
|
206
|
+
s.reject(
|
|
207
|
+
"PYC3605",
|
|
208
|
+
"Static-record constructor field or argument proof is inconsistent",
|
|
209
|
+
argument or construction,
|
|
210
|
+
)
|
|
211
|
+
expression_prelude, expression = s.expression(argument)
|
|
212
|
+
s.check_cancellation()
|
|
213
|
+
prelude.extend(expression_prelude)
|
|
214
|
+
temporary, reference = s.temporary(
|
|
215
|
+
"record_arg",
|
|
216
|
+
construction,
|
|
217
|
+
ordinal,
|
|
218
|
+
s.category_type(str(field.get("category"))),
|
|
219
|
+
expression,
|
|
220
|
+
(argument_id, construction["node_id"], node["node_id"]),
|
|
221
|
+
)
|
|
222
|
+
prelude.append(temporary)
|
|
223
|
+
staged_arguments.append(reference)
|
|
224
|
+
|
|
225
|
+
s.check_cancellation()
|
|
226
|
+
record_spelling = self._planned_name(
|
|
227
|
+
str(definition.get("class_binding_id")),
|
|
228
|
+
s.nodes.get(definition.get("class_node_id"), node),
|
|
229
|
+
"record type",
|
|
230
|
+
)
|
|
231
|
+
initializer_expression = CRecordInitializer(
|
|
232
|
+
_sid("c-record-initializer-", construction["node_id"]),
|
|
233
|
+
CType(record_spelling),
|
|
234
|
+
tuple(staged_arguments),
|
|
235
|
+
s.provenance(construction),
|
|
236
|
+
)
|
|
237
|
+
identifier = s.identifier(source_binding, target)
|
|
238
|
+
declaration = CVariableDeclaration(
|
|
239
|
+
_sid("c-record-instance-", node["node_id"]),
|
|
240
|
+
identifier,
|
|
241
|
+
CType(record_spelling, (CQualifier.CONST,)),
|
|
242
|
+
initializer_expression,
|
|
243
|
+
CStorage.NONE,
|
|
244
|
+
s.provenance(node),
|
|
245
|
+
)
|
|
246
|
+
s.check_cancellation()
|
|
247
|
+
self.constructed_bindings[str(fact["binding_id"])] = identifier
|
|
248
|
+
return tuple(prelude) + (declaration,)
|
|
249
|
+
|
|
250
|
+
def access(self, node: dict[str, Any]) -> tuple[tuple[Any, ...], CMemberAccessExpr]:
|
|
251
|
+
"""Lower one statically-bound immutable field read."""
|
|
252
|
+
|
|
253
|
+
s = self.services
|
|
254
|
+
s.check_cancellation()
|
|
255
|
+
fact = self.access_by_node.get(node.get("node_id"))
|
|
256
|
+
if (
|
|
257
|
+
fact is None
|
|
258
|
+
or fact.get("statically_bound") is not True
|
|
259
|
+
or fact.get("access_mode") != "read"
|
|
260
|
+
):
|
|
261
|
+
s.reject(
|
|
262
|
+
"PYC3607",
|
|
263
|
+
"Static-record field access lacks a complete read proof",
|
|
264
|
+
node,
|
|
265
|
+
)
|
|
266
|
+
field = self.field_by_id.get(fact.get("field_id"))
|
|
267
|
+
binding = self.binding_by_id.get(fact.get("binding_id"))
|
|
268
|
+
if (
|
|
269
|
+
field is None
|
|
270
|
+
or binding is None
|
|
271
|
+
or field.get("record_id") != fact.get("record_id")
|
|
272
|
+
or binding.get("record_id") != fact.get("record_id")
|
|
273
|
+
or binding.get("instance_id") != fact.get("instance_id")
|
|
274
|
+
or fact.get("access_node_id")
|
|
275
|
+
not in tuple(binding.get("allowed_field_access_node_ids", ()))
|
|
276
|
+
or field.get("mutable") is not False
|
|
277
|
+
):
|
|
278
|
+
s.reject(
|
|
279
|
+
"PYC3607",
|
|
280
|
+
"Static-record receiver or field binding proof is inconsistent",
|
|
281
|
+
node,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
receiver = s.nodes.get(node.get("fields", {}).get("value"))
|
|
285
|
+
identifier = self.constructed_bindings.get(str(fact.get("binding_id")))
|
|
286
|
+
if receiver is None or receiver.get("kind") != "Name" or identifier is None:
|
|
287
|
+
s.reject(
|
|
288
|
+
"PYC3607",
|
|
289
|
+
"Static-record field access occurs without its unique local owner",
|
|
290
|
+
node,
|
|
291
|
+
)
|
|
292
|
+
reference = CIdentifierRef(
|
|
293
|
+
_sid(
|
|
294
|
+
"c-record-owner-ref-",
|
|
295
|
+
node["node_id"],
|
|
296
|
+
identifier.binding_id,
|
|
297
|
+
),
|
|
298
|
+
identifier.binding_id,
|
|
299
|
+
s.provenance(receiver),
|
|
300
|
+
)
|
|
301
|
+
s.check_cancellation()
|
|
302
|
+
return (), CMemberAccessExpr(
|
|
303
|
+
_sid("c-record-member-", node["node_id"], str(fact["field_id"])),
|
|
304
|
+
reference,
|
|
305
|
+
str(fact["field_id"]),
|
|
306
|
+
CMemberAccessMode.DIRECT,
|
|
307
|
+
s.provenance(node),
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
def _definition(self, fact: dict[str, Any]) -> CRecordDefinition:
|
|
311
|
+
s = self.services
|
|
312
|
+
s.check_cancellation()
|
|
313
|
+
self._require_closed_definition(fact)
|
|
314
|
+
class_node = s.nodes.get(fact.get("class_node_id"))
|
|
315
|
+
if class_node is None:
|
|
316
|
+
s.reject("PYC3601", "Static-record class source node is absent")
|
|
317
|
+
class_binding_id = str(fact.get("class_binding_id"))
|
|
318
|
+
record_identifier = CIdentifier(
|
|
319
|
+
class_binding_id,
|
|
320
|
+
self._planned_name(class_binding_id, class_node, "record type"),
|
|
321
|
+
s.provenance(class_node),
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
record_fields: list[CRecordField] = []
|
|
325
|
+
for ordinal, field_id in enumerate(tuple(fact.get("field_ids", ()))):
|
|
326
|
+
s.check_cancellation()
|
|
327
|
+
field = self.field_by_id.get(field_id)
|
|
328
|
+
field_node = s.nodes.get((field or {}).get("declaration_node_id"))
|
|
329
|
+
if (
|
|
330
|
+
field is None
|
|
331
|
+
or field.get("record_id") != fact.get("record_id")
|
|
332
|
+
or field.get("ordinal") != ordinal
|
|
333
|
+
or field.get("mutable") is not False
|
|
334
|
+
or field_node is None
|
|
335
|
+
):
|
|
336
|
+
s.reject(
|
|
337
|
+
"PYC3602",
|
|
338
|
+
"Static-record field order or ownership proof is inconsistent",
|
|
339
|
+
field_node or class_node,
|
|
340
|
+
)
|
|
341
|
+
field_binding_id = str(field["field_id"])
|
|
342
|
+
field_identifier = CIdentifier(
|
|
343
|
+
field_binding_id,
|
|
344
|
+
self._planned_name(field_binding_id, field_node, "record field"),
|
|
345
|
+
s.provenance(field_node),
|
|
346
|
+
)
|
|
347
|
+
record_fields.append(
|
|
348
|
+
CRecordField(
|
|
349
|
+
_sid("c-record-field-", field_binding_id),
|
|
350
|
+
field_identifier,
|
|
351
|
+
s.category_type(str(field.get("category"))),
|
|
352
|
+
s.provenance(field_node),
|
|
353
|
+
)
|
|
354
|
+
)
|
|
355
|
+
return CRecordDefinition(
|
|
356
|
+
_sid("c-record-definition-", str(fact["record_id"])),
|
|
357
|
+
record_identifier,
|
|
358
|
+
tuple(record_fields),
|
|
359
|
+
s.provenance(class_node),
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
def _require_closed_definition(self, fact: dict[str, Any]) -> None:
|
|
363
|
+
if (
|
|
364
|
+
not isinstance(fact.get("record_id"), str)
|
|
365
|
+
or not isinstance(fact.get("class_binding_id"), str)
|
|
366
|
+
or not tuple(fact.get("field_ids", ()))
|
|
367
|
+
or fact.get("mutable") is not False
|
|
368
|
+
or fact.get("storage_model") != "automatic-inline-record"
|
|
369
|
+
or fact.get("ownership_model") != "unique-lexical-owner"
|
|
370
|
+
or fact.get("lifetime_model") != "enclosing-function-activation"
|
|
371
|
+
or fact.get("aliasing_model") != "forbidden"
|
|
372
|
+
or fact.get("cleanup_model") != "none"
|
|
373
|
+
or fact.get("nullability_model") != "non-null-by-construction"
|
|
374
|
+
):
|
|
375
|
+
node = self.services.nodes.get(fact.get("class_node_id"))
|
|
376
|
+
self.services.reject(
|
|
377
|
+
"PYC3601",
|
|
378
|
+
"Static-record definition lacks the closed automatic ownership proof",
|
|
379
|
+
node,
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
def _planned_name(
|
|
383
|
+
self, binding_id: str, node: dict[str, Any] | None, subject: str
|
|
384
|
+
) -> str:
|
|
385
|
+
spelling = self.services.generated_names.get(binding_id)
|
|
386
|
+
if not spelling:
|
|
387
|
+
self.services.reject(
|
|
388
|
+
"PYC2942", f"Generated {subject} identifier plan is missing", node
|
|
389
|
+
)
|
|
390
|
+
return str(spelling)
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
__all__ = ["RecordCIRLowerer", "RecordLoweringServices"]
|