Skip to content

Commit 2300cd9

Browse files
Merge branch 'vlang:master' into master
2 parents 1914701 + fb6a6d7 commit 2300cd9

51 files changed

Lines changed: 2350 additions & 259 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎doc/docs.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2007,6 +2007,7 @@ dump(x)
20072007
#### `If` unwrapping
20082008
Anywhere you can use `or {}`, you can also use "if unwrapping". This binds the unwrapped value
20092009
of an expression to a variable when that expression is not none nor an error.
2010+
An optional struct field can be unwrapped this way even after an earlier `none` check.
20102011

20112012
```v
20122013
m := {
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// vtest vflags: -w
2+
import json
3+
4+
struct DecodePropagationPerson {
5+
name string
6+
age int
7+
}
8+
9+
fn decode_propagation_person(s string) !DecodePropagationPerson {
10+
p := json.decode(DecodePropagationPerson, s)!
11+
return p
12+
}
13+
14+
fn test_json_decode_result_propagation_in_test_fn() {
15+
p := json.decode(DecodePropagationPerson, '{"name":"a","age":3}')!
16+
assert p.name == 'a'
17+
assert p.age == 3
18+
people := json.decode([]DecodePropagationPerson, '[{"name":"b"},{"name":"c"}]')!
19+
assert people.map(it.name) == ['b', 'c']
20+
}
21+
22+
fn test_json_decode_result_propagation_in_result_fn() {
23+
p := decode_propagation_person('{"name":"d","age":4}')!
24+
assert p.name == 'd'
25+
assert p.age == 4
26+
if _ := decode_propagation_person('{') {
27+
assert false
28+
} else {
29+
assert err.msg().len > 0
30+
}
31+
}
32+
33+
fn test_json_decode_result_or_block() {
34+
p := json.decode(DecodePropagationPerson, '{"name":"e"}') or {
35+
DecodePropagationPerson{
36+
name: 'fallback'
37+
}
38+
}
39+
assert p.name == 'e'
40+
q := json.decode(DecodePropagationPerson, 'not json') or {
41+
DecodePropagationPerson{
42+
name: 'fallback'
43+
}
44+
}
45+
assert q.name == 'fallback'
46+
}

‎vlib/v/driver/driver.v‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12297,6 +12297,7 @@ pub fn run(args []string) {
1229712297
} else {
1229812298
b.step('finalize')
1229912299
}
12300+
mut cg_pre_sw := time.new_stopwatch()
1230012301
stage_macos_v3_compiler_error_fallback(macos_v3_fallback_file, 'backend code generation')
1230112302
if backend == 'wasm' {
1230212303
if msg := unsupported_backend_error(a, &pre_tc, used_fns, backend) {
@@ -12477,6 +12478,9 @@ pub fn run(args []string) {
1247712478
g.set_incremental_fn_names(incremental_changed_names)
1247812479
g.set_cached_support_declarations(incremental_known_declarations)
1247912480
g.set_scope_parallel_workers(!generic_cache_hit)
12481+
if verbose {
12482+
eprintln(' [ttime] cg setup ${f64(cg_pre_sw.elapsed().microseconds()) / 1000.0:7.2f} ms')
12483+
}
1248012484
g.gen_to_file_with_used_test_options(generated_path, a, cgen_used_fns, &pre_tc, cache_no_parallel_cgen || test_files.len > 0, test_files) or {
1248112485
eprintln('error writing ${generated_path}: ${err}')
1248212486
cleanup_c_build_dir(cc_dir)
@@ -16472,14 +16476,15 @@ fn set_diagnostic_files(mut tc types.TypeChecker, user_files []string) {
1647216476
}
1647316477
ids
1647416478
}
16479+
mut resolver := types.new_shadow_file_resolver()
1647516480
for i in file_ids {
1647616481
node := tc.a.nodes[i]
1647716482
if i < tc.a.user_code_start || node.kind != .file || node.value.len == 0
1647816483
|| node.value in tc.diagnostic_files {
1647916484
continue
1648016485
}
16481-
if types.shadow_roots_own_file(node.value, tc.shadow_diagnostic_root,
16482-
tc.shadow_explicit_roots, tc.shadow_dependency_roots) {
16486+
if resolver.owns_file(node.value, tc.shadow_diagnostic_root, tc.shadow_explicit_roots,
16487+
tc.shadow_dependency_roots) {
1648316488
tc.diagnostic_files[node.value] = true
1648416489
}
1648516490
}

‎vlib/v/gen/c/cleanc.v‎

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3903,8 +3903,8 @@ pub fn (mut g FlatGen) gen_with_used_options(a &flat.FlatAst, used_fns map[strin
39033903
}
39043904
mut prefix := unsafe { g.sb.reuse_as_plain_u8_array() }
39053905
$if !windows {
3906-
if os.getenv('V3_NO_MMAP_CGEN_OUTPUT') == '' {
3907-
write_c_output_mapped(g.output_path, prefix, g.fn_segs, tail, separator) or {
3906+
if os.getenv('V3_NO_WRITEV_CGEN_OUTPUT') == '' {
3907+
write_c_output_vectored(g.output_path, prefix, g.fn_segs, tail, separator) or {
39083908
g.output_error = err.msg()
39093909
}
39103910
unsafe { prefix.free() }
@@ -4428,6 +4428,10 @@ mut:
44284428
return_type types.Type = types.Type(types.void_)
44294429
decl_is_variadic bool
44304430
first_param_is_mut bool
4431+
// registration is precomputed by the parallel prep when signature
4432+
// registration is deferred (has_registration).
4433+
has_registration bool
4434+
registration FnSignatureRegistration
44314435
}
44324436

44334437
struct FnSignatureRegistration {
@@ -4594,8 +4598,13 @@ fn (mut g FlatGen) collect_gen_info(no_parallel bool) {
45944598
if g.output_cross_c {
45954599
g.index_cross_directive_guards()
45964600
}
4597-
fn_preps := g.collect_gen_info_fn_preps(top_level_nodes, no_parallel)
4601+
mut cisub_sw := time.new_stopwatch()
4602+
fn_preps := g.collect_gen_info_fn_preps(top_level_nodes, no_parallel, defer_fn_signature_registrations)
45984603
has_parallel_fn_preps := fn_preps.len == top_level_nodes.len
4604+
if profile {
4605+
g.timing_profile(' [ttime] ci fn preps ${f64(cisub_sw.elapsed().microseconds()) / 1000.0:7.2f} ms')
4606+
cisub_sw.restart()
4607+
}
45994608
for top_level_pos, node_idx in top_level_nodes {
46004609
node := g.a.nodes[node_idx]
46014610
node_ref := g.a.node(flat.NodeId(node_idx))
@@ -4685,7 +4694,14 @@ fn (mut g FlatGen) collect_gen_info(no_parallel bool) {
46854694
ci_ret_ns += time.sys_mono_now() - ci_r0
46864695
}
46874696
if defer_fn_signature_registrations {
4688-
fn_signature_registrations << g.prepare_fn_signature_registration(node.value, full_name, ptypes, shared_params, decl_is_variadic, first_param_is_mut, return_type)
4697+
if prep.has_registration {
4698+
if shared_params.any(it) {
4699+
g.has_shared_params = true
4700+
}
4701+
fn_signature_registrations << prep.registration
4702+
} else {
4703+
fn_signature_registrations << g.prepare_fn_signature_registration(node.value, full_name, ptypes, shared_params, decl_is_variadic, first_param_is_mut, return_type)
4704+
}
46894705
} else {
46904706
g.register_fn_decl_signature_type(node.value, full_name, ptypes, shared_params, decl_is_variadic, first_param_is_mut, return_type)
46914707
}
@@ -4882,10 +4898,18 @@ fn (mut g FlatGen) collect_gen_info(no_parallel bool) {
48824898
continue
48834899
}
48844900
}
4901+
if profile {
4902+
g.timing_profile(' [ttime] ci decl loop ${f64(cisub_sw.elapsed().microseconds()) / 1000.0:7.2f} ms')
4903+
cisub_sw.restart()
4904+
}
48854905
if defer_fn_signature_registrations {
48864906
g.reserve_fn_signature_registrations(fn_signature_registrations)
48874907
g.apply_fn_signature_registrations(fn_signature_registrations)
48884908
}
4909+
if profile {
4910+
g.timing_profile(' [ttime] ci apply sigs ${f64(cisub_sw.elapsed().microseconds()) / 1000.0:7.2f} ms')
4911+
cisub_sw.restart()
4912+
}
48894913
if g.has_shared_params {
48904914
for full_name, flags in preferred_shared_fn_params {
48914915
g.fn_decl_shared_params[full_name] = flags
@@ -11044,6 +11068,14 @@ fn (mut g FlatGen) prepare_fn_signature_registration(name string, full_name stri
1104411068
break
1104511069
}
1104611070
}
11071+
return g.fn_signature_registration_in_module(g.tc.cur_module, name, full_name, ptypes,
11072+
shared_params, is_variadic, is_mut, rt)
11073+
}
11074+
11075+
// fn_signature_registration_in_module computes the spellings a declaration in
11076+
// `module_name` registers. It only reads generator state (the C-name cache just
11077+
// memoizes), so the parallel collect prep can build it on a worker view.
11078+
fn (g &FlatGen) fn_signature_registration_in_module(module_name string, name string, full_name string, ptypes []types.Type, shared_params []bool, is_variadic bool, is_mut bool, rt types.Type) FnSignatureRegistration {
1104711079
mut aliases := [6]string{}
1104811080
mut alias_count := 0
1104911081
if !g.dedup_fn_decl_aliases {
@@ -11052,8 +11084,8 @@ fn (mut g FlatGen) prepare_fn_signature_registration(name string, full_name stri
1105211084
cname := g.cname(name)
1105311085
aliases[alias_count] = cname
1105411086
alias_count++
11055-
if g.tc.cur_module.len > 0 && g.tc.cur_module != 'main' && g.tc.cur_module != 'builtin' {
11056-
dotted_name := '${g.tc.cur_module}.${name}'
11087+
if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
11088+
dotted_name := '${module_name}.${name}'
1105711089
aliases[alias_count] = dotted_name
1105811090
alias_count++
1105911091
cdotted_name := g.cname(dotted_name)
@@ -11075,8 +11107,8 @@ fn (mut g FlatGen) prepare_fn_signature_registration(name string, full_name stri
1107511107
}
1107611108
mut dotted_name := ''
1107711109
mut cdotted_name := ''
11078-
if g.tc.cur_module.len > 0 && g.tc.cur_module != 'main' && g.tc.cur_module != 'builtin' {
11079-
dotted_name = '${g.tc.cur_module}.${name}'
11110+
if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
11111+
dotted_name = '${module_name}.${name}'
1108011112
if dotted_name != name && dotted_name != cname {
1108111113
aliases[alias_count] = dotted_name
1108211114
alias_count++
@@ -11098,7 +11130,7 @@ fn (mut g FlatGen) prepare_fn_signature_registration(name string, full_name stri
1109811130
}
1109911131
}
1110011132
return FnSignatureRegistration{
11101-
module_key: fn_decl_module_key(g.tc.cur_module, name)
11133+
module_key: fn_decl_module_key(module_name, name)
1110211134
short_name: c_short_name_view(name)
1110311135
aliases: aliases
1110411136
alias_count: u8(alias_count)

‎vlib/v/gen/c/fn.v‎

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,8 @@ fn (mut g FlatGen) collect_fn_gen_candidates_range(nodes []i32, start int, end i
293293
qfn := g.qualified_fn_name_in_module_c(item_module, node.value)
294294
is_program_specialization := g.is_program_specialization_fn_node_with_qfn(node,
295295
i, qfn, item_file)
296-
if !g.should_emit_fn_node_in_module_known(node, item_module, item_file, qfn, is_program_specialization) {
296+
if !g.should_emit_fn_node_in_module_known(node, i, item_module, item_file, qfn,
297+
is_program_specialization) {
297298
continue
298299
}
299300
preferred_name := g.fn_c_name_in_module(item_module, node.value)
@@ -323,11 +324,13 @@ fn (g &FlatGen) fn_gen_selection_info() (DirectArrayAccessFns, DirectArrayAccess
323324
mut program_modules := map[string]bool{}
324325
mut non_program_modules := map[string]bool{}
325326
mut scan_file_is_program := false
327+
// Resolving a path is a syscall per file; there is nothing to match when no
328+
// program files are cached, and each file is resolved once for both passes.
329+
mut program_file_flags := map[string]bool{}
326330
for directive_idx in g.top_level_nodes() {
327331
directive := g.a.nodes[directive_idx]
328332
if directive.kind == .file {
329-
scan_file_is_program = g.cache_program_files[directive.value]
330-
|| g.cache_program_files[os.real_path(directive.value)]
333+
scan_file_is_program = g.file_is_cache_program_file(directive.value, mut program_file_flags)
331334
continue
332335
}
333336
if directive.kind == .module_decl && !scan_file_is_program {
@@ -338,8 +341,7 @@ fn (g &FlatGen) fn_gen_selection_info() (DirectArrayAccessFns, DirectArrayAccess
338341
for directive_idx in g.top_level_nodes() {
339342
directive := g.a.nodes[directive_idx]
340343
if directive.kind == .file {
341-
cur_file_is_program = g.cache_program_files[directive.value]
342-
|| g.cache_program_files[os.real_path(directive.value)]
344+
cur_file_is_program = g.file_is_cache_program_file(directive.value, mut program_file_flags)
343345
continue
344346
}
345347
if directive.kind == .module_decl {
@@ -520,6 +522,20 @@ fn (mut g FlatGen) gen_fn_items(items []FlatFnGenItem) {
520522
}
521523
}
522524

525+
// file_is_cache_program_file reports whether `file`, as written or resolved,
526+
// is one of the cached program files, memoizing the answer per file.
527+
fn (g &FlatGen) file_is_cache_program_file(file string, mut memo map[string]bool) bool {
528+
if g.cache_program_files.len == 0 {
529+
return false
530+
}
531+
if known := memo[file] {
532+
return known
533+
}
534+
is_program := g.cache_program_files[file] || g.cache_program_files[os.real_path(file)]
535+
memo[file] = is_program
536+
return is_program
537+
}
538+
523539
fn c_backend_fn_file_rank(file string) int {
524540
if file.ends_with('.c.v') {
525541
return 1
@@ -821,10 +837,11 @@ fn (mut g FlatGen) should_emit_fn_node_in_module(node flat.Node, node_index int,
821837
qfn := g.qualified_fn_name_in_module_c(module_name, node.value)
822838
is_program_specialization := g.is_program_specialization_fn_node_with_qfn(node, node_index,
823839
qfn, file_name)
824-
return g.should_emit_fn_node_in_module_known(node, module_name, file_name, qfn, is_program_specialization)
840+
return g.should_emit_fn_node_in_module_known(node, node_index, module_name, file_name,
841+
qfn, is_program_specialization)
825842
}
826843

827-
fn (mut g FlatGen) should_emit_fn_node_in_module_known(node flat.Node, module_name string, file_name string, qfn string, is_program_specialization bool) bool {
844+
fn (mut g FlatGen) should_emit_fn_node_in_module_known(node flat.Node, node_index int, module_name string, file_name string, qfn string, is_program_specialization bool) bool {
828845
if g.should_rename_user_main_for_tests(module_name, node.value) {
829846
return true
830847
}
@@ -882,7 +899,7 @@ fn (mut g FlatGen) should_emit_fn_node_in_module_known(node flat.Node, module_na
882899
&& g.specialization_signature_has_missing_nominal(node, module_name) {
883900
return false
884901
}
885-
if g.fn_node_is_open_generic_template(node, module_name) {
902+
if g.fn_node_is_open_generic_template(node, node_index, module_name) {
886903
return false
887904
}
888905
// Every concrete specialization materialized from the combined
@@ -981,13 +998,19 @@ fn (g &FlatGen) type_has_missing_qualified_nominal(t types.Type) bool {
981998
}
982999
}
9831000

984-
fn (g &FlatGen) fn_node_is_open_generic_template(node flat.Node, module_name string) bool {
1001+
fn (g &FlatGen) fn_node_is_open_generic_template(node flat.Node, node_index int, module_name string) bool {
9851002
if node.generic_params().len > 0 {
9861003
return true
9871004
}
9881005
if node.value.index_u8(`.`) < 0 {
9891006
return false
9901007
}
1008+
// A monomorphized clone substitutes every type parameter, so its receiver
1009+
// arguments are concrete even when a type is spelled with one capital letter
1010+
// (`Encoder[F].encode`, specialized for a user `struct F`).
1011+
if g.a.specialized_fn_nodes[node_index] {
1012+
return false
1013+
}
9911014
receiver := node.value.all_before_last('.')
9921015
// This declaration gate must inspect the source spelling authoritatively. The
9931016
// shared expression cache can already contain a negative result for the same
@@ -13485,6 +13508,11 @@ fn (mut g FlatGen) gen_arg_for_expected_type(arg_id flat.NodeId, expected types.
1348513508
if g.gen_mut_sum_lvalue_arg(arg_id, expected) {
1348613509
return
1348713510
}
13511+
// A `mut e &T` param is `T**` in C. Transformed method calls reach here
13512+
// instead of gen_call_args, so pass the caller's slot the same way.
13513+
if g.gen_mut_pointer_slot_arg(arg_id, arg_node, expected) {
13514+
return
13515+
}
1348813516
mut needs_addr := false
1348913517
if expected is types.Pointer && !(arg_node.kind == .prefix && arg_node.op == .amp)
1349013518
&& !g.arg_is_null_pointer_literal(arg_id, arg_node) {

‎vlib/v/gen/c/fn_parallel_notd_v3_no_parallel.v‎

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ struct CollectGenInfoFnPrepArgs {
5454
end int
5555
file string
5656
module_name string
57+
// registrations also builds each signature's alias registration.
58+
registrations bool
5759
}
5860

5961
struct CollectGenInfoScanArgs {
@@ -133,8 +135,16 @@ fn collect_gen_info_fn_prep_thread(arg voidptr) voidptr {
133135
cur_module = node.value
134136
} else if node.kind == .fn_decl && (!view.has_used_fn_filter()
135137
|| view.used_fn_contains_in_module(node.value, cur_module)) {
138+
mut prep := view.compute_collect_gen_fn_prep(node, cur_module, cur_file)
139+
if a.registrations {
140+
full_name := qualify_name_in_module(cur_module, node.value)
141+
prep.registration = view.fn_signature_registration_in_module(cur_module, node.value,
142+
full_name, prep.ptypes, prep.shared_params, prep.decl_is_variadic, prep.first_param_is_mut,
143+
prep.return_type)
144+
prep.has_registration = true
145+
}
136146
unsafe {
137-
preps[pos] = view.compute_collect_gen_fn_prep(node, cur_module, cur_file)
147+
preps[pos] = prep
138148
}
139149
}
140150
}
@@ -785,7 +795,7 @@ fn (mut g FlatGen) prepare_shared_sum_and_fixed_array_ret_wrappers(parallel bool
785795
// collect_gen_info_fn_preps resolves used function signatures on the persistent
786796
// worker pool. Registration stays serial in collect_gen_info, preserving all
787797
// source-order and duplicate-declaration semantics.
788-
fn (mut g FlatGen) collect_gen_info_fn_preps(node_ids []i32, no_parallel bool) []CollectGenFnPrep {
798+
fn (mut g FlatGen) collect_gen_info_fn_preps(node_ids []i32, no_parallel bool, with_registrations bool) []CollectGenFnPrep {
789799
if no_parallel || isnil(g.a.worker_pool) || g.a.worker_pool.size() == 0
790800
|| node_ids.len < 2048 || os.getenv('V3_NO_PAR_CGEN_INFO_FNS') != '' {
791801
return []CollectGenFnPrep{}
@@ -823,13 +833,14 @@ fn (mut g FlatGen) collect_gen_info_fn_preps(node_ids []i32, no_parallel bool) [
823833
mut tasks := []workers.Task{cap: n_jobs}
824834
for job in 0 .. n_jobs {
825835
args << CollectGenInfoFnPrepArgs{
826-
g: voidptr(g)
827-
node_ids_ptr: unsafe { voidptr(&node_ids) }
828-
preps_ptr: unsafe { voidptr(&preps) }
829-
start: node_ids.len * job / n_jobs
830-
end: node_ids.len * (job + 1) / n_jobs
831-
file: context_files[job]
832-
module_name: context_modules[job]
836+
g: voidptr(g)
837+
node_ids_ptr: unsafe { voidptr(&node_ids) }
838+
preps_ptr: unsafe { voidptr(&preps) }
839+
start: node_ids.len * job / n_jobs
840+
end: node_ids.len * (job + 1) / n_jobs
841+
file: context_files[job]
842+
module_name: context_modules[job]
843+
registrations: with_registrations
833844
}
834845
}
835846
for job in 0 .. n_jobs {

0 commit comments

Comments
 (0)