Coverage Report

Created: 2026-09-14 07:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm-tools/crates/wit-parser/src/sizealign.rs
Line
Count
Source
1
use alloc::format;
2
use alloc::string::String;
3
use alloc::vec::Vec;
4
use core::{
5
    cmp::Ordering,
6
    num::NonZeroUsize,
7
    ops::{Add, AddAssign},
8
};
9
10
use crate::{FlagsRepr, Int, Resolve, Type, TypeDef, TypeDefKind};
11
12
/// Architecture specific alignment
13
#[derive(Eq, PartialEq, Clone, Copy)]
14
pub enum Alignment {
15
    /// This represents 4 byte alignment on 32bit and 8 byte alignment on 64bit architectures
16
    Pointer,
17
    /// This alignment is architecture independent (derived from integer or float types)
18
    Bytes(NonZeroUsize),
19
}
20
21
impl Default for Alignment {
22
476
    fn default() -> Self {
23
476
        Alignment::Bytes(NonZeroUsize::new(1).unwrap())
24
476
    }
25
}
26
27
impl core::fmt::Debug for Alignment {
28
0
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
29
0
        match self {
30
0
            Alignment::Pointer => f.write_str("ptr"),
31
0
            Alignment::Bytes(b) => f.write_fmt(format_args!("{}", b.get())),
32
        }
33
0
    }
34
}
35
36
impl PartialOrd for Alignment {
37
1.51k
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
38
1.51k
        Some(self.cmp(other))
39
1.51k
    }
40
}
41
42
impl Ord for Alignment {
43
    /// Needed for determining the max alignment of an object from its parts.
44
    /// The ordering is: Bytes(1) < Bytes(2) < Bytes(4) < Pointer < Bytes(8)
45
    /// as a Pointer is either four or eight byte aligned, depending on the architecture
46
1.51k
    fn cmp(&self, other: &Self) -> Ordering {
47
1.51k
        match (self, other) {
48
1
            (Alignment::Pointer, Alignment::Pointer) => Ordering::Equal,
49
66
            (Alignment::Pointer, Alignment::Bytes(b)) => {
50
66
                if b.get() > 4 {
51
7
                    Ordering::Less
52
                } else {
53
59
                    Ordering::Greater
54
                }
55
            }
56
109
            (Alignment::Bytes(b), Alignment::Pointer) => {
57
109
                if b.get() > 4 {
58
2
                    Ordering::Greater
59
                } else {
60
107
                    Ordering::Less
61
                }
62
            }
63
1.34k
            (Alignment::Bytes(a), Alignment::Bytes(b)) => a.cmp(b),
64
        }
65
1.51k
    }
66
}
67
68
impl Alignment {
69
    /// for easy migration this gives you the value for wasm32
70
600
    pub fn align_wasm32(&self) -> usize {
71
600
        match self {
72
105
            Alignment::Pointer => 4,
73
495
            Alignment::Bytes(bytes) => bytes.get(),
74
        }
75
600
    }
76
77
600
    pub fn align_wasm64(&self) -> usize {
78
600
        match self {
79
105
            Alignment::Pointer => 8,
80
495
            Alignment::Bytes(bytes) => bytes.get(),
81
        }
82
600
    }
83
84
0
    pub fn format(&self, ptrsize_expr: &str) -> String {
85
0
        match self {
86
0
            Alignment::Pointer => ptrsize_expr.into(),
87
0
            Alignment::Bytes(bytes) => format!("{}", bytes.get()),
88
        }
89
0
    }
90
}
91
92
/// Architecture specific measurement of position,
93
/// the combined amount in bytes is
94
/// `bytes + pointers * core::mem::size_of::<*const u8>()`
95
#[derive(Default, Clone, Copy, Eq, PartialEq)]
96
pub struct ArchitectureSize {
97
    /// architecture independent bytes
98
    pub bytes: usize,
99
    /// amount of pointer sized units to add
100
    pub pointers: usize,
101
}
102
103
impl Add<ArchitectureSize> for ArchitectureSize {
104
    type Output = ArchitectureSize;
105
106
2.23k
    fn add(self, rhs: ArchitectureSize) -> Self::Output {
107
2.23k
        ArchitectureSize::new(self.bytes + rhs.bytes, self.pointers + rhs.pointers)
108
2.23k
    }
109
}
110
111
impl AddAssign<ArchitectureSize> for ArchitectureSize {
112
0
    fn add_assign(&mut self, rhs: ArchitectureSize) {
113
0
        self.bytes += rhs.bytes;
114
0
        self.pointers += rhs.pointers;
115
0
    }
116
}
117
118
impl From<Alignment> for ArchitectureSize {
119
257
    fn from(align: Alignment) -> Self {
120
257
        match align {
121
257
            Alignment::Bytes(bytes) => ArchitectureSize::new(bytes.get(), 0),
122
0
            Alignment::Pointer => ArchitectureSize::new(0, 1),
123
        }
124
257
    }
125
}
126
127
impl core::fmt::Debug for ArchitectureSize {
128
0
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
129
0
        f.write_str(&self.format("ptrsz"))
130
0
    }
