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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
//! Contains the emit for the unchecked builder type.
//!
//! This represents the core logic that the safe builder is built on top of.
use proc_macro2::TokenStream;
use syn::spanned::Spanned as _;
use super::{BUILDER_BUILD_MUST_USE, BUILDER_MUST_USE, EmitContext};
use crate::model::*;
use crate::util::*;
pub fn emit_unchecked(ctx: &EmitContext<'_>) -> TokenStream {
let EmitContext {
target,
builder,
builder_vis,
unchecked_builder,
unchecked_builder_vis,
impl_generics,
ty_generics,
struct_generics,
where_clause,
fields,
..
} = ctx;
let builder_doc = format!("An _unchecked_ builder type for [`{target}`].");
let field_generics1 = fields.gen_names();
let field_generics2 = fields.gen_names();
let field_default_names = fields
.iter()
.filter(|f| f.default.is_some())
.map(|f| &f.name);
let field_default_values = fields.iter().filter_map(|f| f.default.as_deref());
let field_setters = emit_unchecked_fields(ctx);
let structure_check = emit_structure_check(ctx);
let deprecated_field = fields.iter().find_map(|f| f.deprecated);
let allow_deprecated_field = allow_deprecated(deprecated_field);
quote::quote! {
#[doc = #builder_doc]
///
/// This version being _unchecked_ means it has less safety guarantees:
/// - No tracking is done whether fields are initialized, so [`Self::build`] is `unsafe`.
/// - If dropped, already initialized fields will be leaked.
/// - The same field can be set multiple times. If done, the old value will be leaked.
#[repr(transparent)]
#[must_use = #BUILDER_MUST_USE]
#unchecked_builder_vis struct #unchecked_builder < #struct_generics > #where_clause {
/// Honestly, don't use this directly.
///
/// Using this field directly is equivalent to using [`Self::as_uninit`].
inner: ::core::mem::MaybeUninit< #target < #ty_generics > >,
}
impl < #impl_generics > #unchecked_builder < #ty_generics > #where_clause {
/// Creates a new unchecked builder.
///
/// All default builder values will be set already.
#[inline]
pub const fn new() -> Self {
#allow_deprecated_field
Self { inner: ::core::mem::MaybeUninit::uninit() }
#( . #field_default_names ( #field_default_values ) )*
}
/// Asserts that the fields specified by the const generics as well as all optional
/// fields are initialized and promotes this value into a checked builder.
///
/// # Safety
///
/// The fields whose const generic are `true` and all optional fields have to be
/// initialized.
///
/// Optional fields are initialized by [`Self::new`] by default, however using
/// [`Self::as_uninit`] allows de-initializing them. This means that this function
/// isn't even necessarily safe to call if all const generics are `false`.
#[inline]
#builder_vis const unsafe fn assert_init < #(const #field_generics1: ::core::primitive::bool),* > (self) -> #builder < #ty_generics #(#field_generics2),* > {
#builder {
inner: self,
_unsafe: (),
}
}
/// Returns the finished value.
///
/// # Safety
///
/// This function requires that all fields have been initialized.
#[must_use = #BUILDER_BUILD_MUST_USE]
#[inline]
pub const unsafe fn build(self) -> #target < #ty_generics > {
#structure_check
unsafe {
// SAFETY: caller promises that all fields are initialized
::core::mem::MaybeUninit::assume_init(self.inner)
}
}
/// Gets a mutable reference to the partially initialized data.
#[inline]
pub const fn as_uninit(&mut self) -> &mut ::core::mem::MaybeUninit< #target < #ty_generics > > {
&mut self.inner
}
#field_setters
}
}
}
fn emit_unchecked_fields(ctx: &EmitContext<'_>) -> TokenStream {
let EmitContext { fields, packed, .. } = ctx;
let mut output = TokenStream::new();
let write_ident = if *packed {
simple_ident("write_unaligned")
} else {
simple_ident("write")
};
for FieldInfo {
ident,
name,
ty,
vis,
doc,
deprecated,
..
} in *fields
{
let allow_deprecated = allow_deprecated(*deprecated);
output.extend(quote::quote_spanned! {ident.span()=>
#(#doc)*
#deprecated
#[inline]
// may trigger when field names begin with underscores
#[allow(clippy::used_underscore_binding)]
#vis const fn #name(mut self, value: #ty) -> Self
where
#ty: ::core::marker::Sized,
{
unsafe {
// SAFETY: the value pointed to is in bounds of the object. if `repr(packed)`,
// this uses an unaligned write, otherwise the pointer is aligned for the value
::core::ptr::#write_ident(
#allow_deprecated
&raw mut (*::core::mem::MaybeUninit::as_mut_ptr(&mut self.inner)).#ident,
value,
);
}
self
}
});
}
output
}
fn emit_structure_check(ctx: &EmitContext<'_>) -> TokenStream {
let EmitContext {
target,
impl_generics,
ty_generics,
where_clause,
fields,
packed,
..
} = ctx;
let field_idents1 = fields.iter().map(|f| f.ident);
let field_idents2 = field_idents1.clone();
let field_idents3 = field_idents1.clone();
let field_tys1 = fields.iter().map(|f| f.ty);
let field_tys2 = field_tys1.clone();
let field_alignment_check = if *packed {
TokenStream::new()
} else {
// note: the goal here is to check that no other proc macro attribute added
// `repr(packed)` in such a way that we didn't get to see it. emitting the
// non-packed code for a packed struct would lead to UB.
// the inverse, i.e. emitting packed code for a non-packed struct, however is
// fine. that only adds a few restrictions and unaligned writes, so at worst
// it's suboptimal, but still correct.
quote::quote! {
fn _all_fields_aligned < #impl_generics > ( value: &#target < #ty_generics > ) #where_clause {
#(_ = &value.#field_idents3;)*
}
}
};
quote::quote! {
#[allow(
// triggers if any field is deprecated, but that doesn't matter here
deprecated,
// these may trigger due to the signature and field/type names
clippy::too_many_arguments,
clippy::multiple_bound_locations,
clippy::type_repetition_in_bounds,
clippy::used_underscore_binding,
)]
const {
// statically validate that the macro-seen fields match the final struct.
// this ensures that the set of fields seen by the macro matches the final struct and
// there is no undefined behavior due to asserting additional, uninitialized fields as
// initialized because this macro didn't know about them.
fn _derive_includes_every_field < #impl_generics > ( #( #field_idents1: #field_tys1 ),* ) -> #target < #ty_generics >
#where_clause, #(#field_tys2: ::core::marker::Sized),*
{
#target { #(#field_idents2),* }
}
#field_alignment_check
}
}
}