diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 6bb60cbb39..79c5a95d3d 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -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 diff --git a/vlib/v/ast/type_size_test.v b/vlib/v/ast/type_size_test.v new file mode 100644 index 0000000000..8e203aac4d --- /dev/null +++ b/vlib/v/ast/type_size_test.v @@ -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') +} diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index 2415b71aca..97935bafac 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -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 { diff --git a/vlib/v/builder/builder.v b/vlib/v/builder/builder.v index 266b0c8c5b..8750ebb39d 100644 --- a/vlib/v/builder/builder.v +++ b/vlib/v/builder/builder.v @@ -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') diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index c8ef5617df..6c846a1db0 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -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 -} diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 0fff2cad46..9f3f15ddb9 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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) { diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 440de78324..62d78fa41e 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -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()