Skip to content

Commit 3e9579a

Browse files
committed
fastc: fix strings and infinite loop flow
1 parent 5cbbbfd commit 3e9579a

2 files changed

Lines changed: 77 additions & 11 deletions

File tree

vlib/v3/gen/fastc/fastc.v

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ static void v_fastc_println_bool(bool value) { puts(value ? "true" : "false"); }
5151
static void v_fastc_println_char(char value) { fputc(value, stdout); fputc(10, stdout); }
5252
static void v_fastc_println_signed(long long value) { printf("%lld\n", value); }
5353
static void v_fastc_println_unsigned(unsigned long long value) { printf("%llu\n", value); }
54+
static bool builtin__string_eq(const char *left, const char *right) { return strcmp(left ? left : "", right ? right : "") == 0; }
55+
static bool builtin__string_lt(const char *left, const char *right) { return strcmp(left ? left : "", right ? right : "") < 0; }
5456
5557
/* Float formatting belongs to the V strconv routines. Leaving float and double
5658
* unmatched makes TinyCC reject unsupported printing instead of silently
@@ -323,6 +325,11 @@ struct FastcComptimeBlock {
323325
tok token.Token
324326
}
325327

328+
struct FastcLoopBlockResult {
329+
terminates bool
330+
has_reachable_break bool
331+
}
332+
326333
struct Parser {
327334
prefs &pref.Preferences
328335
path string
@@ -366,6 +373,8 @@ mut:
366373
deferred_lines []string
367374
deferred_block_starts []int
368375
loop_defer_block_starts []int
376+
loop_has_breaks []bool
377+
statement_reachable bool
369378
last_expression_type string
370379
last_expression []FastcExpressionToken
371380
last_multi_return_types []string
@@ -496,6 +505,8 @@ fn generate_source_files(sources []FastcSourceFile, prefs &pref.Preferences) !st
496505
deferred_lines: []string{}
497506
deferred_block_starts: []int{}
498507
loop_defer_block_starts: []int{}
508+
loop_has_breaks: []bool{}
509+
statement_reachable: true
499510
}
500511
gen.s.init(file, source_file.source)
501512
generated := gen.run()!
@@ -4151,6 +4162,8 @@ fn (mut g Parser) parse_function(enabled bool) ! {
41514162
previous_deferred_lines := g.deferred_lines.clone()
41524163
previous_deferred_block_starts := g.deferred_block_starts.clone()
41534164
previous_loop_defer_block_starts := g.loop_defer_block_starts.clone()
4165+
previous_loop_has_breaks := g.loop_has_breaks.clone()
4166+
previous_statement_reachable := g.statement_reachable
41544167
g.in_main = is_main
41554168
g.return_type = return_type
41564169
g.return_types = return_types.clone()
@@ -4160,6 +4173,8 @@ fn (mut g Parser) parse_function(enabled bool) ! {
41604173
g.deferred_lines.clear()
41614174
g.deferred_block_starts.clear()
41624175
g.loop_defer_block_starts.clear()
4176+
g.loop_has_breaks.clear()
4177+
g.statement_reachable = true
41634178
terminates := g.parse_block_body()!
41644179
g.in_main = previous_in_main
41654180
g.return_type = previous_return_type
@@ -4170,6 +4185,8 @@ fn (mut g Parser) parse_function(enabled bool) ! {
41704185
g.deferred_lines = previous_deferred_lines.clone()
41714186
g.deferred_block_starts = previous_deferred_block_starts.clone()
41724187
g.loop_defer_block_starts = previous_loop_defer_block_starts.clone()
4188+
g.loop_has_breaks = previous_loop_has_breaks.clone()
4189+
g.statement_reachable = previous_statement_reachable
41734190
if return_type != 'void' && !terminates {
41744191
if !g.selfhost {
41754192
return g.unsupported('non-void function `${name}` that can fall through')
@@ -4469,6 +4486,7 @@ fn fastc_all_true(values []bool) bool {
44694486

44704487
fn (mut g Parser) parse_block_body() !bool {
44714488
outer_locals := g.locals.clone()
4489+
outer_statement_reachable := g.statement_reachable
44724490
deferred_line_start := g.deferred_lines.len
44734491
deferred_block_start := g.deferred_block_starts.len
44744492
mut terminates := false
@@ -4477,6 +4495,7 @@ fn (mut g Parser) parse_block_body() !bool {
44774495
if g.tok == .eof {
44784496
return g.unsupported('unfinished block')
44794497
}
4498+
g.statement_reachable = outer_statement_reachable && !terminates
44804499
statement_terminates := g.parse_statement()!
44814500
if statement_terminates {
44824501
terminates = true
@@ -4498,6 +4517,7 @@ fn (mut g Parser) parse_block_body() !bool {
44984517
g.deferred_lines.trim(deferred_line_start)
44994518
g.deferred_block_starts.trim(deferred_block_start)
45004519
g.locals = outer_locals.clone()
4520+
g.statement_reachable = outer_statement_reachable
45014521
return terminates
45024522
}
45034523

@@ -4527,9 +4547,12 @@ fn (mut g Parser) parse_statement() !bool {
45274547
if g.loop_defer_block_starts.len == 0 {
45284548
return g.unsupported('`break` outside a loop')
45294549
}
4550+
if g.statement_reachable && g.loop_has_breaks.len > 0 {
4551+
g.loop_has_breaks[g.loop_has_breaks.len - 1] = true
4552+
}
45304553
g.write_deferred_blocks_from(g.loop_defer_block_starts.last())
45314554
g.write_line('break;')
4532-
false
4555+
true
45334556
}
45344557
.key_continue {
45354558
g.next()
@@ -4539,7 +4562,7 @@ fn (mut g Parser) parse_statement() !bool {
45394562
}
45404563
g.write_deferred_blocks_from(g.loop_defer_block_starts.last())
45414564
g.write_line('continue;')
4542-
false
4565+
true
45434566
}
45444567
.key_goto {
45454568
g.next()
@@ -4641,11 +4664,17 @@ fn (mut g Parser) write_all_deferred_scopes() {
46414664
}
46424665
}
46434666

4644-
fn (mut g Parser) parse_loop_block_body() !bool {
4667+
fn (mut g Parser) parse_loop_block_body() !FastcLoopBlockResult {
46454668
g.loop_defer_block_starts << g.deferred_block_starts.len
4669+
g.loop_has_breaks << false
46464670
terminates := g.parse_block_body()!
4671+
has_reachable_break := g.loop_has_breaks.last()
4672+
g.loop_has_breaks.delete_last()
46474673
g.loop_defer_block_starts.delete_last()
4648-
return terminates
4674+
return FastcLoopBlockResult{
4675+
terminates: terminates
4676+
has_reachable_break: has_reachable_break
4677+
}
46494678
}
46504679

46514680
fn (mut g Parser) parse_match_statement() !bool {
@@ -4982,10 +5011,10 @@ fn (mut g Parser) parse_for() !bool {
49825011
g.next()
49835012
g.write_line('for (;;) {')
49845013
g.indent++
4985-
_ = g.parse_loop_block_body()!
5014+
loop_result := g.parse_loop_block_body()!
49865015
g.indent--
49875016
g.write_line('}')
4988-
return false
5017+
return !loop_result.has_reachable_break
49895018
}
49905019
mut item_is_mut := false
49915020
if g.tok == .key_mut {
@@ -7672,6 +7701,11 @@ fn (g &Parser) render_special_expression(tokens []FastcExpressionToken, rendered
76727701
}
76737702
}
76747703
}
7704+
if !g.selfhost {
7705+
if string_comparison := g.render_string_comparison_expression(tokens) {
7706+
return string_comparison
7707+
}
7708+
}
76757709
if g.selfhost {
76767710
if interface_cast := g.render_interface_cast_expression(tokens, rendered_expression) {
76777711
return interface_cast
@@ -10043,7 +10077,7 @@ fn (g &Parser) render_raw_expression_tokens(tokens []FastcExpressionToken) ?stri
1004310077
piece = item.source
1004410078
} else {
1004510079
literal := fastc_c_string(item.lit) or { return none }
10046-
piece = '_S(${literal})'
10080+
piece = if g.selfhost { '_S(${literal})' } else { literal }
1004710081
}
1004810082
} else if item.tok == .char {
1004910083
piece = if item.lit.starts_with('c:') {

vlib/v3/gen/fastc/fastc_test.v

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,20 +1574,33 @@ fn test_comparison_and_logical_operands_are_validated() {
15741574
assert message.contains(expected), message
15751575
}
15761576

1577-
c_source := generate('module main
1577+
c_source := generate("module main
1578+
1579+
fn same(left string, right string) bool {
1580+
return left == right
1581+
}
15781582
15791583
fn main() {
15801584
left := 1
15811585
right := 2
15821586
ok := left < right && true
15831587
println(ok)
15841588
println(left < right)
1585-
}
1586-
',
1589+
println(same('same', 'same'))
1590+
println('alpha' < 'beta')
1591+
println('beta' > 'alpha')
1592+
println('alpha' <= 'alpha')
1593+
println('beta' >= 'beta')
1594+
println('alpha' != 'beta')
1595+
}
1596+
",
15871597
'valid_boolean_operands.v', prefs) or { panic(err) }
15881598
assert c_source.contains('left<right'), c_source
15891599
assert c_source.contains('&&'), c_source
15901600
assert c_source.contains('v_fastc_println_bool'), c_source
1601+
assert c_source.contains('static bool builtin__string_eq'), c_source
1602+
assert c_source.contains('builtin__string_eq(left,right)'), c_source
1603+
assert c_source.contains('builtin__string_lt("alpha","beta")'), c_source
15911604

15921605
root := os.join_path(os.vtmp_dir(), 'v3_fastc_boolean_operands_${os.getpid()}')
15931606
os.rmdir_all(root) or {}
@@ -1603,7 +1616,7 @@ fn main() {
16031616
assert compile_result.exit_code == 0, compile_result.output
16041617
run_result := cmdexec.run(bin_file, [])
16051618
assert run_result.exit_code == 0, run_result.output
1606-
assert run_result.output.trim_space() == 'true\ntrue'
1619+
assert run_result.output.trim_space() == 'true\ntrue\ntrue\ntrue\ntrue\ntrue\ntrue\ntrue'
16071620
}
16081621

16091622
fn test_match_branch_values_must_match_the_subject_type() {
@@ -2393,6 +2406,8 @@ fn test_non_void_functions_must_return_on_every_path() {
23932406
'module main\nfn value() int {}\nfn main() { println(value()) }\n',
23942407
'module main\nfn value() int { if true { return 1 } }\nfn main() { println(value()) }\n',
23952408
'module main\nfn value() int { return }\nfn main() { println(value()) }\n',
2409+
'module main\nfn value() int { for { break } }\nfn main() {}\n',
2410+
'module main\nfn value(flag bool) int { for { if flag { break } } }\nfn main() {}\n',
23962411
] {
23972412
mut message := ''
23982413
_ := generate(source, 'non_void_fallthrough.v', prefs) or {
@@ -2418,6 +2433,23 @@ fn main() {
24182433
'non_void_returns.v', prefs) or { panic(err) }
24192434
assert c_source.contains('return 1;')
24202435
assert c_source.contains('return 2;')
2436+
infinite_source := generate('module main
2437+
2438+
fn wait_forever() int {
2439+
for {}
2440+
}
2441+
2442+
fn nested_wait() int {
2443+
for {
2444+
for {}
2445+
break
2446+
}
2447+
}
2448+
2449+
fn main() {}
2450+
',
2451+
'infinite_loop_returns.v', prefs) or { panic(err) }
2452+
assert infinite_source.count('for (;;) {') == 3, infinite_source
24212453
}
24222454

24232455
fn test_integer_range_caches_bounds() {

0 commit comments

Comments
 (0)