Skip to content

Commit ed43526

Browse files
committed
checker,cgen: fix generic str edge cases
1 parent 5b90700 commit ed43526

4 files changed

Lines changed: 103 additions & 11 deletions

File tree

vlib/v/checker/str.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) ast.Type {
9696
if ftyp == 0 {
9797
return ast.void_type
9898
}
99-
c.markused_string_inter_lit(mut node, ftyp)
10099
c.fail_if_unreadable(expr, ftyp, 'interpolation object')
101100
node.expr_types << ftyp
102101
if i < node.fwidth_exprs.len {
@@ -179,6 +178,7 @@ fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) ast.Type {
179178
&& c.table.cur_fn.name == 'str' && c.table.cur_fn.receiver.name == '${expr}' {
180179
c.error('cannot call `str()` method recursively', expr.pos())
181180
}
181+
c.markused_string_inter_lit(mut node, ftyp, fmt)
182182
}
183183
c.inside_interface_deref = inside_interface_deref_save
184184
if c.pref.warn_about_allocs {

vlib/v/checker/used_features.v

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -213,17 +213,26 @@ fn (mut c Checker) markused_method_call(mut node ast.CallExpr, mut left_expr ast
213213
}
214214
}
215215

216-
fn (mut c Checker) markused_string_inter_lit(mut _ ast.StringInterLiteral, ftyp ast.Type) {
216+
fn string_inter_lit_format_uses_str(fmt u8) bool {
217+
return fmt == `s`
218+
}
219+
220+
fn (mut c Checker) markused_string_inter_lit(mut _ ast.StringInterLiteral, ftyp ast.Type, fmt u8) {
217221
if c.is_builtin_mod {
218222
return
219223
}
224+
uses_str_format := string_inter_lit_format_uses_str(fmt)
220225
if !c.table.sym(ftyp).has_method('str') {
221-
c.table.used_features.auto_str = true
222-
c.markused_auto_str_dependencies(ftyp)
226+
if uses_str_format {
227+
c.table.used_features.auto_str = true
228+
c.markused_auto_str_dependencies(ftyp)
229+
}
223230
} else {
224-
c.markused_generic_str_method(ftyp, c.table.sym(ftyp))
225-
c.mark_type_str_method_as_referenced(ftyp)
226-
c.table.used_features.print_types[ftyp.idx()] = true
231+
if uses_str_format {
232+
c.markused_generic_str_method(ftyp, c.table.sym(ftyp))
233+
c.mark_type_str_method_as_referenced(ftyp)
234+
c.table.used_features.print_types[ftyp.idx()] = true
235+
}
227236
}
228237
if ftyp.is_ptr() {
229238
c.table.used_features.auto_str_ptr = true

vlib/v/gen/c/auto_str_methods.v

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,9 @@ fn (mut g Gen) get_str_fn(typ ast.Type) string {
9292
}
9393
if !g.pref.new_generic_solver {
9494
if str_method := sym.find_method_with_generic_parent('str') {
95-
if str_method.generic_names.len > 0 {
95+
if method_has_generic_source(str_method) {
9696
match mut sym.info {
97-
ast.Struct, ast.SumType, ast.Interface, ast.Alias {
97+
ast.Struct, ast.SumType, ast.Interface, ast.Alias, ast.GenericInst, ast.FnType {
9898
str_fn_name = g.generic_fn_name(g.str_method_concrete_types(unwrapped, sym),
9999
str_fn_name)
100100
}
@@ -675,12 +675,29 @@ fn styp_to_str_fn_name(styp string) string {
675675
return styp.replace_each(['*', '', '.', '__', ' ', '__']) + '_str'
676676
}
677677

678+
fn method_has_generic_source(method ast.Fn) bool {
679+
if method.generic_names.len > 0 {
680+
return true
681+
}
682+
if method.source_fn != unsafe { nil } {
683+
fndecl := unsafe { &ast.FnDecl(method.source_fn) }
684+
return fndecl.generic_names.len > 0
685+
}
686+
return false
687+
}
688+
678689
fn (mut g Gen) str_method_concrete_types(typ ast.Type, sym &ast.TypeSymbol) []ast.Type {
679690
if _ := g.receiver_exact_method_for_type(typ, 'str') {
680691
match sym.info {
681692
ast.Struct, ast.SumType, ast.Interface {
682693
return sym.info.concrete_types.clone()
683694
}
695+
ast.GenericInst {
696+
return sym.info.concrete_types.clone()
697+
}
698+
ast.FnType {
699+
return g.concrete_types_for_fn_type_symbol(sym)
700+
}
684701
ast.Alias {
685702
return g.alias_parent_concrete_types(sym.info)
686703
}
@@ -694,6 +711,12 @@ fn (mut g Gen) str_method_concrete_types(typ ast.Type, sym &ast.TypeSymbol) []as
694711
ast.Struct, ast.SumType, ast.Interface {
695712
return sym.info.concrete_types.clone()
696713
}
714+
ast.GenericInst {
715+
return sym.info.concrete_types.clone()
716+
}
717+
ast.FnType {
718+
return g.concrete_types_for_fn_type_symbol(sym)
719+
}
697720
ast.Alias {
698721
return g.alias_parent_concrete_types(sym.info)
699722
}
@@ -703,6 +726,15 @@ fn (mut g Gen) str_method_concrete_types(typ ast.Type, sym &ast.TypeSymbol) []as
703726
return []ast.Type{}
704727
}
705728

729+
fn (mut g Gen) concrete_types_for_fn_type_symbol(sym &ast.TypeSymbol) []ast.Type {
730+
if sym.info is ast.FnType && sym.generic_types.len > 0
731+
&& !sym.generic_types.any(it.has_flag(.generic)
732+
|| g.table.generic_type_names(it).len > 0) {
733+
return sym.generic_types.clone()
734+
}
735+
return []ast.Type{}
736+
}
737+
706738
fn (mut g Gen) alias_parent_concrete_types(info ast.Alias) []ast.Type {
707739
parent_sym := g.table.sym(info.parent_type)
708740
match parent_sym.info {
@@ -1230,9 +1262,10 @@ fn (mut g Gen) gen_str_for_struct(info ast.Struct, lang ast.Language, styp strin
12301262
}
12311263
if !g.pref.new_generic_solver {
12321264
if str_method := sym.find_method_with_generic_parent('str') {
1233-
if str_method.generic_names.len > 0 && !ftyp_noshared.has_flag(.option) {
1265+
if method_has_generic_source(str_method) && !ftyp_noshared.has_flag(.option) {
12341266
match sym.info {
1235-
ast.Struct, ast.SumType, ast.Interface, ast.Alias {
1267+
ast.Struct, ast.SumType, ast.Interface, ast.Alias, ast.GenericInst,
1268+
ast.FnType {
12361269
field_fn_name = g.generic_fn_name(g.str_method_concrete_types(ftyp_noshared, sym),
12371270
field_fn_name)
12381271
}
@@ -1436,6 +1469,9 @@ fn struct_auto_str_func(sym &ast.TypeSymbol, lang ast.Language, _field_type ast.
14361469
}
14371470
return 'indent_${fn_name}(${obj}, indent_count + 1)', true
14381471
} else if sym.kind == .function {
1472+
if has_custom_str {
1473+
return '${fn_name}(${prefix}it${op}${final_field_name}${sufix})', true
1474+
}
14391475
return '${fn_name}()', true
14401476
} else if sym.kind == .chan {
14411477
return '${fn_name}(${deref}it${op}${final_field_name}${sufix})', true

vlib/v/tests/generics/generic_auto_str_nested_generic_field_test.v

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,18 @@ struct OverrideBox[T] {
2020
val T
2121
}
2222

23+
struct PointerInterpBad[T] {
24+
val T
25+
}
26+
2327
type InterpIntBox = InterpBox[int]
2428

2529
type OverrideIntBox = OverrideBox[[]int]
2630

2731
type Ints = []int
2832

33+
type Parser[T] = fn (string) T
34+
2935
struct StructuredContainer[T] {
3036
box Box[[]T]
3137
}
@@ -34,6 +40,10 @@ struct IntsContainer {
3440
values Ints
3541
}
3642

43+
struct ParserContainer {
44+
parser Parser[int] @[required]
45+
}
46+
3747
struct AssertNode[T] {
3848
val T
3949
}
@@ -73,10 +83,27 @@ fn (box OverrideIntBox) str[T]() string {
7383
return 'override alias ${box.val[0]}'
7484
}
7585

86+
fn (bad &PointerInterpBad[[]T]) str() string {
87+
$if T is int {
88+
$compile_error('pointer interpolation should not register str')
89+
}
90+
return 'bad pointer ${bad.val}'
91+
}
92+
7693
fn (values Ints) str[T]() string {
7794
return 'ints ${values[0]}'
7895
}
7996

97+
fn (p Parser[T]) str[T]() string {
98+
return 'parser'
99+
}
100+
101+
fn new_int_parser() Parser[int] {
102+
return fn (input string) int {
103+
return input.int()
104+
}
105+
}
106+
80107
fn (list AssertList[T]) str() string {
81108
return 'assert list'
82109
}
@@ -121,6 +148,14 @@ fn test_interpolation_registers_only_overridden_alias_str_method() {
121148
assert '${box}' == 'override alias 246'
122149
}
123150

151+
fn test_pointer_interpolation_does_not_register_generic_str_method() {
152+
mut value := PointerInterpBad[[]int]{
153+
val: [135]
154+
}
155+
ptr := &value
156+
assert '${ptr:p}'.len > 0
157+
}
158+
124159
fn test_auto_str_registers_container_alias_parent_str_method() {
125160
c := IntsContainer{
126161
values: Ints([654])
@@ -133,6 +168,18 @@ fn test_interpolation_registers_container_alias_parent_str_method() {
133168
assert '${values}' == 'ints 321'
134169
}
135170

171+
fn test_auto_str_registers_generic_fn_type_alias_str_method() {
172+
c := ParserContainer{
173+
parser: new_int_parser()
174+
}
175+
assert '${c}'.contains('parser: parser')
176+
}
177+
178+
fn test_interpolation_registers_generic_fn_type_alias_str_method() {
179+
parser := new_int_parser()
180+
assert '${parser}' == 'parser'
181+
}
182+
136183
fn test_dump_registers_exact_generic_alias_str_method() {
137184
box := InterpIntBox{
138185
val: 654

0 commit comments

Comments
 (0)