Skip to content

Commit 154812e

Browse files
committed
all: more generics optimizations
1 parent 7335a3a commit 154812e

5 files changed

Lines changed: 107 additions & 19 deletions

File tree

vlib/v/ast/table.v

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2127,6 +2127,23 @@ pub fn (t &Table) is_sumtype_or_in_variant(parent Type, typ Type) bool {
21272127
// can_implicit_array_cast reports whether `got` can be converted to `expected`
21282128
// by boxing each array element into the expected interface or sum type.
21292129
pub fn (t &Table) can_implicit_array_cast(got Type, expected Type) bool {
2130+
if got == 0 || expected == 0 || got == expected {
2131+
return false
2132+
}
2133+
got_idx := got.idx()
2134+
expected_idx := expected.idx()
2135+
if got_idx == expected_idx {
2136+
return false
2137+
}
2138+
if got_idx > 0 && got_idx < t.type_symbols.len && expected_idx > 0
2139+
&& expected_idx < t.type_symbols.len {
2140+
got_kind := t.type_symbols[got_idx].kind
2141+
expected_kind := t.type_symbols[expected_idx].kind
2142+
if (got_kind != .array && got_kind != .alias)
2143+
|| (expected_kind != .array && expected_kind != .alias) {
2144+
return false
2145+
}
2146+
}
21302147
got_unaliased := t.unaliased_type(got)
21312148
expected_unaliased := t.unaliased_type(expected)
21322149
got_sym := t.final_sym(got_unaliased)

vlib/v/checker/checker.v

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4603,8 +4603,20 @@ fn (mut c Checker) stmts_ending_with_expression(mut stmts []ast.Stmt, expected_o
46034603
c.scope_returns = false
46044604
}
46054605

4606+
@[inline]
46064607
fn (mut c Checker) unwrap_generic(typ ast.Type) ast.Type {
4607-
has_generic_parts := typ.has_flag(.generic) || c.type_has_unresolved_generic_parts(typ)
4608+
if typ == 0 {
4609+
return typ
4610+
}
4611+
has_generic_flag := typ.has_flag(.generic)
4612+
if !has_generic_flag {
4613+
idx := typ.idx()
4614+
if idx <= ast.nil_type_idx
4615+
|| (idx < c.generic_parts_cache.len && c.generic_parts_cache[idx] == 1) {
4616+
return typ
4617+
}
4618+
}
4619+
has_generic_parts := has_generic_flag || c.type_has_unresolved_generic_parts(typ)
46084620
if !has_generic_parts {
46094621
return typ
46104622
}

vlib/v/gen/c/fn.v

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,39 @@ fn (mut g Gen) specialized_method_name_from_receiver(method ast.Fn, concrete_rec
533533
return g.generic_fn_name(specialization_types, base_name)
534534
}
535535

536+
fn (mut g Gen) specialized_method_name_from_receiver_context(method ast.Fn, receiver_concrete_types []ast.Type, parent_method ast.Fn, base_name string) string {
537+
if receiver_concrete_types.len == 0 || method.params.len == 0 {
538+
return base_name
539+
}
540+
method_receiver_generic_names := g.table.generic_type_names(method.params[0].typ)
541+
parent_receiver_generic_names := if parent_method.params.len > 0 {
542+
g.table.generic_type_names(parent_method.params[0].typ)
543+
} else {
544+
[]string{}
545+
}
546+
full_method := if parent_method.generic_names.len > method.generic_names.len
547+
|| (method_receiver_generic_names.len == 0 && parent_receiver_generic_names.len > 0) {
548+
parent_method
549+
} else {
550+
method
551+
}
552+
receiver_generic_names := g.table.generic_type_names(full_method.params[0].typ)
553+
if receiver_generic_names.len == 0 {
554+
if full_method.generic_names.len != receiver_concrete_types.len {
555+
return base_name
556+
}
557+
} else if full_method.generic_names.len > 0
558+
&& (full_method.generic_names.len != receiver_generic_names.len
559+
|| full_method.generic_names != receiver_generic_names) {
560+
return base_name
561+
}
562+
specialized_suffix := g.generic_fn_name(receiver_concrete_types, '')
563+
if specialized_suffix != '' && base_name.ends_with(specialized_suffix) {
564+
return base_name
565+
}
566+
return g.generic_fn_name(receiver_concrete_types, base_name)
567+
}
568+
536569
fn (mut g Gen) recover_method_call_concrete_types_from_name(raw_method_name string, method_name string, receiver_type ast.Type, method_for_generics ast.Fn, parent_generic_method ast.Fn) []ast.Type {
537570
if raw_method_name == '' || method_name == ''
538571
|| !raw_method_name.starts_with(method_name + '_T_') {
@@ -2836,17 +2869,16 @@ fn (mut g Gen) resolve_return_type(node ast.CallExpr) ast.Type {
28362869
func := left_sym.find_method_with_generic_parent(node.name) or {
28372870
g.table.find_method(left_sym, node.name) or { return ast.void_type }
28382871
}
2839-
receiver_concrete_types, parent_method := g.receiver_generic_call_context(left_type,
2840-
node.name)
28412872
if func.return_type != 0 && !func.return_type.has_flag(.generic)
2842-
&& !g.type_has_unresolved_generic_parts(func.return_type)
2843-
&& !(receiver_concrete_types.len > 0 && parent_method.params.len > 0) {
2873+
&& !g.type_has_unresolved_generic_parts(func.return_type) {
28442874
return if node.or_block.kind == .absent {
28452875
func.return_type
28462876
} else {
28472877
func.return_type.clear_option_and_result()
28482878
}
28492879
}
2880+
receiver_concrete_types, parent_method := g.receiver_generic_call_context(left_type,
2881+
node.name)
28502882
if func.generic_names.len > 0 {
28512883
mut concrete_types := if node.concrete_types.len == func.generic_names.len
28522884
&& node.concrete_types.all(it != 0 && !it.has_flag(.generic)
@@ -4712,7 +4744,8 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
47124744
method_for_generics.generic_names.clone()
47134745
}
47144746
if has_method {
4715-
name = g.specialized_method_name_from_receiver(full_method, receiver_type, name)
4747+
name = g.specialized_method_name_from_receiver_context(full_method,
4748+
receiver_concrete_types, parent_generic_method, name)
47164749
}
47174750
if has_method && full_method_generic_names_len == 0 {
47184751
concrete_types = []ast.Type{}

vlib/v/gen/c/utils.v

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,17 @@ fn (g &Gen) expr_resolution_cache_key(pos int, default_typ ast.Type, salt u64) u
5959
return key ^ salt
6060
}
6161

62+
@[inline]
63+
fn (g &Gen) type_is_known_concrete(typ ast.Type) bool {
64+
if typ == 0 || typ.has_flag(.generic) {
65+
return false
66+
}
67+
idx := typ.idx()
68+
return idx <= ast.nil_type_idx
69+
|| (idx < g.generic_parts_cache.len && g.generic_parts_cache[idx] == 1)
70+
}
71+
72+
@[inline]
6273
fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type {
6374
if typ == 0 {
6475
return typ
@@ -70,6 +81,10 @@ fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type {
7081
return typ
7182
}
7283
}
84+
return g.unwrap_generic_slow(typ)
85+
}
86+
87+
fn (mut g Gen) unwrap_generic_slow(typ ast.Type) ast.Type {
7388
cache_key := g.type_resolution_cache_key(typ, cgen_unwrap_generic_cache_salt)
7489
if cached := g.unwrap_generic_cache[cache_key] {
7590
return cached
@@ -986,6 +1001,15 @@ fn (mut g Gen) resolved_expr_type(expr ast.Expr, default_typ ast.Type) ast.Type
9861001
}
9871002
ast.Ident {
9881003
if expr.obj is ast.Var {
1004+
if expr.obj.typ != 0 && expr.obj.generic_typ == 0 && !expr.obj.is_inherited
1005+
&& !expr.obj.is_unwrapped && !expr.obj.is_assignment_smartcast
1006+
&& !expr.obj.is_or && expr.obj.orig_type == ast.no_type
1007+
&& expr.obj.smartcasts.len == 0 && expr.obj.ct_type_var == .no_comptime
1008+
&& !g.has_current_generic_context() && !g.has_active_call_generic_context() {
1009+
if g.type_is_known_concrete(expr.obj.typ) {
1010+
return expr.obj.typ
1011+
}
1012+
}
9891013
if g.cur_fn != unsafe { nil } && g.cur_fn.is_method
9901014
&& expr.name == g.cur_fn.receiver.name {
9911015
// In generic contexts, prefer resolving from the receiver declaration

vlib/v/markused/walker.v

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -132,22 +132,24 @@ fn (mut w Walker) mark_fn_as_used(fkey string) {
132132
}
133133

134134
fn (w &Walker) fn_generic_names(node ast.FnDecl) []string {
135+
if !node.is_method {
136+
return node.generic_names
137+
}
135138
mut generic_names := []string{}
136-
if node.is_method {
137-
receiver_sym := w.table.sym(node.receiver.typ)
138-
match receiver_sym.info {
139-
ast.Struct {
140-
generic_names << w.table.get_generic_names(receiver_sym.info.generic_types)
141-
}
142-
ast.Interface {
143-
generic_names << w.table.get_generic_names(receiver_sym.info.generic_types)
144-
}
145-
ast.SumType {
146-
generic_names << w.table.get_generic_names(receiver_sym.info.generic_types)
147-
}
148-
else {}
139+
receiver_sym := w.table.sym(node.receiver.typ)
140+
match receiver_sym.info {
141+
ast.Struct {
142+
generic_names << w.table.get_generic_names(receiver_sym.info.generic_types)
143+
}
144+
ast.Interface {
145+
generic_names << w.table.get_generic_names(receiver_sym.info.generic_types)
149146
}
147+
ast.SumType {
148+
generic_names << w.table.get_generic_names(receiver_sym.info.generic_types)
149+
}
150+
else {}
150151
}
152+
151153
// Only add method-level generic names that aren't already from the receiver
152154
// (V puts inherited receiver generics into node.generic_names too).
153155
for gn in node.generic_names {

0 commit comments

Comments
 (0)