2021-01-18 13:20:06 +01:00
|
|
|
// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
|
2020-05-24 04:43:00 +02:00
|
|
|
// Use of this source code is governed by an MIT license
|
|
|
|
// that can be found in the LICENSE file.
|
|
|
|
module checker
|
|
|
|
|
|
|
|
import v.table
|
2020-05-31 12:58:13 +02:00
|
|
|
import v.token
|
2020-06-16 10:41:51 +02:00
|
|
|
import v.ast
|
2020-05-24 04:43:00 +02:00
|
|
|
|
2020-12-29 16:14:08 +01:00
|
|
|
pub fn (mut c Checker) check_expected_call_arg(got table.Type, expected_ table.Type) ? {
|
|
|
|
mut expected := expected_
|
|
|
|
// variadic
|
|
|
|
if expected.has_flag(.variadic) {
|
|
|
|
exp_type_sym := c.table.get_type_symbol(expected_)
|
|
|
|
exp_info := exp_type_sym.info as table.Array
|
|
|
|
expected = exp_info.elem_type
|
|
|
|
}
|
|
|
|
if c.check_types(got, expected) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
return error('cannot use `${c.table.type_to_str(got.clear_flag(.variadic))}` as `${c.table.type_to_str(expected.clear_flag(.variadic))}`')
|
|
|
|
}
|
|
|
|
|
2020-10-15 12:32:28 +02:00
|
|
|
pub fn (mut c Checker) check_basic(got table.Type, expected table.Type) bool {
|
2020-07-04 19:58:10 +02:00
|
|
|
if got == expected {
|
|
|
|
return true
|
|
|
|
}
|
2020-05-24 04:43:00 +02:00
|
|
|
t := c.table
|
2020-05-27 05:42:48 +02:00
|
|
|
got_idx := t.unalias_num_type(got).idx()
|
|
|
|
exp_idx := t.unalias_num_type(expected).idx()
|
2020-12-29 13:49:43 +01:00
|
|
|
// exp_is_optional := expected.has_flag(.optional)
|
|
|
|
// got_is_optional := got.has_flag(.optional)
|
|
|
|
// if (exp_is_optional && !got_is_optional) || (!exp_is_optional && got_is_optional) {
|
|
|
|
// return false
|
|
|
|
//}
|
2020-05-24 04:43:00 +02:00
|
|
|
// println('check: $got_type_sym.name, $exp_type_sym.name')
|
|
|
|
// # NOTE: use idxs here, and symbols below for perf
|
2021-01-07 07:36:11 +01:00
|
|
|
// got_is_ptr := got.is_ptr()
|
2020-05-24 04:43:00 +02:00
|
|
|
if got_idx == exp_idx {
|
|
|
|
// this is returning true even if one type is a ptr
|
|
|
|
// and the other is not, is this correct behaviour?
|
|
|
|
return true
|
|
|
|
}
|
2020-06-04 14:38:54 +02:00
|
|
|
if got_idx == table.none_type_idx && expected.has_flag(.optional) {
|
2020-12-03 01:03:17 +01:00
|
|
|
return false
|
2020-05-24 04:43:00 +02:00
|
|
|
}
|
2021-01-07 07:36:11 +01:00
|
|
|
exp_is_ptr := expected.is_ptr()
|
2020-05-24 04:43:00 +02:00
|
|
|
// allow pointers to be initialized with 0. TODO: use none instead
|
|
|
|
if exp_is_ptr && got_idx == table.int_type_idx {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if exp_idx == table.voidptr_type_idx || got_idx == table.voidptr_type_idx {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if exp_idx == table.any_type_idx || got_idx == table.any_type_idx {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
// TODO i64 as int etc
|
2021-01-23 09:33:22 +01:00
|
|
|
if (exp_idx in table.pointer_type_idxs || exp_idx in table.number_type_idxs)
|
|
|
|
&& (got_idx in table.pointer_type_idxs || got_idx in table.number_type_idxs) {
|
2020-05-24 04:43:00 +02:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
// if exp_idx in pointer_type_idxs && got_idx in pointer_type_idxs {
|
|
|
|
// return true
|
|
|
|
// }
|
|
|
|
// see hack in checker IndexExpr line #691
|
2021-01-23 09:33:22 +01:00
|
|
|
if (got_idx == table.byte_type_idx && exp_idx == table.byteptr_type_idx)
|
|
|
|
|| (exp_idx == table.byte_type_idx && got_idx == table.byteptr_type_idx) {
|
2020-05-24 04:43:00 +02:00
|
|
|
return true
|
|
|
|
}
|
2021-01-23 09:33:22 +01:00
|
|
|
if (got_idx == table.char_type_idx && exp_idx == table.charptr_type_idx)
|
|
|
|
|| (exp_idx == table.char_type_idx && got_idx == table.charptr_type_idx) {
|
2020-05-24 04:43:00 +02:00
|
|
|
return true
|
|
|
|
}
|
2020-06-29 20:09:09 +02:00
|
|
|
// TODO: this should no longer be needed
|
|
|
|
// if expected == table.t_type && got == table.t_type {
|
2020-08-11 00:51:15 +02:00
|
|
|
// return true
|
2020-06-29 20:09:09 +02:00
|
|
|
// }
|
2020-05-24 04:43:00 +02:00
|
|
|
// # NOTE: use symbols from this point on for perf
|
|
|
|
got_type_sym := t.get_type_symbol(got)
|
|
|
|
exp_type_sym := t.get_type_symbol(expected)
|
|
|
|
//
|
|
|
|
if exp_type_sym.kind == .function && got_type_sym.kind == .int {
|
|
|
|
// TODO temporary
|
|
|
|
// fn == 0
|
|
|
|
return true
|
|
|
|
}
|
2021-02-02 03:58:32 +01:00
|
|
|
// array/map fn
|
|
|
|
if got_type_sym.kind in [.array, .map] && exp_type_sym.kind == got_type_sym.kind {
|
2021-02-01 20:10:24 +01:00
|
|
|
if c.table.type_to_str(got) == c.table.type_to_str(expected).trim('&') {
|
2020-12-19 00:10:11 +01:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
2021-02-02 03:58:54 +01:00
|
|
|
// fixed array fn
|
|
|
|
if got_type_sym.kind == .array_fixed && exp_type_sym.kind == .array_fixed {
|
|
|
|
if c.table.type_to_str(got) == c.table.type_to_str(expected).trim('&') {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
2020-05-24 04:43:00 +02:00
|
|
|
if got_type_sym.kind == .array_fixed && exp_type_sym.kind == .byteptr {
|
|
|
|
info := got_type_sym.info as table.ArrayFixed
|
|
|
|
if info.elem_type.idx() == table.byte_type_idx {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// TODO
|
2020-12-04 04:26:25 +01:00
|
|
|
// if exp_type_sym.name == 'array' || got_type_sym.name == 'array' {
|
|
|
|
if got_idx == table.array_type_idx || exp_idx == table.array_type_idx {
|
2020-05-24 04:43:00 +02:00
|
|
|
return true
|
|
|
|
}
|
2020-12-07 18:13:03 +01:00
|
|
|
// TODO
|
|
|
|
// accept [] when an expected type is an array
|
2021-01-23 09:33:22 +01:00
|
|
|
if got_type_sym.kind == .array && exp_type_sym.kind == .array
|
|
|
|
&& got_type_sym.name == 'array_void' {
|
2020-12-07 18:13:03 +01:00
|
|
|
return true
|
2020-05-24 04:43:00 +02:00
|
|
|
}
|
|
|
|
// type alias
|
2021-01-23 09:33:22 +01:00
|
|
|
if (got_type_sym.kind == .alias && got_type_sym.parent_idx == exp_idx)
|
|
|
|
|| (exp_type_sym.kind == .alias && exp_type_sym.parent_idx == got_idx) {
|
2020-05-24 04:43:00 +02:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
// sum type
|
2021-01-04 19:48:13 +01:00
|
|
|
if c.table.sumtype_has_variant(expected, c.table.mktyp(got)) {
|
2020-07-05 16:25:04 +02:00
|
|
|
return true
|
2020-05-24 04:43:00 +02:00
|
|
|
}
|
|
|
|
// fn type
|
|
|
|
if got_type_sym.kind == .function && exp_type_sym.kind == .function {
|
2020-07-04 19:58:10 +02:00
|
|
|
return c.check_matching_function_symbols(got_type_sym, exp_type_sym)
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-10-15 12:32:28 +02:00
|
|
|
pub fn (mut c Checker) check_matching_function_symbols(got_type_sym &table.TypeSymbol, exp_type_sym &table.TypeSymbol) bool {
|
2020-07-04 19:58:10 +02:00
|
|
|
got_info := got_type_sym.info as table.FnType
|
|
|
|
exp_info := exp_type_sym.info as table.FnType
|
|
|
|
got_fn := got_info.func
|
|
|
|
exp_fn := exp_info.func
|
|
|
|
// we are using check() to compare return type & args as they might include
|
|
|
|
// functions themselves. TODO: optimize, only use check() when needed
|
2020-09-27 03:32:56 +02:00
|
|
|
if got_fn.params.len != exp_fn.params.len {
|
2020-07-04 19:58:10 +02:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
if !c.check_basic(got_fn.return_type, exp_fn.return_type) {
|
|
|
|
return false
|
|
|
|
}
|
2020-09-27 03:32:56 +02:00
|
|
|
for i, got_arg in got_fn.params {
|
|
|
|
exp_arg := exp_fn.params[i]
|
2020-07-04 19:58:10 +02:00
|
|
|
exp_arg_is_ptr := exp_arg.typ.is_ptr() || exp_arg.typ.is_pointer()
|
|
|
|
got_arg_is_ptr := got_arg.typ.is_ptr() || got_arg.typ.is_pointer()
|
|
|
|
if exp_arg_is_ptr != got_arg_is_ptr {
|
2020-07-04 21:24:44 +02:00
|
|
|
exp_arg_pointedness := if exp_arg_is_ptr { 'a pointer' } else { 'NOT a pointer' }
|
|
|
|
got_arg_pointedness := if got_arg_is_ptr { 'a pointer' } else { 'NOT a pointer' }
|
2020-08-11 00:51:15 +02:00
|
|
|
c.add_error_detail("`$exp_fn.name`\'s expected fn argument: `$exp_arg.name` is $exp_arg_pointedness, but the passed fn argument: `$got_arg.name` is $got_arg_pointedness")
|
2020-07-04 19:58:10 +02:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
if !c.check_basic(got_arg.typ, exp_arg.typ) {
|
|
|
|
return false
|
2020-05-24 04:43:00 +02:00
|
|
|
}
|
|
|
|
}
|
2020-07-04 19:58:10 +02:00
|
|
|
return true
|
2020-05-24 04:43:00 +02:00
|
|
|
}
|
2020-05-27 05:42:48 +02:00
|
|
|
|
2020-05-31 12:58:13 +02:00
|
|
|
[inline]
|
2020-10-15 12:32:28 +02:00
|
|
|
fn (mut c Checker) check_shift(left_type table.Type, right_type table.Type, left_pos token.Position, right_pos token.Position) table.Type {
|
2020-05-31 12:58:13 +02:00
|
|
|
if !left_type.is_int() {
|
2020-08-11 01:05:34 +02:00
|
|
|
// maybe it's an int alias? TODO move this to is_int() ?
|
|
|
|
sym := c.table.get_type_symbol(left_type)
|
|
|
|
if sym.kind == .alias && (sym.info as table.Alias).parent_type.is_int() {
|
|
|
|
return left_type
|
|
|
|
}
|
2020-08-11 16:26:49 +02:00
|
|
|
if c.pref.translated && left_type == table.bool_type {
|
|
|
|
// allow `bool << 2` in translated C code
|
|
|
|
return table.int_type
|
|
|
|
}
|
2020-08-11 01:05:34 +02:00
|
|
|
c.error('invalid operation: shift of type `$sym.name`', left_pos)
|
2020-05-31 12:58:13 +02:00
|
|
|
return table.void_type
|
|
|
|
} else if !right_type.is_int() {
|
2021-01-03 23:11:09 +01:00
|
|
|
c.error('cannot shift non-integer type `${c.table.get_type_symbol(right_type).name}` into type `${c.table.get_type_symbol(left_type).name}`',
|
2020-06-19 11:15:15 +02:00
|
|
|
right_pos)
|
2020-05-31 12:58:13 +02:00
|
|
|
return table.void_type
|
|
|
|
}
|
|
|
|
return left_type
|
|
|
|
}
|
|
|
|
|
2020-10-15 12:32:28 +02:00
|
|
|
pub fn (c &Checker) promote(left_type table.Type, right_type table.Type) table.Type {
|
2020-05-27 05:42:48 +02:00
|
|
|
if left_type.is_ptr() || left_type.is_pointer() {
|
|
|
|
if right_type.is_int() {
|
|
|
|
return left_type
|
|
|
|
} else {
|
|
|
|
return table.void_type
|
|
|
|
}
|
|
|
|
} else if right_type.is_ptr() || right_type.is_pointer() {
|
|
|
|
if left_type.is_int() {
|
|
|
|
return right_type
|
|
|
|
} else {
|
|
|
|
return table.void_type
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if left_type == right_type {
|
|
|
|
return left_type // strings, self defined operators
|
|
|
|
}
|
|
|
|
if right_type.is_number() && left_type.is_number() {
|
2020-06-01 21:15:59 +02:00
|
|
|
return c.promote_num(left_type, right_type)
|
2020-09-18 01:03:55 +02:00
|
|
|
} else if left_type.has_flag(.optional) != right_type.has_flag(.optional) {
|
|
|
|
// incompatible
|
|
|
|
return table.void_type
|
2020-06-01 21:15:59 +02:00
|
|
|
} else {
|
|
|
|
return left_type // default to left if not automatic promotion possible
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-15 12:32:28 +02:00
|
|
|
fn (c &Checker) promote_num(left_type table.Type, right_type table.Type) table.Type {
|
2020-06-01 21:15:59 +02:00
|
|
|
// sort the operands to save time
|
|
|
|
mut type_hi := left_type
|
|
|
|
mut type_lo := right_type
|
|
|
|
if type_hi.idx() < type_lo.idx() {
|
|
|
|
type_hi, type_lo = type_lo, type_hi
|
|
|
|
}
|
|
|
|
idx_hi := type_hi.idx()
|
|
|
|
idx_lo := type_lo.idx()
|
|
|
|
// the following comparisons rely on the order of the indices in atypes.v
|
2021-01-11 22:58:15 +01:00
|
|
|
if idx_hi == table.int_literal_type_idx {
|
2020-06-01 21:15:59 +02:00
|
|
|
return type_lo
|
2021-01-11 22:58:15 +01:00
|
|
|
} else if idx_hi == table.float_literal_type_idx {
|
2020-06-01 21:15:59 +02:00
|
|
|
if idx_lo in table.float_type_idxs {
|
2020-05-27 05:42:48 +02:00
|
|
|
return type_lo
|
2020-06-01 21:15:59 +02:00
|
|
|
} else {
|
|
|
|
return table.void_type
|
|
|
|
}
|
|
|
|
} else if type_hi.is_float() {
|
|
|
|
if idx_hi == table.f32_type_idx {
|
|
|
|
if idx_lo in [table.i64_type_idx, table.u64_type_idx] {
|
|
|
|
return table.void_type
|
|
|
|
} else {
|
|
|
|
return type_hi
|
2020-05-27 05:42:48 +02:00
|
|
|
}
|
2021-01-11 22:58:15 +01:00
|
|
|
} else { // f64, float_literal
|
2020-06-02 17:00:14 +02:00
|
|
|
return type_hi
|
2020-05-27 05:42:48 +02:00
|
|
|
}
|
2020-06-01 21:15:59 +02:00
|
|
|
} else if idx_lo >= table.byte_type_idx { // both operands are unsigned
|
|
|
|
return type_hi
|
2021-01-23 09:33:22 +01:00
|
|
|
} else if idx_lo >= table.i8_type_idx
|
|
|
|
&& (idx_hi <= table.i64_type_idx || idx_hi == table.rune_type_idx) { // both signed
|
2020-09-18 01:01:05 +02:00
|
|
|
return if idx_lo == table.i64_type_idx {
|
|
|
|
type_lo
|
|
|
|
} else {
|
|
|
|
type_hi
|
|
|
|
}
|
2020-06-01 21:15:59 +02:00
|
|
|
} else if idx_hi - idx_lo < (table.byte_type_idx - table.i8_type_idx) {
|
|
|
|
return type_lo // conversion unsigned -> signed if signed type is larger
|
2020-05-27 05:42:48 +02:00
|
|
|
} else {
|
2020-06-01 21:15:59 +02:00
|
|
|
return table.void_type // conversion signed -> unsigned not allowed
|
2020-05-27 05:42:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-01 21:15:59 +02:00
|
|
|
// TODO: promote(), check_types(), symmetric_check() and check() overlap - should be rearranged
|
2020-10-15 12:32:28 +02:00
|
|
|
pub fn (mut c Checker) check_types(got table.Type, expected table.Type) bool {
|
2020-07-04 19:58:10 +02:00
|
|
|
if got == expected {
|
|
|
|
return true
|
|
|
|
}
|
2021-01-07 07:36:11 +01:00
|
|
|
got_is_ptr := got.is_ptr()
|
|
|
|
exp_is_ptr := expected.is_ptr()
|
|
|
|
if got_is_ptr && exp_is_ptr {
|
|
|
|
if got.nr_muls() != expected.nr_muls() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2020-05-27 05:42:48 +02:00
|
|
|
exp_idx := expected.idx()
|
|
|
|
got_idx := got.idx()
|
|
|
|
if exp_idx == got_idx {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if exp_idx == table.voidptr_type_idx || exp_idx == table.byteptr_type_idx {
|
|
|
|
if got.is_ptr() || got.is_pointer() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// allow direct int-literal assignment for pointers for now
|
|
|
|
// maybe in the future optionals should be used for that
|
|
|
|
if expected.is_ptr() || expected.is_pointer() {
|
2021-01-11 22:58:15 +01:00
|
|
|
if got == table.int_literal_type {
|
2020-05-27 05:42:48 +02:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if got_idx == table.voidptr_type_idx || got_idx == table.byteptr_type_idx {
|
|
|
|
if expected.is_ptr() || expected.is_pointer() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
2020-06-01 21:15:59 +02:00
|
|
|
if !c.check_basic(got, expected) { // TODO: this should go away...
|
2020-05-27 05:42:48 +02:00
|
|
|
return false
|
|
|
|
}
|
2020-06-01 21:15:59 +02:00
|
|
|
if got.is_number() && expected.is_number() {
|
2020-08-27 06:46:18 +02:00
|
|
|
if got == table.rune_type && expected == table.byte_type {
|
|
|
|
return true
|
|
|
|
} else if expected == table.rune_type && got == table.byte_type {
|
|
|
|
return true
|
|
|
|
}
|
2020-06-01 21:15:59 +02:00
|
|
|
if c.promote_num(expected, got) != expected {
|
|
|
|
// println('could not promote ${c.table.get_type_symbol(got).name} to ${c.table.get_type_symbol(expected).name}')
|
|
|
|
return false
|
|
|
|
}
|
2020-05-27 05:42:48 +02:00
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-10-15 17:30:36 +02:00
|
|
|
pub fn (mut c Checker) check_expected(got table.Type, expected table.Type) ? {
|
|
|
|
if c.check_types(got, expected) {
|
|
|
|
return
|
|
|
|
}
|
2020-12-12 10:42:07 +01:00
|
|
|
return error(c.expected_msg(got, expected))
|
|
|
|
}
|
|
|
|
|
|
|
|
[inline]
|
|
|
|
fn (c &Checker) expected_msg(got table.Type, expected table.Type) string {
|
2020-10-15 17:30:36 +02:00
|
|
|
exps := c.table.type_to_str(expected)
|
|
|
|
gots := c.table.type_to_str(got)
|
2020-12-12 10:42:07 +01:00
|
|
|
return 'expected `$exps`, not `$gots`'
|
2020-10-15 17:30:36 +02:00
|
|
|
}
|
|
|
|
|
2020-10-15 12:32:28 +02:00
|
|
|
pub fn (mut c Checker) symmetric_check(left table.Type, right table.Type) bool {
|
2020-05-27 05:42:48 +02:00
|
|
|
// allow direct int-literal assignment for pointers for now
|
|
|
|
// maybe in the future optionals should be used for that
|
|
|
|
if right.is_ptr() || right.is_pointer() {
|
2021-01-11 22:58:15 +01:00
|
|
|
if left == table.int_literal_type {
|
2020-05-27 05:42:48 +02:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// allow direct int-literal assignment for pointers for now
|
|
|
|
if left.is_ptr() || left.is_pointer() {
|
2021-01-11 22:58:15 +01:00
|
|
|
if right == table.int_literal_type {
|
2020-05-27 05:42:48 +02:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
2020-06-01 21:15:59 +02:00
|
|
|
return c.check_basic(left, right)
|
2020-05-27 05:42:48 +02:00
|
|
|
}
|
2020-06-16 10:41:51 +02:00
|
|
|
|
2020-10-15 12:32:28 +02:00
|
|
|
pub fn (c &Checker) get_default_fmt(ftyp table.Type, typ table.Type) byte {
|
2020-12-01 04:00:23 +01:00
|
|
|
if ftyp.has_flag(.optional) {
|
|
|
|
return `s`
|
|
|
|
} else if typ.is_float() {
|
2020-06-16 10:41:51 +02:00
|
|
|
return `g`
|
2021-01-11 22:58:15 +01:00
|
|
|
} else if typ.is_signed() || typ.is_int_literal() {
|
2020-06-16 10:41:51 +02:00
|
|
|
return `d`
|
|
|
|
} else if typ.is_unsigned() {
|
|
|
|
return `u`
|
|
|
|
} else if typ.is_pointer() {
|
|
|
|
return `p`
|
|
|
|
} else {
|
2020-10-31 18:43:06 +01:00
|
|
|
mut sym := c.table.get_type_symbol(c.unwrap_generic(ftyp))
|
2020-06-24 22:12:33 +02:00
|
|
|
if sym.kind == .alias {
|
|
|
|
// string aliases should be printable
|
|
|
|
info := sym.info as table.Alias
|
2020-09-20 19:51:14 +02:00
|
|
|
sym = c.table.get_type_symbol(info.parent_type)
|
2020-06-24 22:12:33 +02:00
|
|
|
if info.parent_type == table.string_type {
|
|
|
|
return `s`
|
|
|
|
}
|
|
|
|
}
|
2020-12-16 10:07:58 +01:00
|
|
|
if sym.kind == .function {
|
|
|
|
return `s`
|
|
|
|
}
|
2021-01-23 09:33:22 +01:00
|
|
|
if ftyp in [table.string_type, table.bool_type]
|
|
|
|
|| sym.kind in [.enum_, .array, .array_fixed, .struct_, .map, .multi_return, .sum_type, .none_]
|
|
|
|
|| ftyp.has_flag(.optional)|| sym.has_method('str') {
|
2020-06-16 10:41:51 +02:00
|
|
|
return `s`
|
|
|
|
} else {
|
|
|
|
return `_`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-23 23:16:36 +02:00
|
|
|
pub fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) table.Type {
|
2020-06-16 10:41:51 +02:00
|
|
|
for i, expr in node.exprs {
|
|
|
|
ftyp := c.expr(expr)
|
|
|
|
node.expr_types << ftyp
|
|
|
|
typ := c.table.unalias_num_type(ftyp)
|
|
|
|
mut fmt := node.fmts[i]
|
|
|
|
// analyze and validate format specifier
|
2021-01-23 09:33:22 +01:00
|
|
|
if fmt !in [`E`, `F`, `G`, `e`, `f`, `g`, `d`, `u`, `x`, `X`, `o`, `c`, `s`, `p`, `_`] {
|
2020-06-16 10:41:51 +02:00
|
|
|
c.error('unknown format specifier `${fmt:c}`', node.fmt_poss[i])
|
|
|
|
}
|
|
|
|
if fmt == `_` { // set default representation for type if none has been given
|
|
|
|
fmt = c.get_default_fmt(ftyp, typ)
|
|
|
|
if fmt == `_` {
|
2020-06-19 11:15:15 +02:00
|
|
|
if typ != table.void_type {
|
|
|
|
c.error('no known default format for type `${c.table.get_type_name(ftyp)}`',
|
2020-06-16 10:41:51 +02:00
|
|
|
node.fmt_poss[i])
|
2020-06-19 11:15:15 +02:00
|
|
|
}
|
2020-06-16 10:41:51 +02:00
|
|
|
} else {
|
|
|
|
node.fmts[i] = fmt
|
|
|
|
node.need_fmts[i] = false
|
|
|
|
}
|
|
|
|
} else { // check if given format specifier is valid for type
|
2020-10-16 16:28:11 +02:00
|
|
|
if node.precisions[i] != 987698 && !typ.is_float() {
|
2020-06-16 10:41:51 +02:00
|
|
|
c.error('precision specification only valid for float types', node.fmt_poss[i])
|
|
|
|
}
|
|
|
|
if node.pluss[i] && !typ.is_number() {
|
2020-06-26 07:05:07 +02:00
|
|
|
c.error('plus prefix only allowed for numbers', node.fmt_poss[i])
|
2020-06-16 10:41:51 +02:00
|
|
|
}
|
2021-01-23 09:33:22 +01:00
|
|
|
if (typ.is_unsigned() && fmt !in [`u`, `x`, `X`, `o`, `c`])
|
|
|
|
|| (typ.is_signed() && fmt !in [`d`, `x`, `X`, `o`, `c`])
|
|
|
|
|| (typ.is_int_literal() && fmt !in [`d`, `c`, `x`, `X`, `o`, `u`, `x`, `X`, `o`])
|
|
|
|
|| (typ.is_float() && fmt !in [`E`, `F`, `G`, `e`, `f`, `g`])
|
|
|
|
|| (typ.is_pointer() && fmt !in [`p`, `x`, `X`])
|
|
|
|
|| (typ.is_string() && fmt != `s`)
|
|
|
|
|| (typ.idx() in [table.i64_type_idx, table.f64_type_idx] && fmt == `c`) {
|
2020-06-16 10:41:51 +02:00
|
|
|
c.error('illegal format specifier `${fmt:c}` for type `${c.table.get_type_name(ftyp)}`',
|
|
|
|
node.fmt_poss[i])
|
|
|
|
}
|
|
|
|
node.need_fmts[i] = fmt != c.get_default_fmt(ftyp, typ)
|
|
|
|
}
|
2020-12-05 21:41:54 +01:00
|
|
|
// check recursive str
|
|
|
|
if c.cur_fn.is_method && c.cur_fn.name == 'str' && c.cur_fn.receiver.name == expr.str() {
|
|
|
|
c.error('cannot call `str()` method recursively', expr.position())
|
|
|
|
}
|
2020-06-16 10:41:51 +02:00
|
|
|
}
|
|
|
|
return table.string_type
|
|
|
|
}
|
2020-07-05 16:53:56 +02:00
|
|
|
|
2020-10-06 15:34:02 +02:00
|
|
|
pub fn (mut c Checker) infer_fn_types(f table.Fn, mut call_expr ast.CallExpr) {
|
2021-01-22 13:49:56 +01:00
|
|
|
mut inferred_types := []table.Type{}
|
|
|
|
for gi, gt_name in f.generic_names {
|
|
|
|
// skip known types
|
|
|
|
if gi < call_expr.generic_types.len {
|
|
|
|
inferred_types << call_expr.generic_types[gi]
|
|
|
|
continue
|
2021-01-08 00:50:59 +01:00
|
|
|
}
|
2021-01-22 13:49:56 +01:00
|
|
|
mut typ := table.void_type
|
|
|
|
for i, param in f.params {
|
2021-01-29 19:14:22 +01:00
|
|
|
arg_i := if i != 0 && call_expr.is_method { i - 1 } else { i }
|
|
|
|
if call_expr.args.len <= arg_i {
|
2021-01-22 13:49:56 +01:00
|
|
|
break
|
|
|
|
}
|
2021-01-29 19:14:22 +01:00
|
|
|
arg := call_expr.args[arg_i]
|
2021-01-26 08:37:48 +01:00
|
|
|
param_type_sym := c.table.get_type_symbol(param.typ)
|
|
|
|
if param.typ.has_flag(.generic) && param_type_sym.name == gt_name {
|
2021-01-22 13:49:56 +01:00
|
|
|
typ = arg.typ
|
|
|
|
break
|
|
|
|
}
|
|
|
|
arg_sym := c.table.get_type_symbol(arg.typ)
|
|
|
|
if arg_sym.kind == .array && param_type_sym.kind == .array {
|
|
|
|
mut arg_elem_info := arg_sym.info as table.Array
|
|
|
|
mut param_elem_info := param_type_sym.info as table.Array
|
|
|
|
mut arg_elem_sym := c.table.get_type_symbol(arg_elem_info.elem_type)
|
|
|
|
mut param_elem_sym := c.table.get_type_symbol(param_elem_info.elem_type)
|
|
|
|
for {
|
2021-01-23 09:33:22 +01:00
|
|
|
if arg_elem_sym.kind == .array && param_elem_sym.kind == .array
|
|
|
|
&& c.cur_fn.generic_params.filter(it.name == param_elem_sym.name).len == 0 {
|
2021-01-22 13:49:56 +01:00
|
|
|
arg_elem_info = arg_elem_sym.info as table.Array
|
|
|
|
arg_elem_sym = c.table.get_type_symbol(arg_elem_info.elem_type)
|
|
|
|
param_elem_info = param_elem_sym.info as table.Array
|
|
|
|
param_elem_sym = c.table.get_type_symbol(param_elem_info.elem_type)
|
|
|
|
} else {
|
|
|
|
typ = arg_elem_info.elem_type
|
|
|
|
break
|
|
|
|
}
|
2021-01-04 17:40:53 +01:00
|
|
|
}
|
2021-01-22 13:49:56 +01:00
|
|
|
break
|
2020-12-06 04:55:08 +01:00
|
|
|
}
|
2020-10-06 15:34:02 +02:00
|
|
|
}
|
2021-01-22 13:49:56 +01:00
|
|
|
if typ == table.void_type {
|
|
|
|
c.error('could not infer generic type `$gt_name` in call to `$f.name`', call_expr.pos)
|
|
|
|
}
|
2020-10-13 14:15:25 +02:00
|
|
|
if c.pref.is_verbose {
|
|
|
|
s := c.table.type_to_str(typ)
|
|
|
|
println('inferred `$f.name<$s>`')
|
|
|
|
}
|
2021-01-22 13:49:56 +01:00
|
|
|
inferred_types << typ
|
|
|
|
call_expr.generic_types << typ
|
2020-10-06 15:34:02 +02:00
|
|
|
}
|
2021-01-22 13:49:56 +01:00
|
|
|
c.table.register_fn_gen_type(f.name, inferred_types)
|
2020-10-06 15:34:02 +02:00
|
|
|
}
|
2021-01-23 09:19:08 +01:00
|
|
|
|
|
|
|
// resolve_generic_type resolves generics to real types T => int.
|
|
|
|
// Even map[string]map[string]T can be resolved.
|
|
|
|
// This is used for resolving the generic return type of CallExpr white `unwrap_generic` is used to resolve generic usage in FnDecl.
|
2021-01-23 22:41:32 +01:00
|
|
|
fn (mut c Checker) resolve_generic_type(generic_type table.Type, generic_names []string, generic_types []table.Type) ?table.Type {
|
2021-01-23 09:19:08 +01:00
|
|
|
mut sym := c.table.get_type_symbol(generic_type)
|
|
|
|
if sym.name in generic_names {
|
|
|
|
index := generic_names.index(sym.name)
|
2021-01-23 22:41:32 +01:00
|
|
|
mut typ := generic_types[index]
|
2021-01-23 09:19:08 +01:00
|
|
|
typ = typ.set_nr_muls(generic_type.nr_muls())
|
|
|
|
if generic_type.has_flag(.optional) {
|
|
|
|
typ = typ.set_flag(.optional)
|
|
|
|
}
|
|
|
|
return typ
|
|
|
|
} else if sym.kind == .array {
|
|
|
|
info := sym.info as table.Array
|
|
|
|
mut elem_type := info.elem_type
|
|
|
|
mut elem_sym := c.table.get_type_symbol(elem_type)
|
|
|
|
mut dims := 1
|
|
|
|
for mut elem_sym.info is table.Array {
|
|
|
|
elem_type = elem_sym.info.elem_type
|
|
|
|
elem_sym = c.table.get_type_symbol(elem_type)
|
|
|
|
dims++
|
|
|
|
}
|
2021-01-23 22:41:32 +01:00
|
|
|
if typ := c.resolve_generic_type(elem_type, generic_names, generic_types) {
|
2021-01-23 09:19:08 +01:00
|
|
|
idx := c.table.find_or_register_array_with_dims(typ, dims)
|
|
|
|
array_typ := table.new_type(idx)
|
|
|
|
return array_typ
|
|
|
|
}
|
|
|
|
} else if sym.kind == .chan {
|
|
|
|
info := sym.info as table.Chan
|
2021-01-23 22:41:32 +01:00
|
|
|
if typ := c.resolve_generic_type(info.elem_type, generic_names, generic_types) {
|
2021-01-23 09:19:08 +01:00
|
|
|
idx := c.table.find_or_register_chan(typ, typ.nr_muls() > 0)
|
|
|
|
chan_typ := table.new_type(idx)
|
|
|
|
return chan_typ
|
|
|
|
}
|
|
|
|
} else if mut sym.info is table.MultiReturn {
|
|
|
|
mut types := []table.Type{}
|
|
|
|
mut type_changed := false
|
|
|
|
for ret_type in sym.info.types {
|
2021-01-23 22:41:32 +01:00
|
|
|
if typ := c.resolve_generic_type(ret_type, generic_names, generic_types) {
|
2021-01-23 09:19:08 +01:00
|
|
|
types << typ
|
|
|
|
type_changed = true
|
|
|
|
} else {
|
|
|
|
types << ret_type
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if type_changed {
|
|
|
|
idx := c.table.find_or_register_multi_return(types)
|
|
|
|
typ := table.new_type(idx)
|
|
|
|
return typ
|
|
|
|
}
|
|
|
|
} else if mut sym.info is table.Map {
|
|
|
|
mut type_changed := false
|
|
|
|
mut unwrapped_key_type := sym.info.key_type
|
|
|
|
mut unwrapped_value_type := sym.info.value_type
|
2021-01-23 22:41:32 +01:00
|
|
|
if typ := c.resolve_generic_type(sym.info.key_type, generic_names, generic_types) {
|
2021-01-23 09:19:08 +01:00
|
|
|
unwrapped_key_type = typ
|
|
|
|
type_changed = true
|
|
|
|
}
|
2021-01-23 22:41:32 +01:00
|
|
|
if typ := c.resolve_generic_type(sym.info.value_type, generic_names, generic_types) {
|
2021-01-23 09:19:08 +01:00
|
|
|
unwrapped_value_type = typ
|
|
|
|
type_changed = true
|
|
|
|
}
|
|
|
|
if type_changed {
|
|
|
|
idx := c.table.find_or_register_map(unwrapped_key_type, unwrapped_value_type)
|
|
|
|
typ := table.new_type(idx)
|
|
|
|
return typ
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return none
|
|
|
|
}
|