Fold `size_of::<T>()` and `align_of::<T>()` in an array length
Both are compile-time constants in Rust and in Nim, where they are spelled `sizeof` and `alignof`, and both languages get the answer from the same C ABI -- so these are equal by construction rather than by assumption, on the same footing as the `c_type` names. Found by the dependency run, in a `[u8; size_of::<T>()]` buffer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4cebd9c parent: 6c57571 modified
src/ty.rs +39 -0 | @@ -569,6 +569,45 @@ pub fn const_expr(e: &syn::Expr) -> Result<String, String> { | ||
| 569 | 569 | const_expr(&b.right)? |
| 570 | 570 | )) |
| 571 | 571 | } |
| 572 | + // `size_of::<T>()` and `align_of::<T>()` are compile-time constants in | |
| 573 | + // both languages, spelled `sizeof` and `alignof` in Nim, and both are | |
| 574 | + // defined by the same C ABI -- so these are equal by construction | |
| 575 | + // rather than by assumption, like the `c_type` names. | |
| 576 | + Expr::Call(c) => { | |
| 577 | + let Expr::Path(p) = &*c.func else { | |
| 578 | + return Err(format!( | |
| 579 | + "array length calls something rustnim cannot fold: `{}`", | |
| 580 | + quote::quote!(#e) | |
| 581 | + )); | |
| 582 | + }; | |
| 583 | + let seg = p | |
| 584 | + .path | |
| 585 | + .segments | |
| 586 | + .last() | |
| 587 | + .ok_or_else(|| "empty path in an array length".to_string())?; | |
| 588 | + let nim = match seg.ident.to_string().as_str() { | |
| 589 | + "size_of" => "sizeof", | |
| 590 | + "align_of" => "alignof", | |
| 591 | + other => { | |
| 592 | + return Err(format!( | |
| 593 | + "array length calls `{other}`, which rustnim cannot \ | |
| 594 | + evaluate at compile time" | |
| 595 | + )) | |
| 596 | + } | |
| 597 | + }; | |
| 598 | + let PathArguments::AngleBracketed(a) = &seg.arguments else { | |
| 599 | + return Err(format!("`{nim}` in an array length needs a type argument")); | |
| 600 | + }; | |
| 601 | + let t = a | |
| 602 | + .args | |
| 603 | + .iter() | |
| 604 | + .find_map(|g| match g { | |
| 605 | + GenericArgument::Type(t) => Some(map(t)), | |
| 606 | + _ => None, | |
| 607 | + }) | |
| 608 | + .ok_or_else(|| format!("`{nim}` in an array length needs a type argument"))??; | |
| 609 | + Ok(format!("{}({})", nim, t.render())) | |
| 610 | + } | |
| 572 | 611 | Expr::Cast(c) => const_expr(&c.expr), |
| 573 | 612 | other => Err(format!( |
| 574 | 613 | "array length is not a const expression rustnim can render: `{}`", |
| @@ -569,6 +569,45 @@ pub fn const_expr(e: &syn::Expr) -> Result<String, String> { | |||
| 569 | const_expr(&b.right)? | 569 | const_expr(&b.right)? |
| 570 | )) | 570 | )) |
| 571 | } | 571 | } |
| 572 | + // `size_of::<T>()` and `align_of::<T>()` are compile-time constants in | ||
| 573 | + // both languages, spelled `sizeof` and `alignof` in Nim, and both are | ||
| 574 | + // defined by the same C ABI -- so these are equal by construction | ||
| 575 | + // rather than by assumption, like the `c_type` names. | ||
| 576 | + Expr::Call(c) => { | ||
| 577 | + let Expr::Path(p) = &*c.func else { | ||
| 578 | + return Err(format!( | ||
| 579 | + "array length calls something rustnim cannot fold: `{}`", | ||
| 580 | + quote::quote!(#e) | ||
| 581 | + )); | ||
| 582 | + }; | ||
| 583 | + let seg = p | ||
| 584 | + .path | ||
| 585 | + .segments | ||
| 586 | + .last() | ||
| 587 | + .ok_or_else(|| "empty path in an array length".to_string())?; | ||
| 588 | + let nim = match seg.ident.to_string().as_str() { | ||
| 589 | + "size_of" => "sizeof", | ||
| 590 | + "align_of" => "alignof", | ||
| 591 | + other => { | ||
| 592 | + return Err(format!( | ||
| 593 | + "array length calls `{other}`, which rustnim cannot \ | ||
| 594 | + evaluate at compile time" | ||
| 595 | + )) | ||
| 596 | + } | ||
| 597 | + }; | ||
| 598 | + let PathArguments::AngleBracketed(a) = &seg.arguments else { | ||
| 599 | + return Err(format!("`{nim}` in an array length needs a type argument")); | ||
| 600 | + }; | ||
| 601 | + let t = a | ||
| 602 | + .args | ||
| 603 | + .iter() | ||
| 604 | + .find_map(|g| match g { | ||
| 605 | + GenericArgument::Type(t) => Some(map(t)), | ||
| 606 | + _ => None, | ||
| 607 | + }) | ||
| 608 | + .ok_or_else(|| format!("`{nim}` in an array length needs a type argument"))??; | ||
| 609 | + Ok(format!("{}({})", nim, t.render())) | ||
| 610 | + } | ||
| 572 | Expr::Cast(c) => const_expr(&c.expr), | 611 | Expr::Cast(c) => const_expr(&c.expr), |
| 573 | other => Err(format!( | 612 | other => Err(format!( |
| 574 | "array length is not a const expression rustnim can render: `{}`", | 613 | "array length is not a const expression rustnim can render: `{}`", |
modified
tests/cases/052-const-array-len.rs +5 -0 | @@ -29,4 +29,9 @@ fn main() { | ||
| 29 | 29 | |
| 30 | 30 | let z: [u16; BLOCK + 1] = [3u16; BLOCK + 1]; |
| 31 | 31 | println!("{} {}", z.len(), z[4]); |
| 32 | + | |
| 33 | + // `size_of::<T>()` is a compile-time constant in both languages, and both | |
| 34 | + // get the answer from the same C ABI. | |
| 35 | + let w: [u8; core::mem::size_of::<u32>()] = [5u8; core::mem::size_of::<u32>()]; | |
| 36 | + println!("{} {}", w.len(), sum(&w)); | |
| 32 | 37 | } |
| @@ -29,4 +29,9 @@ fn main() { | |||
| 29 | 29 | ||
| 30 | let z: [u16; BLOCK + 1] = [3u16; BLOCK + 1]; | 30 | let z: [u16; BLOCK + 1] = [3u16; BLOCK + 1]; |
| 31 | println!("{} {}", z.len(), z[4]); | 31 | println!("{} {}", z.len(), z[4]); |
| 32 | + | ||
| 33 | + // `size_of::<T>()` is a compile-time constant in both languages, and both | ||
| 34 | + // get the answer from the same C ABI. | ||
| 35 | + let w: [u8; core::mem::size_of::<u32>()] = [5u8; core::mem::size_of::<u32>()]; | ||
| 36 | + println!("{} {}", w.len(), sum(&w)); | ||
| 32 | } | 37 | } |