all: add offsetof (#8380)

pull/8445/head
div72 2021-01-30 14:57:09 +03:00 committed by GitHub
parent c0685eeefc
commit 8c70920695
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 120 additions and 4 deletions

View File

@ -112,6 +112,7 @@ For more details and troubleshooting, please visit the [vab GitHub repository](h
* [Profiling](#profiling)
* [Advanced Topics](#advanced-topics)
* [Memory-unsafe code](#memory-unsafe-code)
* [sizeof and __offsetof](#sizeof-and-__offsetof)
* [Calling C functions from V](#calling-c-functions-from-v)
* [Debugging generated C code](#debugging-generated-c-code)
* [Conditional compilation](#conditional-compilation)
@ -2991,6 +2992,22 @@ println(baz)
println(qux)
```
## sizeof and __offsetof
V supports the usage of `sizeof` to calculate sizes of structs and
`__offsetof` to calculate struct field offsets.
```v
struct Foo {
a int
b int
}
println(sizeof(Foo))
println(__offsetof(Foo, a))
println(__offsetof(Foo, b))
```
## Calling C functions from V
```v
@ -3734,6 +3751,7 @@ type
typeof
union
unsafe
__offsetof
```
See also [Types](#types).

View File

@ -13,9 +13,9 @@ pub type Expr = AnonFn | ArrayDecompose | ArrayInit | AsCast | Assoc | AtExpr |
CTempVar | CallExpr | CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall |
ComptimeSelector | ConcatExpr | EnumVal | FloatLiteral | GoExpr | Ident | IfExpr |
IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr | MapInit |
MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectExpr |
SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral | StructInit |
Type | TypeOf | UnsafeExpr
MatchExpr | None | OffsetOf | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr |
SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral |
StructInit | Type | TypeOf | UnsafeExpr
pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl | DeferStmt |
EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt |
@ -1068,6 +1068,13 @@ pub mut:
typ table.Type
}
pub struct OffsetOf {
pub:
struct_type table.Type
field string
pos token.Position
}
pub struct Likely {
pub:
expr Expr
@ -1197,7 +1204,7 @@ pub fn (expr Expr) position() token.Position {
}
ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr, CastExpr, ChanInit, CharLiteral,
ConcatExpr, Comment, EnumVal, FloatLiteral, GoExpr, Ident, IfExpr, IndexExpr, IntegerLiteral,
Likely, LockExpr, MapInit, MatchExpr, None, OrExpr, ParExpr, PostfixExpr, PrefixExpr,
Likely, LockExpr, MapInit, MatchExpr, None, OffsetOf, OrExpr, ParExpr, PostfixExpr, PrefixExpr,
RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr, StringInterLiteral, StringLiteral,
StructInit, Type, TypeOf, UnsafeExpr {
return expr.pos

View File

@ -290,6 +290,9 @@ pub fn (x Expr) str() string {
SizeOf {
return 'sizeof($x.expr)'
}
OffsetOf {
return '__offsetof($x.struct_type, $x.field)'
}
StringInterLiteral {
mut res := []string{}
res << "'"

View File

@ -3505,6 +3505,9 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
}
return table.u32_type
}
ast.OffsetOf {
return c.offset_of(node)
}
ast.SqlExpr {
return c.sql_expr(mut node)
}
@ -5058,6 +5061,19 @@ pub fn (mut c Checker) chan_init(mut node ast.ChanInit) table.Type {
}
}
pub fn (mut c Checker) offset_of(node ast.OffsetOf) table.Type {
sym := c.table.get_final_type_symbol(node.struct_type)
if sym.kind != .struct_ {
c.error('first argument of __offsetof must be struct', node.pos)
return table.u32_type
}
if !c.table.struct_has_field(node.struct_type, node.field) {
c.error('struct `$sym.name` has no field called `$node.field`', node.pos)
}
return table.u32_type
}
pub fn (mut c Checker) check_dup_keys(node &ast.MapInit, i int) {
key_i := node.keys[i]
if key_i is ast.StringLiteral {

View File

@ -947,6 +947,9 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
ast.SizeOf {
f.size_of(node)
}
ast.OffsetOf {
f.write('__offsetof(${f.table.type_to_str(node.struct_type)}, $node.field)')
}
ast.SqlExpr {
f.sql_expr(node)
}

View File

@ -0,0 +1,8 @@
struct Animal {
breed string
age u64
}
fn main() {
println(__offsetof(Animal, breed) + __offsetof(Animal, age))
}

View File

@ -2821,6 +2821,10 @@ fn (mut g Gen) expr(node ast.Expr) {
styp := g.typ(node_typ)
g.write('/*SizeOf*/ sizeof(${util.no_dots(styp)})')
}
ast.OffsetOf {
styp := g.typ(node.struct_type)
g.write('/*OffsetOf*/ (u32)(__offsetof(${util.no_dots(styp)}, $node.field))')
}
ast.SqlExpr {
g.sql_select_expr(node)
}

View File

@ -541,6 +541,9 @@ fn (mut g JsGen) expr(node ast.Expr) {
ast.SizeOf {
// TODO
}
ast.OffsetOf {
// TODO
}
ast.SqlExpr {
// TODO
}

View File

@ -204,6 +204,25 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
pos: spos.extend(p.tok.position())
}
}
.key_offsetof {
pos := p.tok.position()
p.next() // __offsetof
p.check(.lpar)
st := p.parse_type()
p.check(.comma)
if p.tok.kind != .name {
p.error_with_pos('unexpected `$p.tok.lit`, expecting struct field', p.tok.position())
return ast.Expr{}
}
field := p.tok.lit
p.next()
p.check(.rpar)
node = ast.OffsetOf{
struct_type: st
field: field
pos: pos
}
}
.key_likely, .key_unlikely {
is_likely := p.tok.kind == .key_likely
p.next()

View File

@ -0,0 +1,35 @@
import math.complex
struct Cat {
name string
breed string
age int
}
type Feline = Cat
fn test_offsetof() {
cat := Cat{name: 'Cthulhu' breed: 'Great Old One' age: 2147483647}
unsafe {
assert *(&string(byteptr(&cat) + __offsetof(Cat, name))) == 'Cthulhu'
assert *(&string(byteptr(&cat) + __offsetof(Cat, breed))) == 'Great Old One'
assert *(&int(byteptr(&cat) + __offsetof(Cat, age))) == 2147483647
}
}
fn test_offsetof_struct_from_another_module() {
num := complex.Complex{1.0, 1.0}
unsafe {
assert *(&f64(byteptr(&num) + __offsetof(complex.Complex, re))) == 1.0
assert *(&f64(byteptr(&num) + __offsetof(complex.Complex, im))) == 1.0
}
}
fn test_offsetof_alias() {
fel := Feline{name: 'Cthulhu' breed: 'Great Old One' age: 2147483647}
unsafe {
assert *(&string(byteptr(&fel) + __offsetof(Feline, name))) == 'Cthulhu'
assert *(&string(byteptr(&fel) + __offsetof(Feline, breed))) == 'Great Old One'
assert *(&int(byteptr(&fel) + __offsetof(Feline, age))) == 2147483647
}
}