This commit is contained in:
João Matos 2025-09-12 08:39:53 +00:00 committed by GitHub
commit ee38b34ec9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 143 additions and 13 deletions

View file

@ -193,7 +193,7 @@ by using any of the following commands in a terminal:
* [Trace](#trace)
* [Memory-unsafe code](#memory-unsafe-code)
* [Structs with reference fields](#structs-with-reference-fields)
* [sizeof and __offsetof](#sizeof-and-__offsetof)
* [sizeof, alignof and __offsetof](#sizeof-alignof-and-__offsetof)
* [Limited operator overloading](#limited-operator-overloading)
* [Performance tuning](#performance-tuning)
* [Atomics](#atomics)
@ -6980,9 +6980,10 @@ println(baz)
println(qux)
```
## sizeof and __offsetof
## sizeof, alignof and __offsetof
* `sizeof(Type)` gives the size of a type in bytes.
* `alignof(Type)` gives the alignment of a type in bytes.
* `__offsetof(Struct, field_name)` gives the offset in bytes of a struct field.
```v
@ -6992,6 +6993,7 @@ struct Foo {
}
assert sizeof(Foo) == 8
assert alignof[Foo]() == 4
assert __offsetof(Foo, a) == 0
assert __offsetof(Foo, b) == 4
```

View file

@ -33,6 +33,7 @@ pub const int_type_name = $if new_int ? {
}
pub type Expr = NodeError
| AlignOf
| AnonFn
| ArrayDecompose
| ArrayInit
@ -1956,6 +1957,16 @@ pub mut:
scope &Scope = unsafe { nil }
}
pub struct AlignOf {
pub:
guessed_type bool // a legacy `alignof( GuessedType )` => a deprecation notice, suggesting `v fmt -w .` => `alignof[ Type ]()`
is_type bool
pos token.Pos
pub mut:
expr Expr // checker uses this to set typ, when !is_type
typ Type
}
pub struct SizeOf {
pub:
guessed_type bool // a legacy `sizeof( GuessedType )` => a deprecation notice, suggesting `v fmt -w .` => `sizeof[ Type ]()`
@ -2275,11 +2286,11 @@ pub fn (expr Expr) pos() token.Pos {
// println('compiler bug, unhandled EmptyExpr pos()')
token.Pos{}
}
NodeError, ArrayDecompose, ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr,
CastExpr, ChanInit, CharLiteral, ConcatExpr, Comment, ComptimeCall, ComptimeSelector,
EnumVal, DumpExpr, FloatLiteral, GoExpr, SpawnExpr, Ident, IfExpr, IntegerLiteral,
IsRefType, Likely, LockExpr, MapInit, MatchExpr, None, OffsetOf, OrExpr, ParExpr,
PostfixExpr, PrefixExpr, RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr,
NodeError, AlignOf, ArrayDecompose, ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral,
CallExpr, CastExpr, ChanInit, CharLiteral, ConcatExpr, Comment, ComptimeCall,
ComptimeSelector, EnumVal, DumpExpr, FloatLiteral, GoExpr, SpawnExpr, Ident, IfExpr,
IntegerLiteral, IsRefType, Likely, LockExpr, MapInit, MatchExpr, None, OffsetOf, OrExpr,
ParExpr, PostfixExpr, PrefixExpr, RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr,
StringInterLiteral, StringLiteral, StructInit, TypeNode, TypeOf, UnsafeExpr, ComptimeType,
LambdaExpr, Nil {
expr.pos
@ -2817,7 +2828,7 @@ pub fn (expr Expr) is_literal() bool {
!expr.has_arg && expr.expr.is_literal() && (expr.typ.is_any_kind_of_pointer()
|| expr.typ in [i8_type, i16_type, int_type, i64_type, u8_type, u16_type, u32_type, u64_type, f32_type, f64_type, char_type, bool_type, rune_type])
}
SizeOf, IsRefType {
AlignOf, SizeOf, IsRefType {
expr.is_type || expr.expr.is_literal()
}
else {

View file

@ -413,6 +413,12 @@ pub fn (x Expr) str() string {
stdatomic.sub_i64(&nested_expr_str_calls, 1)
}
match x {
AlignOf {
if x.is_type {
return 'alignof(${global_table.type_to_str(x.typ)})'
}
return 'alignof(${x.expr.str()})'
}
AnonFn {
return 'anon_fn'
}

View file

@ -1310,6 +1310,13 @@ pub fn (t &Table) type_size(typ Type) (int, int) {
return size, align
}
// type_align returns the alignment (in bytes) of `typ`, just like C's alignof().
pub fn (t &Table) type_align(typ Type) int {
// we only care about the second (alignment) return value
_, align := t.type_size(typ)
return align
}
// round_up rounds the number `n` up to the next multiple `multiple`.
// Note: `multiple` must be a power of 2.
@[inline]

View file

@ -3288,7 +3288,7 @@ pub fn (mut c Checker) expr(mut node ast.Expr) ast.Type {
}
return ret_type
}
ast.SizeOf {
ast.AlignOf, ast.SizeOf {
if !node.is_type {
node.typ = c.expr(mut node.expr)
}

View file

@ -435,6 +435,10 @@ fn (mut c Checker) eval_comptime_const_expr(expr ast.Expr, nlevel int) ?ast.Comp
return val
}
}
ast.AlignOf {
a := c.table.type_align(expr.typ)
return i64(a)
}
ast.SizeOf {
s, _ := c.table.type_size(c.unwrap_generic(expr.typ))
return i64(s)

View file

@ -491,6 +491,9 @@ pub fn (mut e Eval) expr(expr ast.Expr, expecting ast.Type) Object {
// eprintln('unhandled struct init at line $expr.pos.line_nr')
return ''
}
ast.AlignOf {
return Uint{e.type_to_align(expr.typ), 64}
}
ast.SizeOf {
return Uint{e.type_to_size(expr.typ), 64}
}
@ -635,6 +638,12 @@ fn (e &Eval) type_to_size(typ ast.Type) u64 {
}
}
// type_to_align returns the natural alignment (in bits) of `typ`.
fn (e &Eval) type_to_align(typ ast.Type) u64 {
// on most ABIs the alignment of a type == its size
return e.type_to_size(typ)
}
fn (e &Eval) get_escape(r rune) rune {
res := match r {
`\\` {

View file

@ -572,6 +572,9 @@ pub fn (mut f Fmt) expr(node_ ast.Expr) {
match mut node {
ast.NodeError {}
ast.EmptyExpr {}
ast.AlignOf {
f.align_of(node)
}
ast.AnonFn {
f.anon_fn(node)
}
@ -2996,6 +2999,20 @@ pub fn (mut f Fmt) selector_expr(node ast.SelectorExpr) {
f.or_expr(node.or_block)
}
pub fn (mut f Fmt) align_of(node ast.AlignOf) {
f.write('alignof')
if node.is_type {
f.write('[')
f.write(f.table.type_to_str_using_aliases(node.typ, f.mod2alias))
f.write(']()')
return
} else {
f.write('(')
f.expr(node.expr)
f.write(')')
}
}
pub fn (mut f Fmt) size_of(node ast.SizeOf) {
f.write('sizeof')
if node.is_type && !node.guessed_type {

View file

@ -3624,6 +3624,9 @@ fn (mut g Gen) expr(node_ ast.Expr) {
ast.EmptyExpr {
g.error('g.expr(): unhandled EmptyExpr', token.Pos{})
}
ast.AlignOf {
g.align_of(node)
}
ast.AnonFn {
g.gen_anon_fn(mut node)
}
@ -7738,6 +7741,17 @@ fn (mut g Gen) get_type(typ ast.Type) ast.Type {
return if typ == g.field_data_type { g.comptime.comptime_for_field_value.typ } else { typ }
}
fn (mut g Gen) align_of(node ast.AlignOf) {
typ := g.type_resolver.typeof_type(node.expr, g.get_type(node.typ))
node_typ := g.unwrap_generic(typ)
sym := g.table.sym(node_typ)
if sym.language == .v && sym.kind in [.placeholder, .any] {
g.error('unknown type `${sym.name}`', node.pos)
}
styp := g.styp(node_typ)
g.write('alignof(${util.no_dots(styp)})')
}
fn (mut g Gen) size_of(node ast.SizeOf) {
typ := g.type_resolver.typeof_type(node.expr, g.get_type(node.typ))
node_typ := g.unwrap_generic(typ)

View file

@ -339,6 +339,7 @@ typedef int (*qsort_callback_func)(const void*, const void*);
#include <string.h>
#include <stdarg.h> // for va_list
#include <stdalign.h> // for alignof
#ifdef __TERMUX__
#if defined __BIONIC_AVAILABILITY_GUARD && __BIONIC_AVAILABILITY_GUARD(28)

View file

@ -633,6 +633,9 @@ pub fn (mut f Gen) expr(node_ ast.Expr) {
ast.SelectorExpr {
f.selector_expr(node)
}
ast.AlignOf {
f.align_of(node)
}
ast.SizeOf {
f.size_of(node)
}
@ -2147,6 +2150,16 @@ pub fn (mut f Gen) selector_expr(node ast.SelectorExpr) {
f.write(node.field_name)
}
pub fn (mut f Gen) align_of(node ast.AlignOf) {
f.write('alignof(')
if node.is_type {
f.write(f.table.type_to_str_using_aliases(node.typ, f.mod2alias))
} else {
f.expr(node.expr)
}
f.write(')')
}
pub fn (mut f Gen) size_of(node ast.SizeOf) {
f.write('sizeof(')
if node.is_type {

View file

@ -1032,7 +1032,7 @@ fn (mut g JsGen) expr(node_ ast.Expr) {
ast.SelectorExpr {
g.gen_selector_expr(node)
}
ast.SizeOf, ast.IsRefType {
ast.AlignOf, ast.SizeOf, ast.IsRefType {
// TODO
}
ast.OffsetOf {

View file

@ -782,7 +782,7 @@ fn (mut w Walker) expr(node_ ast.Expr) {
w.expr(node.high)
}
}
ast.SizeOf, ast.IsRefType {
ast.AlignOf, ast.SizeOf, ast.IsRefType {
w.expr(node.expr)
w.mark_by_type(node.typ)
}

View file

@ -332,8 +332,9 @@ fn (mut p Parser) check_expr(precedence int) !ast.Expr {
}
}
}
.key_sizeof, .key_isreftype {
.key_alignof, .key_sizeof, .key_isreftype {
is_reftype := p.tok.kind == .key_isreftype
is_alignof := p.tok.kind == .key_alignof
p.next() // sizeof
if p.tok.kind == .lsbr {
@ -351,6 +352,12 @@ fn (mut p Parser) check_expr(precedence int) !ast.Expr {
typ: typ
pos: type_pos
}
} else if is_alignof {
node = ast.AlignOf{
is_type: true
typ: typ
pos: type_pos
}
} else {
node = ast.SizeOf{
is_type: true
@ -381,6 +388,12 @@ fn (mut p Parser) check_expr(precedence int) !ast.Expr {
expr: expr
pos: pos
}
} else if is_alignof {
node = ast.AlignOf{
is_type: false
expr: expr
pos: pos
}
} else {
node = ast.SizeOf{
is_type: false
@ -403,6 +416,13 @@ fn (mut p Parser) check_expr(precedence int) !ast.Expr {
typ: arg_type
pos: pos
}
} else if is_alignof {
node = ast.AlignOf{
guessed_type: true
is_type: true
typ: arg_type
pos: pos
}
} else {
node = ast.SizeOf{
guessed_type: true

View file

@ -2670,7 +2670,7 @@ fn (mut p Parser) global_decl() ast.GlobalDecl {
ast.FloatLiteral {
typ = ast.f64_type
}
ast.IntegerLiteral, ast.SizeOf {
ast.AlignOf, ast.IntegerLiteral, ast.SizeOf {
typ = ast.int_type
}
ast.StringLiteral, ast.StringInterLiteral {

View file

@ -0,0 +1,23 @@
struct S1 {
p voidptr
}
struct S2 {
i int
}
fn test_alignof() {
assert alignof[rune]() == 4
assert alignof[[44]u8]() == 1
assert alignof(``) == 4
// depends on -m32/64
assert alignof[S1]() in [u32(4), 8]
s := S2{}
assert alignof(s.i) == 4
assert alignof('hello') == $if x64 {
8
} $else {
4
}
}

View file

@ -121,6 +121,7 @@ pub enum Kind {
key_select
key_like
key_ilike
key_alignof
key_sizeof
key_isreftype
key_likely
@ -305,6 +306,7 @@ fn build_token_str() []string {
s[Kind.key_asm] = 'asm'
s[Kind.key_return] = 'return'
s[Kind.key_module] = 'module'
s[Kind.key_alignof] = 'alignof'
s[Kind.key_sizeof] = 'sizeof'
s[Kind.key_isreftype] = 'isreftype'
s[Kind.key_likely] = '_likely_'
@ -657,6 +659,7 @@ pub fn kind_to_string(k Kind) string {
.key_select { 'key_select' }
.key_like { 'key_like' }
.key_ilike { 'key_ilike' }
.key_alignof { 'key_alignof' }
.key_sizeof { 'key_sizeof' }
.key_isreftype { 'key_isreftype' }
.key_likely { 'key_likely' }