From 1566f7f76637795c19112f278a6bf96dff272474 Mon Sep 17 00:00:00 2001 From: yuyi Date: Thu, 24 Mar 2022 14:59:05 +0800 Subject: [PATCH] checker: minor cleanup in check_types.v (#13816) --- vlib/v/checker/check_types.v | 221 ----------------------------------- vlib/v/checker/checker.v | 41 +++++++ vlib/v/checker/str.v | 186 +++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+), 221 deletions(-) create mode 100644 vlib/v/checker/str.v diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index 48cc1a5a1a..12435e8096 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -4,7 +4,6 @@ module checker import v.ast -import v.token // TODO: promote(), check_types(), symmetric_check() and check() overlap - should be rearranged pub fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool { @@ -524,226 +523,6 @@ pub fn (mut c Checker) symmetric_check(left ast.Type, right ast.Type) bool { return c.check_basic(left, right) } -pub fn (mut c Checker) get_default_fmt(ftyp ast.Type, typ ast.Type) byte { - if ftyp.has_flag(.optional) { - return `s` - } else if typ.is_float() { - return `g` - } else if typ.is_signed() || typ.is_int_literal() { - return `d` - } else if typ.is_unsigned() { - return `u` - } else if typ.is_pointer() { - return `p` - } else { - mut sym := c.table.sym(c.unwrap_generic(ftyp)) - if sym.kind == .alias { - // string aliases should be printable - info := sym.info as ast.Alias - sym = c.table.sym(info.parent_type) - if info.parent_type == ast.string_type { - return `s` - } - } - if sym.kind == .function { - return `s` - } - if ftyp in [ast.string_type, ast.bool_type] - || sym.kind in [.enum_, .array, .array_fixed, .struct_, .map, .multi_return, .sum_type, .interface_, .none_] - || ftyp.has_flag(.optional) || sym.has_method('str') { - return `s` - } else { - return `_` - } - } -} - -pub fn (mut c Checker) fail_if_unreadable(expr ast.Expr, typ ast.Type, what string) { - mut pos := token.Pos{} - match expr { - ast.Ident { - if typ.has_flag(.shared_f) { - if expr.name !in c.rlocked_names && expr.name !in c.locked_names { - action := if what == 'argument' { 'passed' } else { 'used' } - c.error('`$expr.name` is `shared` and must be `rlock`ed or `lock`ed to be $action as non-mut $what', - expr.pos) - } - } - return - } - ast.SelectorExpr { - pos = expr.pos - if typ.has_flag(.shared_f) { - expr_name := '${expr.expr}.$expr.field_name' - if expr_name !in c.rlocked_names && expr_name !in c.locked_names { - action := if what == 'argument' { 'passed' } else { 'used' } - c.error('`$expr_name` is `shared` and must be `rlock`ed or `lock`ed to be $action as non-mut $what', - expr.pos) - } - return - } else { - c.fail_if_unreadable(expr.expr, expr.expr_type, what) - } - } - ast.IndexExpr { - pos = expr.left.pos().extend(expr.pos) - c.fail_if_unreadable(expr.left, expr.left_type, what) - } - else { - pos = expr.pos() - } - } - if typ.has_flag(.shared_f) { - c.error('you have to create a handle and `rlock` it to use a `shared` element as non-mut $what', - pos) - } -} - -pub fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) ast.Type { - inside_println_arg_save := c.inside_println_arg - c.inside_println_arg = true - for i, expr in node.exprs { - ftyp := c.expr(expr) - if ftyp == ast.void_type { - c.error('expression does not return a value', expr.pos()) - } else if ftyp == ast.char_type && ftyp.nr_muls() == 0 { - c.error('expression returning type `char` cannot be used in string interpolation directly, print its address or cast it to an integer instead', - expr.pos()) - } - c.fail_if_unreadable(expr, ftyp, 'interpolation object') - node.expr_types << ftyp - ftyp_sym := c.table.sym(ftyp) - typ := if ftyp_sym.kind == .alias && !ftyp_sym.has_method('str') { - c.table.unalias_num_type(ftyp) - } else { - ftyp - } - mut fmt := node.fmts[i] - // analyze and validate format specifier - if fmt !in [`E`, `F`, `G`, `e`, `f`, `g`, `d`, `u`, `x`, `X`, `o`, `c`, `s`, `S`, `p`, - `b`, `_`] { - 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 == `_` { - if typ != ast.void_type { - c.error('no known default format for type `${c.table.get_type_name(ftyp)}`', - node.fmt_poss[i]) - } - } else { - node.fmts[i] = fmt - node.need_fmts[i] = false - } - } else { // check if given format specifier is valid for type - if node.precisions[i] != 987698 && !typ.is_float() { - c.error('precision specification only valid for float types', node.fmt_poss[i]) - } - if node.pluss[i] && !typ.is_number() { - c.error('plus prefix only allowed for numbers', node.fmt_poss[i]) - } - if (typ.is_unsigned() && fmt !in [`u`, `x`, `X`, `o`, `c`, `b`]) - || (typ.is_signed() && fmt !in [`d`, `x`, `X`, `o`, `c`, `b`]) - || (typ.is_int_literal() - && fmt !in [`d`, `c`, `x`, `X`, `o`, `u`, `x`, `X`, `o`, `b`]) - || (typ.is_float() && fmt !in [`E`, `F`, `G`, `e`, `f`, `g`]) - || (typ.is_pointer() && fmt !in [`p`, `x`, `X`]) - || (typ.is_string() && fmt !in [`s`, `S`]) - || (typ.idx() in [ast.i64_type_idx, ast.f64_type_idx] && fmt == `c`) { - 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) - } - // check recursive str - if c.table.cur_fn.is_method && c.table.cur_fn.name == 'str' - && c.table.cur_fn.receiver.name == expr.str() { - c.error('cannot call `str()` method recursively', expr.pos()) - } - } - c.inside_println_arg = inside_println_arg_save - return ast.string_type -} - -const unicode_lit_overflow_message = 'unicode character exceeds max allowed value of 0x10ffff, consider using a unicode literal (\\u####)' - -// unicode character literals are limited to a maximum value of 0x10ffff -// https://stackoverflow.com/questions/52203351/why-unicode-is-restricted-to-0x10ffff -pub fn (mut c Checker) string_lit(mut node ast.StringLiteral) ast.Type { - mut idx := 0 - for idx < node.val.len { - match node.val[idx] { - `\\` { - mut start_pos := token.Pos{ - ...node.pos - col: node.pos.col + 1 + idx - } - start_idx := idx - idx++ - next_ch := node.val[idx] or { return ast.string_type } - if next_ch == `u` { - idx++ - mut ch := node.val[idx] or { return ast.string_type } - mut hex_char_count := 0 - for ch.is_hex_digit() { - hex_char_count++ - end_pos := token.Pos{ - ...start_pos - len: idx + 1 - start_idx - } - match hex_char_count { - 1...5 {} - 6 { - first_digit := node.val[idx - 5] - 48 - second_digit := node.val[idx - 4] - 48 - if first_digit > 1 { - c.error(checker.unicode_lit_overflow_message, end_pos) - } else if first_digit == 1 && second_digit > 0 { - c.error(checker.unicode_lit_overflow_message, end_pos) - } - } - else { - c.error(checker.unicode_lit_overflow_message, end_pos) - } - } - idx++ - ch = node.val[idx] or { return ast.string_type } - } - } - } - else { - idx++ - } - } - } - return ast.string_type -} - -pub fn (mut c Checker) int_lit(mut node ast.IntegerLiteral) ast.Type { - if node.val.len < 17 { - // can not be a too large number, no need for more expensive checks - return ast.int_literal_type - } - lit := node.val.replace('_', '').all_after('-') - is_neg := node.val.starts_with('-') - limit := if is_neg { '9223372036854775808' } else { '18446744073709551615' } - message := 'integer literal $node.val overflows int' - - if lit.len > limit.len { - c.error(message, node.pos) - } else if lit.len == limit.len { - for i, digit in lit { - if digit > limit[i] { - c.error(message, node.pos) - } else if digit < limit[i] { - break - } - } - } - - return ast.int_literal_type -} - pub fn (mut c Checker) infer_fn_generic_types(func ast.Fn, mut node ast.CallExpr) { mut inferred_types := []ast.Type{} for gi, gt_name in func.generic_names { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index d4684e4dab..81a5366d2e 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -4171,3 +4171,44 @@ fn (mut c Checker) ensure_type_exists(typ ast.Type, pos token.Pos) ? { else {} } } + +pub fn (mut c Checker) fail_if_unreadable(expr ast.Expr, typ ast.Type, what string) { + mut pos := token.Pos{} + match expr { + ast.Ident { + if typ.has_flag(.shared_f) { + if expr.name !in c.rlocked_names && expr.name !in c.locked_names { + action := if what == 'argument' { 'passed' } else { 'used' } + c.error('`$expr.name` is `shared` and must be `rlock`ed or `lock`ed to be $action as non-mut $what', + expr.pos) + } + } + return + } + ast.SelectorExpr { + pos = expr.pos + if typ.has_flag(.shared_f) { + expr_name := '${expr.expr}.$expr.field_name' + if expr_name !in c.rlocked_names && expr_name !in c.locked_names { + action := if what == 'argument' { 'passed' } else { 'used' } + c.error('`$expr_name` is `shared` and must be `rlock`ed or `lock`ed to be $action as non-mut $what', + expr.pos) + } + return + } else { + c.fail_if_unreadable(expr.expr, expr.expr_type, what) + } + } + ast.IndexExpr { + pos = expr.left.pos().extend(expr.pos) + c.fail_if_unreadable(expr.left, expr.left_type, what) + } + else { + pos = expr.pos() + } + } + if typ.has_flag(.shared_f) { + c.error('you have to create a handle and `rlock` it to use a `shared` element as non-mut $what', + pos) + } +} diff --git a/vlib/v/checker/str.v b/vlib/v/checker/str.v new file mode 100644 index 0000000000..3b4414b462 --- /dev/null +++ b/vlib/v/checker/str.v @@ -0,0 +1,186 @@ +// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module checker + +import v.ast +import v.token + +pub fn (mut c Checker) get_default_fmt(ftyp ast.Type, typ ast.Type) byte { + if ftyp.has_flag(.optional) { + return `s` + } else if typ.is_float() { + return `g` + } else if typ.is_signed() || typ.is_int_literal() { + return `d` + } else if typ.is_unsigned() { + return `u` + } else if typ.is_pointer() { + return `p` + } else { + mut sym := c.table.sym(c.unwrap_generic(ftyp)) + if sym.kind == .alias { + // string aliases should be printable + info := sym.info as ast.Alias + sym = c.table.sym(info.parent_type) + if info.parent_type == ast.string_type { + return `s` + } + } + if sym.kind == .function { + return `s` + } + if ftyp in [ast.string_type, ast.bool_type] + || sym.kind in [.enum_, .array, .array_fixed, .struct_, .map, .multi_return, .sum_type, .interface_, .none_] + || ftyp.has_flag(.optional) || sym.has_method('str') { + return `s` + } else { + return `_` + } + } +} + +pub fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) ast.Type { + inside_println_arg_save := c.inside_println_arg + c.inside_println_arg = true + for i, expr in node.exprs { + ftyp := c.expr(expr) + if ftyp == ast.void_type { + c.error('expression does not return a value', expr.pos()) + } else if ftyp == ast.char_type && ftyp.nr_muls() == 0 { + c.error('expression returning type `char` cannot be used in string interpolation directly, print its address or cast it to an integer instead', + expr.pos()) + } + c.fail_if_unreadable(expr, ftyp, 'interpolation object') + node.expr_types << ftyp + ftyp_sym := c.table.sym(ftyp) + typ := if ftyp_sym.kind == .alias && !ftyp_sym.has_method('str') { + c.table.unalias_num_type(ftyp) + } else { + ftyp + } + mut fmt := node.fmts[i] + // analyze and validate format specifier + if fmt !in [`E`, `F`, `G`, `e`, `f`, `g`, `d`, `u`, `x`, `X`, `o`, `c`, `s`, `S`, `p`, + `b`, `_`] { + 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 == `_` { + if typ != ast.void_type { + c.error('no known default format for type `${c.table.get_type_name(ftyp)}`', + node.fmt_poss[i]) + } + } else { + node.fmts[i] = fmt + node.need_fmts[i] = false + } + } else { // check if given format specifier is valid for type + if node.precisions[i] != 987698 && !typ.is_float() { + c.error('precision specification only valid for float types', node.fmt_poss[i]) + } + if node.pluss[i] && !typ.is_number() { + c.error('plus prefix only allowed for numbers', node.fmt_poss[i]) + } + if (typ.is_unsigned() && fmt !in [`u`, `x`, `X`, `o`, `c`, `b`]) + || (typ.is_signed() && fmt !in [`d`, `x`, `X`, `o`, `c`, `b`]) + || (typ.is_int_literal() + && fmt !in [`d`, `c`, `x`, `X`, `o`, `u`, `x`, `X`, `o`, `b`]) + || (typ.is_float() && fmt !in [`E`, `F`, `G`, `e`, `f`, `g`]) + || (typ.is_pointer() && fmt !in [`p`, `x`, `X`]) + || (typ.is_string() && fmt !in [`s`, `S`]) + || (typ.idx() in [ast.i64_type_idx, ast.f64_type_idx] && fmt == `c`) { + 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) + } + // check recursive str + if c.table.cur_fn.is_method && c.table.cur_fn.name == 'str' + && c.table.cur_fn.receiver.name == expr.str() { + c.error('cannot call `str()` method recursively', expr.pos()) + } + } + c.inside_println_arg = inside_println_arg_save + return ast.string_type +} + +const unicode_lit_overflow_message = 'unicode character exceeds max allowed value of 0x10ffff, consider using a unicode literal (\\u####)' + +// unicode character literals are limited to a maximum value of 0x10ffff +// https://stackoverflow.com/questions/52203351/why-unicode-is-restricted-to-0x10ffff +pub fn (mut c Checker) string_lit(mut node ast.StringLiteral) ast.Type { + mut idx := 0 + for idx < node.val.len { + match node.val[idx] { + `\\` { + mut start_pos := token.Pos{ + ...node.pos + col: node.pos.col + 1 + idx + } + start_idx := idx + idx++ + next_ch := node.val[idx] or { return ast.string_type } + if next_ch == `u` { + idx++ + mut ch := node.val[idx] or { return ast.string_type } + mut hex_char_count := 0 + for ch.is_hex_digit() { + hex_char_count++ + end_pos := token.Pos{ + ...start_pos + len: idx + 1 - start_idx + } + match hex_char_count { + 1...5 {} + 6 { + first_digit := node.val[idx - 5] - 48 + second_digit := node.val[idx - 4] - 48 + if first_digit > 1 { + c.error(checker.unicode_lit_overflow_message, end_pos) + } else if first_digit == 1 && second_digit > 0 { + c.error(checker.unicode_lit_overflow_message, end_pos) + } + } + else { + c.error(checker.unicode_lit_overflow_message, end_pos) + } + } + idx++ + ch = node.val[idx] or { return ast.string_type } + } + } + } + else { + idx++ + } + } + } + return ast.string_type +} + +pub fn (mut c Checker) int_lit(mut node ast.IntegerLiteral) ast.Type { + if node.val.len < 17 { + // can not be a too large number, no need for more expensive checks + return ast.int_literal_type + } + lit := node.val.replace('_', '').all_after('-') + is_neg := node.val.starts_with('-') + limit := if is_neg { '9223372036854775808' } else { '18446744073709551615' } + message := 'integer literal $node.val overflows int' + + if lit.len > limit.len { + c.error(message, node.pos) + } else if lit.len == limit.len { + for i, digit in lit { + if digit > limit[i] { + c.error(message, node.pos) + } else if digit < limit[i] { + break + } + } + } + + return ast.int_literal_type +}