Skip to content

Commit faeb987

Browse files
committed
compiler: fix generic sumtype aliases and duplicate imports
1 parent 31b6787 commit faeb987

8 files changed

Lines changed: 163 additions & 31 deletions

File tree

vlib/v/builder/builder.v

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ pub fn (mut b Builder) parse_imports() {
284284
util.timing_measure(@METHOD)
285285
}
286286
mut done_imports := []string{}
287+
mut done_import_paths := map[string]bool{}
287288
if b.pref.is_vsh {
288289
done_imports << 'os'
289290
}
@@ -331,6 +332,11 @@ pub fn (mut b Builder) parse_imports() {
331332
ast_file.path, imp.pos)
332333
break
333334
}
335+
import_path_key := comparable_real_path(import_path)
336+
if import_path_key in done_import_paths {
337+
done_imports << mod
338+
continue
339+
}
334340
v_files := b.v_files_from_dir(import_path)
335341
if v_files.len == 0 {
336342
// v.parsers[i].error_with_token_index('cannot import module "${mod}" (no .v files in "${import_path}")', v.parsers[i].import_ast.get_import_tok_idx(mod))
@@ -358,6 +364,7 @@ pub fn (mut b Builder) parse_imports() {
358364
return
359365
}
360366
done_imports << mod
367+
done_import_paths[import_path_key] = true
361368
}
362369
}
363370
b.resolve_deps()

vlib/v/builder/builder_test.v

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,45 @@ fn test_empty_local_dir_does_not_shadow_vlib_module() {
216216
assert res.output.trim_space() == 'true'
217217
}
218218