131
}
132
133
impl ArchitectureSize {
134
6.97k
    pub fn new(bytes: usize, pointers: usize) -> Self {
135
6.97k
        Self { bytes, pointers }
136
6.97k
    }
137
138
272
    pub fn max<B: core::borrow::Borrow<Self>>(&self, other: B) -> Self {
139
272
        let other = other.borrow();
140
272
        let self32 = self.size_wasm32();
141
272
        let self64 = self.size_wasm64();
142
272
        let other32 = other.size_wasm32();
143
272
        let other64 = other.size_wasm64();
144
272
        if self32 >= other32 && self64 >= other64 {
145
89
            *self
146
183
        } else if self32 <= other32 && self64 <= other64 {
147
183
            *other
148
        } else {
149
            // we can assume a combination of bytes and pointers, so align to at least pointer size
150
0
            let new32 = align_to(self32.max(other32), 4);
151
0
            let new64 = align_to(self64.max(other64), 8);
152
0
            ArchitectureSize::new(new32 + new32 - new64, (new64 - new32) / 4)
153
        }
154
272
    }
155
156
0
    pub fn add_bytes(&self, b: usize) -> Self {
157
0
        Self::new(self.bytes + b, self.pointers)
158
0
    }
159
160
    /// The effective offset/size is
161
    /// `constant_bytes() + core::mem::size_of::<*const u8>() * pointers_to_add()`
162
0
    pub fn constant_bytes(&self) -> usize {
163
0
        self.bytes
164
0
    }
165
166
0
    pub fn pointers_to_add(&self) -> usize {
167
0
        self.pointers
168
0
    }
169
170
    /// Shortcut for compatibility with previous versions
171
2.18k
    pub fn size_wasm32(&self) -> usize {
172
2.18k
        self.bytes + self.pointers * 4
173
2.18k
    }
174
175
2.18k
    pub fn size_wasm64(&self) -> usize {
176
2.18k
        self.bytes + self.pointers * 8
177
2.18k
    }
178
179
    /// prefer this over >0
180
0
    pub fn is_empty(&self) -> bool {
181
0
        self.bytes == 0 && self.pointers == 0
182
0
    }
183
184
    // create a suitable expression in bytes from a pointer size argument
185
0
    pub fn format(&self, ptrsize_expr: &str) -> String {
186
0
        self.format_term(ptrsize_expr, false)
187
0
    }
188
189
    // create a suitable expression in bytes from a pointer size argument,
190
    // extended API with optional brackets around the sum
191
0
    pub fn format_term(&self, ptrsize_expr: &str, suppress_brackets: bool) -> String {
192
0
        if self.pointers != 0 {
193
0
            if self.bytes > 0 {
194
                // both
195
0
                if suppress_brackets {
196
0
                    format!(
197
                        "{}+{}*{ptrsize_expr}",
198
0
                        self.constant_bytes(),
199
0
                        self.pointers_to_add()
200
                    )
201
                } else {
202
0
                    format!(
203
                        "({}+{}*{ptrsize_expr})",
204
0
                        self.constant_bytes(),
205
0
                        self.pointers_to_add()
206
                    )
207
                }
208
0
            } else if self.pointers == 1 {
209
                // one pointer
210
0
                ptrsize_expr.into()
211
            } else {
212
                // only pointer
213
0
                if suppress_brackets {
214
0
                    format!("{}*{ptrsize_expr}", self.pointers_to_add())
215
                } else {
216
0
                    format!("({}*{ptrsize_expr})", self.pointers_to_add())
217
                }
218
            }
219
        } else {
220
            // only bytes
221
0
            format!("{}", self.constant_bytes())
222
        }
223
0
    }
224
}
225
226
/// Information per structure element
227
#[derive(Default)]
228
pub struct ElementInfo {
229
    pub size: ArchitectureSize,
230
    pub align: Alignment,
231
}
232
233
impl From<Alignment> for ElementInfo {
234
257
    fn from(align: Alignment) -> Self {
235
257
        ElementInfo {
236
257
            size: align.into(),
237
257
            align,
238
257
        }
239
257
    }
240
}
241
242
impl ElementInfo {
243
571
    fn new(size: ArchitectureSize, align: Alignment) -> Self {
244
571
        Self { size, align }
245
571
    }
246
}
247
248
/// Collect size and alignment for sub-elements of a structure
249
#[derive(Default)]
250
pub struct SizeAlign {
251
    map: Vec<ElementInfo>,
252
}
253
254
impl SizeAlign {
255
596
    pub fn fill(&mut self, resolve: &Resolve) -> anyhow::Result<()> {
256
596
        self.map = Vec::new();
257
600
        for (_, ty) in resolve.types.iter() {
258
600
            let pair = self.calculate(ty)?;
259
600
            self.map.push(pair);
260
        }
261
596
        Ok(())
262
596
    }
263
264
600
    fn calculate(&self, ty: &TypeDef) -> anyhow::Result<ElementInfo> {
265
600
        Ok(match &ty.kind {
266
40
            TypeDefKind::Type(t) => ElementInfo::new(self.size(t), self.align(t)),
267
0
            TypeDefKind::FixedLengthList(t, size) => {
268
0
                let field_align = self.align(t);
269
0
                let field_size = self.size(t);
270
0
                let bytes = field_size.bytes.checked_mul(*size as usize).ok_or_else(|| {
271
0
                    anyhow::anyhow!(
272
                        "size of fixed-length list of {size} elements overflows the target architecture's address space"
273
                    )
274
0
                })?;
275
0
                let pointers = field_size.pointers.checked_mul(*size as usize).ok_or_else(|| {
276
0
                    anyhow::anyhow!(
277
                        "size of fixed-length list of {size} elements overflows the target architecture's address space"
278
                    )
279
0
                })?;
280
0
                ElementInfo::new(ArchitectureSize::new(bytes, pointers), field_align)
281
            }
282
            TypeDefKind::List(_) => {
283
59
                ElementInfo::new(ArchitectureSize::new(0, 2), Alignment::Pointer)
284
            }
285
            TypeDefKind::Map(_, _) => {
286
0
                ElementInfo::new(ArchitectureSize::new(0, 2), Alignment::Pointer)
287
            }
288
39
            TypeDefKind::Record(r) => self.record(r.fields.iter().map(|f| &f.ty)),
289
209
            TypeDefKind::Tuple(t) => self.record(t.types.iter()),
290
20
            TypeDefKind::Flags(f) => match f.repr() {
291
20
                FlagsRepr::U8 => int_size_align(Int::U8),
292
0
                FlagsRepr::U16 => int_size_align(Int::U16),
293
0
                FlagsRepr::U32(n) => ElementInfo::new(
294
0
                    ArchitectureSize::new(n * 4, 0),
295
0
                    Alignment::Bytes(NonZeroUsize::new(4).unwrap()),
296
                ),
297
            },
298
63
            TypeDefKind::Variant(v) => self.variant(v.tag(), v.cases.iter().map(|c| c.ty.as_ref())),
299
9
            TypeDefKind::Enum(e) => self.variant(e.tag(), []),
300
70
            TypeDefKind::Option(t) => self.variant(Int::U8, [Some(t)]),
301
101
            TypeDefKind::Result(r) => self.variant(Int::U8, [r.ok.as_ref(), r.err.as_ref()]),
302
            // A resource is represented as an index.
303
            // A future is represented as an index.
304
            // A stream is represented as an index.
305
            // An error is represented as an index.
306
            TypeDefKind::Handle(_) | TypeDefKind::Future(_) | TypeDefKind::Stream(_) => {
307
9
                int_size_align(Int::U32)
308
            }
309
            // This shouldn't be used for anything since raw resources aren't part of the ABI -- just handles to
310
            // them.
311
20
            TypeDefKind::Resource => ElementInfo::new(
312
20
                ArchitectureSize::new(usize::MAX, 0),
313
20
                Alignment::Bytes(NonZeroUsize::new(usize::MAX).unwrap()),
314
            ),
315
0
            TypeDefKind::Unknown => unreachable!(),
316
        })
317
600
    }
318
319
2.94k
    pub fn size(&self, ty: &Type) -> ArchitectureSize {
320
2.94k
        match ty {
321
1.46k
            Type::Bool | Type::U8 | Type::S8 => ArchitectureSize::new(1, 0),
322
55
            Type::U16 | Type::S16 => ArchitectureSize::new(2, 0),
323
            Type::U32 | Type::S32 | Type::F32 | Type::Char | Type::ErrorContext => {
324
65
                ArchitectureSize::new(4, 0)
325
            }
326
103
            Type::U64 | Type::S64 | Type::F64 => ArchitectureSize::new(8, 0),
327
6
            Type::String => ArchitectureSize::new(0, 2),
328
1.24k
            Type::Id(id) => self.map[id.index()].size,
329
        }
330
2.94k
    }
331
332
2.97k
    pub fn align(&self, ty: &Type) -> Alignment {
333
2.97k
        match ty {
334
1.47k
            Type::Bool | Type::U8 | Type::S8 => Alignment::Bytes(NonZeroUsize::new(1).unwrap()),
335
56
            Type::U16 | Type::S16 => Alignment::Bytes(NonZeroUsize::new(2).unwrap()),
336
            Type::U32 | Type::S32 | Type::F32 | Type::Char | Type::ErrorContext => {
337
75
                Alignment::Bytes(NonZeroUsize::new(4).unwrap())
338
            }
339
105
            Type::U64 | Type::S64 | Type::F64 => Alignment::Bytes(NonZeroUsize::new(8).unwrap()),
340
6
            Type::String => Alignment::Pointer,
341
1.25k
            Type::Id(id) => self.map[id.index()].align,
342
        }
343
2.97k
    }
344
345
248
    pub fn field_offsets<'a>(
346
248
        &self,
347
248
        types: impl IntoIterator<Item = &'a Type>,
348
248
    ) -> Vec<(ArchitectureSize, &'a Type)> {
349
248
        let mut cur = ArchitectureSize::default();
350
248
        types
351
248
            .into_iter()
352
1.01k
            .map(|ty| {
353
1.01k
                let ret = align_to_arch(cur, self.align(ty));
354
1.01k
                cur = ret + self.size(ty);
355
1.01k
                (ret, ty)
356
1.01k
            })
<wit_parser::sizealign::SizeAlign>::field_offsets::<core::slice::iter::Iter<wit_parser::Type>>::{closure#0}
Line
Count
Source
352
916
            .map(|ty| {
353
916
                let ret = align_to_arch(cur, self.align(ty));
354
916
                cur = ret + self.size(ty);
355
916
                (ret, ty)
356
916
            })
<wit_parser::sizealign::SizeAlign>::field_offsets::<core::iter::adapters::map::Map<core::slice::iter::Iter<wit_parser::Field>, wasm_tools_fuzz::wit64::run::{closure#3}>>::{closure#0}
Line
Count
Source
352
99
            .map(|ty| {
353
99
                let ret = align_to_arch(cur, self.align(ty));
354
99
                cur = ret + self.size(ty);
355
99
                (ret, ty)
356
99
            })
Unexecuted instantiation: <wit_parser::sizealign::SizeAlign>::field_offsets::<_>::{closure#0}
357
248
            .collect()
358
248
    }
<wit_parser::sizealign::SizeAlign>::field_offsets::<core::slice::iter::Iter<wit_parser::Type>>
Line
Count
Source
345
209
    pub fn field_offsets<'a>(
346
209
        &self,
347
209
        types: impl IntoIterator<Item = &'a Type>,
348
209
    ) -> Vec<(ArchitectureSize, &'a Type)> {
349
209
        let mut cur = ArchitectureSize::default();
350
209
        types
351
209
            .into_iter()
352
209
            .map(|ty| {
353
                let ret = align_to_arch(cur, self.align(ty));
354
                cur = ret + self.size(ty);
355
                (ret, ty)
356
            })
357
209
            .collect()
358
209
    }
<wit_parser::sizealign::SizeAlign>::field_offsets::<core::iter::adapters::map::Map<core::slice::iter::Iter<wit_parser::Field>, wasm_tools_fuzz::wit64::run::{closure#3}>>
Line
Count
Source
345
39
    pub fn field_offsets<'a>(
346
39
        &self,
347
39
        types: impl IntoIterator<Item = &'a Type>,
348
39
    ) -> Vec<(ArchitectureSize, &'a Type)> {
349
39
        let mut cur = ArchitectureSize::default();
350
39
        types
351
39
            .into_iter()
352
39
            .map(|ty| {
353
                let ret = align_to_arch(cur, self.align(ty));
354
                cur = ret + self.size(ty);
355
                (ret, ty)
356
            })
357
39
            .collect()
358
39
    }
Unexecuted instantiation: <wit_parser::sizealign::SizeAlign>::field_offsets::<_>
359
360
24
    pub fn payload_offset<'a>(
361
24
        &self,
362
24
        tag: Int,
363
24
        cases: impl IntoIterator<Item = Option<&'a Type>>,
364
24
    ) -> ArchitectureSize {
365
24
        let mut max_align = Alignment::default();
366
63
        for ty in cases {
367
63
            if let Some(ty) = ty {
368
28
                max_align = max_align.max(self.align(ty));
369
35
            }
370
        }
371
24
        let tag_size = int_size_align(tag).size;
372
24
        align_to_arch(tag_size, max_align)
373
24
    }
<wit_parser::sizealign::SizeAlign>::payload_offset::<core::iter::adapters::map::Map<core::slice::iter::Iter<wit_parser::Case>, wasm_tools_fuzz::wit64::run::{closure#6}>>
Line
Count
Source
360
24
    pub fn payload_offset<'a>(
361
24
        &self,
362
24
        tag: Int,
363
24
        cases: impl IntoIterator<Item = Option<&'a Type>>,
364
24
    ) -> ArchitectureSize {
365
24
        let mut max_align = Alignment::default();
366
63
        for ty in cases {
367
63
            if let Some(ty) = ty {
368
28
                max_align = max_align.max(self.align(ty));
369
35
            }
370
        }
371
24
        let tag_size = int_size_align(tag).size;
372
24
        align_to_arch(tag_size, max_align)
373
24
    }
Unexecuted instantiation: <wit_parser::sizealign::SizeAlign>::payload_offset::<_>
374
375
248
    pub fn record<'a>(&self, types: impl IntoIterator<Item = &'a Type>) -> ElementInfo {
376
248
        let mut size = ArchitectureSize::default();
377
248
        let mut align = Alignment::default();
378
1.01k
        for ty in types {
379
1.01k
            let field_size = self.size(ty);
380
1.01k
            let field_align = self.align(ty);
381
1.01k
            size = align_to_arch(size, field_align) + field_size;
382
1.01k
            align = align.max(field_align);
383
1.01k
        }
384
248
        ElementInfo::new(align_to_arch(size, align), align)
385
248
    }
