diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index da676cf976..ff48aa4f12 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -746,12 +746,6 @@ fn (s string) == (a string) bool { if s.len != a.len { return false } - if s.len > 0 { - last_idx := s.len - 1 - if s[last_idx] != a[last_idx] { - return false - } - } unsafe { return vmemcmp(s.str, a.str, a.len) == 0 } diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index da72db7e27..568f187550 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -360,6 +360,7 @@ pub: is_global bool is_volatile bool is_deprecated bool + is_embed bool pub mut: is_recursive bool is_part_of_union bool @@ -485,6 +486,7 @@ pub: next_comments []Comment has_prev_newline bool has_break_line bool + is_embed bool pub mut: expr Expr // `val1` name string // 'field1' diff --git a/vlib/v/ast/scope.v b/vlib/v/ast/scope.v index fb2ecc48d1..ff12e9e9d3 100644 --- a/vlib/v/ast/scope.v +++ b/vlib/v/ast/scope.v @@ -40,6 +40,7 @@ pub fn new_scope(parent &Scope, start_pos int) &Scope { } */ +@[inline] fn (s &Scope) dont_lookup_parent() bool { return s.parent == unsafe { nil } || s.detached_from_parent } diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 310140b1b8..fdf08d2e81 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -2000,7 +2000,7 @@ pub fn (mut t Table) unwrap_generic_type_ex(typ Type, generic_names []string, co } } // Update type in `info.embeds`, if it's embed - if fields[i].name.len > 1 && fields[i].name[0].is_capital() { + if fields[i].is_embed { mut parent_sym := t.sym(typ) mut parent_info := parent_sym.info if mut parent_info is Struct { @@ -2186,7 +2186,7 @@ pub fn (mut t Table) generic_insts_to_concrete() { fields[i].typ = t_typ } // Update type in `info.embeds`, if it's embed - if fields[i].name.len > 1 && fields[i].name[0].is_capital() { + if fields[i].is_embed { for mut embed in parent_info.embeds { if embed == orig_type { embed = fields[i].typ diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index df595c661f..a780a1c61a 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -1701,7 +1701,7 @@ pub fn (t &TypeSymbol) embed_name() string { pub fn (t &TypeSymbol) has_method(name string) bool { for mut method in unsafe { t.methods } { - if method.name == name { + if method.name.len == name.len && method.name == name { return true } } @@ -1715,7 +1715,7 @@ pub fn (t &TypeSymbol) has_method_with_generic_parent(name string) bool { pub fn (t &TypeSymbol) find_method(name string) ?Fn { for mut method in unsafe { t.methods } { - if method.name == name { + if method.name.len == name.len && method.name == name { return method } } @@ -1830,7 +1830,7 @@ pub fn (t &TypeSymbol) has_field(name string) bool { fn (a &Aggregate) find_field(name string) ?StructField { for mut field in unsafe { a.fields } { - if field.name == name { + if field.name.len == name.len && field.name == name { return field } } @@ -1839,7 +1839,7 @@ fn (a &Aggregate) find_field(name string) ?StructField { pub fn (i &Interface) find_field(name string) ?StructField { for mut field in unsafe { i.fields } { - if field.name == name { + if field.name.len == name.len && field.name == name { return field } } @@ -1848,7 +1848,7 @@ pub fn (i &Interface) find_field(name string) ?StructField { pub fn (i &Interface) find_method(name string) ?Fn { for mut method in unsafe { i.methods } { - if method.name == name { + if method.name.len == name.len && method.name == name { return method } } @@ -1857,7 +1857,7 @@ pub fn (i &Interface) find_method(name string) ?Fn { pub fn (i &Interface) has_method(name string) bool { for mut method in unsafe { i.methods } { - if method.name == name { + if method.name.len == name.len && method.name == name { return true } } @@ -1866,7 +1866,7 @@ pub fn (i &Interface) has_method(name string) bool { pub fn (s Struct) find_field(name string) ?StructField { for mut field in unsafe { s.fields } { - if field.name == name { + if name.len == field.name.len && field.name == name { return field } } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 2347ec099c..118d8705f8 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -5360,10 +5360,10 @@ fn (c &Checker) check_import_sym_conflict(ident string) bool { for import_sym in c.file.imports { // Check if alias exists or not if !import_sym.alias.is_blank() { - if import_sym.alias == ident { + if import_sym.alias.len == ident.len && import_sym.alias == ident { return true } - } else if import_sym.mod == ident { + } else if import_sym.mod.len == ident.len && import_sym.mod == ident { return true } } diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v index 0477e0d7a8..592aef3caa 100644 --- a/vlib/v/checker/struct.v +++ b/vlib/v/checker/struct.v @@ -806,8 +806,7 @@ or use an explicit `unsafe{ a[..] }`, if you do not want a copy of the slice.', // all the fields of initialized embedded struct are ignored, they are considered initialized sym := c.table.sym(init_field.typ) - if init_field.name != '' && init_field.name[0].is_capital() && sym.kind == .struct - && sym.language == .v { + if init_field.is_embed && sym.kind == .struct && sym.language == .v { struct_fields := c.table.struct_fields(sym) for struct_field in struct_fields { inited_fields << struct_field.name @@ -928,7 +927,7 @@ fn (mut c Checker) check_uninitialized_struct_fields_and_embeds(node ast.StructI continue } sym := c.table.sym(field.typ) - if field.name != '' && field.name[0].is_capital() && sym.info is ast.Struct { + if field.is_embed && sym.info is ast.Struct { // struct embeds continue } @@ -957,13 +956,14 @@ fn (mut c Checker) check_uninitialized_struct_fields_and_embeds(node ast.StructI } continue } - if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !field.typ.has_flag(.option) + field_is_option := field.typ.has_flag(.option) + if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !field_is_option && !node.has_update_expr && !c.pref.translated && !c.file.is_translated { c.error('reference field `${type_sym.name}.${field.name}` must be initialized', node.pos) continue } - if !field.typ.has_flag(.option) { + if !field_is_option { if sym.kind == .struct { c.check_ref_fields_initialized(sym, mut checked_types, '${type_sym.name}.${field.name}', node.pos) @@ -976,7 +976,7 @@ fn (mut c Checker) check_uninitialized_struct_fields_and_embeds(node ast.StructI } } // Do not allow empty uninitialized interfaces - if sym.kind == .interface && !node.has_update_expr && !field.typ.has_flag(.option) + if sym.kind == .interface && !node.has_update_expr && !field_is_option && sym.language != .js && !field.attrs.contains('noinit') { // TODO: should be an error instead, but first `ui` needs updating. c.note('interface field `${type_sym.name}.${field.name}` must be initialized', @@ -996,7 +996,7 @@ fn (mut c Checker) check_uninitialized_struct_fields_and_embeds(node ast.StructI c.error('field `${type_sym.name}.${field.name}` must be initialized', node.pos) } if !node.has_update_expr && !field.has_default_expr && !field.typ.is_ptr() - && !field.typ.has_flag(.option) { + && !field_is_option { field_final_sym := c.table.final_sym(field.typ) if field_final_sym.kind == .struct { mut zero_struct_init := ast.StructInit{ @@ -1063,7 +1063,7 @@ fn (mut c Checker) check_ref_fields_initialized(struct_sym &ast.TypeSymbol, mut if sym.language == .c { continue } - if field.name != '' && field.name[0].is_capital() && sym.language == .v { + if field.is_embed && sym.language == .v { // an embedded struct field continue } @@ -1106,7 +1106,7 @@ fn (mut c Checker) check_ref_fields_initialized_note(struct_sym &ast.TypeSymbol, if sym.language == .c { continue } - if field.name != '' && field.name[0].is_capital() && sym.language == .v { + if field.is_embed && sym.language == .v { // an embedded struct field continue } diff --git a/vlib/v/depgraph/depgraph.v b/vlib/v/depgraph/depgraph.v index 725f918346..fd277bf5a7 100644 --- a/vlib/v/depgraph/depgraph.v +++ b/vlib/v/depgraph/depgraph.v @@ -55,12 +55,14 @@ pub fn (o &OrderedDepMap) get(name string) []string { return res } +@[direct_array_access] pub fn (mut o OrderedDepMap) delete(name string) { if name !in o.data { panic('delete: no such key: ${name}') } for i, _ in o.keys { - if o.keys[i] == name { + item := o.keys[i] + if item.len == name.len && item == name { o.keys.delete(i) break } diff --git a/vlib/v/gen/c/array.v b/vlib/v/gen/c/array.v index e11bbdd6a0..6398a0cbe7 100644 --- a/vlib/v/gen/c/array.v +++ b/vlib/v/gen/c/array.v @@ -1086,8 +1086,12 @@ fn (mut g Gen) gen_array_contains_methods() { ptr_typ := g.equality_fn(elem_type) fn_builder.writeln('\t\tif (${ptr_typ}_sumtype_eq(((${elem_type_str}*)a.data)[i], v)) {') } else if elem_kind == .alias && elem_is_not_ptr { - ptr_typ := g.equality_fn(elem_type) - fn_builder.writeln('\t\tif (${ptr_typ}_alias_eq(((${elem_type_str}*)a.data)[i], v)) {') + if g.no_eq_method_types[elem_type] { + fn_builder.writeln('\t\tif (((${elem_type_str}*)a.data)[i] == v) {') + } else { + ptr_typ := g.equality_fn(elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_alias_eq(((${elem_type_str}*)a.data)[i], v)) {') + } } else { fn_builder.writeln('\t\tif (((${elem_type_str}*)a.data)[i] == v) {') } @@ -1124,8 +1128,12 @@ fn (mut g Gen) gen_array_contains_methods() { ptr_typ := g.equality_fn(elem_type) fn_builder.writeln('\t\tif (${ptr_typ}_sumtype_eq(a[i], v)) {') } else if elem_kind == .alias && elem_is_not_ptr { - ptr_typ := g.equality_fn(elem_type) - fn_builder.writeln('\t\tif (${ptr_typ}_alias_eq(a[i], v)) {') + if g.no_eq_method_types[elem_type] { + fn_builder.writeln('\t\tif (a[i] == v) {') + } else { + ptr_typ := g.equality_fn(elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_alias_eq(a[i], v)) {') + } } else { fn_builder.writeln('\t\tif (a[i] == v) {') } diff --git a/vlib/v/gen/c/auto_eq_methods.v b/vlib/v/gen/c/auto_eq_methods.v index 6e10e8733b..e5cb1edb73 100644 --- a/vlib/v/gen/c/auto_eq_methods.v +++ b/vlib/v/gen/c/auto_eq_methods.v @@ -97,8 +97,12 @@ fn (mut g Gen) gen_sumtype_equality_fn(left_type ast.Type) string { eq_fn := g.gen_map_equality_fn(typ) fn_builder.writeln('\t\treturn ${eq_fn}_map_eq(*${left_arg}, *${right_arg});') } else if variant.sym.kind == .alias && !typ.is_ptr() { - eq_fn := g.gen_alias_equality_fn(typ) - fn_builder.writeln('\t\treturn ${eq_fn}_alias_eq(*${left_arg}, *${right_arg});') + if g.no_eq_method_types[typ] { + fn_builder.writeln('\t\treturn *${left_arg} == *${right_arg};') + } else { + eq_fn := g.gen_alias_equality_fn(typ) + fn_builder.writeln('\t\treturn ${eq_fn}_alias_eq(*${left_arg}, *${right_arg});') + } } else if variant.sym.kind == .function { fn_builder.writeln('\t\treturn *((voidptr*)(*${left_arg})) == *((voidptr*)(*${right_arg}));') } else { @@ -242,8 +246,12 @@ fn (mut g Gen) gen_struct_equality_fn(left_type ast.Type) string { eq_fn := g.gen_map_equality_fn(field.typ) fn_builder.write_string('${eq_fn}_map_eq(${left_arg}, ${right_arg})') } else if field_type.sym.kind == .alias && !field.typ.is_ptr() { - eq_fn := g.gen_alias_equality_fn(field.typ) - fn_builder.write_string('${eq_fn}_alias_eq(${left_arg}, ${right_arg})') + if g.no_eq_method_types[field.typ] { + fn_builder.write_string('${left_arg} == ${right_arg}') + } else { + eq_fn := g.gen_alias_equality_fn(field.typ) + fn_builder.write_string('${eq_fn}_alias_eq(${left_arg}, ${right_arg})') + } } else if field_type.sym.kind == .function && !field.typ.has_flag(.option) { fn_builder.write_string('*((voidptr*)(${left_arg})) == *((voidptr*)(${right_arg}))') } else if field_type.sym.kind == .interface @@ -385,8 +393,12 @@ fn (mut g Gen) gen_array_equality_fn(left_type ast.Type) string { eq_fn := g.gen_map_equality_fn(elem.typ) fn_builder.writeln('\t\tif (!${eq_fn}_map_eq(((${ptr_elem_styp}*)${left_data})[i], ((${ptr_elem_styp}*)${right_data})[i])) {') } else if elem.sym.kind == .alias && !elem.typ.is_ptr() { - eq_fn := g.gen_alias_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_alias_eq(((${ptr_elem_styp}*)${left_data})[i], ((${ptr_elem_styp}*)${right_data})[i])) {') + if g.no_eq_method_types[elem.typ] { + fn_builder.writeln('\t\tif (((${ptr_elem_styp}*)${left_data})[i] != ((${ptr_elem_styp}*)${right_data})[i]) {') + } else { + eq_fn := g.gen_alias_equality_fn(elem.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_alias_eq(((${ptr_elem_styp}*)${left_data})[i], ((${ptr_elem_styp}*)${right_data})[i])) {') + } } else if elem.sym.kind == .function { fn_builder.writeln('\t\tif (*((voidptr*)((byte*)${left_data}+(i*${left_elem}))) != *((voidptr*)((byte*)${right_data}+(i*${right_elem})))) {') } else { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 777b6c1ed1..5c1b7c47cf 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -87,7 +87,8 @@ mut: file &ast.File = unsafe { nil } table &ast.Table = unsafe { nil } styp_cache map[ast.Type]string - unique_file_path_hash u64 // a hash of file.path, used for making auxiliary fn generation unique (like `compare_xyz`) + no_eq_method_types map[ast.Type]bool // types that does not need to call its auto eq methods for optimization + unique_file_path_hash u64 // a hash of file.path, used for making auxiliary fn generation unique (like `compare_xyz`) fn_decl &ast.FnDecl = unsafe { nil } // pointer to the FnDecl we are currently inside otherwise 0 last_fn_c_name string tmp_count int // counter for unique tmp vars (_tmp1, _tmp2 etc); resets at the start of each fn. @@ -466,6 +467,9 @@ pub fn gen(files []&ast.File, mut table ast.Table, pref_ &pref.Preferences) (str for k, v in g.autofree_methods { global_g.autofree_methods[k] = v } + for k, v in g.no_eq_method_types { + global_g.no_eq_method_types[k] = v + } } } else { util.timing_start('cgen serial processing') @@ -1062,13 +1066,13 @@ pub fn (mut g Gen) write_typeof_functions() { // V type to C typecc @[inline] fn (mut g Gen) styp(t ast.Type) string { - if t.has_flag(.option) { + if !t.has_option_or_result() { + return g.base_type(t) + } else if t.has_flag(.option) { // Register an optional if it's not registered yet return g.register_option(t) - } else if t.has_flag(.result) { - return g.register_result(t) } else { - return g.base_type(t) + return g.register_result(t) } } @@ -1098,7 +1102,7 @@ fn (mut g Gen) base_type(_t ast.Type) string { if t.has_flag(.shared_f) { styp = g.find_or_register_shared(t, styp) } - nr_muls := g.unwrap_generic(t).nr_muls() + nr_muls := t.nr_muls() if nr_muls > 0 { styp += strings.repeat(`*`, nr_muls) } diff --git a/vlib/v/gen/c/infix.v b/vlib/v/gen/c/infix.v index 17ceba36b3..13334cc67e 100644 --- a/vlib/v/gen/c/infix.v +++ b/vlib/v/gen/c/infix.v @@ -193,21 +193,35 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) { } match kind { .alias { - ptr_typ := g.equality_fn(left.typ) - if node.op == .ne { - g.write('!') + // optimize simple eq/ne operation on numbers + if left.unaliased_sym.is_int() { + if left.typ.is_ptr() { + g.write('*'.repeat(left.typ.nr_muls())) + } + g.expr(node.left) + g.write(' ${node.op} ') + if right.typ.is_ptr() { + g.write('*'.repeat(right.typ.nr_muls())) + } + g.expr(node.right) + g.no_eq_method_types[left.typ] = true + } else { + ptr_typ := g.equality_fn(left.typ) + if node.op == .ne { + g.write('!') + } + g.write('${ptr_typ}_alias_eq(') + if left.typ.is_ptr() { + g.write('*'.repeat(left.typ.nr_muls())) + } + g.expr(node.left) + g.write(', ') + if right.typ.is_ptr() { + g.write('*'.repeat(right.typ.nr_muls())) + } + g.expr(node.right) + g.write(')') } - g.write('${ptr_typ}_alias_eq(') - if left.typ.is_ptr() { - g.write('*'.repeat(left.typ.nr_muls())) - } - g.expr(node.left) - g.write(', ') - if right.typ.is_ptr() { - g.write('*'.repeat(right.typ.nr_muls())) - } - g.expr(node.right) - g.write(')') } .array { ptr_typ := g.equality_fn(left.unaliased.clear_flag(.shared_f)) @@ -515,10 +529,10 @@ fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) { expr: node.left expr_type: node.left_type } - g.infix_expr_in_optimization(new_node_left, node.right) + g.infix_expr_in_optimization(new_node_left, node.left_type, node.right) } } else { - g.infix_expr_in_optimization(node.left, node.right) + g.infix_expr_in_optimization(node.left, node.left_type, node.right) } g.write(')') return @@ -604,7 +618,7 @@ fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) { // `a in [1,2,3]!` optimization => `a == 1 || a == 2 || a == 3` // avoids an allocation g.write('(') - g.infix_expr_in_optimization(node.left, node.right) + g.infix_expr_in_optimization(node.left, node.left_type, node.right) g.write(')') return } @@ -642,8 +656,9 @@ fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) { // infix_expr_in_optimization optimizes ` in ` expressions, // and transform them in a series of equality comparison // i.e. `a in [1,2,3]` => `a == 1 || a == 2 || a == 3` -fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, right ast.ArrayInit) { +fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, left_type ast.Type, right ast.ArrayInit) { mut elem_sym := g.table.sym(right.elem_type) + left_parent_idx := g.table.sym(left_type).parent_idx for i, array_expr in right.exprs { match elem_sym.kind { .string, .alias, .sum_type, .map, .interface, .array, .struct { @@ -659,9 +674,10 @@ fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, right ast.ArrayInit) { } else { g.write('_SLIT_EQ(${var}.str, ${var}.len, "${slit}")') } - unsafe { - goto end + if i < right.exprs.len - 1 { + g.write(' || ') } + continue } else if array_expr is ast.StringLiteral { g.write('fast_string_eq(') } else { @@ -674,7 +690,23 @@ fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, right ast.ArrayInit) { } else { ptr_typ := g.equality_fn(right.elem_type) if elem_sym.kind == .alias { - g.write('${ptr_typ}_alias_eq(') + // optimization for alias to number + if elem_sym.is_int() { + g.expr(left) + g.write(' == ') + if left_parent_idx != 0 && !((array_expr is ast.SelectorExpr + && array_expr.typ == left_type) + || (array_expr is ast.Ident && array_expr.obj.typ == left_type)) { + g.write('(${g.styp(left_parent_idx)})') + } + g.expr(array_expr) + if i < right.exprs.len - 1 { + g.write(' || ') + } + continue + } else { + g.write('${ptr_typ}_alias_eq(') + } } else if elem_sym.kind == .sum_type { g.write('${ptr_typ}_sumtype_eq(') } else if elem_sym.kind == .map { @@ -698,7 +730,6 @@ fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, right ast.ArrayInit) { g.expr(array_expr) } } - end: if i < right.exprs.len - 1 { g.write(' || ') } diff --git a/vlib/v/gen/c/json.v b/vlib/v/gen/c/json.v index 2f4e23dca4..6e19fb2414 100644 --- a/vlib/v/gen/c/json.v +++ b/vlib/v/gen/c/json.v @@ -782,7 +782,7 @@ fn (mut g Gen) gen_struct_enc_dec(utyp ast.Type, type_info ast.TypeInfo, styp st } } else { // embeded - if name.len > 0 && name[0].is_capital() && field_sym.info is ast.Struct { + if field.is_embed && field_sym.info is ast.Struct { for embed in info.embeds { if embed == int(field.typ) { prefix_embed := if embed_prefix != '' { diff --git a/vlib/v/gen/c/utils.v b/vlib/v/gen/c/utils.v index 7dd7f35656..bd88216c17 100644 --- a/vlib/v/gen/c/utils.v +++ b/vlib/v/gen/c/utils.v @@ -31,7 +31,7 @@ fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type { } } } - } else if typ.has_flag(.generic) && g.table.sym(typ).kind == .struct { + } else if g.table.sym(typ).kind == .struct { // resolve selector `a.foo` where `a` is struct[T] on non generic function sym := g.table.sym(typ) if sym.info is ast.Struct { diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 67a228cc68..3dcfbec167 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -2214,6 +2214,7 @@ fn (mut p Parser) note_with_pos(s string, pos token.Pos) { } } +@[direct_array_access] fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt { // in here might be 1) multi-expr 2) multi-assign // 1, a, c ... } // multi-expression diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index 69239fb976..669b0e966f 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -340,6 +340,7 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl { attrs: p.attrs is_pub: is_embed || is_field_pub is_mut: is_embed || is_field_mut + is_embed: is_embed is_global: is_field_global is_volatile: is_field_volatile is_deprecated: is_field_deprecated @@ -503,6 +504,7 @@ fn (mut p Parser) struct_init(typ_str string, kind ast.StructInitKind, is_option parent_type: typ has_prev_newline: has_prev_newline has_break_line: has_break_line + is_embed: field_name.len > 0 && field_name[0].is_capital() } } }