1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
// Copyright 2024-2026 Jonathan Shook
// SPDX-License-Identifier: Apache-2.0
//! Polydat compiler diagnostic event stream.
//!
//! The compiler emits typed events for each step: parsing, binding
//! resolution, module inlining, type adaptation, constant folding,
//! fusion, and compilation level selection.
//!
//! Events are tagged with severity levels:
//! - **Info**: normal compilation steps (parsed, resolved, folded)
//! - **Advisory**: type coercions, widenings, and implicit conversions
//! that the user should be aware of for module design quality
//! - **Warning**: potential performance or correctness issues
//! - **Error**: compilation failures (surfaced as Result::Err, not events)
/// Severity level for compiler diagnostic events.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EventLevel {
/// Normal compilation step — informational only.
Info,
/// Design advisory — implicit conversion or coercion that the user
/// should review for module quality. Query with `--diagnose`.
Advisory,
/// Potential performance or correctness issue.
Warning,
}
/// A diagnostic event from the Polydat compilation pipeline.
#[derive(Debug, Clone)]
pub enum CompileEvent {
/// DSL source parsed into AST.
Parsed {
/// Top-level statements in the file.
statements: usize,
},
/// A binding was resolved from DSL to a node.
BindingResolved {
/// The binding's name.
name: String,
/// The node type it resolved to.
node_type: String,
},
/// A module was loaded and inlined.
ModuleInlined {
/// The module's name.
name: String,
/// Nodes the inlining added to the graph.
nodes_added: usize,
},
/// A legacy binding chain was translated to Polydat source.
LegacyTranslated {
/// The binding's name.
name: String,
/// The Polydat expression it became.
polydat_expr: String,
},
/// Type adapter inserted between mismatched ports.
TypeAdapterInserted {
/// The producing node.
from_node: String,
/// The consuming node.
to_node: String,
/// The adapter node inserted between them.
adapter: String,
},
/// Init-time constant folded (SRD 44).
ConstantFolded {
/// The node folded.
node: String,
/// The constant's rendered value.
value: String,
},
/// Fusion pattern matched and applied (SRD 36).
FusionApplied {
/// The fusion pattern's name.
pattern: String,
/// Nodes the fused node replaced.
nodes_replaced: usize,
},
/// Output declared.
OutputDeclared {
/// The output's name.
name: String,
},
/// Compilation level selected for a node.
CompileLevelSelected {
/// The node.
node: String,
/// The level's name.
level: String,
},
/// Workload parameter injected as constant.
ParamInjected {
/// The parameter's name.
name: String,
/// The value injected.
value: String,
},
/// Config wire connected to a cycle-time source (performance warning).
ConfigWireCycleWarning {
/// The consuming node.
node: String,
/// The config port fed by a cycle-time source.
port: String,
},
/// Auto-widening type coercion inserted by the compiler.
TypeWidening {
/// The source type.
from: &'static str,
/// The type widened to.
to: &'static str,
/// Where the widening was inserted.
context: String,
},
/// Warning during compilation.
Warning {
/// The warning text.
message: String,
},
/// An extern with no default: `None` until the host sets it, and
/// every consumer reads `None` through it (engine_parity.md, A12).
ExternWithoutDefault {
/// The extern's name.
name: String,
/// Its declared type.
port_type: String,
},
/// Summary of the compiled program.
Summary {
/// Nodes in the compiled graph.
nodes: usize,
/// Declared outputs.
outputs: usize,
/// Init-time constants folded.
constants_folded: usize,
},
/// A module-level pragma was acknowledged. Recorded once per
/// recognised `// @pragma: <name>` directive at the top of the
/// source. Lets `--diagnose` show which graph transforms the
/// module asked for.
PragmaAcknowledged {
/// The pragma's name.
name: String,
/// The source line it appears on.
line: usize,
},
/// An unrecognised module-level pragma was seen. Pragmas are
/// forward-compatible: an old binary parses a newer module
/// that opts into features it doesn't support, and the only
/// effect is this advisory.
UnknownPragma {
/// The pragma's name.
name: String,
/// The source line it appears on.
line: usize,
},
/// Strict-wire mode auto-inserted an assertion node between
/// `from_node` and `to_node`. SRD 15 §"Strict Wire Mode".
AssertionInserted {
/// The producing node.
from_node: String,
/// The consuming node.
to_node: String,
/// The assertion kind inserted.
kind: String,
},
/// Strict-wire mode considered inserting an assertion but
/// proved it redundant. The reason field names which skip
/// rule applied (constant source, upstream assertion, etc.).
AssertionSkipped {
/// The producing node.
from_node: String,
/// The consuming node.
to_node: String,
/// The skip rule that applied.
reason: String,
},
/// A tile hole was typed (SRD 114 §4): its expression, the wire
/// type the compiler inferred, the declared type if any, the
/// contextual expectation of its position, the encoder chosen, and
/// the adapter inserted between wire and declared type if one was.
TileHoleTyped {
/// The tile's name.
tile: String,
/// The hole's expression text.
hole: String,
/// The wire type the compiler inferred.
wire_type: String,
/// The declared type, if any.
declared: Option<String>,
/// The contextual expectation of the hole's position.
expectation: String,
/// The encoder chosen.
encoder: String,
/// The adapter inserted between wire and declared type, if any.
adapter: Option<String>,
},
/// A tile's skeleton (SRD 114 §6, §10): how many static runs it
/// copies and their byte total, its holes, branches, and
/// projections, and the source of each projection body program.
TileCompiled {
/// The tile's name.
tile: String,
/// The tile's encoding.
encoding: String,
/// Static runs the skeleton copies.
statics: usize,
/// Their byte total.
static_bytes: usize,
/// Holes.
holes: usize,
/// Branches.
branches: usize,
/// Projections.
projections: usize,
/// The source of each projection body program.
bodies: Vec<String>,
},
}
impl CompileEvent {
/// The severity level of this event.
pub fn level(&self) -> EventLevel {
match self {
// Info: normal steps
CompileEvent::Parsed { .. } => EventLevel::Info,
CompileEvent::BindingResolved { .. } => EventLevel::Info,
CompileEvent::ModuleInlined { .. } => EventLevel::Info,
CompileEvent::OutputDeclared { .. } => EventLevel::Info,
CompileEvent::CompileLevelSelected { .. } => EventLevel::Info,
CompileEvent::ParamInjected { .. } => EventLevel::Info,
CompileEvent::ConstantFolded { .. } => EventLevel::Info,
CompileEvent::FusionApplied { .. } => EventLevel::Info,
CompileEvent::Summary { .. } => EventLevel::Info,
CompileEvent::TileHoleTyped { adapter: None, .. } => EventLevel::Info,
CompileEvent::TileCompiled { .. } => EventLevel::Info,
// Advisory: implicit conversions the user should review
CompileEvent::TileHoleTyped {
adapter: Some(_), ..
} => EventLevel::Advisory,
CompileEvent::TypeAdapterInserted { .. } => EventLevel::Advisory,
CompileEvent::TypeWidening { .. } => EventLevel::Advisory,
CompileEvent::LegacyTranslated { .. } => EventLevel::Advisory,
CompileEvent::PragmaAcknowledged { .. } => EventLevel::Advisory,
CompileEvent::AssertionInserted { .. } => EventLevel::Advisory,
CompileEvent::AssertionSkipped { .. } => EventLevel::Advisory,
// Warning: potential issues
CompileEvent::ConfigWireCycleWarning { .. } => EventLevel::Warning,
CompileEvent::Warning { .. } => EventLevel::Warning,
CompileEvent::ExternWithoutDefault { .. } => EventLevel::Warning,
CompileEvent::UnknownPragma { .. } => EventLevel::Warning,
}
}
}
/// Collects diagnostic events during compilation.
#[derive(Debug, Default)]
pub struct CompileEventLog {
events: Vec<CompileEvent>,
}
impl CompileEventLog {
/// An empty log.
pub fn new() -> Self {
Self { events: Vec::new() }
}
/// Record an event.
pub fn push(&mut self, event: CompileEvent) {
self.events.push(event);
}
/// Every event recorded, in order.
pub fn events(&self) -> &[CompileEvent] {
&self.events
}
/// Whether no event has been recorded.
pub fn is_empty(&self) -> bool {
self.events.is_empty()
}
/// Return only advisory-level events (type coercions, widenings).
/// These are the "module design quality" messages users query with --diagnose.
pub fn advisories(&self) -> Vec<&CompileEvent> {
self.events
.iter()
.filter(|e| e.level() == EventLevel::Advisory)
.collect()
}
/// Return only warning-level events.
pub fn warnings(&self) -> Vec<&CompileEvent> {
self.events
.iter()
.filter(|e| e.level() == EventLevel::Warning)
.collect()
}
/// Format all events as human-readable diagnostic lines.
/// Each line is prefixed with the severity tag.
pub fn format(&self) -> String {
self.events.iter().map(|e| {
let tag = match e.level() {
EventLevel::Info => "info",
EventLevel::Advisory => "advisory",
EventLevel::Warning => "warning",
};
let msg = match e {
CompileEvent::Parsed { statements } =>
format!("parsed {statements} statement(s)"),
CompileEvent::BindingResolved { name, node_type } =>
format!("resolved '{name}' → {node_type}"),
CompileEvent::ModuleInlined { name, nodes_added } =>
format!("module '{name}' inlined ({nodes_added} nodes)"),
CompileEvent::LegacyTranslated { name, polydat_expr } =>
format!("legacy '{name}' → {polydat_expr}"),
CompileEvent::TypeAdapterInserted { from_node, to_node, adapter } =>
format!("type adapter {adapter}: {from_node} → {to_node}"),
CompileEvent::ConstantFolded { node, value } =>
format!("constant folded: {node} → {value}"),
CompileEvent::FusionApplied { pattern, nodes_replaced } =>
format!("fusion: {pattern} ({nodes_replaced} nodes replaced)"),
CompileEvent::OutputDeclared { name } =>
format!("output '{name}'"),
CompileEvent::CompileLevelSelected { node, level } =>
format!("{node} → {level}"),
CompileEvent::ParamInjected { name, value } =>
format!("param '{name}' = {value}"),
CompileEvent::ConfigWireCycleWarning { node, port } =>
format!("config wire '{port}' on '{node}' connected to cycle-time source"),
CompileEvent::TypeWidening { from, to, context } =>
format!("widening {from} → {to} in {context}"),
CompileEvent::Warning { message } =>
message.to_string(),
CompileEvent::ExternWithoutDefault { name, port_type } =>
format!("extern '{name}' ({port_type}) has no default: it is `None` until the host sets it"),
CompileEvent::Summary { nodes, outputs, constants_folded } =>
format!("{nodes} nodes, {outputs} outputs, {constants_folded} constant(s) folded"),
CompileEvent::PragmaAcknowledged { name, line } =>
format!("pragma '{name}' acknowledged (line {line})"),
CompileEvent::UnknownPragma { name, line } =>
format!("unknown pragma '{name}' at line {line}; ignored"),
CompileEvent::AssertionInserted { from_node, to_node, kind } =>
format!("assertion inserted: {from_node} → {to_node} ({kind})"),
CompileEvent::AssertionSkipped { from_node, to_node, reason } =>
format!("assertion skipped: {from_node} → {to_node} ({reason})"),
CompileEvent::TileHoleTyped { tile, hole, wire_type, declared, expectation, encoder, adapter } =>
format!(
"tile '{tile}' hole `{hole}`: wire {wire_type}{} expects {expectation}, encoder {encoder}{}",
declared.as_ref().map(|d| format!(", declared {d},")).unwrap_or_else(|| ",".to_string()),
adapter.as_ref().map(|a| format!(", adapter {a}")).unwrap_or_default()
),
CompileEvent::TileCompiled { tile, encoding, statics, static_bytes, holes, branches, projections, .. } =>
format!(
"tile '{tile}' ({encoding}): {statics} static run(s), {static_bytes} bytes; {holes} hole(s), {branches} branch(es), {projections} projection(s)"
),
};
format!("polydat[{tag}]: {msg}")
}).collect::<Vec<_>>().join("\n")
}
}