v/vlib/v/ast/types.v

1340 lines
32 KiB
V
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
//
// Type layout information (32 bits)
// flag (8 bits) | nr_muls (8 bits) | idx (16 bits)
// pack: (int(flag)<<24) | (nr_muls<<16) | u16(idx)
// unpack:
// flag: (int(type)>>24) & 0xff
// nr_muls: (int(type)>>16) & 0xff
// idx: u16(type) & 0xffff
module ast
import strings
import v.pref
pub type Type = int
pub type TypeInfo = Aggregate
| Alias
| Array
| ArrayFixed
| Chan
| Enum
| FnType
| GenericInst
| Interface
| Map
| MultiReturn
| Struct
| SumType
| Thread
pub enum Language {
v
c
js
amd64 // aka x86_64
i386
arm64 // 64-bit arm
arm32 // 32-bit arm
rv64 // 64-bit risc-v
rv32 // 32-bit risc-v
}
pub fn pref_arch_to_table_language(pref_arch pref.Arch) Language {
return match pref_arch {
.amd64 {
Language.amd64
}
.arm64 {
Language.arm64
}
.arm32 {
Language.arm32
}
.rv64 {
Language.rv64
}
.rv32 {
Language.rv32
}
.i386 {
Language.i386
}
._auto, ._max {
Language.v
}
}
}
// Represents a type that only needs an identifier, e.g. int, array_int.
// A pointer type `&T` would have a TypeSymbol `T`.
// Note: For a Type, use:
// * Table.type_to_str(typ) not TypeSymbol.name.
// * Table.type_kind(typ) not TypeSymbol.kind.
// Each TypeSymbol is entered into `Table.types`.
// See also: Table.get_type_symbol.
pub struct TypeSymbol {
pub:
parent_idx int
pub mut:
info TypeInfo
kind Kind
name string // the internal & source name of the type, i.e. `[5]int`.
cname string // the name with no dots for use in the generated C code
methods []Fn
mod string
is_public bool
language Language
idx int
}
// max of 8
pub enum TypeFlag {
optional
variadic
generic
shared_f
atomic_f
}
/*
To save precious TypeFlag bits the 4 possible ShareTypes are coded in the two
bits `shared` and `atomic_or_rw` (see sharetype_from_flags() below).
*/
pub enum ShareType {
mut_t
shared_t
atomic_t
}
pub fn (t ShareType) str() string {
match t {
.mut_t { return 'mut' }
.shared_t { return 'shared' }
.atomic_t { return 'atomic' }
}
}
// <atomic.h> defines special typenames
pub fn (t Type) atomic_typename() string {
idx := t.idx()
match idx {
ast.u32_type_idx { return 'atomic_uint' }
ast.int_type_idx { return 'atomic_int' }
ast.u64_type_idx { return 'atomic_ullong' }
ast.i64_type_idx { return 'atomic_llong' }
else { return 'unknown_atomic' }
}
}
pub fn sharetype_from_flags(is_shared bool, is_atomic bool) ShareType {
return ShareType((int(is_atomic) << 1) | int(is_shared))
}
pub fn (t Type) share() ShareType {
return sharetype_from_flags(t.has_flag(.shared_f), t.has_flag(.atomic_f))
}
// return TypeSymbol idx for `t`
[inline]
pub fn (t Type) idx() int {
return u16(t) & 0xffff
}
[inline]
pub fn (t Type) is_void() bool {
return t == ast.void_type
}
[inline]
pub fn (t Type) is_full() bool {
return t != 0 && t != ast.void_type
}
// return nr_muls for `t`
[inline]
pub fn (t Type) nr_muls() int {
return (int(t) >> 16) & 0xff
}
// return true if `t` is a pointer (nr_muls>0)
[inline]
pub fn (t Type) is_ptr() bool {
// any normal pointer, i.e. &Type, &&Type etc;
// NB: voidptr, charptr and byteptr are NOT included!
return (int(t) >> 16) & 0xff > 0
}
[inline]
pub fn (t Type) is_any_kind_of_pointer() bool {
return (int(t) >> 16) & 0xff > 0 || (u16(t) & 0xffff) in ast.pointer_type_idxs
}
// set nr_muls on `t` and return it
[inline]
pub fn (t Type) set_nr_muls(nr_muls int) Type {
if nr_muls < 0 || nr_muls > 255 {
panic('set_nr_muls: nr_muls must be between 0 & 255')
}
return int(t) & 0xff00ffff | (nr_muls << 16)
}
// increments nr_nuls on `t` and return it
[inline]
pub fn (t Type) to_ptr() Type {
nr_muls := (int(t) >> 16) & 0xff
if nr_muls == 255 {
panic('to_ptr: nr_muls is already at max of 255')
}
return int(t) & 0xff00ffff | ((nr_muls + 1) << 16)
}
// decrement nr_muls on `t` and return it
[inline]
pub fn (t Type) deref() Type {
nr_muls := (int(t) >> 16) & 0xff
if nr_muls == 0 {
panic('deref: type `$t` is not a pointer')
}
return int(t) & 0xff00ffff | ((nr_muls - 1) << 16)
}
// set `flag` on `t` and return `t`
[inline]
pub fn (t Type) set_flag(flag TypeFlag) Type {
return int(t) | (1 << (int(flag) + 24))
}
// clear `flag` on `t` and return `t`
[inline]
pub fn (t Type) clear_flag(flag TypeFlag) Type {
return int(t) & ~(1 << (int(flag) + 24))
}
// clear all flags
[inline]
pub fn (t Type) clear_flags() Type {
return int(t) & 0xffffff
}
// return true if `flag` is set on `t`
[inline]
pub fn (t Type) has_flag(flag TypeFlag) bool {
return int(t) & (1 << (int(flag) + 24)) > 0
}
// debug returns a verbose representation of the information in ts, useful for tracing/debugging
pub fn (ts TypeSymbol) debug() []string {
mut res := []string{}
ts.dbg_common(mut res)
res << 'info: $ts.info'
res << 'methods ($ts.methods.len): ' + ts.methods.map(it.str()).join(', ')
return res
}
// same as .debug(), but without the verbose .info and .methods fields
pub fn (ts TypeSymbol) dbg() []string {
mut res := []string{}
ts.dbg_common(mut res)
return res
}
fn (ts TypeSymbol) dbg_common(mut res []string) {
res << 'idx: 0x$ts.idx.hex()'
res << 'parent_idx: 0x$ts.parent_idx.hex()'
res << 'mod: $ts.mod'
res << 'name: $ts.name'
res << 'cname: $ts.cname'
res << 'kind: $ts.kind'
res << 'is_public: $ts.is_public'
res << 'language: $ts.language'
}
pub fn (t Type) str() string {
return 'ast.Type(0x$t.hex() = ${u32(t)})'
}
pub fn (t &Table) type_str(typ Type) string {
sym := t.get_type_symbol(typ)
return sym.name
}
// debug returns a verbose representation of the information in the type `t`, useful for tracing/debugging
pub fn (t Type) debug() []string {
mut res := []string{}
res << 'idx: 0x${t.idx().hex():-8}'
res << 'type: 0x${t.hex():-8}'
res << 'nr_muls: $t.nr_muls()'
if t.has_flag(.optional) {
res << 'optional'
}
if t.has_flag(.variadic) {
res << 'variadic'
}
if t.has_flag(.generic) {
res << 'generic'
}
if t.has_flag(.shared_f) {
res << 'shared_f'
}
if t.has_flag(.atomic_f) {
res << 'atomic_f'
}
return res
}
// copy flags & nr_muls from `t_from` to `t` and return `t`
[inline]
pub fn (t Type) derive(t_from Type) Type {
return (0xffff0000 & t_from) | u16(t)
}
// copy flags from `t_from` to `t` and return `t`
[inline]
pub fn (t Type) derive_add_muls(t_from Type) Type {
return Type((0xff000000 & t_from) | u16(t)).set_nr_muls(t.nr_muls() + t_from.nr_muls())
}
// return new type with TypeSymbol idx set to `idx`
[inline]
pub fn new_type(idx int) Type {
if idx < 1 || idx > 65535 {
panic('new_type: idx must be between 1 & 65535')
}
return idx
}
// return new type with TypeSymbol idx set to `idx` & nr_muls set to `nr_muls`
[inline]
pub fn new_type_ptr(idx int, nr_muls int) Type {
if idx < 1 || idx > 65535 {
panic('new_type_ptr: idx must be between 1 & 65535')
}
if nr_muls < 0 || nr_muls > 255 {
panic('new_type_ptr: nr_muls must be between 0 & 255')
}
return (nr_muls << 16) | u16(idx)
}
[inline]
pub fn (typ Type) is_pointer() bool {
// builtin pointer types (voidptr, byteptr, charptr)
return typ.idx() in ast.pointer_type_idxs
}
[inline]
pub fn (typ Type) is_real_pointer() bool {
return typ.is_ptr() || typ.is_pointer()
}
[inline]
pub fn (typ Type) is_float() bool {
return typ.clear_flags() in ast.float_type_idxs
}
[inline]
pub fn (typ Type) is_int() bool {
return typ.clear_flags() in ast.integer_type_idxs
}
[inline]
pub fn (typ Type) is_int_valptr() bool {
return typ.idx() in ast.integer_type_idxs
}
[inline]
pub fn (typ Type) is_float_valptr() bool {
return typ.idx() in ast.float_type_idxs
}
[inline]
pub fn (typ Type) is_pure_int() bool {
return int(typ) in ast.integer_type_idxs
}
[inline]
pub fn (typ Type) is_pure_float() bool {
return int(typ) in ast.float_type_idxs
}
[inline]
pub fn (typ Type) is_signed() bool {
return typ.idx() in ast.signed_integer_type_idxs
}
[inline]
pub fn (typ Type) is_unsigned() bool {
return typ.idx() in ast.unsigned_integer_type_idxs
}
[inline]
pub fn (typ Type) is_int_literal() bool {
return int(typ) == ast.int_literal_type_idx
}
[inline]
pub fn (typ Type) is_number() bool {
return typ.clear_flags() in ast.number_type_idxs
}
[inline]
pub fn (typ Type) is_string() bool {
return typ.idx() in ast.string_type_idxs
}
[inline]
pub fn (typ Type) is_bool() bool {
return typ.idx() == ast.bool_type_idx
}
pub const (
void_type_idx = 1
voidptr_type_idx = 2
byteptr_type_idx = 3
charptr_type_idx = 4
i8_type_idx = 5
i16_type_idx = 6
int_type_idx = 7
i64_type_idx = 8
isize_type_idx = 9
byte_type_idx = 10
u16_type_idx = 11
u32_type_idx = 12
u64_type_idx = 13
usize_type_idx = 14
f32_type_idx = 15
f64_type_idx = 16
char_type_idx = 17
bool_type_idx = 18
none_type_idx = 19
string_type_idx = 20
rune_type_idx = 21
array_type_idx = 22
map_type_idx = 23
chan_type_idx = 24
any_type_idx = 25
float_literal_type_idx = 26
int_literal_type_idx = 27
thread_type_idx = 28
error_type_idx = 29
u8_type_idx = 30
)
pub const (
integer_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx,
byte_type_idx, u8_type_idx, u16_type_idx, u32_type_idx, u64_type_idx, isize_type_idx,
usize_type_idx, int_literal_type_idx, rune_type_idx]
signed_integer_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx,
isize_type_idx]
unsigned_integer_type_idxs = [byte_type_idx, u16_type_idx, u32_type_idx, u64_type_idx,
usize_type_idx]
float_type_idxs = [f32_type_idx, f64_type_idx, float_literal_type_idx]
number_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx,
byte_type_idx, u16_type_idx, u32_type_idx, u64_type_idx, isize_type_idx, usize_type_idx,
f32_type_idx, f64_type_idx, int_literal_type_idx, float_literal_type_idx, rune_type_idx]
pointer_type_idxs = [voidptr_type_idx, byteptr_type_idx, charptr_type_idx]
string_type_idxs = [string_type_idx]
)
pub const (
void_type = new_type(void_type_idx)
ovoid_type = new_type(void_type_idx).set_flag(.optional) // the return type of `fn () ?`
voidptr_type = new_type(voidptr_type_idx)
byteptr_type = new_type(byteptr_type_idx)
charptr_type = new_type(charptr_type_idx)
i8_type = new_type(i8_type_idx)
int_type = new_type(int_type_idx)
i16_type = new_type(i16_type_idx)
i64_type = new_type(i64_type_idx)
isize_type = new_type(isize_type_idx)
byte_type = new_type(byte_type_idx)
u8_type = new_type(u8_type_idx)
u16_type = new_type(u16_type_idx)
u32_type = new_type(u32_type_idx)
u64_type = new_type(u64_type_idx)
usize_type = new_type(usize_type_idx)
f32_type = new_type(f32_type_idx)
f64_type = new_type(f64_type_idx)
char_type = new_type(char_type_idx)
bool_type = new_type(bool_type_idx)
none_type = new_type(none_type_idx)
string_type = new_type(string_type_idx)
rune_type = new_type(rune_type_idx)
array_type = new_type(array_type_idx)
map_type = new_type(map_type_idx)
chan_type = new_type(chan_type_idx)
any_type = new_type(any_type_idx)
float_literal_type = new_type(float_literal_type_idx)
int_literal_type = new_type(int_literal_type_idx)
thread_type = new_type(thread_type_idx)
error_type = new_type(error_type_idx)
charptr_types = [charptr_type, new_type(char_type_idx).set_nr_muls(1)]
byteptr_types = [byteptr_type, new_type(byte_type_idx).set_nr_muls(1)]
voidptr_types = [voidptr_type, new_type(voidptr_type_idx).set_nr_muls(1)]
cptr_types = merge_types(voidptr_types, byteptr_types, charptr_types)
)
pub fn merge_types(params ...[]Type) []Type {
mut res := []Type{}
for types in params {
res << types
}
return res
}
pub const (
// must be in the same order as the idx consts above
builtin_type_names = ['void', 'voidptr', 'byteptr', 'charptr', 'i8', 'i16', 'int', 'i64', 'isize',
'byte', 'u16', 'u32', 'u64', 'usize', 'f32', 'f64', 'char', 'bool', 'none', 'string', 'rune',
'array', 'map', 'chan', 'any', 'float_literal', 'int_literal', 'thread', 'Error', 'u8']
)
pub struct MultiReturn {
pub mut:
types []Type
}
pub struct FnType {
pub mut:
is_anon bool
has_decl bool
func Fn
}
// returns TypeSymbol kind only if there are no type modifiers
pub fn (t &Table) type_kind(typ Type) Kind {
if typ.nr_muls() > 0 || typ.has_flag(.optional) {
return Kind.placeholder
}
return t.get_type_symbol(typ).kind
}
pub enum Kind {
placeholder
void
voidptr
byteptr
charptr
i8
i16
int
i64
isize
byte
u8
u16
u32
u64
usize
f32
f64
char
rune
bool
none_
string
array
array_fixed
map
chan
any
struct_
generic_inst
multi_return
sum_type
alias
enum_
function
interface_
float_literal
int_literal
aggregate
thread
}
pub fn (t &TypeSymbol) str() string {
return t.name
}
[inline]
pub fn (t &TypeSymbol) enum_info() Enum {
match mut t.info {
Enum { return t.info }
else { panic('TypeSymbol.enum_info(): no enum info for type: $t.name') }
}
}
[inline]
pub fn (t &TypeSymbol) mr_info() MultiReturn {
match mut t.info {
MultiReturn { return t.info }
else { panic('TypeSymbol.mr_info(): no multi return info for type: $t.name') }
}
}
[inline]
pub fn (t &TypeSymbol) array_info() Array {
match mut t.info {
Array { return t.info }
else { panic('TypeSymbol.array_info(): no array info for type: $t.name') }
}
}
[inline]
pub fn (t &TypeSymbol) array_fixed_info() ArrayFixed {
match mut t.info {
ArrayFixed { return t.info }
else { panic('TypeSymbol.array_fixed(): no array fixed info for type: $t.name') }
}
}
[inline]
pub fn (t &TypeSymbol) chan_info() Chan {
match mut t.info {
Chan { return t.info }
else { panic('TypeSymbol.chan_info(): no chan info for type: $t.name') }
}
}
[inline]
pub fn (t &TypeSymbol) thread_info() Thread {
match mut t.info {
Thread { return t.info }
else { panic('TypeSymbol.thread_info(): no thread info for type: $t.name') }
}
}
[inline]
pub fn (t &TypeSymbol) map_info() Map {
match mut t.info {
Map { return t.info }
else { panic('TypeSymbol.map_info(): no map info for type: $t.name') }
}
}
[inline]
pub fn (t &TypeSymbol) struct_info() Struct {
match mut t.info {
Struct { return t.info }
else { panic('TypeSymbol.struct_info(): no struct info for type: $t.name') }
}
}
[inline]
pub fn (t &TypeSymbol) sumtype_info() SumType {
match mut t.info {
SumType { return t.info }
else { panic('TypeSymbol.sumtype_info(): no sumtype info for type: $t.name') }
}
}
pub fn (t &TypeSymbol) is_heap() bool {
if t.kind == .struct_ {
info := t.info as Struct
return info.is_heap
} else {
return false
}
}
/*
pub fn (t TypeSymbol) str() string {
return t.name
}
*/
pub fn (mut t Table) register_builtin_type_symbols() {
// reserve index 0 so nothing can go there
// save index check, 0 will mean not found
t.register_type_symbol(kind: .placeholder, name: 'reserved_0')
t.register_type_symbol(kind: .void, name: 'void', cname: 'void', mod: 'builtin')
t.register_type_symbol(kind: .voidptr, name: 'voidptr', cname: 'voidptr', mod: 'builtin')
t.register_type_symbol(kind: .byteptr, name: 'byteptr', cname: 'byteptr', mod: 'builtin')
t.register_type_symbol(kind: .charptr, name: 'charptr', cname: 'charptr', mod: 'builtin')
t.register_type_symbol(kind: .i8, name: 'i8', cname: 'i8', mod: 'builtin')
t.register_type_symbol(kind: .i16, name: 'i16', cname: 'i16', mod: 'builtin')
t.register_type_symbol(kind: .int, name: 'int', cname: 'int', mod: 'builtin')
t.register_type_symbol(kind: .i64, name: 'i64', cname: 'i64', mod: 'builtin')
t.register_type_symbol(kind: .isize, name: 'isize', cname: 'isize', mod: 'builtin')
t.register_type_symbol(kind: .byte, name: 'byte', cname: 'byte', mod: 'builtin')
t.register_type_symbol(kind: .u16, name: 'u16', cname: 'u16', mod: 'builtin')
t.register_type_symbol(kind: .u32, name: 'u32', cname: 'u32', mod: 'builtin')
t.register_type_symbol(kind: .u64, name: 'u64', cname: 'u64', mod: 'builtin')
t.register_type_symbol(kind: .usize, name: 'usize', cname: 'usize', mod: 'builtin')
t.register_type_symbol(kind: .f32, name: 'f32', cname: 'f32', mod: 'builtin')
t.register_type_symbol(kind: .f64, name: 'f64', cname: 'f64', mod: 'builtin')
t.register_type_symbol(kind: .char, name: 'char', cname: 'char', mod: 'builtin')
t.register_type_symbol(kind: .bool, name: 'bool', cname: 'bool', mod: 'builtin')
t.register_type_symbol(kind: .none_, name: 'none', cname: 'none', mod: 'builtin')
t.register_type_symbol(kind: .string, name: 'string', cname: 'string', mod: 'builtin')
t.register_type_symbol(kind: .rune, name: 'rune', cname: 'rune', mod: 'builtin')
t.register_type_symbol(kind: .array, name: 'array', cname: 'array', mod: 'builtin')
t.register_type_symbol(kind: .map, name: 'map', cname: 'map', mod: 'builtin')
t.register_type_symbol(kind: .chan, name: 'chan', cname: 'chan', mod: 'builtin')
t.register_type_symbol(kind: .any, name: 'any', cname: 'any', mod: 'builtin')
t.register_type_symbol(
kind: .float_literal
name: 'float literal'
cname: 'float_literal'
mod: 'builtin'
)
t.register_type_symbol(
kind: .int_literal
name: 'int literal'
cname: 'int_literal'
mod: 'builtin'
)
t.register_type_symbol(
kind: .thread
name: 'thread'
cname: '__v_thread'
mod: 'builtin'
info: Thread{
return_type: ast.void_type
}
)
t.register_type_symbol(kind: .interface_, name: 'IError', cname: 'IError', mod: 'builtin')
t.register_type_symbol(kind: .u8, name: 'zu8', cname: 'zu8', mod: 'builtin')
}
[inline]
pub fn (t &TypeSymbol) is_pointer() bool {
return t.kind in [.byteptr, .charptr, .voidptr]
}
[inline]
pub fn (t &TypeSymbol) is_int() bool {
res := t.kind in [.i8, .i16, .int, .i64, .isize, .byte, .u16, .u32, .u64, .usize, .int_literal,
.rune]
if !res && t.kind == .alias {
return (t.info as Alias).parent_type.is_number()
}
return res
}
[inline]
pub fn (t &TypeSymbol) is_float() bool {
return t.kind in [.f32, .f64, .float_literal]
}
[inline]
pub fn (t &TypeSymbol) is_string() bool {
return t.kind == .string
}
[inline]
pub fn (t &TypeSymbol) is_number() bool {
return t.is_int() || t.is_float()
}
[inline]
pub fn (t &TypeSymbol) is_primitive() bool {
return t.is_number() || t.is_pointer() || t.is_string()
}
[inline]
pub fn (t &TypeSymbol) is_builtin() bool {
return t.mod == 'builtin'
}
// for debugging/errors only, perf is not an issue
pub fn (k Kind) str() string {
k_str := match k {
.placeholder { 'placeholder' }
.void { 'void' }
.voidptr { 'voidptr' }
.charptr { 'charptr' }
.byteptr { 'byteptr' }
.struct_ { 'struct' }
.int { 'int' }
.i8 { 'i8' }
.i16 { 'i16' }
.i64 { 'i64' }
.isize { 'isize' }
.byte { 'byte' }
.u8 { 'u8' }
.u16 { 'u16' }
.u32 { 'u32' }
.u64 { 'u64' }
.usize { 'usize' }
.int_literal { 'int_literal' }
.f32 { 'f32' }
.f64 { 'f64' }
.float_literal { 'float_literal' }
.string { 'string' }
.char { 'char' }
.bool { 'bool' }
.none_ { 'none' }
.array { 'array' }
.array_fixed { 'array_fixed' }
.map { 'map' }
.chan { 'chan' }
.multi_return { 'multi_return' }
.sum_type { 'sum_type' }
.alias { 'alias' }
.enum_ { 'enum' }
.any { 'any' }
.function { 'function' }
.interface_ { 'interface' }
.generic_inst { 'generic_inst' }
.rune { 'rune' }
.aggregate { 'aggregate' }
.thread { 'thread' }
}
return k_str
}
pub fn (kinds []Kind) str() string {
mut kinds_str := ''
for i, k in kinds {
kinds_str += k.str()
if i < kinds.len - 1 {
kinds_str += '_'
}
}
return kinds_str
}
pub struct Struct {
pub:
attrs []Attr
pub mut:
embeds []Type
fields []StructField
is_typedef bool // C. [typedef]
is_union bool
is_heap bool
is_generic bool
generic_types []Type
concrete_types []Type
parent_type Type
}
// instantiation of a generic struct
pub struct GenericInst {
pub mut:
parent_idx int // idx of the base generic struct
concrete_types []Type // concrete types, e.g. <int, string>
}
pub struct Interface {
pub mut:
types []Type // all types that implement this interface
fields []StructField
methods []Fn
ifaces []Type
// `I1 is I2` conversions
conversions map[int][]Type
// generic interface support
is_generic bool
generic_types []Type
concrete_types []Type
parent_type Type
}
pub struct Enum {
pub:
vals []string
is_flag bool
is_multi_allowed bool
}
pub struct Alias {
pub:
parent_type Type
language Language
is_import bool
}
pub struct Aggregate {
mut:
fields []StructField // used for faster lookup inside the module
pub:
types []Type
}
/*
pub struct Field {
pub:
name string
pub mut:
typ Type
default_expr Expr
has_default_expr bool
default_expr_typ Type
default_val string
attrs []Attr
is_pub bool
is_mut bool
is_global bool
}
*/
pub fn (f &StructField) equals(o &StructField) bool {
// TODO: f.is_mut == o.is_mut was removed here to allow read only access
// to (mut/not mut), but otherwise equal fields; some other new checks are needed:
// - if node is declared mut, and we mutate node.stmts, all stmts fields must be mutable
// - same goes for pub and global, if we call the field from another module
return f.name == o.name && f.typ == o.typ && f.is_pub == o.is_pub && f.is_global == o.is_global
}
pub struct Array {
pub:
nr_dims int
pub mut:
elem_type Type
}
pub struct ArrayFixed {
pub:
size int
size_expr Expr // used by fmt for e.g. ´[my_const]byte´
pub mut:
elem_type Type
}
pub struct Chan {
pub mut:
elem_type Type
is_mut bool
}
pub struct Thread {
pub mut:
return_type Type
}
pub struct Map {
pub mut:
key_type Type
value_type Type
}
pub struct SumType {
pub:
variants []Type
pub mut:
fields []StructField
found_fields bool
// generic sumtype support
is_generic bool
generic_types []Type
concrete_types []Type
parent_type Type
}
// human readable type name
pub fn (t &Table) type_to_str(typ Type) string {
return t.type_to_str_using_aliases(typ, map[string]string{})
}
// type name in code (for builtin)
pub fn (mytable &Table) type_to_code(t Type) string {
match t {
ast.int_literal_type, ast.float_literal_type { return mytable.get_type_symbol(t).kind.str() }
else { return mytable.type_to_str_using_aliases(t, map[string]string{}) }
}
}
// import_aliases is a map of imported symbol aliases 'module.Type' => 'Type'
pub fn (t &Table) type_to_str_using_aliases(typ Type, import_aliases map[string]string) string {
sym := t.get_type_symbol(typ)
mut res := sym.name
// Note, that the duplication of code in some of the match branches here
// is VERY deliberate. DO NOT be tempted to use `else {}` instead, because
// that strongly reduces the usefullness of the exhaustive checking that
// match does.
// Using else{} here led to subtle bugs in vfmt discovered *months*
// after the original code was written.
// It is important that each case here is handled *explicitly* and
// *clearly*, and that when a new kind is added, it should also be handled
// explicitly.
match sym.kind {
.int_literal, .float_literal {
res = sym.name
}
.byteptr {
res = '&byte'
}
.charptr {
res = '&char'
}
.i8, .i16, .int, .i64, .isize, .byte, .u8, .u16, .u32, .u64, .usize, .f32, .f64, .char,
.rune, .string, .bool, .none_, .voidptr {
// primitive types
res = sym.kind.str()
}
.array {
if typ == ast.array_type {
return 'array'
}
if typ.has_flag(.variadic) {
res = t.type_to_str_using_aliases(t.value_type(typ), import_aliases)
} else {
if sym.info is Array {
elem_str := t.type_to_str_using_aliases(sym.info.elem_type, import_aliases)
res = '[]$elem_str'
} else {
res = 'array'
}
}
}
.array_fixed {
info := sym.info as ArrayFixed
elem_str := t.type_to_str_using_aliases(info.elem_type, import_aliases)
if info.size_expr is EmptyExpr {
res = '[$info.size]$elem_str'
} else {
res = '[$info.size_expr]$elem_str'
}
}
.chan {
// TODO currently the `chan` struct in builtin is not considered a struct but a chan
if sym.mod != 'builtin' && sym.name != 'chan' {
info := sym.info as Chan
mut elem_type := info.elem_type
mut mut_str := ''
if info.is_mut {
mut_str = 'mut '
elem_type = elem_type.set_nr_muls(elem_type.nr_muls() - 1)
}
elem_str := t.type_to_str_using_aliases(elem_type, import_aliases)
res = 'chan $mut_str$elem_str'
}
}
.function {
info := sym.info as FnType
if !t.is_fmt {
res = t.fn_signature(info.func, type_only: true)
} else {
if res.starts_with('fn (') {
// fn foo ()
has_names := info.func.params.any(it.name.len > 0)
res = t.fn_signature_using_aliases(info.func, import_aliases,
type_only: !has_names
)
} else {
// FnFoo
res = t.shorten_user_defined_typenames(res, import_aliases)
}
}
}
.map {
if int(typ) == ast.map_type_idx {
return 'map'
}
info := sym.info as Map
key_str := t.type_to_str_using_aliases(info.key_type, import_aliases)
val_str := t.type_to_str_using_aliases(info.value_type, import_aliases)
res = 'map[$key_str]$val_str'
}
.multi_return {
res = '('
info := sym.info as MultiReturn
for i, typ2 in info.types {
if i > 0 {
res += ', '
}
res += t.type_to_str_using_aliases(typ2, import_aliases)
}
res += ')'
}
.struct_, .interface_, .sum_type {
if typ.has_flag(.generic) {
match sym.info {
Struct, Interface, SumType {
res += '<'
for i, gtyp in sym.info.generic_types {
res += t.get_type_symbol(gtyp).name
if i != sym.info.generic_types.len - 1 {
res += ', '
}
}
res += '>'
}
else {}
}
} else {
res = t.shorten_user_defined_typenames(res, import_aliases)
}
}
.generic_inst {
info := sym.info as GenericInst
res = sym.name.all_before('<')
res += '<'
for i, ctyp in info.concrete_types {
res += t.get_type_symbol(ctyp).name
if i != info.concrete_types.len - 1 {
res += ', '
}
}
res += '>'
res = t.shorten_user_defined_typenames(res, import_aliases)
}
.void {
if typ.has_flag(.optional) {
return '?'
}
return 'void'
}
.thread {
rtype := sym.thread_info().return_type
if rtype != 1 {
res = 'thread ' + t.type_to_str_using_aliases(rtype, import_aliases)
}
}
.alias, .any, .aggregate, .placeholder, .enum_ {
res = t.shorten_user_defined_typenames(res, import_aliases)
}
}
mut nr_muls := typ.nr_muls()
if typ.has_flag(.shared_f) {
nr_muls--
res = 'shared ' + res
}
if nr_muls > 0 && !typ.has_flag(.variadic) {
res = strings.repeat(`&`, nr_muls) + res
}
if typ.has_flag(.optional) {
res = '?' + res
}
return res
}
fn (t Table) shorten_user_defined_typenames(originalname string, import_aliases map[string]string) string {
mut res := originalname
if t.cmod_prefix.len > 0 && res.starts_with(t.cmod_prefix) {
// cur_mod.Type => Type
res = res.replace_once(t.cmod_prefix, '')
} else if res in import_aliases {
res = import_aliases[res]
} else {
// types defined by the user
// mod.submod.submod2.Type => submod2.Type
mut parts := res.split('.')
if parts.len > 1 {
ind := parts.len - 2
if t.is_fmt {
// Rejoin the module parts for correct usage of aliases
parts[ind] = parts[..ind + 1].join('.')
}
if parts[ind] in import_aliases {
parts[ind] = import_aliases[parts[ind]]
}
res = parts[ind..].join('.')
} else {
res = parts[0]
}
}
return res
}
pub struct FnSignatureOpts {
skip_receiver bool
type_only bool
}
pub fn (t &Table) fn_signature(func &Fn, opts FnSignatureOpts) string {
return t.fn_signature_using_aliases(func, map[string]string{}, opts)
}
pub fn (t &Table) fn_signature_using_aliases(func &Fn, import_aliases map[string]string, opts FnSignatureOpts) string {
mut sb := strings.new_builder(20)
if !opts.skip_receiver {
sb.write_string('fn ')
// TODO write receiver
}
if !opts.type_only {
sb.write_string('$func.name')
}
sb.write_string('(')
start := int(opts.skip_receiver)
for i in start .. func.params.len {
if i != start {
sb.write_string(', ')
}
param := func.params[i]
mut typ := param.typ
if param.is_mut {
typ = typ.deref()
sb.write_string('mut ')
}
if !opts.type_only {
sb.write_string('$param.name ')
}
styp := t.type_to_str_using_aliases(typ, import_aliases)
if i == func.params.len - 1 && func.is_variadic {
sb.write_string('...$styp')
} else {
sb.write_string('$styp')
}
}
sb.write_string(')')
if func.return_type != ast.void_type {
sb.write_string(' ${t.type_to_str_using_aliases(func.return_type, import_aliases)}')
}
return sb.str()
}
pub fn (t &TypeSymbol) embed_name() string {
// main.Abc<int> => Abc<int>
mut embed_name := t.name.split('.').last()
// remove generic part from name
// Abc<int> => Abc
if embed_name.contains('<') {
embed_name = embed_name.split('<')[0]
}
return embed_name
}
pub fn (t &TypeSymbol) has_method(name string) bool {
t.find_method(name) or { return false }
return true
}
pub fn (t &TypeSymbol) find_method(name string) ?Fn {
for method in t.methods {
if method.name == name {
return method
}
}
return none
}
pub fn (t &TypeSymbol) find_method_with_generic_parent(name string) ?Fn {
if m := t.find_method(name) {
return m
}
mut table := global_table
match t.info {
Struct, Interface, SumType {
if t.info.parent_type.has_flag(.generic) {
parent_sym := table.get_type_symbol(t.info.parent_type)
if x := parent_sym.find_method(name) {
match parent_sym.info {
Struct, Interface, SumType {
mut method := x
generic_names := parent_sym.info.generic_types.map(table.get_type_symbol(it).name)
if rt := table.resolve_generic_to_concrete(method.return_type,
generic_names, t.info.concrete_types)
{
method.return_type = rt
}
method.params = method.params.clone()
for mut param in method.params {
if pt := table.resolve_generic_to_concrete(param.typ,
generic_names, t.info.concrete_types)
{
param.typ = pt
}
}
method.generic_names.clear()
return method
}
else {}
}
} else {
}
}
}
else {}
}
return none
}
pub fn (t &TypeSymbol) str_method_info() (bool, bool, int) {
mut has_str_method := false
mut expects_ptr := false
mut nr_args := 0
if sym_str_method := t.find_method('str') {
has_str_method = true
nr_args = sym_str_method.params.len
if nr_args > 0 {
expects_ptr = sym_str_method.params[0].typ.is_ptr()
}
}
return has_str_method, expects_ptr, nr_args
}
pub fn (t &TypeSymbol) find_field(name string) ?StructField {
match t.info {
Aggregate { return t.info.find_field(name) }
Struct { return t.info.find_field(name) }
Interface { return t.info.find_field(name) }
SumType { return t.info.find_field(name) }
else { return none }
}
}
fn (a &Aggregate) find_field(name string) ?StructField {
for field in a.fields {
if field.name == name {
return field
}
}
return none
}
pub fn (i &Interface) find_field(name string) ?StructField {
for field in i.fields {
if field.name == name {
return field
}
}
return none
}
pub fn (i &Interface) find_method(name string) ?Fn {
for method in i.methods {
if method.name == name {
return method
}
}
return none
}
pub fn (i &Interface) has_method(name string) bool {
if _ := i.find_method(name) {
return true
}
return false
}
pub fn (s Struct) find_field(name string) ?StructField {
for field in s.fields {
if field.name == name {
return field
}
}
return none
}
pub fn (s Struct) get_field(name string) StructField {
if field := s.find_field(name) {
return field
}
panic('unknown field `$name`')
}
pub fn (s &SumType) find_field(name string) ?StructField {
for field in s.fields {
if field.name == name {
return field
}
}
return none
}
pub fn (i Interface) defines_method(name string) bool {
for method in i.methods {
if method.name == name {
return true
}
}
return false
}