diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index d75ce7025b1b56..381a395a5b57b4 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -250,6 +250,8 @@ pub mut: @[minify] pub struct SumType { +pub: + attrs []Attr pub mut: fields []StructField found_fields bool @@ -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 +} + // defines special typenames pub fn (t Type) atomic_typename() string { idx := t.idx() diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 2aaf3593f419c9..12dfc2f7982844 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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 := '' + 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() diff --git a/vlib/v/checker/tests/message_on_duplicate_using_generic.out b/vlib/v/checker/tests/message_on_duplicate_using_generic.out new file mode 100644 index 00000000000000..f6dbf100faced3 --- /dev/null +++ b/vlib/v/checker/tests/message_on_duplicate_using_generic.out @@ -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: + 23 | _ := Name4[int](8) + 24 | _ := Name5[int](9) + 25 | _ := Name6[int](10) + | ~~~~~~~~~~~~~~ + 26 | } diff --git a/vlib/v/checker/tests/message_on_duplicate_using_generic.vv b/vlib/v/checker/tests/message_on_duplicate_using_generic.vv new file mode 100644 index 00000000000000..9e2753f64ed6d0 --- /dev/null +++ b/vlib/v/checker/tests/message_on_duplicate_using_generic.vv @@ -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) +} diff --git a/vlib/v/gen/c/auto_str_methods.v b/vlib/v/gen/c/auto_str_methods.v index 62e640fe5e5437..bf33e637f2b67f 100644 --- a/vlib/v/gen/c/auto_str_methods.v +++ b/vlib/v/gen/c/auto_str_methods.v @@ -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) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 27ca5faa34d01e..4637b5051bd59f 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -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)}";') } @@ -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)}";') } @@ -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)};') } @@ -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)};') @@ -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 { diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index d47fc90cc0e649..6b4b5186e03cf7 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -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 diff --git a/vlib/v/tests/ignore_duplicate_using_generic_test.v b/vlib/v/tests/ignore_duplicate_using_generic_test.v new file mode 100644 index 00000000000000..3cdee14ceee01a --- /dev/null +++ b/vlib/v/tests/ignore_duplicate_using_generic_test.v @@ -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)) +}