<wit_parser::sizealign::SizeAlign>::record::<core::slice::iter::Iter<wit_parser::Type>>
Line
Count
Source
375
209
    pub fn record<'a>(&self, types: impl IntoIterator<Item = &'a Type>) -> ElementInfo {
376
209
        let mut size = ArchitectureSize::default();
377
209
        let mut align = Alignment::default();
378
916
        for ty in types {
379
916
            let field_size = self.size(ty);
380
916
            let field_align = self.align(ty);
381
916
            size = align_to_arch(size, field_align) + field_size;
382
916
            align = align.max(field_align);
383
916
        }
384
209
        ElementInfo::new(align_to_arch(size, align), align)
385
209
    }
<wit_parser::sizealign::SizeAlign>::record::<core::iter::adapters::map::Map<core::slice::iter::Iter<wit_parser::Field>, <wit_parser::sizealign::SizeAlign>::calculate::{closure#2}>>
Line
Count
Source
375
39
    pub fn record<'a>(&self, types: impl IntoIterator<Item = &'a Type>) -> ElementInfo {
376
39
        let mut size = ArchitectureSize::default();
377
39
        let mut align = Alignment::default();
378
99
        for ty in types {
379
99
            let field_size = self.size(ty);
380
99
            let field_align = self.align(ty);
381
99
            size = align_to_arch(size, field_align) + field_size;
382
99
            align = align.max(field_align);
383
99
        }
384
39
        ElementInfo::new(align_to_arch(size, align), align)
385
39
    }
