Skip to content

Commit 7327160

Browse files
committed
move evaluation to its own pass after constant resolution
1 parent 9f534b2 commit 7327160

File tree

5 files changed

+94
-25
lines changed

5 files changed

+94
-25
lines changed

compiler/plc_driver/src/pipelines.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,11 @@ impl ParsedProject {
561561
global_index.import(indexer::index(&builtins));
562562

563563
//TODO: evaluate constants should probably be a participant
564-
let (index, unresolvables) = plc::resolver::const_evaluator::evaluate_constants(global_index);
564+
let (mut index, unresolvables) = plc::resolver::const_evaluator::evaluate_constants(global_index);
565+
566+
// Fix up enum defaults after constants are resolved
567+
index.finalize_enum_defaults();
568+
565569
IndexedProject { project: ParsedProject { units }, index, unresolvables }
566570
}
567571
}

src/index.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2025,6 +2025,59 @@ impl Index {
20252025
self.type_index.pou_types.insert(datatype.get_name().to_lowercase(), datatype);
20262026
}
20272027

2028+
/// Fixes up enum types to set their default initial values.
2029+
/// This must be called after constant resolution, as it needs to evaluate
2030+
/// constant expressions to determine which variant is zero.
2031+
///
2032+
/// For each enum without an explicit initializer, this sets the initial_value to:
2033+
/// 1. The zero-variant (if one exists), or
2034+
/// 2. The first variant (as fallback)
2035+
pub fn finalize_enum_defaults(&mut self) {
2036+
// Process all types and update enum defaults
2037+
let mut fixed_types = Vec::new();
2038+
2039+
for (name, mut datatypes) in self.type_index.types.drain(..) {
2040+
for mut datatype in datatypes.drain(..) {
2041+
if let DataTypeInformation::Enum { variants, .. } = &datatype.information {
2042+
// Only process if there's no explicit initializer
2043+
if datatype.initial_value.is_none() && !variants.is_empty() {
2044+
let mut zero_variant_id: Option<ConstId> = None;
2045+
let mut first_variant_id: Option<ConstId> = None;
2046+
2047+
// Look for a variant that evaluates to zero, or use the first one
2048+
for (idx, variant) in variants.iter().enumerate() {
2049+
if let Some(variant_init) = variant.initial_value {
2050+
if idx == 0 {
2051+
first_variant_id = Some(variant_init);
2052+
}
2053+
2054+
if let Ok(0) =
2055+
self.constant_expressions.get_constant_int_statement_value(&variant_init)
2056+
{
2057+
zero_variant_id = Some(variant_init);
2058+
break;
2059+
}
2060+
}
2061+
}
2062+
2063+
// Prefer zero variant, fall back to first variant
2064+
let default_value = zero_variant_id.or(first_variant_id);
2065+
if let Some(const_id) = default_value {
2066+
datatype.initial_value = Some(const_id);
2067+
}
2068+
}
2069+
}
2070+
2071+
fixed_types.push((name.clone(), datatype));
2072+
}
2073+
}
2074+
2075+
// Re-insert all types
2076+
for (name, datatype) in fixed_types {
2077+
self.type_index.types.insert(name, datatype);
2078+
}
2079+
}
2080+
20282081
pub fn find_callable_instance_variable(
20292082
&self,
20302083
context: Option<&str>,

src/index/indexer/user_type_indexer.rs

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -292,10 +292,8 @@ impl UserTypeIndexer<'_, '_> {
292292

293293
fn index_enum_type(&mut self, name: &str, numeric_type: &str, elements: &AstNode) {
294294
let mut variants = Vec::new();
295-
let mut zero_value_const_id: Option<ConstId> = None;
296-
let mut first_element_const_id: Option<ConstId> = None;
297295

298-
for (idx, ele) in flatten_expression_list(elements).iter().enumerate() {
296+
for ele in flatten_expression_list(elements) {
299297
let variant = get_enum_element_name(ele);
300298
if let AstStatement::Assignment(Assignment { right, .. }) = ele.get_stmt() {
301299
let scope = self.current_scope();
@@ -306,16 +304,6 @@ impl UserTypeIndexer<'_, '_> {
306304
None,
307305
);
308306

309-
// Track if we have a zero element and remember its ConstId
310-
if let AstStatement::Literal(AstLiteral::Integer(0)) = right.get_stmt() {
311-
zero_value_const_id = Some(init);
312-
}
313-
314-
// Remember the first element's ConstId
315-
if idx == 0 {
316-
first_element_const_id = Some(init);
317-
}
318-
319307
variants.push(self.index.register_enum_variant(
320308
name,
321309
&variant,
@@ -327,17 +315,6 @@ impl UserTypeIndexer<'_, '_> {
327315
}
328316
}
329317

330-
// If no explicit initializer was provided, determine the default value
331-
if self.pending_initializer.is_none() {
332-
self.pending_initializer = if zero_value_const_id.is_some() {
333-
// If zero is defined, use its ConstId
334-
zero_value_const_id
335-
} else {
336-
// Otherwise use the first element's ConstId
337-
first_element_const_id
338-
};
339-
}
340-
341318
let information = DataTypeInformation::Enum {
342319
name: name.to_owned(),
343320
variants,

src/test_utils.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ pub mod tests {
141141
id_provider: IdProvider,
142142
) -> Lowered {
143143
let (mut index, _) = evaluate_constants(index);
144+
index.finalize_enum_defaults();
145+
144146
let mut all_annotations = AnnotationMapImpl::default();
145147

146148
let (mut annotations, ..) = TypeAnnotator::visit_unit(&index, &unit, id_provider.clone());
@@ -302,6 +304,7 @@ pub mod tests {
302304
},
303305
);
304306
let (mut index, ..) = evaluate_constants(index);
307+
index.finalize_enum_defaults();
305308
let mut all_annotations = AnnotationMapImpl::default();
306309
let units = units
307310
.into_iter()
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: (%COMPILE %s && %RUN) | %CHECK %s
2+
3+
VAR_GLOBAL CONSTANT
4+
MYCONST: INT := 0;
5+
END_VAR
6+
7+
// Test enum with no explicit default and a zero variant that is NOT also the first variant - BUT instead of a literal use a constant to initialize
8+
TYPE Status : INT (
9+
active := 1,
10+
inactive := MYCONST,
11+
error := 2
12+
);
13+
END_TYPE
14+
15+
VAR_GLOBAL
16+
s_global: Status; // Should initialize to inactive
17+
END_VAR
18+
19+
PROGRAM prog
20+
VAR
21+
s1 : Status; // Should initialize to inactive
22+
END_VAR
23+
// CHECK: 0
24+
printf('%d$N', s1); // inactive
25+
26+
// CHECK: 0
27+
printf('%d$N', s_global); // inactive
28+
END_PROGRAM
29+
30+
FUNCTION main
31+
prog();
32+
END_FUNCTION

0 commit comments

Comments
 (0)