Skip to content

Commit ed082fe

Browse files
committed
v3: validate more fastc expression types
1 parent d7de568 commit ed082fe

2 files changed

Lines changed: 89 additions & 12 deletions

File tree

vlib/v3/gen/fastc/fastc.v

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,9 @@ fn (mut g Parser) parse_function() ! {
304304
// result type. Reject them until the direct parser tracks the required type.
305305
return g.unsupported('narrow integer function types')
306306
}
307+
if name == 'main' && params.len == 0 && return_type != 'void' {
308+
return g.unsupported('main function returning `${return_type}`')
309+
}
307310
g.expect(.lcbr)!
308311
is_main := name == 'main' && params.len == 0
309312
if is_main {
@@ -564,9 +567,14 @@ fn (mut g Parser) parse_for() !bool {
564567
}
565568
g.next()
566569
start := g.read_expression([token.Token.dotdot])!
567-
start_type := fastc_normalize_inferred_type(g.last_expression_type)
570+
start_expression_type := g.last_expression_type
568571
g.expect(.dotdot)!
569572
end := g.read_expression([token.Token.lcbr])!
573+
end_expression_type := g.last_expression_type
574+
if !fastc_is_integer_expression_type(start_expression_type)
575+
|| !fastc_is_integer_expression_type(end_expression_type) {
576+
return g.unsupported('range bounds of types `${start_expression_type}` and `${end_expression_type}` must both be integers')
577+
}
570578
g.expect(.lcbr)!
571579
start_name := g.temporary_name('range_start')
572580
end_name := g.temporary_name('range_end')
@@ -575,7 +583,7 @@ fn (mut g Parser) parse_for() !bool {
575583
g.write_line('__typeof__((${end})) ${end_name} = (${end});')
576584
g.write_line('for (__typeof__((${start_name})) ${name} = (${start_name}); ${name} < (${end_name}); ${name}++) {')
577585
g.locals[name] = FastcLocal{
578-
typ: start_type
586+
typ: fastc_normalize_inferred_type(start_expression_type)
579587
}
580588
g.indent++
581589
_ = g.parse_block_body()!
@@ -716,6 +724,10 @@ fn (mut g Parser) parse_simple_statement() ! {
716724
if !fastc_call_types_are_compatible(actual_type, expected_type) {
717725
return g.unsupported('assignment of type `${actual_type}` to `${name}` of type `${expected_type}`')
718726
}
727+
if operator != .assign && (!fastc_is_numeric_expression_type(actual_type)
728+
|| !fastc_is_numeric_expression_type(expected_type)) {
729+
return g.unsupported('arithmetic assignment `${operator.str()}` on non-numeric type `${expected_type}`')
730+
}
719731
g.consume_statement_end()
720732
g.write_line('${name}${operator.str()}${value};')
721733
return
@@ -887,7 +899,7 @@ fn (mut g Parser) read_expression_with_prefix(prefix string, stops []token.Token
887899
return g.unsupported('unbalanced expression')
888900
}
889901
g.validate_expression_calls(expression_tokens)!
890-
g.last_expression_type = g.infer_expression_type(expression_tokens)
902+
g.last_expression_type = g.infer_expression_type(expression_tokens)!
891903
return result.str().trim_space()
892904
}
893905

@@ -954,7 +966,7 @@ fn (g &Parser) validate_expression_calls(tokens []FastcExpressionToken) ! {
954966
return g.unsupported('function `${name}` call with ${call_args.len} arguments instead of ${signature.parameter_types.len}')
955967
}
956968
for argument_index, argument in call_args {
957-
actual_type := g.infer_expression_type(argument)
969+
actual_type := g.infer_expression_type(argument)!
958970
expected_type := signature.parameter_types[argument_index]
959971
if actual_type.len == 0 {
960972
return g.unsupported('unverifiable argument ${argument_index + 1} to function `${name}`')
@@ -967,6 +979,7 @@ fn (g &Parser) validate_expression_calls(tokens []FastcExpressionToken) ! {
967979
if call_args.len != 1 {
968980
return g.unsupported('function `${name}` call with ${call_args.len} arguments')
969981
}
982+
_ = g.infer_expression_type(call_args[0])!
970983
} else if _ := fastc_primitive_c_type(name) {
971984
if call_args.len != 1 {
972985
return g.unsupported('cast `${name}` with ${call_args.len} arguments')
@@ -1031,7 +1044,7 @@ fn fastc_call_arguments(tokens []FastcExpressionToken, open int, close int) ![][
10311044
return call_args
10321045
}
10331046

1034-
fn (g &Parser) infer_expression_type(tokens []FastcExpressionToken) string {
1047+
fn (g &Parser) infer_expression_type(tokens []FastcExpressionToken) !string {
10351048
if tokens.len == 0 {
10361049
return ''
10371050
}
@@ -1090,10 +1103,18 @@ fn (g &Parser) infer_expression_type(tokens []FastcExpressionToken) string {
10901103
}
10911104
}
10921105
if tokens[start].tok in [.plus, .minus] {
1093-
return g.infer_expression_type(tokens[start + 1..end])
1106+
operand_type := g.infer_expression_type(tokens[start + 1..end])!
1107+
if !fastc_is_numeric_expression_type(operand_type) {
1108+
return g.unsupported('arithmetic `${tokens[start].tok.str()}` on non-numeric type `${operand_type}`')
1109+
}
1110+
return operand_type
10941111
}
10951112
if tokens[end - 1].tok in [.inc, .dec] {
1096-
return g.infer_expression_type(tokens[start..end - 1])
1113+
operand_type := g.infer_expression_type(tokens[start..end - 1])!
1114+
if !fastc_is_numeric_expression_type(operand_type) {
1115+
return g.unsupported('arithmetic `${tokens[end - 1].tok.str()}` on non-numeric type `${operand_type}`')
1116+
}
1117+
return operand_type
10971118
}
10981119
mut depth := 0
10991120
for i in start .. end {
@@ -1106,12 +1127,16 @@ fn (g &Parser) infer_expression_type(tokens []FastcExpressionToken) string {
11061127
continue
11071128
}
11081129
if tokens[i].tok.is_assignment() {
1109-
return g.infer_expression_type(tokens[start..i])
1130+
return g.infer_expression_type(tokens[start..i])!
11101131
}
11111132
if tokens[i].tok in [.plus, .minus, .mul, .amp, .pipe, .xor] && i > start {
1112-
left_type := g.infer_expression_type(tokens[start..i])
1113-
right_type := g.infer_expression_type(tokens[i + 1..end])
1114-
return fastc_common_arithmetic_type(left_type, right_type)
1133+
left_type := g.infer_expression_type(tokens[start..i])!
1134+
right_type := g.infer_expression_type(tokens[i + 1..end])!
1135+
common_type := fastc_common_arithmetic_type(left_type, right_type)
1136+
if common_type.len == 0 {
1137+
return g.unsupported('arithmetic `${tokens[i].tok.str()}` operands of types `${left_type}` and `${right_type}`')
1138+
}
1139+
return common_type
11151140
}
11161141
}
11171142
return ''
@@ -1127,7 +1152,7 @@ fn fastc_number_expression_type(literal string) string {
11271152
}
11281153

11291154
fn fastc_common_arithmetic_type(left string, right string) string {
1130-
if left == right {
1155+
if left == right && fastc_is_numeric_expression_type(left) {
11311156
return left
11321157
}
11331158
if left == 'integer literal' && fastc_is_integer_type(right) {
@@ -1145,6 +1170,14 @@ fn fastc_common_arithmetic_type(left string, right string) string {
11451170
return ''
11461171
}
11471172

1173+
fn fastc_is_numeric_expression_type(typ string) bool {
1174+
return typ in ['integer literal', 'float literal', 'f32', 'f64'] || fastc_is_integer_type(typ)
1175+
}
1176+
1177+
fn fastc_is_integer_expression_type(typ string) bool {
1178+
return typ == 'integer literal' || fastc_is_integer_type(typ)
1179+
}
1180+
11481181
fn fastc_call_types_are_compatible(actual string, expected string) bool {
11491182
if actual == expected {
11501183
return true

vlib/v3/gen/fastc/fastc_test.v

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,50 @@ fn main() {
302302
assert c_source.contains('count+=3;')
303303
}
304304

305+
fn test_main_must_not_return_a_value() {
306+
prefs := pref.new_preferences()
307+
mut message := ''
308+
_ := generate('module main\nfn main() int { return 7 }\n', 'value_returning_main.v', prefs) or {
309+
message = err.msg()
310+
''
311+
}
312+
assert message.contains('main function returning `int`'), message
313+
}
314+
315+
fn test_range_bounds_must_be_integers() {
316+
prefs := pref.new_preferences()
317+
for source in [
318+
'module main\nfn main() { for i in 0.0 .. 2.0 { println(i) } }\n',
319+
'module main\nfn main() { for i in 0 .. 2.0 { println(i) } }\n',
320+
'module main\nfn main() { for i in false .. true { println(i) } }\n',
321+
] {
322+
mut message := ''
323+
_ := generate(source, 'invalid_range_bounds.v', prefs) or {
324+
message = err.msg()
325+
''
326+
}
327+
assert message.contains('range bounds of types'), message
328+
assert message.contains('must both be integers'), message
329+
}
330+
}
331+
332+
fn test_arithmetic_operands_must_be_numeric() {
333+
prefs := pref.new_preferences()
334+
for source in [
335+
'module main\nfn main() { println(true + false) }\n',
336+
'module main\nfn main() { value := true * false; println(value) }\n',
337+
'module main\nfn main() { mut value := true; value += false; println(value) }\n',
338+
] {
339+
mut message := ''
340+
_ := generate(source, 'non_numeric_arithmetic.v', prefs) or {
341+
message = err.msg()
342+
''
343+
}
344+
assert message.contains('arithmetic'), message
345+
assert message.contains('non-numeric') || message.contains('operands of types'), message
346+
}
347+
}
348+
305349
fn test_bare_return_from_main_emits_zero() {
306350
prefs := pref.new_preferences()
307351
c_source := generate('module main

0 commit comments

Comments
 (0)