Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

checker,cgen: fix duplicates in generic sumtype #20936

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions vlib/v/ast/types.v
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ pub mut:

@[minify]
pub struct SumType {
pub:
attrs []Attr
pub mut:
fields []StructField
found_fields bool
Expand All @@ -262,6 +264,17 @@ pub mut:
parent_type Type
}

// used to check whether there a generic type which is duplicate, returns array with no duplicates
pub fn (st &SumType) get_deduplicated_variants() []Type {
mut variants := []Type{}
for v in st.variants {
if v !in variants {
variants << v
}
}
return variants
}

// <atomic.h> defines special typenames
pub fn (t Type) atomic_typename() string {
idx := t.idx()
Expand Down
31 changes: 31 additions & 0 deletions vlib/v/checker/checker.v
Original file line number Diff line number Diff line change
Expand Up @@ -3082,6 +3082,37 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
ft := c.table.type_to_str(from_type)
tt := c.table.type_to_str(to_type)
c.error('cannot cast `${ft}` to `${tt}`', node.pos)
} else if to_sym_info.variants.len != to_sym_info.get_deduplicated_variants().len {
if !to_sym_info.attrs.any(!it.has_arg && it.name == 'ignore_generic_duplicates') {
// TODO: not a option type because of autofree bug; see https://github.com/vlang/v/issues/20937
mut message := '<no message was defined>'
mut message_type := 'error'
for attr in to_sym_info.attrs {
if attr.name == 'on_generic_duplicate' && attr.has_arg {
message = attr.arg
}
}
if message.starts_with('error: ') {
message = message[7..]
} else if message.starts_with('warn: ') {
message_type = 'warn'
message = message[6..]
} else if message.starts_with('notice: ') {
message_type = 'notice'
message = message[8..]
} else if message.starts_with('note: ') {
message_type = 'notice'
message = message[6..]
}
if message.len > 0 {
match message_type {
'warn' { c.warn('duplicate type: ${message}', node.pos) }
'notice' { c.note('duplicate type: ${message}', node.pos) }
'error' { c.error('duplicate type: ${message}', node.pos) }
else {}
}
}
}
}
} else if mut to_sym.info is ast.Alias && !(final_to_sym.kind == .struct_ && final_to_is_ptr) {
if (!c.check_types(from_type, to_sym.info.parent_type) && !(final_to_sym.is_int()
Expand Down
34 changes: 34 additions & 0 deletions vlib/v/checker/tests/message_on_duplicate_using_generic.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
vlib/v/checker/tests/message_on_duplicate_using_generic.vv:21:7: notice: duplicate type: blah blah blah 22222
19 | fn main() {
20 | _ := Name1[int](2)
21 | _ := Name2[int](4)
| ~~~~~~~~~~~~~
22 | _ := Name3[int](6)
23 | _ := Name4[int](8)
vlib/v/checker/tests/message_on_duplicate_using_generic.vv:22:7: warning: duplicate type: blah blah blah 33333
20 | _ := Name1[int](2)
21 | _ := Name2[int](4)
22 | _ := Name3[int](6)
| ~~~~~~~~~~~~~
23 | _ := Name4[int](8)
24 | _ := Name5[int](9)
vlib/v/checker/tests/message_on_duplicate_using_generic.vv:20:7: error: duplicate type: blah blah blah 11111
18 |
19 | fn main() {
20 | _ := Name1[int](2)
| ~~~~~~~~~~~~~
21 | _ := Name2[int](4)
22 | _ := Name3[int](6)
vlib/v/checker/tests/message_on_duplicate_using_generic.vv:24:7: error: duplicate type: blah blah blah 55555
22 | _ := Name3[int](6)
23 | _ := Name4[int](8)
24 | _ := Name5[int](9)
| ~~~~~~~~~~~~~
25 | _ := Name6[int](10)
26 | }
vlib/v/checker/tests/message_on_duplicate_using_generic.vv:25:7: error: duplicate type: <no message was defined>
23 | _ := Name4[int](8)
24 | _ := Name5[int](9)
25 | _ := Name6[int](10)
| ~~~~~~~~~~~~~~
26 | }
26 changes: 26 additions & 0 deletions vlib/v/checker/tests/message_on_duplicate_using_generic.vv
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
@[on_generic_duplicate: 'blah blah blah 11111']
type Name1[T] = T | int | string

@[on_generic_duplicate: 'notice: blah blah blah 22222']
type Name2[T] = T | int | string

@[on_generic_duplicate: 'warn: blah blah blah 33333']
type Name3[T] = T | int | string

@[on_generic_duplicate: 'note: blah blah blah 44444']
@[ignore_generic_duplicates]
type Name4[T] = T | int | string

@[on_generic_duplicate: 'error: blah blah blah 55555']
type Name5[T] = T | int | string

type Name6[T] = T | int | string

fn main() {
_ := Name1[int](2)
_ := Name2[int](4)
_ := Name3[int](6)
_ := Name4[int](8)
_ := Name5[int](9)
_ := Name6[int](10)
}
2 changes: 1 addition & 1 deletion vlib/v/gen/c/auto_str_methods.v
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ fn (mut g Gen) gen_str_for_union_sum_type(info ast.SumType, styp string, typ_str
clean_sum_type_v_type_name = util.strip_main_name(typ_str)
}
fn_builder.writeln('\tswitch(x._typ) {')
for typ in info.variants {
for typ in info.get_deduplicated_variants() {
typ_name := g.typ(typ)
mut func_name := g.get_str_fn(typ)
sym := g.table.sym(typ)
Expand Down
15 changes: 9 additions & 6 deletions vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,7 @@ pub fn (mut g Gen) write_typeof_functions() {
g.writeln('${static_prefix}char * v_typeof_sumtype_${sym.cname}(int sidx) { /* ${sym.name} */ ')
if g.pref.build_mode == .build_module {
g.writeln('\t\tif( sidx == _v_type_idx_${sym.cname}() ) return "${util.strip_main_name(sym.name)}";')
for v in sum_info.variants {
for v in sum_info.get_deduplicated_variants() {
subtype := g.table.sym(v)
g.writeln('\tif( sidx == _v_type_idx_${subtype.cname}() ) return "${util.strip_main_name(subtype.name)}";')
}
Expand All @@ -934,7 +934,7 @@ pub fn (mut g Gen) write_typeof_functions() {
tidx := g.table.find_type_idx(sym.name)
g.writeln('\tswitch(sidx) {')
g.writeln('\t\tcase ${tidx}: return "${util.strip_main_name(sym.name)}";')
for v in sum_info.variants {
for v in sum_info.get_deduplicated_variants() {
subtype := g.table.sym(v)
g.writeln('\t\tcase ${v.idx()}: return "${util.strip_main_name(subtype.name)}";')
}
Expand All @@ -946,7 +946,7 @@ pub fn (mut g Gen) write_typeof_functions() {
g.writeln('${static_prefix}int v_typeof_sumtype_idx_${sym.cname}(int sidx) { /* ${sym.name} */ ')
if g.pref.build_mode == .build_module {
g.writeln('\t\tif( sidx == _v_type_idx_${sym.cname}() ) return ${int(ityp)};')
for v in sum_info.variants {
for v in sum_info.get_deduplicated_variants() {
subtype := g.table.sym(v)
g.writeln('\tif( sidx == _v_type_idx_${subtype.cname}() ) return ${int(v)};')
}
Expand All @@ -955,7 +955,7 @@ pub fn (mut g Gen) write_typeof_functions() {
tidx := g.table.find_type_idx(sym.name)
g.writeln('\tswitch(sidx) {')
g.writeln('\t\tcase ${tidx}: return ${int(ityp)};')
for v in sum_info.variants {
for v in sum_info.get_deduplicated_variants() {
g.writeln('\t\tcase ${v.idx()}: return ${int(v)};')
}
g.writeln('\t\tdefault: return ${int(ityp)};')
Expand Down Expand Up @@ -6399,13 +6399,16 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) {
struct_names[name] = true
g.typedefs.writeln('typedef struct ${name} ${name};')
g.type_definitions.writeln('')

g.type_definitions.writeln('// Union sum type ${name} = ')
for variant in sym.info.variants {
variants := sym.info.get_deduplicated_variants()
for variant in variants {
g.type_definitions.writeln('// | ${variant:4d} = ${g.typ(variant.idx()):-20s}')
}
g.type_definitions.writeln('struct ${name} {')
g.type_definitions.writeln('\tunion {')
for variant in sym.info.variants {

for variant in variants {
variant_sym := g.table.sym(variant)
mut var := variant.ref()
if variant_sym.info is ast.FnType {
Expand Down
1 change: 1 addition & 0 deletions vlib/v/parser/parser.v
Original file line number Diff line number Diff line change
Expand Up @@ -4373,6 +4373,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
cname: util.no_dots(prepend_mod_name)
mod: p.mod
info: ast.SumType{
attrs: p.attrs
variants: variant_types
is_generic: generic_types.len > 0
generic_types: generic_types
Expand Down
15 changes: 15 additions & 0 deletions vlib/v/tests/ignore_duplicate_using_generic_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@[ignore_generic_duplicates]
type Name[T] = T | int | string

fn f(n Name[string]) {
assert n as string == 'lolol'
}

fn g(n Name[int]) {
assert n as int == 123
}

fn test_main() {
f(Name[string]('lolol'))
g(Name[int](123))
}