Skip to main content

rucc_sema/
print.rs

1//! The printer for the typed tree, which is what `--emit=tast` writes.
2//!
3//! Design: `spec/07-types-and-semantics.md` section 7.1.
4//!
5//! This one does not print C and does not try to. The tree it prints is not source any more:
6//! every conversion the language performs is a node of its own, so the shortest useful
7//! expression has more nodes than the program has operators, and writing that back as C would
8//! print exactly the text that hides what there is to see. What comes out instead is one node
9//! per line, indented by depth, with the type spelled out at every expression.
10//!
11//! The single most useful thing it does is make a conversion visible. When an IR bug turns out
12//! to be a sema bug, the question is almost always which conversion is missing or which one is
13//! the wrong one, and this is the artifact that answers it without a debugger.
14//!
15//! # Cross references
16//!
17//! A tree with jump tables in it is not a tree. A `switch` holds a table of cases whose bodies
18//! are statements inside its own body, and a `goto` names a label defined somewhere else
19//! entirely. Printing those by recursion would print the same statement twice, so they are
20//! printed as references instead, written `#n` after the word that says what kind of thing `n`
21//! counts: `case #3` is the fourth entry of the case table, `decl #3` the fourth declaration,
22//! `label #3` the fourth label. The numbers are arena indices, which is what makes a dump
23//! greppable: the definition and every use of one thing carry the same number.
24//!
25//! # Using it
26//!
27//! ```
28//! use rucc_base::Interner;
29//! use rucc_diag::Span;
30//! use rucc_sema::{Category, Const, Conversion, Expr, ExprKind, Printer, Tast};
31//! use rucc_types::{IntKind, Types};
32//!
33//! let types = Types::new();
34//! let names = Interner::new();
35//! let (char_type, int) = (types.int(IntKind::Char), types.int(IntKind::Int));
36//! let mut tast = Tast::new();
37//!
38//! let c = tast.add_const(Const::Int(97));
39//! let c = tast.expr(Expr::new(ExprKind::Const(c), char_type, Category::Rvalue), Span::DUMMY);
40//! let widened = ExprKind::Convert { kind: Conversion::Arithmetic, operand: c };
41//! let widened = tast.expr(Expr::new(widened, int, Category::Rvalue), Span::DUMMY);
42//!
43//! let mut printer = Printer::new(&tast, &types, &names);
44//! printer.expr(widened);
45//! assert_eq!(printer.finish(), "convert arithmetic : int\n  const 97 : char\n");
46//! ```
47
48use rucc_base::Interner;
49use rucc_types::{TypeKind, Types, spell};
50
51use crate::decl::{DeclId, DeclKind, Definition, Linkage, StorageDuration};
52use crate::expr::{Category, Expr, ExprId, ExprKind};
53use crate::stmt::{CaseId, Stmt, StmtId};
54use crate::tast::{Base, Const, LabelId, Tast};
55
56/// The whole typed translation unit, as text.
57#[must_use]
58pub fn print(tast: &Tast, types: &Types, names: &Interner) -> String {
59    let mut printer = Printer::new(tast, types, names);
60    printer.unit();
61    printer.finish()
62}
63
64/// A typed tree being written out.
65///
66/// The whole unit is [`print()`]. This is here for the caller that wants one subtree, which is
67/// what a test wants and what a diagnostic that quotes a node would want.
68#[derive(Debug)]
69pub struct Printer<'a> {
70    tast: &'a Tast,
71    types: &'a Types,
72    names: &'a Interner,
73    out: String,
74    depth: usize,
75}
76
77impl<'a> Printer<'a> {
78    /// A printer over one tree, whose types are in `types` and whose names are in `names`.
79    #[must_use]
80    pub fn new(tast: &'a Tast, types: &'a Types, names: &'a Interner) -> Printer<'a> {
81        Printer { tast, types, names, out: String::new(), depth: 0 }
82    }
83
84    /// The text written so far.
85    #[must_use]
86    pub fn finish(self) -> String {
87        self.out
88    }
89
90    /// Every declaration of the translation unit, in the order they were declared.
91    pub fn unit(&mut self) {
92        for &id in self.tast.top_level() {
93            self.decl(id);
94        }
95    }
96
97    /// One declaration, and its initializer or its body.
98    pub fn decl(&mut self, id: DeclId) {
99        let node = &self.tast[id];
100        let mut head = format!("decl #{}", id.index());
101        if let Some(name) = node.name {
102            head.push(' ');
103            head.push_str(self.names.resolve(name));
104        }
105        head.push_str(" : ");
106        head.push_str(&spell(self.types, self.names, node.ty));
107        head.push_str(match node.kind {
108            DeclKind::Object => " object",
109            DeclKind::Function => " function",
110        });
111        head.push_str(match node.linkage {
112            Linkage::None => "",
113            Linkage::Internal => " internal",
114            Linkage::External => " external",
115        });
116        if node.kind == DeclKind::Object {
117            head.push_str(match node.duration {
118                StorageDuration::Static => " static",
119                StorageDuration::Thread => " thread",
120                StorageDuration::Automatic => " automatic",
121            });
122        }
123        head.push_str(match node.state {
124            Definition::Declared => " declared",
125            Definition::Tentative => " tentative",
126            Definition::Defined => " defined",
127        });
128        if let Some(align) = node.alignment {
129            head.push_str(&format!(" alignas {align}"));
130        }
131        self.line(&head);
132
133        // An initializer that is present and empty is `= {}`, which zero-initializes and is not
134        // the same as no initializer at all, so the word is written whether there is anything
135        // under it or not.
136        if let Some(list) = node.init {
137            self.depth += 1;
138            self.line("init");
139            self.depth += 1;
140            // Copied out because printing a value takes `&mut self`, so the borrow of the
141            // table cannot be held across the walk. The same is true of every run below.
142            let entries = self.tast[list].to_vec();
143            for entry in entries {
144                let mut at = format!("+{}", entry.offset);
145                if entry.is_bit_field() {
146                    at.push_str(&format!(" bit {} width {}", entry.bit_offset, entry.bit_width));
147                }
148                self.line(&at);
149                self.depth += 1;
150                self.expr(entry.value);
151                self.depth -= 1;
152            }
153            self.depth -= 2;
154        }
155        // Before the body, because the body refers to them and a reader who meets `decl #1` in
156        // an expression should have been told what it is first.
157        let params = self.tast[id].params;
158        if !params.is_empty() {
159            self.depth += 1;
160            self.line("params");
161            self.depth += 1;
162            let params = self.tast[params].to_vec();
163            for param in params {
164                self.decl(param);
165            }
166            self.depth -= 2;
167        }
168        if let Some(body) = self.tast[id].body {
169            self.depth += 1;
170            self.line("body");
171            self.depth += 1;
172            self.stmt(body);
173            self.depth -= 2;
174        }
175    }
176
177    /// One statement and everything under it.
178    pub fn stmt(&mut self, id: StmtId) {
179        match self.tast[id] {
180            Stmt::Error => self.line("error"),
181            Stmt::Empty => self.line("empty"),
182            Stmt::Expr(value) => {
183                self.line("expr");
184                self.under(|p| p.expr(value));
185            }
186            Stmt::Block(body) => {
187                self.line("block");
188                self.depth += 1;
189                let body = self.tast[body].to_vec();
190                for stmt in body {
191                    self.stmt(stmt);
192                }
193                self.depth -= 1;
194            }
195            Stmt::Decls(decls) => {
196                self.line("decls");
197                self.depth += 1;
198                let decls = self.tast[decls].to_vec();
199                for decl in decls {
200                    self.decl(decl);
201                }
202                self.depth -= 1;
203            }
204            Stmt::If { cond, then, otherwise } => {
205                self.line("if");
206                self.depth += 1;
207                self.group("cond", |p| p.expr(cond));
208                self.group("then", |p| p.stmt(then));
209                if let Some(otherwise) = otherwise {
210                    self.group("else", |p| p.stmt(otherwise));
211                }
212                self.depth -= 1;
213            }
214            Stmt::While { cond, body } => {
215                self.line("while");
216                self.depth += 1;
217                self.group("cond", |p| p.expr(cond));
218                self.group("body", |p| p.stmt(body));
219                self.depth -= 1;
220            }
221            Stmt::DoWhile { body, cond } => {
222                self.line("do-while");
223                self.depth += 1;
224                self.group("body", |p| p.stmt(body));
225                self.group("cond", |p| p.expr(cond));
226                self.depth -= 1;
227            }
228            Stmt::For { init, cond, step, body } => {
229                self.line("for");
230                self.depth += 1;
231                if let Some(init) = init {
232                    self.group("init", |p| p.stmt(init));
233                }
234                if let Some(cond) = cond {
235                    self.group("cond", |p| p.expr(cond));
236                }
237                if let Some(step) = step {
238                    self.group("step", |p| p.expr(step));
239                }
240                self.group("body", |p| p.stmt(body));
241                self.depth -= 1;
242            }
243            Stmt::Switch { cond, body, cases, default } => {
244                self.line("switch");
245                self.depth += 1;
246                self.group("cond", |p| p.expr(cond));
247                self.line("cases");
248                self.depth += 1;
249                for index in cases.iter() {
250                    self.case(index);
251                }
252                if default.is_some() {
253                    self.line("default");
254                }
255                self.depth -= 1;
256                self.group("body", |p| p.stmt(body));
257                self.depth -= 1;
258            }
259            // The value is in the table under the `switch` and is not repeated here, so that
260            // the jump table has one home and a case in the body is a reference into it.
261            Stmt::Case { case, body } => {
262                self.line(&format!("case #{}", case.index()));
263                self.under(|p| p.stmt(body));
264            }
265            Stmt::Default { body } => {
266                self.line("default");
267                self.under(|p| p.stmt(body));
268            }
269            Stmt::Label { label, body } => {
270                let head = self.label(label);
271                self.line(&format!("label {head}"));
272                self.under(|p| p.stmt(body));
273            }
274            Stmt::Goto(label) => {
275                let target = self.label(label);
276                self.line(&format!("goto {target}"));
277            }
278            Stmt::IndirectGoto(target) => {
279                self.line("indirect-goto");
280                self.under(|p| p.expr(target));
281            }
282            Stmt::Break => self.line("break"),
283            Stmt::Continue => self.line("continue"),
284            Stmt::Return(None) => self.line("return"),
285            Stmt::Return(Some(value)) => {
286                self.line("return");
287                self.under(|p| p.expr(value));
288            }
289        }
290    }
291
292    /// One expression, its type, and everything under it.
293    pub fn expr(&mut self, id: ExprId) {
294        let node = self.tast[id];
295        let head = self.head(node);
296        let ty = spell(self.types, self.names, node.ty);
297        let category = match node.category {
298            Category::Rvalue => "",
299            Category::Lvalue => " lvalue",
300            Category::Bitfield => " bit-field",
301            Category::Function => " function",
302        };
303        self.line(&format!("{head} : {ty}{category}"));
304        self.depth += 1;
305        self.operands(node.kind);
306        self.depth -= 1;
307    }
308
309    /// What an expression is, without its type or its operands.
310    fn head(&self, node: Expr) -> String {
311        match node.kind {
312            ExprKind::Error => "error".to_owned(),
313            ExprKind::Const(value) => match self.tast[value] {
314                // Hexadecimal for the same reason the C printer uses it: a decimal spelling
315                // that reads back unchanged needs a shortest round trip algorithm, and one
316                // without such an algorithm quietly prints a different number.
317                Const::Int(value) => format!("const {value}"),
318                Const::Float(value) => format!("const {}", value.to_hex()),
319                Const::Address(address) => {
320                    let base = match address.base {
321                        Base::Decl(decl) => format!("decl #{}", decl.index()),
322                        Base::Str(id) => format!("string {}", self.tast[id].spell()),
323                    };
324                    format!("const address {base} + {}", address.offset)
325                }
326            },
327            ExprKind::Str(value) => format!("string {}", self.tast[value].spell()),
328            ExprKind::Decl(decl) => {
329                let mut head = format!("decl #{}", decl.index());
330                if let Some(name) = self.tast[decl].name {
331                    head.push(' ');
332                    head.push_str(self.names.resolve(name));
333                }
334                head
335            }
336            ExprKind::Member { base, field } => {
337                let mut head = format!("member #{field}");
338                if let Some(name) = self.field_name(base, field) {
339                    head.push(' ');
340                    head.push_str(name);
341                }
342                head
343            }
344            ExprKind::Subscript { .. } => "subscript".to_owned(),
345            ExprKind::Call { .. } => "call".to_owned(),
346            ExprKind::Unary { op, .. } if op.is_postfix() => {
347                format!("unary post {}", op.spelling())
348            }
349            ExprKind::Unary { op, .. } => format!("unary {}", op.spelling()),
350            ExprKind::Binary { op, .. } => format!("binary {}", op.spelling()),
351            // The computation type is written only when it is not the type of the assignment
352            // itself, which is the case that is worth seeing: `i /= 0.5` divides in `double`.
353            ExprKind::Assign { op, computation, .. } => {
354                let mut head = match op {
355                    None => "assign =".to_owned(),
356                    Some(op) => format!("assign {}=", op.spelling()),
357                };
358                if computation != node.ty {
359                    let ty = spell(self.types, self.names, computation);
360                    head.push_str(&format!(" in {ty}"));
361                }
362                head
363            }
364            ExprKind::Cond { .. } => "cond".to_owned(),
365            ExprKind::Comma { .. } => "comma".to_owned(),
366            ExprKind::Cast(_) => "cast".to_owned(),
367            ExprKind::Convert { kind, .. } => format!("convert {}", kind.as_str()),
368            ExprKind::CompoundLiteral(decl) => format!("compound-literal #{}", decl.index()),
369            ExprKind::StmtExpr(_) => "stmt-expr".to_owned(),
370            ExprKind::LabelAddr(label) => format!("label-addr {}", self.label(label)),
371            ExprKind::VaArg { .. } => "va-arg".to_owned(),
372        }
373    }
374
375    /// Whatever hangs under an expression, already indented by the caller.
376    fn operands(&mut self, kind: ExprKind) {
377        match kind {
378            ExprKind::Error
379            | ExprKind::Const(_)
380            | ExprKind::Str(_)
381            | ExprKind::Decl(_)
382            | ExprKind::LabelAddr(_) => {}
383            // A compound literal is a declaration of its own, printed where it is used, since
384            // it has no other place in the tree to be printed from.
385            ExprKind::CompoundLiteral(decl) => self.decl(decl),
386            ExprKind::StmtExpr(body) => self.stmt(body),
387            ExprKind::Member { base, .. }
388            | ExprKind::Cast(base)
389            | ExprKind::VaArg { list: base }
390            | ExprKind::Convert { operand: base, .. }
391            | ExprKind::Unary { operand: base, .. } => self.expr(base),
392            ExprKind::Subscript { base: lhs, index: rhs }
393            | ExprKind::Binary { lhs, rhs, .. }
394            | ExprKind::Assign { lhs, rhs, .. }
395            | ExprKind::Comma { lhs, rhs } => {
396                self.expr(lhs);
397                self.expr(rhs);
398            }
399            ExprKind::Call { callee, args } => {
400                self.expr(callee);
401                let args = self.tast[args].to_vec();
402                for arg in args {
403                    self.expr(arg);
404                }
405            }
406            ExprKind::Cond { cond, then, otherwise } => {
407                self.expr(cond);
408                self.expr(then);
409                self.expr(otherwise);
410            }
411        }
412    }
413
414    /// One entry of a case table, which is a value or a range of them.
415    fn case(&mut self, id: CaseId) {
416        let case = self.tast[id];
417        let head = if case.low == case.high {
418            format!("case #{} {}", id.index(), case.low)
419        } else {
420            format!("case #{} {} ... {}", id.index(), case.low, case.high)
421        };
422        self.line(&head);
423    }
424
425    /// A label, as its index and its name.
426    fn label(&self, id: LabelId) -> String {
427        format!("#{} {}", id.index(), self.names.resolve(self.tast[id].name))
428    }
429
430    /// The name of the member at an index, where the base is a record that has one there.
431    ///
432    /// It is a convenience and not a fact the tree depends on. The index is what the node
433    /// holds, an anonymous member has no name to print, and a member of an incomplete record
434    /// cannot happen but is not worth panicking over in a printer.
435    fn field_name(&self, base: ExprId, field: u32) -> Option<&'a str> {
436        let ty = self.types.canonical(self.tast[base].ty);
437        let TypeKind::Record(record) = self.types.kind(ty) else { return None };
438        let field = self.types.record_info(record).fields.get(field as usize)?;
439        Some(self.names.resolve(field.name?))
440    }
441
442    /// Writes a named group and puts what the closure writes one level under it.
443    fn group(&mut self, name: &str, write: impl FnOnce(&mut Printer<'a>)) {
444        self.line(name);
445        self.under(write);
446    }
447
448    /// Writes what the closure writes one level in.
449    fn under(&mut self, write: impl FnOnce(&mut Printer<'a>)) {
450        self.depth += 1;
451        write(self);
452        self.depth -= 1;
453    }
454
455    /// Writes one line at the current depth.
456    fn line(&mut self, text: &str) {
457        for _ in 0..self.depth {
458            self.out.push_str("  ");
459        }
460        self.out.push_str(text);
461        self.out.push('\n');
462    }
463}
464
465#[cfg(test)]
466mod tests {
467    use rucc_ast::{BinaryOp, UnaryOp};
468    use rucc_diag::Span;
469    use rucc_types::{ArrayLen, IntKind};
470
471    use super::*;
472    use crate::decl::{Decl, DeclList, InitEntry};
473    use crate::expr::{Conversion, Expr};
474    use crate::stmt::Case;
475    use crate::tast::Label;
476
477    struct Fixture {
478        tast: Tast,
479        types: Types,
480        names: Interner,
481    }
482
483    impl Fixture {
484        fn new() -> Fixture {
485            Fixture { tast: Tast::new(), types: Types::new(), names: Interner::new() }
486        }
487
488        fn int(&self) -> rucc_types::TypeId {
489            self.types.int(IntKind::Int)
490        }
491
492        /// An rvalue of the given type and kind, which is most of what a test needs.
493        fn value(&mut self, kind: ExprKind, ty: rucc_types::TypeId) -> ExprId {
494            self.tast.expr(Expr::new(kind, ty, Category::Rvalue), Span::DUMMY)
495        }
496
497        fn constant(&mut self, value: i128, ty: rucc_types::TypeId) -> ExprId {
498            let id = self.tast.add_const(Const::Int(value));
499            self.value(ExprKind::Const(id), ty)
500        }
501
502        fn text(&self, write: impl FnOnce(&mut Printer<'_>)) -> String {
503            let mut printer = Printer::new(&self.tast, &self.types, &self.names);
504            write(&mut printer);
505            printer.finish()
506        }
507    }
508
509    #[test]
510    fn an_expression_carries_its_type_on_every_line() {
511        let mut f = Fixture::new();
512        let int = f.int();
513        let left = f.constant(1, int);
514        let right = f.constant(2, int);
515        let sum = f.value(ExprKind::Binary { op: BinaryOp::Add, lhs: left, rhs: right }, int);
516
517        assert_eq!(f.text(|p| p.expr(sum)), "binary + : int\n  const 1 : int\n  const 2 : int\n");
518    }
519
520    #[test]
521    fn a_conversion_is_what_the_dump_is_for() {
522        let mut f = Fixture::new();
523        let (char_type, long) = (f.types.int(IntKind::Char), f.types.int(IntKind::Long));
524        let object = f.tast.decl(object_decl(char_type), Span::DUMMY);
525        let name = f
526            .tast
527            .expr(Expr::new(ExprKind::Decl(object), char_type, Category::Lvalue), Span::DUMMY);
528        let read =
529            f.value(ExprKind::Convert { kind: Conversion::Lvalue, operand: name }, char_type);
530        let widened =
531            f.value(ExprKind::Convert { kind: Conversion::Arithmetic, operand: read }, long);
532
533        // The two steps that got a `char` to a `long` are each a line, which is the whole
534        // reason this printer exists rather than one that writes the C back.
535        assert_eq!(
536            f.text(|p| p.expr(widened)),
537            "convert arithmetic : long\n  convert lvalue : char\n    decl #0 : char lvalue\n"
538        );
539    }
540
541    #[test]
542    fn a_category_is_written_and_an_rvalue_is_the_silent_one() {
543        let mut f = Fixture::new();
544        let int = f.int();
545        let object = f.tast.decl(object_decl(int), Span::DUMMY);
546        let name =
547            f.tast.expr(Expr::new(ExprKind::Decl(object), int, Category::Lvalue), Span::DUMMY);
548        let bits =
549            f.tast.expr(Expr::new(ExprKind::Decl(object), int, Category::Bitfield), Span::DUMMY);
550
551        assert_eq!(f.text(|p| p.expr(name)), "decl #0 : int lvalue\n");
552        assert_eq!(f.text(|p| p.expr(bits)), "decl #0 : int bit-field\n");
553    }
554
555    #[test]
556    fn a_postfix_operator_is_not_printed_as_the_prefix_one() {
557        let mut f = Fixture::new();
558        let int = f.int();
559        let one = f.constant(1, int);
560        let post = f.value(ExprKind::Unary { op: UnaryOp::PostInc, operand: one }, int);
561        let pre = f.value(ExprKind::Unary { op: UnaryOp::PreInc, operand: one }, int);
562
563        assert!(f.text(|p| p.expr(post)).starts_with("unary post ++"));
564        assert!(f.text(|p| p.expr(pre)).starts_with("unary ++ :"));
565    }
566
567    #[test]
568    fn a_compound_assignment_keeps_its_operator() {
569        let mut f = Fixture::new();
570        let int = f.int();
571        let one = f.constant(1, int);
572        let plain =
573            f.value(ExprKind::Assign { op: None, computation: int, lhs: one, rhs: one }, int);
574        let shl =
575            ExprKind::Assign { op: Some(BinaryOp::Shl), computation: int, lhs: one, rhs: one };
576        let compound = f.value(shl, int);
577
578        assert!(f.text(|p| p.expr(plain)).starts_with("assign = :"));
579        assert!(f.text(|p| p.expr(compound)).starts_with("assign <<= :"));
580    }
581
582    #[test]
583    fn a_case_is_a_reference_into_the_table_and_not_a_second_copy_of_it() {
584        let mut f = Fixture::new();
585        let int = f.int();
586        let cond = f.constant(0, int);
587        let empty = f.tast.stmt(Stmt::Empty, Span::DUMMY);
588        let cases = f.tast.add_cases(&[
589            Case { low: 1, high: 1, body: empty },
590            Case { low: 2, high: 9, body: empty },
591        ]);
592        let first = f.tast.stmt(
593            Stmt::Case { case: cases.iter().next().expect("a case"), body: empty },
594            Span::DUMMY,
595        );
596        let fallback = f.tast.stmt(Stmt::Default { body: empty }, Span::DUMMY);
597        let body = f.tast.add_stmt_refs(&[first, fallback]);
598        let body = f.tast.stmt(Stmt::Block(body), Span::DUMMY);
599        let switch =
600            f.tast.stmt(Stmt::Switch { cond, body, cases, default: Some(empty) }, Span::DUMMY);
601
602        assert_eq!(
603            f.text(|p| p.stmt(switch)),
604            "\
605switch
606  cond
607    const 0 : int
608  cases
609    case #0 1
610    case #1 2 ... 9
611    default
612  body
613    block
614      case #0
615        empty
616      default
617        empty
618"
619        );
620    }
621
622    #[test]
623    fn a_label_and_the_goto_that_reaches_it_carry_the_same_number() {
624        let mut f = Fixture::new();
625        let name = f.names.intern("done");
626        let label = f.tast.add_label(Label { name, stmt: None });
627        let empty = f.tast.stmt(Stmt::Empty, Span::DUMMY);
628        let target = f.tast.stmt(Stmt::Label { label, body: empty }, Span::DUMMY);
629        let jump = f.tast.stmt(Stmt::Goto(label), Span::DUMMY);
630        f.tast.define_label(label, target);
631
632        assert_eq!(f.text(|p| p.stmt(target)), "label #0 done\n  empty\n");
633        assert_eq!(f.text(|p| p.stmt(jump)), "goto #0 done\n");
634    }
635
636    #[test]
637    fn a_declaration_says_what_it_is_and_an_empty_initializer_is_still_one() {
638        let mut f = Fixture::new();
639        let int = f.int();
640        let array = f.types.array(int, ArrayLen::Fixed(2));
641        let mut decl = object_decl(array);
642        decl.name = Some(f.names.intern("a"));
643        decl.linkage = Linkage::Internal;
644        decl.duration = StorageDuration::Static;
645        decl.alignment = Some(16);
646        decl.init = Some(f.tast.add_init_entries(&[]));
647        let id = f.tast.decl(decl, Span::DUMMY);
648
649        assert_eq!(
650            f.text(|p| p.decl(id)),
651            "decl #0 a : int [2] object internal static defined alignas 16\n  init\n"
652        );
653    }
654
655    #[test]
656    fn an_initializer_prints_where_each_value_goes() {
657        let mut f = Fixture::new();
658        let int = f.int();
659        let array = f.types.array(int, ArrayLen::Fixed(2));
660        let one = f.constant(1, int);
661        let entries = f.tast.add_init_entries(&[
662            InitEntry::at(0, one),
663            InitEntry { offset: 4, value: one, bit_offset: 3, bit_width: 5 },
664        ]);
665        let mut decl = object_decl(array);
666        decl.init = Some(entries);
667        let id = f.tast.decl(decl, Span::DUMMY);
668
669        assert_eq!(
670            f.text(|p| p.decl(id)),
671            "\
672decl #0 : int [2] object automatic defined
673  init
674    +0
675      const 1 : int
676    +4 bit 3 width 5
677      const 1 : int
678"
679        );
680    }
681
682    #[test]
683    fn a_unit_is_its_declarations_in_order() {
684        let mut f = Fixture::new();
685        let int = f.int();
686        let first = f.tast.decl(object_decl(int), Span::DUMMY);
687        let second = f.tast.decl(object_decl(int), Span::DUMMY);
688        f.tast.add_top_level(first);
689        f.tast.add_top_level(second);
690
691        assert_eq!(
692            print(&f.tast, &f.types, &f.names),
693            "decl #0 : int object automatic defined\ndecl #1 : int object automatic defined\n"
694        );
695    }
696
697    fn object_decl(ty: rucc_types::TypeId) -> Decl {
698        Decl {
699            name: None,
700            ty,
701            kind: DeclKind::Object,
702            linkage: Linkage::None,
703            duration: StorageDuration::Automatic,
704            state: Definition::Defined,
705            alignment: None,
706            init: None,
707            params: DeclList::EMPTY,
708            body: None,
709        }
710    }
711}