219+
fn test_imports_with_same_resolved_path_are_parsed_once() {
220+
os.chdir(test_path)!
221+
workspace := os.join_path(test_path, 'run_duplicate_resolved_import_path')
222+
project_dir := os.join_path(workspace, 'dupmod')
223+
defer {
224+
os.chdir(test_path) or {}
225+
os.rmdir_all(workspace) or {}
226+
}
227+
os.mkdir_all(os.join_path(project_dir, 'providers'))!
228+
os.write_file(os.join_path(project_dir, 'v.mod'), "Module {\n\tname: 'dupmod'\n}\n")!
229+
os.write_file(os.join_path(project_dir, 'dupmod.v'), 'module dupmod
230+
231+
import providers
232+
233+
pub fn make_client() providers.Client {
234+
return providers.Client{}
235+
}
236+
')!
237+
os.write_file(os.join_path(project_dir, 'main_test.v'), 'module main
238+
239+
import dupmod
240+
import dupmod.providers
241+
242+
fn test_client() {
243+
_ := dupmod.make_client()
244+
_ := providers.Client{}
245+
}
246+
')!
247+
os.write_file(os.join_path(project_dir, 'providers', 'client.v'), 'module providers
248+
249+
pub struct Client {}
250+
')!
251+
os.chdir(project_dir)!
252+
253+
res := os.execute('${os.quoted_path(vexe)} -check main_test.v')
254+
assert res.exit_code == 0, res.output
255+
assert !res.output.contains('cannot register struct')
256+
}
257+
219258
fn test_removed_src_layout_error_mentions_vmod_subdirs() {
220259
os.chdir(test_path)!
221260
project_dir := os.join_path(test_path, 'run_removed_src_project')

vlib/v/checker/check_types.v

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1908,8 +1908,9 @@ fn (mut c Checker) infer_fn_generic_types(func &ast.Fn, mut node ast.CallExpr) {
19081908
s := c.table.type_to_str(typ)
19091909
println('inferred `${func.name}[${s}]`')
19101910
}
1911-
inferred_types << c.unwrap_generic(typ)
1912-
node.concrete_types << typ
1911+
concrete_typ := ast.mktyp(c.unwrap_generic(typ))
1912+
inferred_types << concrete_typ
1913+
node.concrete_types << concrete_typ
19131914
}
19141915

19151916
if c.table.register_fn_concrete_types(func.fkey(), inferred_types) {

vlib/v/checker/match.v

Lines changed: 68 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,49 @@ fn (mut c Checker) get_match_case_literal_value(mut expr ast.Expr) ?i64 {
684684
return none
685685
}
686686

687+
fn (mut c Checker) match_sumtype_has_variant(parent ast.Type, variant ast.Type) bool {
688+
if c.table.sumtype_has_variant_recursive(parent, variant, true) {
689+
return true
690+
}
691+
if c.table.sym(parent).kind == .sum_type {
692+
return false
693+
}
694+
for candidate in c.concrete_sumtype_variants(parent) {
695+
if c.match_sumtype_variant_is_handled(candidate, variant) {
696+
return true
697+
}
698+
}
699+
return false
700+
}
701+
702+
fn (mut c Checker) match_sumtype_missing_variants(parent ast.Type, handled []ast.Type) []ast.Type {
703+
if c.table.sym(parent).kind == .sum_type {
704+
return c.table.sumtype_missing_variants(parent, handled)
705+
}
706+
mut missing := []ast.Type{}
707+
for variant in c.concrete_sumtype_variants(parent) {
708+
mut is_handled := false
709+
for handled_variant in handled {
710+
if c.match_sumtype_variant_is_handled(variant, handled_variant) {
711+
is_handled = true
712+
break
713+
}
714+
}
715+
if !is_handled {
716+
missing << variant
717+
}
718+
}
719+
return missing
720+
}
721+
722+
fn (mut c Checker) match_sumtype_variant_is_handled(variant ast.Type, handled ast.Type) bool {
723+
unaliased_variant := c.table.fully_unaliased_type(variant)
724+
unaliased_handled := c.table.fully_unaliased_type(handled)
725+
return unaliased_variant.idx() == unaliased_handled.idx()
726+
&& unaliased_variant.has_flag(.option) == unaliased_handled.has_flag(.option)
727+
&& unaliased_variant.nr_muls() == unaliased_handled.nr_muls()
728+
}
729+
687730
fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSymbol, cond_final_sym ast.TypeSymbol) {
688731
c.expected_type = node.expected_type
689732
if node.cond_type.idx() == 0 {
@@ -694,6 +737,8 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
694737
is_alias_to_matchable_type := cond_type_sym.kind == .alias
695738
&& cond_final_sym.kind in [.interface, .sum_type]
696739
cond_match_sym := if is_alias_to_matchable_type { cond_final_sym } else { cond_type_sym }
740+
sumtype_match_variants := c.concrete_sumtype_variants(cond_match_type)
741+
is_cond_match_sumtype := sumtype_match_variants.len > 0
697742
mut enum_ref_checked := false
698743
mut is_comptime_value_match := false
699744
// branch_exprs is a histogram of how many times
@@ -908,12 +953,12 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
908953
}
909954
}
910955
}
911-
} else if cond_match_sym.info is ast.SumType {
912-
if !c.table.sumtype_has_variant_recursive(cond_match_type, expr_type, true) {
956+
} else if is_cond_match_sumtype {
957+
if !c.match_sumtype_has_variant(cond_match_type, expr_type) {
913958
expr_str := c.table.type_to_str(expr_type)
914959
expect_str := c.table.type_to_str(node.cond_type)
915960
sumtype_variant_names :=
916-
c.table.sumtype_matchable_variants(cond_match_type).map(c.table.type_to_str_using_aliases(it, {}))
961+
sumtype_match_variants.map(c.table.type_to_str_using_aliases(it, {}))
917962
suggestion := util.new_suggestion(expr_str, sumtype_variant_names)
918963
c.error(suggestion.say('`${expect_str}` has no variant `${expr_str}`'),
919964
expr.pos())
@@ -941,7 +986,7 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
941986
}
942987
// when match is type matching, then register smart cast for every branch
943988
if expr_types.len > 0 {
944-
if cond_match_sym.kind in [.sum_type, .interface] {
989+
if is_cond_match_sumtype || cond_match_sym.kind == .interface {
945990
mut expr_type := ast.no_type
946991
if expr_types.len > 1 {
947992
mut agg_name := strings.new_builder(20)
@@ -999,34 +1044,34 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
9991044
}
10001045
}
10011046
} else {
1002-
match cond_match_sym.info {
1003-
ast.SumType {
1004-
for v in c.table.sumtype_missing_variants(cond_match_type, branch_expr_types) {
1005-
is_exhaustive = false
1006-
unhandled << '`${c.table.type_to_str(v)}`'
1007-
}
1047+
if is_cond_match_sumtype {
1048+
for v in c.match_sumtype_missing_variants(cond_match_type, branch_expr_types) {
1049+
is_exhaustive = false
1050+
unhandled << '`${c.table.type_to_str(v)}`'
10081051
}
1009-
//
1010-
ast.Enum {
1011-
for v in cond_match_sym.info.vals {
1012-
mut is_handled := v in branch_exprs
1013-
if !is_handled && is_multi_allowed_enum_match {
1014-
if enum_val := c.table.find_enum_field_val(cond_match_sym.name, v) {
1015-
is_handled = enum_val in branch_enum_values
1052+
} else {
1053+
match cond_match_sym.info {
1054+
ast.Enum {
1055+
for v in cond_match_sym.info.vals {
1056+
mut is_handled := v in branch_exprs
1057+
if !is_handled && is_multi_allowed_enum_match {
1058+
if enum_val := c.table.find_enum_field_val(cond_match_sym.name, v) {
1059+
is_handled = enum_val in branch_enum_values
1060+
}
1061+
}
1062+
if !is_handled {
1063+
is_exhaustive = false
1064+
unhandled << '`.${v}`'
10161065
}
10171066
}
1018-
if !is_handled {
1067+
if cond_match_sym.info.is_flag {
10191068
is_exhaustive = false
1020-
unhandled << '`.${v}`'
10211069
}
10221070
}
1023-
if cond_match_sym.info.is_flag {
1071+
else {
10241072
is_exhaustive = false
10251073
}
10261074
}
1027-
else {
1028-
is_exhaustive = false
1029-
}
10301075
}
10311076
}
10321077
if node.branches.len == 0 {

vlib/v/gen/c/fn.v

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,8 +410,9 @@ fn (mut g Gen) resolve_current_fn_generic_type(typ ast.Type) ast.Type {
410410
if generic_names.len == 0 || g.cur_concrete_types.len == 0 {
411411
return g.unwrap_generic(typ)
412412
}
413+
concrete_types := g.cur_concrete_types.map(ast.mktyp(it))
413414
mut muttable := unsafe { &ast.Table(g.table) }
414-
if resolved := muttable.convert_generic_type(typ, generic_names, g.cur_concrete_types) {
415+
if resolved := muttable.convert_generic_type(typ, generic_names, concrete_types) {
415416
return g.unwrap_generic(resolved)
416417
}
417418
return g.unwrap_generic(g.recheck_concrete_type(typ))
@@ -2017,7 +2018,13 @@ fn (mut g Gen) fn_decl_params(params []ast.Param, scope &ast.Scope, is_variadic
20172018
if i >= param_count {
20182019
break
20192020
}
2020-
mut typ := g.unwrap_generic(param.typ)
2021+
mut typ := if !param.typ.has_flag(.variadic) && g.cur_concrete_types.len > 0
2022+
&& param.orig_typ != 0 && (param.orig_typ.has_flag(.generic)
2023+
|| g.type_has_unresolved_generic_parts(param.orig_typ)) {
2024+
g.resolve_current_fn_generic_type(param.orig_typ)
2025+
} else {
2026+
g.unwrap_generic(param.typ)
2027+
}
20212028
if g.pref.translated && g.file.is_translated && param.typ.has_flag(.variadic) {
20222029
typ = g.table.sym(typ).array_info().elem_type.set_flag(.variadic)
20232030
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
struct GenericSumtypeAliasMatchItem {}
2+
3+
type GenericSumtypeAliasMatchValue[T] = string | int | T
4+
5+
fn generic_sumtype_alias_match_exhaustive[T](value GenericSumtypeAliasMatchValue[T]) int {
6+
return match value {
7+
string { 1 }
8+
int { 2 }
9+
T { 3 }
10+
}
11+
}
12+
13+
fn test_generic_sumtype_alias_match_with_generic_variant_is_exhaustive() {
14+
assert generic_sumtype_alias_match_exhaustive(GenericSumtypeAliasMatchItem{}) == 3
15+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
type GenericSumtypeAliasValue[T] = string | int | T
2+
3+
fn generic_sumtype_alias_accepts_int_literal[T](value GenericSumtypeAliasValue[T]) int {
4+
_ = value
5+
return 0
6+
}
7+
8+
fn test_generic_sumtype_alias_accepts_int_literal() {
9+
assert generic_sumtype_alias_accepts_int_literal(5) == 0
10+
}

vlib/v/util/module.v

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub fn qualify_import(pref_ &pref.Preferences, mod string, file_path string) str
2828
for search_path in mod_paths {
2929
try_path := os.join_path_single(search_path, mod_path)
3030
if os.is_dir(try_path) {
31-
if m1 := mod_path_to_full_name(pref_, mod, try_path) {
31+
if m1 := import_path_to_full_name(pref_, mod, try_path) {
3232
trace_qualify(@FN, mod, file_path, 'import_res 1', m1, try_path)
3333
// > qualify_import: term | file_path: /v/vls/server/diagnostics.v | => import_res 1: term ; /v/cleanv/vlib/term
3434
return m1
@@ -41,7 +41,7 @@ pub fn qualify_import(pref_ &pref.Preferences, mod string, file_path string) str
4141
} else {
4242
os.join_path_single(os.getwd(), file_path)
4343
}
44-
if m1 := mod_path_to_full_name(pref_, mod, abs_file_path) {
44+
if m1 := import_path_to_full_name(pref_, mod, abs_file_path) {
4545
trace_qualify(@FN, mod, file_path, 'import_res 2', m1, abs_file_path)
4646
// > qualify_module: analyzer | file_path: /v/vls/analyzer/store.v | => module_res 2: analyzer ; clean_file_path - getwd == mod
4747
// > qualify_import: analyzer.depgraph | file_path: /v/vls/analyzer/store.v | => import_res 2: analyzer.depgraph ; /v/vls/analyzer/store.v
@@ -116,6 +116,14 @@ pub fn qualify_module(pref_ &pref.Preferences, mod string, file_path string) str
116116
// 2022-01-30 it leads to path differences, and the / version on windows triggers a module lookip bug,
117117
// 2022-01-30 leading to completely different errors)
118118
fn mod_path_to_full_name(pref_ &pref.Preferences, mod string, path string) !string {
119+
return mod_path_to_full_name_with_options(pref_, mod, path, false)
120+
}
121+
122+
fn import_path_to_full_name(pref_ &pref.Preferences, mod string, path string) !string {
123+
return mod_path_to_full_name_with_options(pref_, mod, path, true)
124+
}
125+
126+
fn mod_path_to_full_name_with_options(pref_ &pref.Preferences, mod string, path string, allow_shorter_name bool) !string {
119127
// TODO: explore using `pref.lookup_path` & `os.vmodules_paths()`
120128
// absolute paths instead of 'vlib' & '.vmodules'
121129
mut vmod_folders := ['vlib', '.vmodules', 'modules']
@@ -169,7 +177,7 @@ fn mod_path_to_full_name(pref_ &pref.Preferences, mod string, path string) !stri
169177
relative_parts := real_try_path.all_after(prefix).split(os.path_separator)
170178
mod_full_name := normalize_base_url_mod_name(relative_parts.join('.'),
171179
try_path)
172-
if mod_full_name.len < mod.len {
180+
if !allow_shorter_name && mod_full_name.len < mod.len {
173181
return mod
174182
}
175183
if !module_name_has_empty_part(mod_full_name) {
@@ -197,7 +205,7 @@ fn mod_path_to_full_name(pref_ &pref.Preferences, mod string, path string) !stri
197205
if last_v_mod > -1 {
198206
mod_full_name := normalize_base_url_mod_name(try_path_parts[last_v_mod..].join('.'),
199207
try_path)
200-
if mod_full_name.len < mod.len {
208+
if !allow_shorter_name && mod_full_name.len < mod.len {
201209
return mod
202210
}
203211
if !module_name_has_empty_part(mod_full_name) {

0 commit comments

Comments
 (0)