1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//! Type checking, conversions, initialization, constant evaluation, and the typed AST.
//!
//! Design: `spec/07-types-and-semantics.md`. Layer rank 7, see `spec/18-package-layout.md`.
//!
//! # Status
//!
//! The typed tree is here: the arenas, the nodes for every typed expression and statement, the
//! declarations with their linkage and storage duration, and the flattened initializers. So are
//! the two things the checking rests on, which are the [`Scopes`] a name is resolved against and
//! the [`Conv`] that writes the conversions the language performs without being asked.
//!
//! The [`Checker`] fills the tree in, and so far it fills in expressions: every operator of 6.5
//! except the ones that name a type. The [`Eval`] that folds a checked expression to a constant
//! is here too, over the arithmetic operators, which is what a case label, an enumerator, an
//! array bound and a bit-field width are each going to ask for. So is the type builder, which
//! turns a specifier list and a declarator into a [`TypeId`](rucc_types::TypeId): pointers,
//! arrays including the variable length ones, prototypes, tags referred to and declared, the
//! members of a `struct` or a `union` laid out with their bit-fields, the enumerators of an
//! `enum` with the C23 rules about what they are kept in, and everything a declarator is allowed
//! and not allowed to say about each. Address constants wait on declarations, and so do
//! declarations, statements and initialization, in that order.
//!
//! Every crate in the workspace is published, and publishing implies a promise. This one is
//! tier 3: its Rust API is explicitly unstable and will change without a major version bump.
//! Depend on the `rucc` binary's behaviour, not on this.
//!
//! # What a typed tree is for
//!
//! Every expression carries a [`TypeId`](rucc_types::TypeId), every conversion the language
//! performs without being asked is a [`Conversion`] node, every constant that can be folded has
//! been, and every use of a name points at the [`Decl`] it resolved to. Nothing downstream
//! derives any of that a second time. If the two operands of an addition in this tree do not
//! already have the same type then semantic analysis has a bug, and the verifier in
//! `spec/08-ir.md` is written to say so rather than to paper over it.
//!
//! That rule is worth stating as a cost, because it is one. A tree with explicit conversions is
//! larger than one without, and `(long)a + (long)b` is three nodes where the source has one
//! operator. What it buys is that the walk to the IR has no judgement left in it: it reads what
//! is there. Every compiler that leaves the conversions implicit ends up with two places that
//! know the conversion rules, and the second one is always slightly wrong.
//!
//! ```
//! use rucc_ast::BinaryOp;
//! use rucc_diag::Span;
//! use rucc_sema::{Category, Const, Expr, ExprKind, Tast};
//! use rucc_types::{IntKind, Types};
//!
//! let types = Types::new();
//! let int = types.int(IntKind::Int);
//! let mut tast = Tast::new();
//!
//! let one = tast.add_const(Const::Int(1));
//! let left = tast.expr(Expr::new(ExprKind::Const(one), int, Category::Rvalue), Span::DUMMY);
//! let right = tast.expr(Expr::new(ExprKind::Const(one), int, Category::Rvalue), Span::DUMMY);
//! let sum = ExprKind::Binary { op: BinaryOp::Add, lhs: left, rhs: right };
//! let sum = tast.expr(Expr::new(sum, int, Category::Rvalue), Span::DUMMY);
//!
//! assert_eq!(tast[sum].ty, int);
//! assert_eq!(tast.counts().exprs, 3);
//! ```
//!
//! # What is not in the tree
//!
//! A `typedef` is not, because it is a name for a type and the type table keeps it as sugar. An
//! enumerator is not, because it is a constant and the expressions that used it hold the value.
//! A tag is not, for the same reason. What is left is the objects and the functions, which are
//! what has to exist at run time and what the walk to the IR wants a list of.
pub use crate;
pub use crateConv;
pub use crate;
pub use crate;
pub use crate;
pub use crate;
pub use crate;
pub use crate;
pub use crate;
/// The milestone in `spec/17-milestones.md` that fills this crate in.
pub const MILESTONE: &str = "M2";