rucc_sema/lib.rs
1//! Type checking, conversions, initialization, constant evaluation, and the typed AST.
2//!
3//! Design: `spec/07-types-and-semantics.md`. Layer rank 7, see `spec/18-package-layout.md`.
4//!
5//! # Status
6//!
7//! The typed tree is here: the arenas, the nodes for every typed expression and statement, the
8//! declarations with their linkage and storage duration, and the flattened initializers. So are
9//! the two things the checking rests on, which are the [`Scopes`] a name is resolved against and
10//! the [`Conv`] that writes the conversions the language performs without being asked.
11//!
12//! The [`Checker`] fills the tree in. Expressions are done, which is every operator of 6.5: the
13//! ones that name a type, being the cast, `sizeof`, `alignof`, `offsetof`, `_Generic`, `va_arg`
14//! and the two `__builtin` forms that take a type name; the compound literal and GNU's cast to a
15//! union, which build an object rather than producing a value; and GNU's statement expression and
16//! label address. Declarations are done as well: what kind of thing a name is, who else
17//! can see it, how long it lives, how much of a definition it is, and what a second declaration of
18//! the same name does to the first. Statements are done, and with them the function definition and
19//! the walk over a whole translation unit: a body is one scope with its parameters, the labels are
20//! resolved over the function rather than in order, each `switch` collects its cases into one
21//! table, and `break`, `continue` and `return` are checked against what encloses them. What waits
22//! on a control flow graph is reachability, which is where `control reaches end of non-void
23//! function` lives.
24//!
25//! The [`Eval`] that folds a checked expression to a constant is here too, over the arithmetic
26//! operators and over the addresses, so `&x`, `&s.field + 3` and a string literal each fold to
27//! the object and the offset that a static initializer needs and an object file relocates. That
28//! is what a case label, an enumerator, an array bound, a bit-field width and the initializer of
29//! an object that exists before the program runs are each going to ask for. So is the type builder, which turns a specifier list and a declarator
30//! into a [`TypeId`](rucc_types::TypeId): pointers, arrays including the variable length ones,
31//! prototypes, tags referred to and declared, the members of a `struct` or a `union` laid out
32//! with their bit-fields, the enumerators of an `enum` with the C23 rules about what they are
33//! kept in, and everything a declarator is allowed and not allowed to say about each.
34//!
35//! Initialization is here, which is the walk that turns an initializer into the list of what
36//! goes at which offset: brace elision, designation including the GNU forms, a string literal
37//! filling a character array, an array taking its length from what was written into it, and the
38//! bit-fields and flexible array members that make an offset more than a number, and each
39//! element of an object with static storage duration is required to be a constant expression,
40//! which for a pointer means an address and for a `constexpr` object means a number. The unnamed
41//! object a compound literal builds is here as well, and it lives as long as the block it was
42//! written in or as long as the program where it was written outside one.
43//!
44//! Every crate in the workspace is published, and publishing implies a promise. This one is
45//! tier 3: its Rust API is explicitly unstable and will change without a major version bump.
46//! Depend on the `rucc` binary's behaviour, not on this.
47//!
48//! # What a typed tree is for
49//!
50//! Every expression carries a [`TypeId`](rucc_types::TypeId), every conversion the language
51//! performs without being asked is a [`Conversion`] node, every constant that can be folded has
52//! been, and every use of a name points at the [`Decl`] it resolved to. Nothing downstream
53//! derives any of that a second time. If the two operands of an addition in this tree do not
54//! already have the same type then semantic analysis has a bug, and the verifier in
55//! `spec/08-ir.md` is written to say so rather than to paper over it.
56//!
57//! That rule is worth stating as a cost, because it is one. A tree with explicit conversions is
58//! larger than one without, and `(long)a + (long)b` is three nodes where the source has one
59//! operator. What it buys is that the walk to the IR has no judgement left in it: it reads what
60//! is there. Every compiler that leaves the conversions implicit ends up with two places that
61//! know the conversion rules, and the second one is always slightly wrong.
62//!
63//! ```
64//! use rucc_ast::BinaryOp;
65//! use rucc_diag::Span;
66//! use rucc_sema::{Category, Const, Expr, ExprKind, Tast};
67//! use rucc_types::{IntKind, Types};
68//!
69//! let types = Types::new();
70//! let int = types.int(IntKind::Int);
71//! let mut tast = Tast::new();
72//!
73//! let one = tast.add_const(Const::Int(1));
74//! let left = tast.expr(Expr::new(ExprKind::Const(one), int, Category::Rvalue), Span::DUMMY);
75//! let right = tast.expr(Expr::new(ExprKind::Const(one), int, Category::Rvalue), Span::DUMMY);
76//! let sum = ExprKind::Binary { op: BinaryOp::Add, lhs: left, rhs: right };
77//! let sum = tast.expr(Expr::new(sum, int, Category::Rvalue), Span::DUMMY);
78//!
79//! assert_eq!(tast[sum].ty, int);
80//! assert_eq!(tast.counts().exprs, 3);
81//! ```
82//!
83//! # What is not in the tree
84//!
85//! A `typedef` is not, because it is a name for a type and the type table keeps it as sugar. An
86//! enumerator is not, because it is a constant and the expressions that used it hold the value.
87//! A tag is not, for the same reason. What is left is the objects and the functions, which are
88//! what has to exist at run time and what the walk to the IR wants a list of.
89
90#![doc(html_root_url = "https://docs.rs/rucc-sema/0.3.3")]
91
92mod asm;
93mod check;
94mod convert;
95mod decl;
96mod eval;
97mod expr;
98mod print;
99mod scope;
100mod stmt;
101mod tast;
102
103pub use crate::asm::{
104 Asm, AsmId, AsmOperand, AsmOperandList, LabelList, LabelRef, StrList, StrRef,
105};
106pub use crate::check::{Checked, Checker, Context, library_name};
107pub use crate::convert::Conv;
108pub use crate::decl::{
109 Decl, DeclId, DeclKind, DeclList, DeclRef, Definition, InitEntry, InitList, Linkage,
110 StorageDuration,
111};
112pub use crate::eval::{Eval, NotConstant};
113pub use crate::expr::{
114 Category, Classify, Conversion, Expr, ExprId, ExprKind, ExprList, ExprRef, Sign,
115};
116pub use crate::print::{Printer, print};
117pub use crate::scope::{Binding, Scopes, Tag, TagKind};
118pub use crate::stmt::{Case, CaseId, CaseList, Stmt, StmtId, StmtList, StmtRef};
119pub use crate::tast::{Address, Base, Const, ConstId, Counts, Label, LabelId, StrId, Tast};
120
121/// The milestone in `spec/17-milestones.md` that fills this crate in.
122pub const MILESTONE: &str = "M2";
123
124#[cfg(test)]
125mod tests {
126 #[test]
127 fn milestone_is_recorded() {
128 assert!(super::MILESTONE.starts_with('M'));
129 }
130}