Skip to main content

chipi_core/
validate.rs

1//! Semantic validation for instruction definitions.
2//!
3//! Validates a parsed definition for:
4//! - Name uniqueness
5//! - Type resolution
6//! - Bit coverage completeness
7//! - Pattern conflicts between instructions
8
9use std::collections::{HashMap, HashSet};
10
11use crate::error::{Error, ErrorKind, Span};
12use crate::parser::dsl_to_hardware;
13use crate::types::*;
14
15/// Validate a decoder definition and convert to a validated IR.
16///
17/// This performs multiple checks and transformations:
18/// - Converts bit ranges from DSL notation to hardware notation
19/// - Checks for duplicate names
20/// - Resolves field types
21/// - Validates bit coverage and patterns
22///
23/// The result is language-agnostic. Language-specific configuration
24/// (type mappings, dispatch strategies) is supplied separately to backends.
25pub fn validate(def: &DecoderDef) -> Result<ValidatedDef, Vec<Error>> {
26    let mut errors = Vec::new();
27
28    // Collect sub-decoder names for type resolution
29    let sub_decoder_names: HashSet<String> =
30        def.sub_decoders.iter().map(|sd| sd.name.clone()).collect();
31
32    // Phase 0: Convert all bit ranges from DSL notation to hardware notation
33    let instructions = convert_bit_ranges(def);
34
35    // Phase 1: Name uniqueness
36    check_name_uniqueness(&instructions, &def.type_aliases, &mut errors);
37
38    // Phase 2: Type resolution (skip sub-decoder types, they're valid)
39    resolve_types(
40        &instructions,
41        &def.type_aliases,
42        &sub_decoder_names,
43        &mut errors,
44    );
45
46    // Phase 3: Bit coverage
47    check_bit_coverage(&instructions, def.config.width, &mut errors);
48
49    // Phase 3b: Max units check (if configured)
50    if def.config.max_units.is_some() {
51        check_max_units(&instructions, &def.config, &mut errors);
52    }
53
54    // Phase 4: Pattern conflicts
55    check_pattern_conflicts(&instructions, &mut errors);
56
57    // Phase 5: Map validation
58    check_maps(&def.maps, &mut errors);
59
60    // Phase 6: Format validation
61    check_formats(&instructions, &def.maps, &mut errors);
62
63    // Phase 7: Validate sub-decoders
64    let validated_sub_decoders = validate_sub_decoders(&def.sub_decoders, &mut errors);
65
66    // Phase 8: Validate sub-decoder field widths
67    check_sub_decoder_field_widths(
68        &instructions,
69        &def.sub_decoders,
70        &sub_decoder_names,
71        &mut errors,
72    );
73
74    if !errors.is_empty() {
75        return Err(errors);
76    }
77
78    // Build validated instructions
79    let validated_instructions = instructions
80        .into_iter()
81        .map(|instr| {
82            let resolved_fields = instr
83                .segments
84                .iter()
85                .filter_map(|seg| {
86                    if let Segment::Field {
87                        name,
88                        field_type,
89                        ranges,
90                        ..
91                    } = seg
92                    {
93                        let resolved = resolve_field_type(
94                            field_type,
95                            &def.type_aliases,
96                            &sub_decoder_names,
97                            &def.sub_decoders,
98                        );
99                        Some(ResolvedField {
100                            name: name.clone(),
101                            ranges: ranges.clone(),
102                            resolved_type: resolved,
103                        })
104                    } else {
105                        None
106                    }
107                })
108                .collect();
109
110            ValidatedInstruction {
111                name: instr.name.clone(),
112                segments: instr.segments.clone(),
113                resolved_fields,
114                format_lines: instr.format_lines.clone(),
115                span: instr.span.clone(),
116            }
117        })
118        .collect();
119
120    Ok(ValidatedDef {
121        config: def.config.clone(),
122        type_aliases: def.type_aliases.clone(),
123        maps: def.maps.clone(),
124        instructions: validated_instructions,
125        sub_decoders: validated_sub_decoders,
126    })
127}
128
129/// Convert all DSL bit ranges to hardware (LSB=0) notation.
130/// Now supports cross-unit fields by splitting them into multiple BitRange objects.
131fn convert_bit_ranges(def: &DecoderDef) -> Vec<InstructionDef> {
132    let width = def.config.width;
133    let order = def.config.bit_order;
134
135    def.instructions
136        .iter()
137        .map(|instr| {
138            let segments = instr
139                .segments
140                .iter()
141                .map(|seg| match seg {
142                    Segment::Fixed {
143                        ranges,
144                        pattern,
145                        span,
146                    } => {
147                        // Convert each DSL range to hardware notation
148                        // Since ranges is already vec![range], we just need to convert it
149                        let hw_ranges =
150                            dsl_to_hardware(ranges[0].start, ranges[0].end, width, order);
151                        Segment::Fixed {
152                            ranges: hw_ranges,
153                            pattern: pattern.clone(),
154                            span: span.clone(),
155                        }
156                    }
157                    Segment::Field {
158                        name,
159                        field_type,
160                        ranges,
161                        span,
162                    } => {
163                        // Convert each DSL range to hardware notation
164                        let hw_ranges =
165                            dsl_to_hardware(ranges[0].start, ranges[0].end, width, order);
166                        Segment::Field {
167                            name: name.clone(),
168                            field_type: field_type.clone(),
169                            ranges: hw_ranges,
170                            span: span.clone(),
171                        }
172                    }
173                })
174                .collect();
175
176            InstructionDef {
177                name: instr.name.clone(),
178                segments,
179                format_lines: instr.format_lines.clone(),
180                span: instr.span.clone(),
181            }
182        })
183        .collect()
184}
185
186fn check_name_uniqueness(
187    instructions: &[InstructionDef],
188    type_aliases: &[TypeAlias],
189    errors: &mut Vec<Error>,
190) {
191    let mut seen_instructions: HashMap<&str, &Span> = HashMap::new();
192    for instr in instructions {
193        if let Some(prev_span) = seen_instructions.get(instr.name.as_str()) {
194            errors.push(
195                Error::new(
196                    ErrorKind::DuplicateInstructionName(instr.name.clone()),
197                    instr.span.clone(),
198                )
199                .with_help(format!("first defined at line {}", prev_span.line)),
200            );
201        } else {
202            seen_instructions.insert(&instr.name, &instr.span);
203        }
204    }
205
206    let mut seen_types: HashMap<&str, &Span> = HashMap::new();
207    for ta in type_aliases {
208        if let Some(prev_span) = seen_types.get(ta.name.as_str()) {
209            errors.push(
210                Error::new(
211                    ErrorKind::DuplicateTypeAlias(ta.name.clone()),
212                    ta.span.clone(),
213                )
214                .with_help(format!("first defined at line {}", prev_span.line)),
215            );
216        } else {
217            seen_types.insert(&ta.name, &ta.span);
218        }
219    }
220}
221
222fn resolve_types(
223    instructions: &[InstructionDef],
224    type_aliases: &[TypeAlias],
225    sub_decoder_names: &HashSet<String>,
226    errors: &mut Vec<Error>,
227) {
228    let alias_names: HashSet<&str> = type_aliases.iter().map(|ta| ta.name.as_str()).collect();
229
230    for instr in instructions {
231        for seg in &instr.segments {
232            if let Segment::Field {
233                field_type, span, ..
234            } = seg
235            {
236                if let FieldType::Alias(alias_name) = field_type {
237                    if !alias_names.contains(alias_name.as_str())
238                        && !is_builtin_type(alias_name)
239                        && !sub_decoder_names.contains(alias_name)
240                    {
241                        errors.push(Error::new(
242                            ErrorKind::UnresolvedType(alias_name.clone()),
243                            span.clone(),
244                        ));
245                    }
246                }
247            }
248        }
249    }
250}
251
252fn check_bit_coverage(instructions: &[InstructionDef], width: u32, errors: &mut Vec<Error>) {
253    for instr in instructions {
254        // Group ranges by unit (flattening all segment ranges)
255        let mut units_map: HashMap<u32, Vec<BitRange>> = HashMap::new();
256
257        for seg in &instr.segments {
258            match seg {
259                Segment::Fixed {
260                    ranges,
261                    pattern,
262                    span,
263                    ..
264                } => {
265                    // Check pattern length matches total range width
266                    let total_width: u32 = ranges.iter().map(|r| r.width()).sum();
267                    if pattern.len() as u32 != total_width {
268                        errors.push(Error::new(
269                            ErrorKind::PatternLengthMismatch {
270                                instruction: instr.name.clone(),
271                                expected: total_width,
272                                got: pattern.len() as u32,
273                            },
274                            span.clone(),
275                        ));
276                    }
277                    for range in ranges {
278                        units_map.entry(range.unit).or_default().push(*range);
279                    }
280                }
281                Segment::Field { ranges, .. } => {
282                    for range in ranges {
283                        units_map.entry(range.unit).or_default().push(*range);
284                    }
285                }
286            };
287        }
288
289        // For unit 0: require full coverage
290        if let Some(unit0_ranges) = units_map.get(&0) {
291            let mut covered = vec![false; width as usize];
292
293            for range in unit0_ranges {
294                for bit in range.end..=range.start {
295                    let idx = bit as usize;
296                    if covered[idx] {
297                        errors.push(Error::new(
298                            ErrorKind::OverlappingBits {
299                                instruction: instr.name.clone(),
300                                bit,
301                            },
302                            instr.span.clone(),
303                        ));
304                    }
305                    covered[idx] = true;
306                }
307            }
308
309            let missing: Vec<u32> = covered
310                .iter()
311                .enumerate()
312                .filter(|&(_, c)| !c)
313                .map(|(i, _)| i as u32)
314                .collect();
315
316            if !missing.is_empty() {
317                errors.push(Error::new(
318                    ErrorKind::BitCoverageGap {
319                        instruction: instr.name.clone(),
320                        missing_bits: missing,
321                    },
322                    instr.span.clone(),
323                ));
324            }
325        }
326
327        // For units 1+: only check for overlaps, gaps are allowed
328        for (unit_idx, ranges) in &units_map {
329            if *unit_idx == 0 {
330                continue;
331            }
332
333            let mut covered = vec![false; width as usize];
334            for range in ranges {
335                for bit in range.end..=range.start {
336                    let idx = bit as usize;
337                    if covered[idx] {
338                        errors.push(Error::new(
339                            ErrorKind::OverlappingBits {
340                                instruction: instr.name.clone(),
341                                bit,
342                            },
343                            instr.span.clone(),
344                        ));
345                    }
346                    covered[idx] = true;
347                }
348            }
349        }
350    }
351}
352
353/// Check that instructions don't exceed the configured max_units limit.
354fn check_max_units(
355    instructions: &[InstructionDef],
356    config: &DecoderConfig,
357    errors: &mut Vec<Error>,
358) {
359    let max_units = config
360        .max_units
361        .expect("check_max_units called without max_units configured");
362
363    for instr in instructions {
364        // Find the maximum unit index across all segments
365        let max_unit = instr
366            .segments
367            .iter()
368            .flat_map(|seg| match seg {
369                Segment::Fixed { ranges, .. } | Segment::Field { ranges, .. } => ranges.iter(),
370            })
371            .map(|range| range.unit)
372            .max()
373            .unwrap_or(0);
374
375        let required_units = max_unit + 1;
376
377        if required_units > max_units {
378            errors.push(Error::new(
379                ErrorKind::ExceedsMaxUnits {
380                    instruction: instr.name.clone(),
381                    required: required_units,
382                    max_units,
383                },
384                instr.span.clone(),
385            ).with_help(format!(
386                "set max_units = {} in the decoder block or remove max_units to allow any length",
387                required_units
388            )));
389        }
390    }
391}
392
393fn check_pattern_conflicts(instructions: &[InstructionDef], errors: &mut Vec<Error>) {
394    // O(n^2) check: two instructions conflict if all their shared fixed bit positions
395    // have compatible (identical) values.
396    for i in 0..instructions.len() {
397        for j in (i + 1)..instructions.len() {
398            if patterns_conflict(&instructions[i], &instructions[j]) {
399                errors.push(Error::new(
400                    ErrorKind::PatternConflict {
401                        a: instructions[i].name.clone(),
402                        b: instructions[j].name.clone(),
403                    },
404                    instructions[j].span.clone(),
405                ));
406            }
407        }
408    }
409}
410
411/// Check if two instructions have conflicting fixed bit patterns.
412/// A conflict occurs when both instructions have identical fixed bits at the same positions.
413fn patterns_conflict(a: &InstructionDef, b: &InstructionDef) -> bool {
414    let a_fixed = fixed_bit_map(a);
415    let b_fixed = fixed_bit_map(b);
416
417    for (&bit, &a_val) in &a_fixed {
418        if let Some(&b_val) = b_fixed.get(&bit) {
419            if a_val != b_val {
420                return false;
421            }
422        }
423    }
424
425    if a_fixed.len() != b_fixed.len() {
426        return false;
427    }
428
429    true
430}
431
432fn fixed_bit_map(instr: &InstructionDef) -> HashMap<(u32, u32), Bit> {
433    let mut map = HashMap::new();
434    for seg in &instr.segments {
435        if let Segment::Fixed {
436            ranges, pattern, ..
437        } = seg
438        {
439            let mut bit_idx = 0;
440            for range in ranges {
441                for i in 0..range.width() as usize {
442                    if bit_idx < pattern.len() {
443                        let hw_bit = range.start - i as u32;
444                        // Key is (unit, hw_bit) to avoid collisions between units
445                        map.insert((range.unit, hw_bit), pattern[bit_idx]);
446                        bit_idx += 1;
447                    }
448                }
449            }
450        }
451    }
452    map
453}
454
455fn check_maps(maps: &[MapDef], errors: &mut Vec<Error>) {
456    let mut seen_names: HashMap<&str, &Span> = HashMap::new();
457
458    for map in maps {
459        // Duplicate map names
460        if let Some(prev) = seen_names.get(map.name.as_str()) {
461            errors.push(
462                Error::new(
463                    ErrorKind::DuplicateMapName(map.name.clone()),
464                    map.span.clone(),
465                )
466                .with_help(format!("first defined at line {}", prev.line)),
467            );
468        } else {
469            seen_names.insert(&map.name, &map.span);
470        }
471
472        // Check for duplicate entries (same key pattern)
473        let mut seen_keys: Vec<&Vec<MapKey>> = Vec::new();
474        for entry in &map.entries {
475            if seen_keys.iter().any(|k| *k == &entry.keys) {
476                errors.push(Error::new(
477                    ErrorKind::DuplicateMapEntry {
478                        map: map.name.clone(),
479                    },
480                    entry.span.clone(),
481                ));
482            } else {
483                seen_keys.push(&entry.keys);
484            }
485        }
486
487        // Check param uniqueness within map
488        let mut seen_params: HashSet<&str> = HashSet::new();
489        for param in &map.params {
490            if !seen_params.insert(param.as_str()) {
491                errors.push(Error::new(
492                    ErrorKind::InvalidFormatString(format!(
493                        "duplicate parameter '{}' in map '{}'",
494                        param, map.name
495                    )),
496                    map.span.clone(),
497                ));
498            }
499        }
500    }
501}
502
503fn check_formats(instructions: &[InstructionDef], maps: &[MapDef], errors: &mut Vec<Error>) {
504    let map_names: HashMap<&str, &MapDef> = maps.iter().map(|m| (m.name.as_str(), m)).collect();
505
506    for instr in instructions {
507        if instr.format_lines.is_empty() {
508            continue;
509        }
510
511        let field_names: HashSet<String> = instr
512            .segments
513            .iter()
514            .filter_map(|seg| {
515                if let Segment::Field { name, .. } = seg {
516                    Some(name.clone())
517                } else {
518                    None
519                }
520            })
521            .collect();
522
523        // Check guard ordering: all non-last format lines must have guards
524        for (i, fl) in instr.format_lines.iter().enumerate() {
525            if i < instr.format_lines.len() - 1 && fl.guard.is_none() {
526                errors.push(Error::new(
527                    ErrorKind::UnguardedNonLastFormatLine {
528                        instruction: instr.name.clone(),
529                    },
530                    fl.span.clone(),
531                ));
532            }
533
534            // Check guard field references
535            if let Some(guard) = &fl.guard {
536                for cond in &guard.conditions {
537                    check_guard_operand_field(
538                        &cond.left,
539                        &field_names,
540                        &instr.name,
541                        &fl.span,
542                        errors,
543                    );
544                    check_guard_operand_field(
545                        &cond.right,
546                        &field_names,
547                        &instr.name,
548                        &fl.span,
549                        errors,
550                    );
551                }
552            }
553
554            // Check format string field references
555            for piece in &fl.pieces {
556                if let FormatPiece::FieldRef { expr, .. } = piece {
557                    check_format_expr_fields(
558                        expr,
559                        &field_names,
560                        &instr.name,
561                        &fl.span,
562                        &map_names,
563                        errors,
564                    );
565                }
566            }
567        }
568    }
569}
570
571fn check_guard_operand_field(
572    operand: &GuardOperand,
573    field_names: &HashSet<String>,
574    instr_name: &str,
575    span: &Span,
576    errors: &mut Vec<Error>,
577) {
578    match operand {
579        GuardOperand::Field(name) => {
580            if !field_names.contains(name.as_str()) {
581                errors.push(Error::new(
582                    ErrorKind::UndefinedFieldInGuard {
583                        instruction: instr_name.to_string(),
584                        field: name.clone(),
585                    },
586                    span.clone(),
587                ));
588            }
589        }
590        GuardOperand::Expr { left, right, .. } => {
591            check_guard_operand_field(left, field_names, instr_name, span, errors);
592            check_guard_operand_field(right, field_names, instr_name, span, errors);
593        }
594        GuardOperand::Literal(_) => {}
595    }
596}
597
598fn check_format_expr_fields(
599    expr: &FormatExpr,
600    field_names: &HashSet<String>,
601    instr_name: &str,
602    span: &Span,
603    maps: &HashMap<&str, &MapDef>,
604    errors: &mut Vec<Error>,
605) {
606    match expr {
607        FormatExpr::Field(name) => {
608            if !field_names.contains(name.as_str()) {
609                errors.push(Error::new(
610                    ErrorKind::UndefinedFieldInFormat {
611                        instruction: instr_name.to_string(),
612                        field: name.clone(),
613                    },
614                    span.clone(),
615                ));
616            }
617        }
618        FormatExpr::Ternary { field, .. } => {
619            if !field_names.contains(field.as_str()) {
620                errors.push(Error::new(
621                    ErrorKind::UndefinedFieldInFormat {
622                        instruction: instr_name.to_string(),
623                        field: field.clone(),
624                    },
625                    span.clone(),
626                ));
627            }
628        }
629        FormatExpr::Arithmetic { left, right, .. } => {
630            check_format_expr_fields(left, field_names, instr_name, span, maps, errors);
631            check_format_expr_fields(right, field_names, instr_name, span, maps, errors);
632        }
633        FormatExpr::IntLiteral(_) => {}
634        FormatExpr::MapCall { map_name, args, .. } => {
635            if let Some(map_def) = maps.get(map_name.as_str()) {
636                if args.len() != map_def.params.len() {
637                    errors.push(Error::new(
638                        ErrorKind::MapArgCountMismatch {
639                            map: map_name.clone(),
640                            expected: map_def.params.len(),
641                            got: args.len(),
642                        },
643                        span.clone(),
644                    ));
645                }
646            } else {
647                errors.push(Error::new(
648                    ErrorKind::UndefinedMap(map_name.clone()),
649                    span.clone(),
650                ));
651            }
652            for arg in args {
653                check_format_expr_fields(arg, field_names, instr_name, span, maps, errors);
654            }
655        }
656        FormatExpr::BuiltinCall { args, .. } => {
657            // Builtins are already validated during parsing
658            for arg in args {
659                check_format_expr_fields(arg, field_names, instr_name, span, maps, errors);
660            }
661        }
662        FormatExpr::SubDecoderAccess { field, .. } => {
663            if !field_names.contains(field.as_str()) {
664                errors.push(Error::new(
665                    ErrorKind::UndefinedFieldInFormat {
666                        instruction: instr_name.to_string(),
667                        field: field.clone(),
668                    },
669                    span.clone(),
670                ));
671            }
672            // Fragment name validation is deferred to after sub-decoders are validated
673        }
674    }
675}
676
677fn resolve_field_type(
678    field_type: &FieldType,
679    type_aliases: &[TypeAlias],
680    sub_decoder_names: &HashSet<String>,
681    sub_decoders: &[SubDecoderDef],
682) -> ResolvedFieldType {
683    match field_type {
684        FieldType::Alias(name) => {
685            // Check if it's a sub-decoder reference
686            if sub_decoder_names.contains(name) {
687                let sd = sub_decoders.iter().find(|sd| sd.name == *name).unwrap();
688                let base = match sd.width {
689                    w if w <= 8 => "u8",
690                    w if w <= 16 => "u16",
691                    _ => "u32",
692                };
693                return ResolvedFieldType {
694                    base_type: base.to_string(),
695                    alias_name: None,
696                    transforms: Vec::new(),
697                    display_format: None,
698                    sub_decoder: Some(name.clone()),
699                };
700            }
701            if let Some(alias) = type_aliases.iter().find(|ta| ta.name == *name) {
702                ResolvedFieldType {
703                    base_type: alias.base_type.clone(),
704                    alias_name: Some(name.clone()),
705                    transforms: alias.transforms.clone(),
706                    display_format: alias.display_format,
707                    sub_decoder: None,
708                }
709            } else {
710                // Built-in type used as alias
711                ResolvedFieldType {
712                    base_type: resolve_builtin(name),
713                    alias_name: None,
714                    transforms: Vec::new(),
715                    display_format: None,
716                    sub_decoder: None,
717                }
718            }
719        }
720        FieldType::Inline {
721            base_type,
722            transforms,
723        } => ResolvedFieldType {
724            base_type: resolve_builtin(base_type),
725            alias_name: None,
726            transforms: transforms.clone(),
727            display_format: None,
728            sub_decoder: None,
729        },
730    }
731}
732
733fn is_builtin_type(name: &str) -> bool {
734    matches!(
735        name,
736        "u1" | "u2"
737            | "u3"
738            | "u4"
739            | "u5"
740            | "u6"
741            | "u7"
742            | "u8"
743            | "u16"
744            | "u32"
745            | "i8"
746            | "i16"
747            | "i32"
748            | "bool"
749    )
750}
751
752fn resolve_builtin(name: &str) -> String {
753    match name {
754        "u1" | "u2" | "u3" | "u4" | "u5" | "u6" | "u7" => "u8".to_string(),
755        other => other.to_string(),
756    }
757}
758
759/// Validate all sub-decoders and return validated versions.
760fn validate_sub_decoders(
761    sub_decoders: &[SubDecoderDef],
762    errors: &mut Vec<Error>,
763) -> Vec<ValidatedSubDecoder> {
764    let mut validated = Vec::new();
765
766    for sd in sub_decoders {
767        // Convert bit ranges
768        let instructions: Vec<_> = sd
769            .instructions
770            .iter()
771            .map(|instr| {
772                let segments: Vec<Segment> = instr
773                    .segments
774                    .iter()
775                    .map(|seg| match seg {
776                        Segment::Fixed {
777                            ranges,
778                            pattern,
779                            span,
780                        } => {
781                            let hw_ranges = crate::parser::dsl_to_hardware(
782                                ranges[0].start,
783                                ranges[0].end,
784                                sd.width,
785                                sd.bit_order,
786                            );
787                            Segment::Fixed {
788                                ranges: hw_ranges,
789                                pattern: pattern.clone(),
790                                span: span.clone(),
791                            }
792                        }
793                        Segment::Field {
794                            name,
795                            field_type,
796                            ranges,
797                            span,
798                        } => {
799                            let hw_ranges = crate::parser::dsl_to_hardware(
800                                ranges[0].start,
801                                ranges[0].end,
802                                sd.width,
803                                sd.bit_order,
804                            );
805                            Segment::Field {
806                                name: name.clone(),
807                                field_type: field_type.clone(),
808                                ranges: hw_ranges,
809                                span: span.clone(),
810                            }
811                        }
812                    })
813                    .collect();
814                (instr, segments)
815            })
816            .collect();
817
818        // Check bit coverage for each sub-decoder instruction
819        for (instr, segments) in &instructions {
820            let as_instr = InstructionDef {
821                name: instr.name.clone(),
822                segments: segments.clone(),
823                format_lines: Vec::new(),
824                span: instr.span.clone(),
825            };
826            check_bit_coverage(&[as_instr], sd.width, errors);
827        }
828
829        // Validate fragment name consistency
830        let mut fragment_names: Option<Vec<String>> = None;
831        for instr in &sd.instructions {
832            let names: Vec<String> = instr.fragments.iter().map(|f| f.name.clone()).collect();
833            if let Some(ref expected) = fragment_names {
834                if &names != expected {
835                    errors.push(Error::new(
836                        ErrorKind::InconsistentFragmentNames {
837                            subdecoder: sd.name.clone(),
838                            instruction: instr.name.clone(),
839                            expected: expected.clone(),
840                            got: names,
841                        },
842                        instr.span.clone(),
843                    ));
844                }
845            } else {
846                fragment_names = Some(names);
847            }
848        }
849
850        let fragment_names = fragment_names.unwrap_or_default();
851
852        // Validate maps within sub-decoder
853        check_maps(&sd.maps, errors);
854
855        // Build validated sub-decoder instructions
856        let validated_instructions = instructions
857            .into_iter()
858            .map(|(instr, segments)| {
859                let resolved_fields = segments
860                    .iter()
861                    .filter_map(|seg| {
862                        if let Segment::Field {
863                            name,
864                            field_type,
865                            ranges,
866                            ..
867                        } = seg
868                        {
869                            let resolved =
870                                resolve_field_type(&field_type, &[], &HashSet::new(), &[]);
871                            Some(ResolvedField {
872                                name: name.clone(),
873                                ranges: ranges.clone(),
874                                resolved_type: resolved,
875                            })
876                        } else {
877                            None
878                        }
879                    })
880                    .collect();
881
882                ValidatedSubInstruction {
883                    name: instr.name.clone(),
884                    segments,
885                    resolved_fields,
886                    fragments: instr.fragments.clone(),
887                    span: instr.span.clone(),
888                }
889            })
890            .collect();
891
892        validated.push(ValidatedSubDecoder {
893            name: sd.name.clone(),
894            width: sd.width,
895            bit_order: sd.bit_order,
896            fragment_names,
897            maps: sd.maps.clone(),
898            instructions: validated_instructions,
899        });
900    }
901
902    validated
903}
904
905/// Check that sub-decoder field widths don't exceed the sub-decoder's declared width.
906fn check_sub_decoder_field_widths(
907    instructions: &[InstructionDef],
908    sub_decoders: &[SubDecoderDef],
909    sub_decoder_names: &HashSet<String>,
910    errors: &mut Vec<Error>,
911) {
912    for instr in instructions {
913        for seg in &instr.segments {
914            if let Segment::Field {
915                name,
916                field_type,
917                ranges,
918                span,
919            } = seg
920            {
921                if let FieldType::Alias(alias_name) = field_type {
922                    if sub_decoder_names.contains(alias_name) {
923                        let sd = sub_decoders
924                            .iter()
925                            .find(|sd| sd.name == *alias_name)
926                            .unwrap();
927                        let field_width: u32 = ranges.iter().map(|r| r.width()).sum();
928                        if field_width > sd.width {
929                            errors.push(Error::new(
930                                ErrorKind::SubDecoderFieldTooWide {
931                                    field: name.clone(),
932                                    field_width,
933                                    subdecoder: alias_name.clone(),
934                                    subdecoder_width: sd.width,
935                                },
936                                span.clone(),
937                            ));
938                        }
939                    }
940                }
941            }
942        }
943    }
944}