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