386
387
0
    pub fn params<'a>(&self, types: impl IntoIterator<Item = &'a Type>) -> ElementInfo {
388
0
        self.record(types.into_iter())
389
0
    }
390
391
204
    fn variant<'a>(
392
204
        &self,
393
204
        tag: Int,
394
204
        types: impl IntoIterator<Item = Option<&'a Type>>,
395
204
    ) -> ElementInfo {
396
        let ElementInfo {
397
204
            size: discrim_size,
398
204
            align: discrim_align,
399
204
        } = int_size_align(tag);
400
204
        let mut case_size = ArchitectureSize::default();
401
204
        let mut case_align = Alignment::default();
402
335
        for ty in types {
403
335
            if let Some(ty) = ty {
404
272
                case_size = case_size.max(&self.size(ty));
405
272
                case_align = case_align.max(self.align(ty));
406
272
            }
407
        }
408
204
        let align = discrim_align.max(case_align);
409
204
        let discrim_aligned = align_to_arch(discrim_size, case_align);
410
204
        let size_sum = discrim_aligned + case_size;
411
204
        ElementInfo::new(align_to_arch(size_sum, align), align)
412
204
    }
<wit_parser::sizealign::SizeAlign>::variant::<[core::option::Option<&wit_parser::Type>; 0]>
Line
Count
Source
391
9
    fn variant<'a>(
