ast: add a type_size() method (#14213)

spaceface 2022-04-28 22:35:10 +02:00 committed by Jef Roosens
parent 7fc9e3a4c1
commit 893e2ff6cb
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
7 changed files with 150 additions and 61 deletions

View File

@ -44,6 +44,7 @@ pub mut:
mdeprecated_msg map[string]string // module deprecation message
mdeprecated_after map[string]time.Time // module deprecation date
builtin_pub_fns map[string]bool
pointer_size int
}
// used by vls to avoid leaks

View File

@ -0,0 +1,54 @@
import v.builder
import v.parser
import v.pref
struct T01 {
a int
b byte
c int
}
type T02 = string
type T03 = int | string
type T04 = []T03
type T05 = [47]T03
interface T06 {
a int
}
interface T07 {
T06
b int
}
struct T08 {
T01
x string
}
fn test_type_size() ? {
mut pref := pref.new_preferences()
$if x64 {
pref.m64 = true
}
mut b := builder.new_builder(pref)
mut files := b.get_builtin_files()
b.set_module_lookup_paths()
parser.parse_files(files, b.table, b.pref)
b.parse_imports()
parser.parse_file(@FILE, b.table, .parse_comments, b.pref)
mut t := b.table
assert sizeof(T01) == t.type_size(t.type_idxs['main.T01'] ?)
assert sizeof(T02) == t.type_size(t.type_idxs['main.T02'] ?)
assert sizeof(T03) == t.type_size(t.type_idxs['main.T03'] ?)
assert sizeof(T04) == t.type_size(t.type_idxs['main.T04'] ?)
assert sizeof(T05) == t.type_size(t.type_idxs['main.T05'] ?)
assert sizeof(T06) == t.type_size(t.type_idxs['main.T06'] ?)
assert sizeof(T07) == t.type_size(t.type_idxs['main.T07'] ?)
assert sizeof(T08) == t.type_size(t.type_idxs['main.T08'] ?)
println('done')
}

View File

@ -823,6 +823,97 @@ pub fn (t &TypeSymbol) is_builtin() bool {
return t.mod == 'builtin'
}
// type_size returns the size in bytes of `typ`, similarly to C's `sizeof()`.
pub fn (t &Table) type_size(typ Type) int {
if typ.has_flag(.optional) {
return t.type_size(ast.error_type_idx)
}
if typ.nr_muls() > 0 {
return t.pointer_size
}
sym := t.sym(typ)
match sym.kind {
.placeholder, .void, .none_ {
return 0
}
.voidptr, .byteptr, .charptr, .function, .usize, .isize, .any, .thread, .chan {
return t.pointer_size
}
.i8, .u8, .char, .bool {
return 1
}
.i16, .u16 {
return 2
}
.int, .u32, .rune, .f32, .enum_ {
return 4
}
.i64, .u64, .int_literal, .f64, .float_literal {
return 8
}
.alias {
return t.type_size((sym.info as Alias).parent_type)
}
.struct_, .string, .multi_return {
mut max_alignment := 0
mut total_size := 0
types := if sym.info is Struct {
sym.info.fields.map(it.typ)
} else {
(sym.info as MultiReturn).types
}
for ftyp in types {
field_size := t.type_size(ftyp)
alignment := if field_size > t.pointer_size { t.pointer_size } else { field_size }
if alignment > max_alignment {
max_alignment = alignment
}
total_size = round_up(total_size, alignment) + field_size
}
return round_up(total_size, max_alignment)
}
.sum_type, .interface_, .aggregate {
match sym.info {
SumType, Aggregate {
return (sym.info.fields.len + 2) * t.pointer_size
}
Interface {
mut res := (sym.info.fields.len + 2) * t.pointer_size
for etyp in sym.info.embeds {
res += t.type_size(etyp) - 2 * t.pointer_size
}
return res
}
else {
// unreachable
return 0
}
}
}
.array_fixed {
info := sym.info as ArrayFixed
return info.size * t.type_size(info.elem_type)
}
// TODO hardcoded:
.map {
return if t.pointer_size == 8 { 120 } else { 80 }
}
.array {
return if t.pointer_size == 8 { 32 } else { 24 }
}
.generic_inst {
return 0
}
}
}
// round_up rounds the number `n` up to the next multiple `multiple`.
// Note: `multiple` must be a power of 2.
[inline]
fn round_up(n int, multiple int) int {
return (n + multiple - 1) & -multiple
}
// for debugging/errors only, perf is not an issue
pub fn (k Kind) str() string {
return match k {

View File

@ -53,6 +53,7 @@ pub fn new_builder(pref &pref.Preferences) Builder {
if pref.use_color == .never {
util.emanager.set_support_color(false)
}
table.pointer_size = if pref.m64 { 8 } else { 4 }
msvc := find_msvc(pref.m64) or {
if pref.ccompiler == 'msvc' {
// verror('Cannot find MSVC on this OS')

View File

@ -709,38 +709,3 @@ pub fn (mut c Checker) infer_fn_generic_types(func ast.Fn, mut node ast.CallExpr
c.need_recheck_generic_fns = true
}
}
pub fn (c &Checker) sizeof_integer(a ast.Type) int {
t := if a in ast.unsigned_integer_type_idxs { a.flip_signedness() } else { a }
r := match t {
ast.char_type_idx, ast.i8_type_idx {
1
}
ast.i16_type_idx {
2
}
ast.int_type_idx {
4
}
ast.rune_type_idx {
4
}
ast.i64_type_idx {
8
}
ast.isize_type_idx {
if c.pref.m64 { 8 } else { 4 }
}
ast.int_literal_type {
s := c.table.type_to_str(a)
panic('`$s` has unknown size')
0
}
else {
s := c.table.type_to_str(a)
panic('`$s` is not an integer')
0
}
}
return r
}

View File

@ -643,8 +643,8 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
} else if is_left_type_signed != is_right_type_signed
&& left_type != ast.int_literal_type_idx
&& right_type != ast.int_literal_type_idx {
ls := c.sizeof_integer(left_type)
rs := c.sizeof_integer(right_type)
ls := c.table.type_size(left_type)
rs := c.table.type_size(right_type)
// prevent e.g. `u32 == i16` but not `u16 == i32` as max_u16 fits in i32
// TODO u32 == i32, change < to <=
if (is_left_type_signed && ls < rs) || (is_right_type_signed && rs < ls) {

View File

@ -127,30 +127,7 @@ fn (mut c Checker) eval_comptime_const_expr(expr ast.Expr, nlevel int) ?ast.Comp
// return expr.val.i64()
// }
ast.SizeOf {
xtype := expr.typ
if xtype.is_real_pointer() {
if c.pref.m64 {
return 8 // 64bit platform
}
return 4 // 32bit platform
}
if int(xtype) == xtype.idx() {
match xtype {
ast.char_type { return 1 }
ast.i8_type { return 1 }
ast.i16_type { return 2 }
ast.int_type { return 4 }
ast.i64_type { return 8 }
//
ast.byte_type { return 1 }
// ast.u8_type { return 1 }
ast.u16_type { return 2 }
ast.u32_type { return 4 }
ast.u64_type { return 8 }
else {}
}
}
return none
return c.table.type_size(expr.typ)
}
ast.FloatLiteral {
x := expr.val.f64()