rucc_lower/unit.rs
1//! The module level of the walk: what a translation unit's declarations become.
2//!
3//! Design: `spec/08-ir.md` section 8.9.
4//!
5//! One typed tree becomes one [`Module`]. A file-scope object becomes a global with an image
6//! built from its initializer, a function becomes a [`Func`] whose body is built by
7//! [`body`](mod@crate::body), and a string literal becomes an unnamed constant global that
8//! whatever mentioned it points at.
9//!
10//! # What an image is
11//!
12//! An initializer arrives here already flattened: one entry per scalar that is stored, each
13//! with the byte offset it goes at, with every designator and every nested brace already
14//! resolved. So building the image is a walk over the entries in offset order, filling the gaps
15//! between them with zeros, and the only thing that has to be worked out per entry is whether
16//! the value is a number, a run of bytes from a string literal, or the address of something the
17//! linker has to place.
18//!
19//! # Names
20//!
21//! An object with linkage is known by the name it was written with, and there is nothing to
22//! invent. A `static` inside a function has no linkage and still needs a name in the object
23//! file, so it gets `name.N`, which is what gcc does and is why two functions may each have a
24//! `static int count;` without colliding. A string literal has no name at all and gets
25//! `.Lstr.N`, whose leading dot keeps it out of the symbol table on every target that has the
26//! convention.
27
28use std::cmp::Ordering;
29use std::collections::{BTreeMap, HashMap, HashSet};
30
31use rucc_base::{Interner, Symbol};
32use rucc_diag::{Diagnostic, Span};
33use rucc_ir::{
34 DataList, Datum, Func, Global, Imm, Linkage as IrLinkage, Module, Reloc, TlsModel, Type,
35};
36use rucc_sema::{
37 Base, Const, Conversion, DeclId, DeclKind, Definition, Eval, ExprId, ExprKind, InitEntry,
38 InitList, Linkage, StorageDuration, StrId, Tast,
39};
40use rucc_target::TargetInfo;
41use rucc_types::{TypeId, TypeKind, Types, compatible};
42
43use crate::abi::{self, Plan};
44use crate::body;
45use crate::repr;
46
47/// Everything the walk reads, which is a checked translation unit and the target it is for.
48///
49/// The interner is mutable because the walk invents names the program never wrote: the label a
50/// string literal is emitted under, and the mangled name of a function-scope `static`.
51#[derive(Debug)]
52pub struct Context<'a> {
53 /// The typed tree.
54 pub tast: &'a Tast,
55 /// The types it points into.
56 pub types: &'a Types,
57 /// What is being compiled for, which is where every width and every alignment comes from.
58 pub target: &'a TargetInfo,
59 /// The name table.
60 pub names: &'a mut Interner,
61}
62
63/// What the walk produced.
64#[derive(Debug)]
65pub struct Lowered {
66 /// The module, which is complete even when something was reported: a construct that is not
67 /// supported yet leaves the rest of the function around it intact.
68 pub module: Module,
69 /// What was reported, in the order it was found.
70 pub diagnostics: Vec<Diagnostic>,
71}
72
73/// Walks a checked translation unit and builds the IR for it.
74///
75/// `name` is the module's name, which is the file the tree came from.
76#[must_use]
77pub fn lower(name: &str, cx: Context<'_>) -> Lowered {
78 let Context { tast, types, target, names } = cx;
79 let module = Module::new(names.intern(name), target);
80 let mut unit = Unit {
81 tast,
82 types,
83 target,
84 names,
85 module,
86 diagnostics: Vec::new(),
87 strings: HashMap::new(),
88 statics: HashMap::new(),
89 done: HashSet::new(),
90 };
91 unit.run();
92 Lowered { module: unit.module, diagnostics: unit.diagnostics }
93}
94
95/// The walk over one translation unit, and everything it has built so far.
96pub(crate) struct Unit<'a> {
97 pub(crate) tast: &'a Tast,
98 pub(crate) types: &'a Types,
99 pub(crate) target: &'a TargetInfo,
100 pub(crate) names: &'a mut Interner,
101 pub(crate) module: Module,
102 pub(crate) diagnostics: Vec<Diagnostic>,
103 /// The global each string literal was emitted as, so that two mentions of one literal are
104 /// one object.
105 strings: HashMap<StrId, Symbol>,
106 /// The name each object with no linkage was given.
107 statics: HashMap<DeclId, Symbol>,
108 /// What has been emitted, because a redeclaration is the same declaration seen twice.
109 done: HashSet<DeclId>,
110}
111
112// The debug is by hand and short: a translation unit is not something anybody wants printed as
113// a `{:?}`, and the module has a printer of its own for when they do.
114impl std::fmt::Debug for Unit<'_> {
115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116 f.debug_struct("Unit")
117 .field("module", &self.module.counts())
118 .field("diagnostics", &self.diagnostics.len())
119 .finish()
120 }
121}
122
123impl Unit<'_> {
124 /// Every declaration the file made, in the order it made them.
125 fn run(&mut self) {
126 for index in 0..self.tast.top_level().len() {
127 let decl = self.tast.top_level()[index];
128 if !self.done.insert(decl) {
129 continue;
130 }
131 match self.tast[decl].kind {
132 DeclKind::Function => self.function(decl),
133 DeclKind::Object => self.object(decl),
134 }
135 }
136 }
137
138 /// One object with static storage duration.
139 fn object(&mut self, decl: DeclId) {
140 let tast = self.tast;
141 let node = &tast[decl];
142 let (ty, state, init) = (node.ty, node.state, node.init);
143 let (linkage, duration, alignment) = (node.linkage, node.duration, node.alignment);
144 let span = tast.decl_span(decl);
145 if duration == StorageDuration::Automatic {
146 // A block-scope object with automatic storage is a slot or a value in the function
147 // that declares it, and the body is what makes it. Nothing is emitted here.
148 return;
149 }
150
151 let symbol = self.symbol_of(decl);
152 let size = repr::size_of(self.types, self.target, ty);
153 let align = alignment.unwrap_or_else(|| repr::align_of(self.types, self.target, ty));
154 let mut global = Global::new(symbol, size, align);
155 global.linkage = match linkage {
156 Linkage::External => IrLinkage::External,
157 Linkage::Internal | Linkage::None => IrLinkage::Internal,
158 };
159 global.tls = (duration == StorageDuration::Thread).then_some(TlsModel::GlobalDynamic);
160 global.constant = repr::is_read_only(self.types, ty);
161 global.init = match state {
162 // `extern int x;` and nothing else names an object another translation unit
163 // defines. The global is here so that a reference to it has something to resolve
164 // against, and it has no image, which is what makes it a declaration.
165 Definition::Declared => None,
166 Definition::Tentative => Some(self.zeros(size)),
167 Definition::Defined => {
168 let (data, covered) = self.image(init, size, span);
169 // The object is as large as its image when the image is the larger of the two.
170 // A structure whose last member is a flexible array is the only way that
171 // happens: `sizeof` answers without the array and an initializer that fills it
172 // makes an object big enough to hold what was written. C 6.7.2.1p18 leaves the
173 // size to the implementation, gcc grows the object, and this does the same
174 // rather than hand the linker a size the image does not fit in.
175 global.size = size.max(covered);
176 Some(data)
177 }
178 };
179 self.module.add_global(global);
180 }
181
182 /// One function, with its body when it has one.
183 fn function(&mut self, decl: DeclId) {
184 let tast = self.tast;
185 let node = &tast[decl];
186 let (ty, linkage, body) = (node.ty, node.linkage, node.body);
187 let span = tast.decl_span(decl);
188 let Some(name) = node.name else { return };
189 let Some(plan) = self.plan(ty, &[], span) else { return };
190
191 let mut func = Func::new(name, plan.signature.clone());
192 func.linkage = match linkage {
193 Linkage::Internal | Linkage::None => IrLinkage::Internal,
194 Linkage::External => IrLinkage::External,
195 };
196 if body.is_some() {
197 body::lower(self, decl, &mut func, &plan);
198 }
199 self.module.add_func(func);
200 }
201
202 /// How everything a call to this function type hands over travels, and [`None`] for one the
203 /// walk cannot make.
204 ///
205 /// `actual` is the types of the arguments at a call site, which matter only past the end of
206 /// the prototype: what a variadic argument does is decided from what was written there, and
207 /// there is no parameter to decide it from. A definition passes nothing for it.
208 pub(crate) fn plan(&mut self, ty: TypeId, actual: &[TypeId], span: Span) -> Option<Plan> {
209 self.plan_with(ty, actual, false, span)
210 }
211
212 /// The same, as the call site sees it rather than as the function does.
213 ///
214 /// The two differ for a type that is not a prototype. An old style definition is the one of
215 /// those that knows what its parameters are, and 6.5.2.2p6 checks a call against a prototype
216 /// and against nothing at all otherwise, so a parameter it disagrees with does not make the
217 /// call wrong and cannot be what the argument travels as either: the value at the call is
218 /// the argument's own type and nothing converted it. So a parameter the argument facing it
219 /// is compatible with is used, which is the usual case and is what makes the call go to the
220 /// name, and one it is not compatible with gives way to what was actually written. A call
221 /// like that is undefined behaviour if control reaches it and the file still has to
222 /// translate, which is the same position [`Body::direct`](crate::body) already takes.
223 pub(crate) fn call_plan(&mut self, ty: TypeId, actual: &[TypeId], span: Span) -> Option<Plan> {
224 self.plan_with(ty, actual, true, span)
225 }
226
227 fn plan_with(
228 &mut self,
229 ty: TypeId,
230 actual: &[TypeId],
231 at_call: bool,
232 span: Span,
233 ) -> Option<Plan> {
234 let canonical = self.types.canonical(ty);
235 let canonical = match self.types.kind(canonical) {
236 // A call goes through a pointer to a function, and the type in hand may be either.
237 TypeKind::Pointer(pointee) => self.types.canonical(pointee),
238 _ => canonical,
239 };
240 let TypeKind::Function(id) = self.types.kind(canonical) else {
241 self.unsupported("a call through something that is not a function", span);
242 return None;
243 };
244 let signature = self.types.signature(id);
245 let ret = signature.ret;
246 // A function declared without a prototype takes what it is given, which is what a
247 // signature with no parameters and no end to them says. C23 removed these and this is
248 // what `int f();` means in every dialect before it.
249 let variadic = signature.variadic || !signature.prototyped;
250 let params = if at_call && !signature.prototyped {
251 // An argument past the end of the list has no parameter to travel as, which is what
252 // a call to an unprototyped function with more arguments than the definition takes
253 // is, so the list ends where the arguments do.
254 signature
255 .params
256 .iter()
257 .zip(actual)
258 .map(|(¶m, &arg)| if compatible(self.types, param, arg) { param } else { arg })
259 .collect()
260 } else {
261 signature.params.clone()
262 };
263
264 match abi::plan(self.types, self.target, ret, ¶ms, actual, variadic) {
265 Ok(plan) => Some(plan),
266 Err(what) => {
267 self.unsupported(what, span);
268 None
269 }
270 }
271 }
272
273 /// The image of an initializer: the entries in ascending order, with the gaps zeroed, and
274 /// how many bytes it covers.
275 ///
276 /// The count is the size that was asked for except when a flexible array member was given
277 /// something to hold, which is the one case where an image is larger than the type it is an
278 /// image of.
279 pub(crate) fn image(
280 &mut self,
281 init: Option<InitList>,
282 size: u64,
283 span: Span,
284 ) -> (DataList, u64) {
285 let Some(init) = init else { return (self.zeros(size), size) };
286 let (data, at) = self.pieces(init, size, span);
287 (self.module.push_data(&data), at)
288 }
289
290 /// The data an image is made of, before it becomes a [`DataList`].
291 ///
292 /// This is apart from [`Self::image`] so that an image can be built inside another one,
293 /// which is what a compound literal used as a value in an initializer needs.
294 fn pieces(&mut self, init: InitList, size: u64, span: Span) -> (Vec<Datum>, u64) {
295 let entries = self.in_image_order(&self.tast[init]);
296 let mut packed = self.packed(&entries, size);
297 let mut data: Vec<Datum> = Vec::with_capacity(entries.len());
298 let mut at = 0;
299 for entry in entries {
300 let piece = self.entry(entry, &mut packed, size);
301 if piece.is_empty() {
302 continue;
303 }
304 let covered: u64 = piece.iter().map(|datum| datum.size(&self.module)).sum();
305 match entry.offset.cmp(&at) {
306 Ordering::Greater => data.push(Datum::Zero(entry.offset - at)),
307 // An entry that begins inside the one before it, which is neither the same
308 // place nor a later one. A union whose members are initialized through two
309 // designators is the way to write it. The earlier bytes are already in the
310 // list and the image cannot take them out again, so this is refused, and
311 // nothing here is wrong enough to drop the rest of the image.
312 Ordering::Less => {
313 self.unsupported("an initializer that writes over an earlier one", span);
314 continue;
315 }
316 Ordering::Equal => {}
317 }
318 at = entry.offset + covered;
319 data.extend(piece);
320 }
321 if at < size {
322 // The tail of a partly initialized object, which C says is zero. So is the tail of
323 // an array the initializer did not fill, and so is every byte of padding.
324 data.push(Datum::Zero(size - at));
325 at = size;
326 }
327 (data, at)
328 }
329
330 /// The entries an image is written from, which is not the order they were written in.
331 ///
332 /// A designator names a place, and the places may be named in any order at all:
333 /// `{ .b = 2, .a = 1 }` is the same object as `{ .a = 1, .b = 2 }` and C says so in as many
334 /// words. An image is bytes in ascending order, so the entries are put in that order here.
335 /// The sort is stable, which is what makes the rest of the rule work: naming one place
336 /// twice is legal and the last of them is the one that stands, so among the entries at one
337 /// offset the written order is kept and all but the last are dropped.
338 ///
339 /// A bit-field is never dropped, because several of them share one offset without writing
340 /// over anything. Which bytes they came to is settled by [`Self::packed`] before this runs
341 /// and the whole run goes in under the first entry that has a bit in it.
342 fn in_image_order(&self, entries: &[InitEntry]) -> Vec<InitEntry> {
343 let mut sorted = entries.to_vec();
344 sorted.sort_by_key(|entry| entry.offset);
345 let mut kept: Vec<InitEntry> = Vec::with_capacity(sorted.len());
346 for entry in sorted {
347 if !entry.is_bit_field() {
348 let over = |last: &InitEntry| last.offset == entry.offset && !last.is_bit_field();
349 while kept.last().is_some_and(over) {
350 kept.pop();
351 }
352 }
353 kept.push(entry);
354 }
355 kept
356 }
357
358 /// What one entry of an initializer puts in the image.
359 ///
360 /// A bit-field is not a datum of its own, because two of them can live in one byte and an
361 /// image is written in bytes. They were put together into their bytes by [`Self::packed`]
362 /// before this ran, and the whole run of bytes goes in under the first entry that has a
363 /// bit in it, which is why a later one in the same run answers with nothing.
364 ///
365 /// An entry is usually one datum and a compound literal read is the reason the answer is a
366 /// list: that entry is a whole object and puts as many data in as the object it is.
367 fn entry(&mut self, entry: InitEntry, packed: &mut BTreeMap<u64, u8>, size: u64) -> Vec<Datum> {
368 if entry.is_bit_field() {
369 let Some(bytes) = take_run(packed, entry.offset) else { return Vec::new() };
370 return vec![Datum::Bytes(self.module.push_bytes(&bytes))];
371 }
372 if let Some(literal) = self.literal_read(entry.value) {
373 return self.literal_image(literal, self.tast.expr_span(entry.value));
374 }
375 // How much room is left in the object, which is what a string literal longer than the
376 // array it initializes is cut down to. An entry that begins where the object ends is the
377 // initializer of a flexible array member, and there the object grows to hold what was
378 // written rather than the value being cut to fit, so nothing is taken off it.
379 let room = if entry.offset < size { size - entry.offset } else { u64::MAX };
380 self.datum(entry.value, room).into_iter().collect()
381 }
382
383 /// The compound literal an entry reads, if that is what the entry is.
384 ///
385 /// Reading an object is a node of its own, so a literal used as a value comes through as a
386 /// read of a literal. A literal whose address is taken is not a read and is not this: that
387 /// one folds to an address and goes in as a relocation, with the object it points at emitted
388 /// on its own.
389 fn literal_read(&self, value: ExprId) -> Option<DeclId> {
390 let ExprKind::Convert { kind: Conversion::Lvalue, operand } = self.tast[value].kind else {
391 return None;
392 };
393 match self.tast[operand].kind {
394 ExprKind::CompoundLiteral(decl) => Some(decl),
395 _ => None,
396 }
397 }
398
399 /// The bytes a compound literal contributes where it is read, which are its own image.
400 ///
401 /// The literal has static storage duration here, since a file-scope initializer is the only
402 /// place this is reached from, and C 6.7.11p4 is what lets it stand as a constant element.
403 /// Its own initializer is built at the offset the entry is at, so the parent image ends up
404 /// with the literal's bytes laid into it rather than a name pointing at a second object.
405 fn literal_image(&mut self, literal: DeclId, span: Span) -> Vec<Datum> {
406 let size = repr::size_of(self.types, self.target, self.tast[literal].ty);
407 let Some(init) = self.tast[literal].init else {
408 return if size == 0 { Vec::new() } else { vec![Datum::Zero(size)] };
409 };
410 self.pieces(init, size, span).0
411 }
412
413 /// The bit-fields of an initializer, put together into the bytes they lie in.
414 ///
415 /// Only the bytes something was stored in are in the map. A field whose value is zero
416 /// leaves nothing behind, which is right: what an image does not say is zero anyway. A
417 /// field named twice takes only the bits of the field, so the last of them stands and does
418 /// not read as the two values together.
419 fn packed(&mut self, entries: &[InitEntry], size: u64) -> BTreeMap<u64, u8> {
420 let mut bytes = BTreeMap::new();
421 for entry in entries.iter().filter(|entry| entry.is_bit_field()) {
422 let Some(folded) = self.fold(entry.value) else { continue };
423 let Const::Int(number) = folded else {
424 let span = self.tast.expr_span(entry.value);
425 let what = "a bit-field initialized by something that is not an integer";
426 self.unsupported(what, span);
427 continue;
428 };
429 let width = entry.bit_width;
430 let ones = if width >= 128 { u128::MAX } else { (1u128 << width) - 1 };
431 let mut mask = ones << entry.bit_offset;
432 let mut placed = ((number as u128) & ones) << entry.bit_offset;
433 let mut at = entry.offset;
434 while mask != 0 && at < size {
435 let (bits, keep) = ((placed & 0xff) as u8, !((mask & 0xff) as u8));
436 if bits != 0 || bytes.contains_key(&at) {
437 let byte = bytes.entry(at).or_insert(0);
438 *byte = (*byte & keep) | bits;
439 }
440 mask >>= 8;
441 placed >>= 8;
442 at += 1;
443 }
444 }
445 bytes
446 }
447
448 /// One entry of an image, given how many bytes are left in the object it goes in.
449 fn datum(&mut self, value: ExprId, room: u64) -> Option<Datum> {
450 let tast = self.tast;
451 let ty = tast[value].ty;
452 let span = tast.expr_span(value);
453 if let TypeKind::Array { .. } = self.types.kind(self.types.canonical(ty)) {
454 // An array in an initializer is a string literal initializing it, because that is
455 // the only way an array is ever a value. `char s[2] = "hi";` drops the terminator,
456 // which is the one case where the literal is longer than what it initializes.
457 let ExprKind::Str(id) = tast[value].kind else {
458 self.unsupported("this initializer", span);
459 return None;
460 };
461 let bytes = tast[id].bytes(self.target);
462 let take = bytes.len().min(usize::try_from(room).unwrap_or(usize::MAX));
463 return Some(Datum::Bytes(self.module.push_bytes(&bytes[..take])));
464 }
465
466 let size = repr::size_of(self.types, self.target, ty);
467 match self.fold(value)? {
468 Const::Int(number) => {
469 let ty = repr::value_type(self.types, self.target, ty)?;
470 // An integer constant of pointer type is a null pointer constant, which is what
471 // `NULL` is, or an address the program wrote as a number. An image is bytes and
472 // `ptr` says nothing about how many, so it goes in as the integer it is at the
473 // width the target's addresses have. An address the linker has to fill in is
474 // the arm below, and is the only one that stays a pointer.
475 let ty = if ty.is_ptr() { Type::int(self.target.pointer_width) } else { ty };
476 let imm = self.module.add_imm(Imm::int(number, ty));
477 Some(Datum::Scalar { ty, value: imm })
478 }
479 Const::Float(number) => {
480 let ty = repr::value_type(self.types, self.target, ty)?;
481 let imm = self.module.add_imm(Imm::from_bits(number.to_bits()));
482 Some(Datum::Scalar { ty, value: imm })
483 }
484 Const::Address(address) => {
485 let symbol = match address.base {
486 Base::Decl(decl) => {
487 // A compound literal is an object nothing declares, so the address of
488 // one is also the only thing that asks for it to be emitted. Without
489 // this the image names a symbol the module never defines and the link
490 // is what finds out. Anything with a name of its own is left alone,
491 // since the walk over the unit reaches those on its own.
492 if self.tast[decl].name.is_none() {
493 self.local_static(decl);
494 }
495 self.symbol_of(decl)
496 }
497 Base::Str(id) => self.string(id),
498 };
499 let addend = i64::try_from(address.offset).unwrap_or(0);
500 let size = u32::try_from(size).unwrap_or(0);
501 Some(Datum::Addr(self.module.add_reloc(Reloc { symbol, addend, size })))
502 }
503 }
504 }
505
506 /// An image of nothing but zeros, which is what a tentative definition has.
507 fn zeros(&mut self, size: u64) -> DataList {
508 if size == 0 {
509 return DataList::EMPTY;
510 }
511 self.module.push_data(&[Datum::Zero(size)])
512 }
513
514 /// The global a string literal is emitted as, making it the first time it is asked for.
515 pub(crate) fn string(&mut self, id: StrId) -> Symbol {
516 if let Some(&symbol) = self.strings.get(&id) {
517 return symbol;
518 }
519 let literal = &self.tast[id];
520 let bytes = literal.bytes(self.target);
521 let align = literal.encoding.element_width(self.target) / 8;
522 let symbol = self.names.intern(&format!(".Lstr.{}", self.strings.len()));
523
524 let mut global = Global::new(symbol, bytes.len() as u64, align.max(1));
525 global.linkage = IrLinkage::Internal;
526 // Not because the type says so, since a literal is an array of `char` and not of
527 // `const char`, but because writing to one is undefined and every target puts them
528 // somewhere read-only.
529 global.constant = true;
530 let range = self.module.push_bytes(&bytes);
531 global.init = Some(self.module.push_data(&[Datum::Bytes(range)]));
532 self.module.add_global(global);
533 self.strings.insert(id, symbol);
534 symbol
535 }
536
537 /// The name an object or a function is known by in the object file.
538 pub(crate) fn symbol_of(&mut self, decl: DeclId) -> Symbol {
539 let tast = self.tast;
540 let node = &tast[decl];
541 if node.linkage != Linkage::None {
542 return node.name.unwrap_or_else(|| self.names.intern(".Lanon"));
543 }
544 if let Some(&symbol) = self.statics.get(&decl) {
545 return symbol;
546 }
547 // A `static` in a function, or a compound literal with static storage duration. The
548 // number is what makes two of them in two functions two objects.
549 let base = match node.name {
550 Some(name) => self.names.resolve(name).to_string(),
551 None => ".Lanon".to_string(),
552 };
553 let symbol = self.names.intern(&format!("{base}.{}", self.statics.len()));
554 self.statics.insert(decl, symbol);
555 symbol
556 }
557
558 /// Emits the global for an object with static storage duration declared inside a function.
559 pub(crate) fn local_static(&mut self, decl: DeclId) {
560 if !self.done.insert(decl) {
561 return;
562 }
563 match self.tast[decl].kind {
564 // A function declared inside a body is a declaration of the function, not an
565 // object with static storage that happens to be one.
566 DeclKind::Function => self.function(decl),
567 DeclKind::Object => self.object(decl),
568 }
569 }
570
571 /// The value of a constant expression, reporting what folding it reported.
572 fn fold(&mut self, expr: ExprId) -> Option<Const> {
573 let mut eval = Eval::new(self.tast, self.types, self.target, self.names);
574 let folded = eval.constant(expr);
575 let reported = eval.finish();
576 self.diagnostics.extend(reported);
577 match folded {
578 Ok(value) => Some(value),
579 Err(stop) => {
580 if !stop.poisoned {
581 let span = self.tast.expr_span(stop.at);
582 self.unsupported("an initializer this compiler cannot fold", span);
583 }
584 None
585 }
586 }
587 }
588
589 /// Reports a construct the walk does not build IR for yet.
590 pub(crate) fn unsupported(&mut self, what: &str, span: Span) {
591 self.diagnostics.push(
592 Diagnostic::error(format!("{what} is not supported yet"), span).with_code("E0519"),
593 );
594 }
595}
596
597/// The run of bytes a bit-field entry starts, taken out of the map.
598///
599/// [`None`] when there is no byte at that offset, which means either that every bit-field in
600/// it was initialized to zero or that an earlier entry in the same run already took it.
601fn take_run(bytes: &mut BTreeMap<u64, u8>, start: u64) -> Option<Vec<u8>> {
602 let mut run = vec![bytes.remove(&start)?];
603 let mut at = start + 1;
604 while let Some(byte) = bytes.remove(&at) {
605 run.push(byte);
606 at += 1;
607 }
608 Some(run)
609}