392
9
        &self,
393
9
        tag: Int,
394
9
        types: impl IntoIterator<Item = Option<&'a Type>>,
395
9
    ) -> ElementInfo {
396
        let ElementInfo {
397
9
            size: discrim_size,
398
9
            align: discrim_align,
399
9
        } = int_size_align(tag);
400
9
        let mut case_size = ArchitectureSize::default();
401
9
        let mut case_align = Alignment::default();
402
9
        for ty in types {
403
0
            if let Some(ty) = ty {
404
0
                case_size = case_size.max(&self.size(ty));
405
0
                case_align = case_align.max(self.align(ty));
406
0
            }
407
        }
408
9
        let align = discrim_align.max(case_align);
409
9
        let discrim_aligned = align_to_arch(discrim_size, case_align);
410
9
        let size_sum = discrim_aligned + case_size;
411
9
        ElementInfo::new(align_to_arch(size_sum, align), align)
412
9
    }
<wit_parser::sizealign::SizeAlign>::variant::<[core::option::Option<&wit_parser::Type>; 1]>
Line
Count
Source
391
70
    fn variant<'a>(
392
70
        &self,
393
70
        tag: Int,
394
70
        types: impl IntoIterator<Item = Option<&'a Type>>,
395
70
    ) -> ElementInfo {
396
        let ElementInfo {
397
70
            size: discrim_size,
398
70
            align: discrim_align,
399
70
        } = int_size_align(tag);
400
70
        let mut case_size = ArchitectureSize::default();
401
70
        let mut case_align = Alignment::default();
402
70
        for ty in types {
403
70
            if let Some(ty) = ty {
404
70
                case_size = case_size.max(&self.size(ty));
405
70
                case_align = case_align.max(self.align(ty));
406
70
            }
407
        }
408
70
        let align = discrim_align.max(case_align);
409
70
        let discrim_aligned = align_to_arch(discrim_size, case_align);
410
70
        let size_sum = discrim_aligned + case_size;
411
70
        ElementInfo::new(align_to_arch(size_sum, align), align)
412
70
    }
