1 // SPDX-License-Identifier: Apache-2.0 OR MIT 2 3 use proc_macro2::{Span, TokenStream}; 4 use quote::{format_ident, quote}; 5 use syn::{ 6 braced, 7 parse::{End, Parse}, 8 parse_quote, 9 punctuated::Punctuated, 10 spanned::Spanned, 11 token, Attribute, Block, Expr, ExprCall, ExprPath, Ident, Path, Token, Type, 12 }; 13 14 use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; 15 16 pub(crate) struct Initializer { 17 attrs: Vec<InitializerAttribute>, 18 this: Option<This>, 19 path: Path, 20 brace_token: token::Brace, 21 fields: Punctuated<InitializerField, Token![,]>, 22 rest: Option<(Token![..], Expr)>, 23 error: Option<(Token![?], Type)>, 24 } 25 26 struct This { 27 _and_token: Token![&], 28 ident: Ident, 29 _in_token: Token![in], 30 } 31 32 struct InitializerField { 33 attrs: Vec<Attribute>, 34 kind: InitializerKind, 35 } 36 37 enum InitializerKind { 38 Value { 39 ident: Ident, 40 value: Option<(Token![:], Expr)>, 41 }, 42 Init { 43 ident: Ident, 44 _left_arrow_token: Token![<-], 45 value: Expr, 46 }, 47 Code { 48 _underscore_token: Token![_], 49 _colon_token: Token![:], 50 block: Block, 51 }, 52 } 53 54 impl InitializerKind { 55 fn ident(&self) -> Option<&Ident> { 56 match self { 57 Self::Value { ident, .. } | Self::Init { ident, .. } => Some(ident), 58 Self::Code { .. } => None, 59 } 60 } 61 } 62 63 enum InitializerAttribute { 64 DefaultError(DefaultErrorAttribute), 65 } 66 67 struct DefaultErrorAttribute { 68 ty: Box<Type>, 69 } 70 71 pub(crate) fn expand( 72 Initializer { 73 attrs, 74 this, 75 path, 76 brace_token, 77 fields, 78 rest, 79 error, 80 }: Initializer, 81 default_error: Option<&'static str>, 82 pinned: bool, 83 dcx: &mut DiagCtxt, 84 ) -> Result<TokenStream, ErrorGuaranteed> { 85 let error = error.map_or_else( 86 || { 87 if let Some(default_error) = attrs.iter().fold(None, |acc, attr| { 88 #[expect(irrefutable_let_patterns)] 89 if let InitializerAttribute::DefaultError(DefaultErrorAttribute { ty }) = attr { 90 Some(ty.clone()) 91 } else { 92 acc 93 } 94 }) { 95 default_error 96 } else if let Some(default_error) = default_error { 97 syn::parse_str(default_error).unwrap() 98 } else { 99 dcx.error(brace_token.span.close(), "expected `? <type>` after `}`"); 100 parse_quote!(::core::convert::Infallible) 101 } 102 }, 103 |(_, err)| Box::new(err), 104 ); 105 let slot = format_ident!("slot"); 106 let (has_data_trait, get_data, init_from_closure) = if pinned { 107 ( 108 format_ident!("HasPinData"), 109 format_ident!("__pin_data"), 110 format_ident!("pin_init_from_closure"), 111 ) 112 } else { 113 ( 114 format_ident!("HasInitData"), 115 format_ident!("__init_data"), 116 format_ident!("init_from_closure"), 117 ) 118 }; 119 let init_kind = get_init_kind(rest, dcx); 120 let zeroable_check = match init_kind { 121 InitKind::Normal => quote!(), 122 InitKind::Zeroing => quote! { 123 // The user specified `..Zeroable::zeroed()` at the end of the list of fields. 124 // Therefore we check if the struct implements `Zeroable` and then zero the memory. 125 // This allows us to also remove the check that all fields are present (since we 126 // already set the memory to zero and that is a valid bit pattern). 127 fn assert_zeroable<T: ?::core::marker::Sized>(_: *mut T) 128 where T: ::pin_init::Zeroable 129 {} 130 // Ensure that the struct is indeed `Zeroable`. 131 assert_zeroable(#slot); 132 // SAFETY: The type implements `Zeroable` by the check above. 133 unsafe { ::core::ptr::write_bytes(#slot, 0, 1) }; 134 }, 135 }; 136 let this = match this { 137 None => quote!(), 138 Some(This { ident, .. }) => quote! { 139 // Create the `this` so it can be referenced by the user inside of the 140 // expressions creating the individual fields. 141 let #ident = unsafe { ::core::ptr::NonNull::new_unchecked(slot) }; 142 }, 143 }; 144 // `mixed_site` ensures that the data is not accessible to the user-controlled code. 145 let data = Ident::new("__data", Span::mixed_site()); 146 let init_fields = init_fields(&fields, pinned, &data, &slot); 147 let field_check = make_field_check(&fields, init_kind, &path); 148 Ok(quote! {{ 149 // Get the data about fields from the supplied type. 150 // SAFETY: TODO 151 let #data = unsafe { 152 use ::pin_init::__internal::#has_data_trait; 153 // Can't use `<#path as #has_data_trait>::#get_data`, since the user is able to omit 154 // generics (which need to be present with that syntax). 155 #path::#get_data() 156 }; 157 // Ensure that `#data` really is of type `#data` and help with type inference: 158 let init = #data.__make_closure::<_, #error>( 159 move |slot| { 160 #zeroable_check 161 #this 162 #init_fields 163 #field_check 164 // SAFETY: we are the `init!` macro that is allowed to call this. 165 Ok(unsafe { ::pin_init::__internal::InitOk::new() }) 166 } 167 ); 168 let init = move |slot| -> ::core::result::Result<(), #error> { 169 init(slot).map(|__InitOk| ()) 170 }; 171 // SAFETY: TODO 172 unsafe { ::pin_init::#init_from_closure::<_, #error>(init) } 173 }}) 174 } 175 176 enum InitKind { 177 Normal, 178 Zeroing, 179 } 180 181 fn get_init_kind(rest: Option<(Token![..], Expr)>, dcx: &mut DiagCtxt) -> InitKind { 182 let Some((dotdot, expr)) = rest else { 183 return InitKind::Normal; 184 }; 185 match &expr { 186 Expr::Call(ExprCall { func, args, .. }) if args.is_empty() => match &**func { 187 Expr::Path(ExprPath { 188 attrs, 189 qself: None, 190 path: 191 Path { 192 leading_colon: None, 193 segments, 194 }, 195 }) if attrs.is_empty() 196 && segments.len() == 2 197 && segments[0].ident == "Zeroable" 198 && segments[0].arguments.is_none() 199 && segments[1].ident == "init_zeroed" 200 && segments[1].arguments.is_none() => 201 { 202 return InitKind::Zeroing; 203 } 204 _ => {} 205 }, 206 _ => {} 207 } 208 dcx.error( 209 dotdot.span().join(expr.span()).unwrap_or(expr.span()), 210 "expected nothing or `..Zeroable::init_zeroed()`.", 211 ); 212 InitKind::Normal 213 } 214 215 /// Generate the code that initializes the fields of the struct using the initializers in `field`. 216 fn init_fields( 217 fields: &Punctuated<InitializerField, Token![,]>, 218 pinned: bool, 219 data: &Ident, 220 slot: &Ident, 221 ) -> TokenStream { 222 let mut guards = vec![]; 223 let mut guard_attrs = vec![]; 224 let mut res = TokenStream::new(); 225 for InitializerField { attrs, kind } in fields { 226 let cfgs = { 227 let mut cfgs = attrs.clone(); 228 cfgs.retain(|attr| attr.path().is_ident("cfg")); 229 cfgs 230 }; 231 232 let ident = match kind { 233 InitializerKind::Value { ident, .. } => ident, 234 InitializerKind::Init { ident, .. } => ident, 235 InitializerKind::Code { block, .. } => { 236 let stmt = &block.stmts; 237 res.extend(quote! { 238 #(#attrs)* 239 { 240 #(#stmt)* 241 } 242 }); 243 continue; 244 } 245 }; 246 247 let slot = if pinned { 248 quote! { 249 // SAFETY: 250 // - `slot` is valid and properly aligned. 251 // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. 252 // - `make_field_check` prevents `#ident` from being used twice, therefore 253 // `(*slot).#ident` is exclusively accessed and has not been initialized. 254 (unsafe { #data.#ident(#slot) }) 255 } 256 } else { 257 quote! { 258 // For `init!()` macro, everything is unpinned. 259 // SAFETY: 260 // - `&raw mut (*slot).#ident` is valid. 261 // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned. 262 // - `make_field_check` prevents `#ident` from being used twice, therefore 263 // `(*slot).#ident` is exclusively accessed and has not been initialized. 264 (unsafe { 265 ::pin_init::__internal::Slot::<::pin_init::__internal::Unpinned, _>::new( 266 &raw mut (*#slot).#ident 267 ) 268 }) 269 } 270 }; 271 272 // `mixed_site` ensures that the guard is not accessible to the user-controlled code. 273 let guard = format_ident!("__{ident}_guard", span = Span::mixed_site()); 274 275 let init = match kind { 276 InitializerKind::Value { ident, value } => { 277 let value = value 278 .as_ref() 279 .map(|(_, value)| quote!(#value)) 280 .unwrap_or_else(|| quote!(#ident)); 281 282 quote! { 283 #(#attrs)* 284 let mut #guard = #slot.write(#value); 285 286 } 287 } 288 InitializerKind::Init { value, .. } => { 289 quote! { 290 #(#attrs)* 291 let mut #guard = #slot.init(#value)?; 292 } 293 } 294 InitializerKind::Code { .. } => unreachable!(), 295 }; 296 297 res.extend(quote! { 298 #init 299 300 #(#cfgs)* 301 // Allow `non_snake_case` since the same warning is going to be reported for the struct 302 // field. 303 #[allow(unused_variables, non_snake_case)] 304 let #ident = #guard.let_binding(); 305 }); 306 307 guards.push(guard); 308 guard_attrs.push(cfgs); 309 } 310 quote! { 311 #res 312 // If execution reaches this point, all fields have been initialized. Therefore we can now 313 // dismiss the guards by forgetting them. 314 #( 315 #(#guard_attrs)* 316 ::core::mem::forget(#guards); 317 )* 318 } 319 } 320 321 /// Generate the check for ensuring that every field has been initialized and aligned. 322 fn make_field_check( 323 fields: &Punctuated<InitializerField, Token![,]>, 324 init_kind: InitKind, 325 path: &Path, 326 ) -> TokenStream { 327 let field_attrs: Vec<_> = fields 328 .iter() 329 .filter_map(|f| f.kind.ident().map(|_| &f.attrs)) 330 .collect(); 331 let field_name: Vec<_> = fields.iter().filter_map(|f| f.kind.ident()).collect(); 332 let zeroing_trailer = match init_kind { 333 InitKind::Normal => None, 334 InitKind::Zeroing => Some(quote! { 335 ..::core::mem::zeroed() 336 }), 337 }; 338 quote! { 339 #[allow(unreachable_code)] 340 // We use unreachable code to perform field checks. They're still checked by the compiler. 341 // SAFETY: this code is never executed. 342 let _ = || unsafe { 343 // Create references to ensure that the initialized field is properly aligned. 344 // Unaligned fields will cause the compiler to emit E0793. We do not support 345 // unaligned fields since `Init::__init` requires an aligned pointer; the call to 346 // `ptr::write` for value-initialization case has the same requirement. 347 #( 348 #(#field_attrs)* 349 let _ = &(*slot).#field_name; 350 )* 351 352 // If the zeroing trailer is not present, this checks that all fields have been 353 // mentioned exactly once. If the zeroing trailer is present, all missing fields will be 354 // zeroed, so this checks that all fields have been mentioned at most once. The use of 355 // struct initializer will still generate very natural error messages for any misuse. 356 ::core::ptr::write(slot, #path { 357 #( 358 #(#field_attrs)* 359 #field_name: loop {}, 360 )* 361 #zeroing_trailer 362 }) 363 }; 364 } 365 } 366 367 impl Parse for Initializer { 368 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> { 369 let attrs = input.call(Attribute::parse_outer)?; 370 let this = input.peek(Token![&]).then(|| input.parse()).transpose()?; 371 let path = input.parse()?; 372 let content; 373 let brace_token = braced!(content in input); 374 let mut fields = Punctuated::new(); 375 loop { 376 let lh = content.lookahead1(); 377 if lh.peek(End) || lh.peek(Token![..]) { 378 break; 379 } else if lh.peek(Ident) || lh.peek(Token![_]) || lh.peek(Token![#]) { 380 fields.push_value(content.parse()?); 381 let lh = content.lookahead1(); 382 if lh.peek(End) { 383 break; 384 } else if lh.peek(Token![,]) { 385 fields.push_punct(content.parse()?); 386 } else { 387 return Err(lh.error()); 388 } 389 } else { 390 return Err(lh.error()); 391 } 392 } 393 let rest = content 394 .peek(Token![..]) 395 .then(|| Ok::<_, syn::Error>((content.parse()?, content.parse()?))) 396 .transpose()?; 397 let error = input 398 .peek(Token![?]) 399 .then(|| Ok::<_, syn::Error>((input.parse()?, input.parse()?))) 400 .transpose()?; 401 let attrs = attrs 402 .into_iter() 403 .map(|a| { 404 if a.path().is_ident("default_error") { 405 a.parse_args::<DefaultErrorAttribute>() 406 .map(InitializerAttribute::DefaultError) 407 } else { 408 Err(syn::Error::new_spanned(a, "unknown initializer attribute")) 409 } 410 }) 411 .collect::<Result<Vec<_>, _>>()?; 412 Ok(Self { 413 attrs, 414 this, 415 path, 416 brace_token, 417 fields, 418 rest, 419 error, 420 }) 421 } 422 } 423 424 impl Parse for DefaultErrorAttribute { 425 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> { 426 Ok(Self { ty: input.parse()? }) 427 } 428 } 429 430 impl Parse for This { 431 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> { 432 Ok(Self { 433 _and_token: input.parse()?, 434 ident: input.parse()?, 435 _in_token: input.parse()?, 436 }) 437 } 438 } 439 440 impl Parse for InitializerField { 441 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> { 442 let attrs = input.call(Attribute::parse_outer)?; 443 Ok(Self { 444 attrs, 445 kind: input.parse()?, 446 }) 447 } 448 } 449 450 impl Parse for InitializerKind { 451 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> { 452 let lh = input.lookahead1(); 453 if lh.peek(Token![_]) { 454 Ok(Self::Code { 455 _underscore_token: input.parse()?, 456 _colon_token: input.parse()?, 457 block: input.parse()?, 458 }) 459 } else if lh.peek(Ident) { 460 let ident = input.parse()?; 461 let lh = input.lookahead1(); 462 if lh.peek(Token![<-]) { 463 Ok(Self::Init { 464 ident, 465 _left_arrow_token: input.parse()?, 466 value: input.parse()?, 467 }) 468 } else if lh.peek(Token![:]) { 469 Ok(Self::Value { 470 ident, 471 value: Some((input.parse()?, input.parse()?)), 472 }) 473 } else if lh.peek(Token![,]) || lh.peek(End) { 474 Ok(Self::Value { ident, value: None }) 475 } else { 476 Err(lh.error()) 477 } 478 } else { 479 Err(lh.error()) 480 } 481 } 482 } 483