<wit_parser::sizealign::SizeAlign>::variant::<[core::option::Option<&wit_parser::Type>; 2]>
Line
Count
Source
391
101
    fn variant<'a>(
392
101
        &self,
393
101
        tag: Int,
394
101
        types: impl IntoIterator<Item = Option<&'a Type>>,
395
101
    ) -> ElementInfo {
396
        let ElementInfo {
397
101
            size: discrim_size,
398
101
            align: discrim_align,
399
101
        } = int_size_align(tag);
400
101
        let mut case_size = ArchitectureSize::default();
401
101
        let mut case_align = Alignment::default();
402
202
        for ty in types {
403
202
            if let Some(ty) = ty {
404
174
                case_size = case_size.max(&self.size(ty));
405
174
                case_align = case_align.max(self.align(ty));
406
174
            }
407
        }
408
101
        let align = discrim_align.max(case_align);
409
101
        let discrim_aligned = align_to_arch(discrim_size, case_align);
410
101
        let size_sum = discrim_aligned + case_size;
411
101
        ElementInfo::new(align_to_arch(size_sum, align), align)
412
101
    }
<wit_parser::sizealign::SizeAlign>::variant::<core::iter::adapters::map::Map<core::slice::iter::Iter<wit_parser::Case>, <wit_parser::sizealign::SizeAlign>::calculate::{closure#3}>>
Line
Count
Source
391
24
    fn variant<'a>(
392
24
        &self,
393
24
        tag: Int,
394
24
        types: impl IntoIterator<Item = Option<&'a Type>>,
395
24
    ) -> ElementInfo {
396
        let ElementInfo {
397
24
            size: discrim_size,
398
24
            align: discrim_align,
399
24
        } = int_size_align(tag);
400
24
        let mut case_size = ArchitectureSize::default();
401
24
        let mut case_align = Alignment::default();
402
63
        for ty in types {
403
63
            if let Some(ty) = ty {
404
28
                case_size = case_size.max(&self.size(ty));
405
28
                case_align = case_align.max(self.align(ty));
406
35
            }
407
        }
408
24
        let align = discrim_align.max(case_align);
409
24
        let discrim_aligned = align_to_arch(discrim_size, case_align);
410
24
        let size_sum = discrim_aligned + case_size;
411
24
        ElementInfo::new(align_to_arch(size_sum, align), align)
412
24
    }
413
}
414
415
257
fn int_size_align(i: Int) -> ElementInfo {
416
257
    match i {
417
248
        Int::U8 => Alignment::Bytes(NonZeroUsize::new(1).unwrap()),
418
0
        Int::U16 => Alignment::Bytes(NonZeroUsize::new(2).unwrap()),
419
9
        Int::U32 => Alignment::Bytes(NonZeroUsize::new(4).unwrap()),
420
0
        Int::U64 => Alignment::Bytes(NonZeroUsize::new(8).unwrap()),
421
    }
422
257
    .into()
423
257
}
424
425
/// Increase `val` to a multiple of `align`;
426
/// `align` must be a power of two
427
2.86k
pub(crate) fn align_to(val: usize, align: usize) -> usize {
428
2.86k
    (val + align - 1) & !(align - 1)
429
2.86k
}
430
431
/// Increase `val` to a multiple of `align`, with special handling for pointers;
432
/// `align` must be a power of two or `Alignment::Pointer`
433
2.71k
pub fn align_to_arch(val: ArchitectureSize, align: Alignment) -> ArchitectureSize {
434
2.71k
    match align {
435
        Alignment::Pointer => {
436
151
            let new32 = align_to(val.bytes, 4);
437
151
            if new32 != align_to(new32, 8) {
438
64
                ArchitectureSize::new(new32 - 4, val.pointers + 1)
439
            } else {
440
87
                ArchitectureSize::new(new32, val.pointers)
441
            }
442
        }
443
2.55k
        Alignment::Bytes(align_bytes) => {
444
2.55k
            let align_bytes = align_bytes.get();
445
2.55k
            if align_bytes > 4 && (val.pointers & 1) != 0 {
446
3
                let new_bytes = align_to(val.bytes, align_bytes);
447
3
                if (new_bytes - val.bytes) >= 4 {
448
                    // up to four extra bytes fit together with a the extra 32 bit pointer
449
                    // and the 64 bit pointer is always 8 bytes (so no change in value)
450
1
                    ArchitectureSize::new(new_bytes - 8, val.pointers + 1)
451
                } else {
452
                    // there is no room to combine, so the odd pointer aligns to 8 bytes
453
2
                    ArchitectureSize::new(new_bytes + 8, val.pointers - 1)
454
                }
455
            } else {
456
2.55k
                ArchitectureSize::new(align_to(val.bytes, align_bytes), val.pointers)
457
            }
458
        }
459
    }
460
2.71k
}
461
462
#[cfg(test)]
463
mod test {
464
    use super::*;
465
    use alloc::string::ToString;
466
    use alloc::vec;
467
468
    #[test]
469
    fn align() {
470
        // u8 + ptr
471
        assert_eq!(
472
            align_to_arch(ArchitectureSize::new(1, 0), Alignment::Pointer),
473
            ArchitectureSize::new(0, 1)
474
        );
475
        // u8 + u64
476
        assert_eq!(
477
            align_to_arch(
478
                ArchitectureSize::new(1, 0),
479
                Alignment::Bytes(NonZeroUsize::new(8).unwrap())
480
            ),
481
            ArchitectureSize::new(8, 0)
482
        );
483
        // u8 + u32
484
        assert_eq!(
485
            align_to_arch(
486
                ArchitectureSize::new(1, 0),
487
                Alignment::Bytes(NonZeroUsize::new(4).unwrap())
488
            ),
489
            ArchitectureSize::new(4, 0)
490
        );
491
        // ptr + u64
492
        assert_eq!(
493
            align_to_arch(
494
                ArchitectureSize::new(0, 1),
495
                Alignment::Bytes(NonZeroUsize::new(8).unwrap())
496
            ),
497
            ArchitectureSize::new(8, 0)
498
        );
499
        // u32 + ptr
500
        assert_eq!(
501
            align_to_arch(ArchitectureSize::new(4, 0), Alignment::Pointer),
502
            ArchitectureSize::new(0, 1)
503
        );
504
        // u32, ptr + u64
505
        assert_eq!(
506
            align_to_arch(
507
                ArchitectureSize::new(0, 2),
508
                Alignment::Bytes(NonZeroUsize::new(8).unwrap())
509
            ),
510
            ArchitectureSize::new(0, 2)
511
        );
512
        // ptr, u8 + u64
513
        assert_eq!(
514
            align_to_arch(
515
                ArchitectureSize::new(1, 1),
516
                Alignment::Bytes(NonZeroUsize::new(8).unwrap())
517
            ),
518
            ArchitectureSize::new(0, 2)
519
        );
520
        // ptr, u8 + ptr
521
        assert_eq!(
522
            align_to_arch(ArchitectureSize::new(1, 1), Alignment::Pointer),
523
            ArchitectureSize::new(0, 2)
524
        );
525
        // ptr, ptr, u8 + u64
526
        assert_eq!(
527
            align_to_arch(
528
                ArchitectureSize::new(1, 2),
529
                Alignment::Bytes(NonZeroUsize::new(8).unwrap())
530
            ),
531
            ArchitectureSize::new(8, 2)
532
        );
533
        assert_eq!(
534
            align_to_arch(
535
                ArchitectureSize::new(30, 3),
536
                Alignment::Bytes(NonZeroUsize::new(8).unwrap())
537
            ),
538
            ArchitectureSize::new(40, 2)
539
        );
540
541
        assert_eq!(
542
            ArchitectureSize::new(12, 0).max(&ArchitectureSize::new(0, 2)),
543
            ArchitectureSize::new(8, 1)
544
        );
545
        assert_eq!(
546
            ArchitectureSize::new(10, 0).max(&ArchitectureSize::new(0, 2)),
547
            ArchitectureSize::new(8, 1)
548
        );
549
550
        assert_eq!(
551
            align_to_arch(
552
                ArchitectureSize::new(2, 0),
553
                Alignment::Bytes(NonZeroUsize::new(8).unwrap())
554
            ),
555
            ArchitectureSize::new(8, 0)
556
        );
557
        assert_eq!(
558
            align_to_arch(ArchitectureSize::new(2, 0), Alignment::Pointer),
559
            ArchitectureSize::new(0, 1)
560
        );
561
    }
562
563
    #[test]
564
    fn resource_size() {
565
        // keep it identical to the old behavior
566
        let obj = SizeAlign::default();
567
        let elem = obj
568
            .calculate(&TypeDef {
569
                name: None,
570
                kind: TypeDefKind::Resource,
571
                owner: crate::TypeOwner::None,
572
                docs: Default::default(),
573
                stability: Default::default(),
574
                span: Default::default(),
575
                external_id: Default::default(),
576
            })
577
            .unwrap();
578
        assert_eq!(elem.size, ArchitectureSize::new(usize::MAX, 0));
579
        assert_eq!(
580
            elem.align,
581
            Alignment::Bytes(NonZeroUsize::new(usize::MAX).unwrap())
582
        );
583
    }
584
    #[test]
585
    fn result_ptr_10() {
586
        let mut obj = SizeAlign::default();
587
        let mut resolve = Resolve::default();
588
        let tuple = crate::Tuple {
589
            types: vec![Type::U16, Type::U16, Type::U16, Type::U16, Type::U16],
590
        };
591
        let id = resolve.types.alloc(TypeDef {
592
            name: None,
593
            kind: TypeDefKind::Tuple(tuple),
594
            owner: crate::TypeOwner::None,
595
            docs: Default::default(),
596
            stability: Default::default(),
597
            span: Default::default(),
598
            external_id: Default::default(),
599
        });
600
        obj.fill(&resolve).unwrap();
601
        let my_result = crate::Result_ {
602
            ok: Some(Type::String),
603
            err: Some(Type::Id(id)),
604
        };
605
        let elem = obj
606
            .calculate(&TypeDef {
607
                name: None,
608
                kind: TypeDefKind::Result(my_result),
609
                owner: crate::TypeOwner::None,
610
                docs: Default::default(),
611
                stability: Default::default(),
612
                span: Default::default(),
613
                external_id: Default::default(),
614
            })
615
            .unwrap();
616
        assert_eq!(elem.size, ArchitectureSize::new(8, 2));
617
        assert_eq!(elem.align, Alignment::Pointer);
618
    }
619
620
    #[test]
621
    fn fixed_length_list_size_overflow_returns_error_instead_of_panicking() {
622
        // Regression test: a fixed-length list whose element-size * length
623
        // overflows `usize` used to panic via `.unwrap()` on a `None` from
624
        // `checked_mul`. It must now return an `Err` instead.
625
        let mut obj = SizeAlign::default();
626
        let mut resolve = Resolve::default();
627
628
        // `type a = list<u64, 4294967295>;`
629
        let a = resolve.types.alloc(TypeDef {
630
            name: None,
631
            kind: TypeDefKind::FixedLengthList(Type::U64, u32::MAX),
632
            owner: crate::TypeOwner::None,
633
            docs: Default::default(),
634
            stability: Default::default(),
635
            span: Default::default(),
636
            external_id: Default::default(),
637
        });
638
        // `type b = list<a, 4294967295>;` -- `a`'s size (8 * u32::MAX) times
639
        // `u32::MAX` again overflows `usize` on both 32- and 64-bit targets.
640
        resolve.types.alloc(TypeDef {
641
            name: None,
642
            kind: TypeDefKind::FixedLengthList(Type::Id(a), u32::MAX),
643
            owner: crate::TypeOwner::None,
644
            docs: Default::default(),
645
            stability: Default::default(),
646
            span: Default::default(),
647
            external_id: Default::default(),
648
        });
649
650
        let err = obj.fill(&resolve).unwrap_err();
651
        assert!(err.to_string().contains("overflows"));
652
653
        // A benign, non-overflowing fixed-length list still computes fine.
654
        let mut obj = SizeAlign::default();
655
        let mut resolve = Resolve::default();
656
        resolve.types.alloc(TypeDef {
657
            name: None,
658
            kind: TypeDefKind::FixedLengthList(Type::U64, 2),
659
            owner: crate::TypeOwner::None,
660
            docs: Default::default(),
661
            stability: Default::default(),
662
            span: Default::default(),
663
            external_id: Default::default(),
664
        });
665
        obj.fill(&resolve).unwrap();
666
    }
667
    #[test]
668
    fn result_ptr_64bit() {
669
        let obj = SizeAlign::default();
670
        let my_record = crate::Record {
671
            fields: vec![
672
                crate::Field {
673
                    name: String::new(),
674
                    ty: Type::String,
675
                    docs: Default::default(),
676
                    span: Default::default(),
677
                },
678
                crate::Field {
679
                    name: String::new(),
680
                    ty: Type::U64,
681
                    docs: Default::default(),
682
                    span: Default::default(),
683
                },
684
            ],
685
        };
686
        let elem = obj
687
            .calculate(&TypeDef {
688
                name: None,
689
                kind: TypeDefKind::Record(my_record),
690
                owner: crate::TypeOwner::None,
691
                docs: Default::default(),
692
                stability: Default::default(),
693
                span: Default::default(),
694
                external_id: Default::default(),
695
            })
696
            .unwrap();
697
        assert_eq!(elem.size, ArchitectureSize::new(8, 2));
698
        assert_eq!(elem.align, Alignment::Bytes(NonZeroUsize::new(8).unwrap()));
699
    }
700
}