Skip to content

Commit aea6cfe

Browse files
committed
v3: validate fastc constants and loops
1 parent cb46a90 commit aea6cfe

2 files changed

Lines changed: 177 additions & 44 deletions

File tree

vlib/v3/gen/fastc/fastc.v

Lines changed: 101 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ struct Parser {
290290
declared_kinds map[string]FastcDeclaredTypeKind
291291
struct_fields map[string]map[string]string
292292
constants map[string]string
293+
public_constants map[string]bool
293294
globals map[string]string
294295
used_function_names map[string]bool
295296
selfhost bool
@@ -356,14 +357,15 @@ fn generate_source_files(sources []FastcSourceFile, prefs &pref.Preferences) !st
356357
mut declared_kinds := map[string]FastcDeclaredTypeKind{}
357358
mut struct_fields := map[string]map[string]string{}
358359
mut constants := map[string]string{}
360+
mut public_constants := map[string]bool{}
359361
mut globals := map[string]string{}
360362
for source_file in sources {
361363
collect_declared_types(source_file.source, source_file.path,
362364
source_file.header.module_name, prefs, mut declared_types, mut declared_kinds)!
363365
}
364366
for source_file in sources {
365367
collect_constant_names(source_file.source, source_file.path,
366-
source_file.header.module_name, prefs, mut constants)!
368+
source_file.header.module_name, prefs, mut constants, mut public_constants)!
367369
collect_global_names(source_file.source, source_file.path, source_file.header.module_name,
368370
prefs, mut globals)!
369371
}
@@ -392,11 +394,11 @@ fn generate_source_files(sources []FastcSourceFile, prefs &pref.Preferences) !st
392394
declared_kinds, mut struct_fields, mut composite_types)!
393395
mut constant_types := map[string]string{}
394396
constant_declarations := fastc_generate_constant_declarations(sources, prefs, declared_types,
395-
declared_kinds, struct_fields, functions, constants, mut constant_types)!
397+
declared_kinds, struct_fields, functions, constants, public_constants, mut constant_types)!
396398
mut global_types := map[string]string{}
397399
global_output := fastc_generate_global_declarations(sources, prefs, declared_types,
398-
declared_kinds, struct_fields, functions, constants, constant_types, globals, mut
399-
global_types)!
400+
declared_kinds, struct_fields, functions, constants, public_constants, constant_types,
401+
globals, mut global_types)!
400402
for constant_type in constant_types.values() {
401403
fastc_register_composite_type(constant_type, mut composite_types)
402404
}
@@ -419,6 +421,7 @@ fn generate_source_files(sources []FastcSourceFile, prefs &pref.Preferences) !st
419421
declared_kinds: declared_kinds
420422
struct_fields: struct_fields
421423
constants: constants
424+
public_constants: public_constants
422425
globals: globals
423426
used_function_names: used_function_names
424427
selfhost: prefs.building_v
@@ -842,16 +845,18 @@ fn collect_declared_types(source string, path string, module_name string, prefs
842845
}
843846
}
844847

845-
fn collect_constant_names(source string, path string, module_name string, prefs &pref.Preferences, mut constants map[string]string) ! {
848+
fn collect_constant_names(source string, path string, module_name string, prefs &pref.Preferences, mut constants map[string]string, mut public_constants map[string]bool) ! {
846849
mut file_set := token.FileSet.new()
847850
mut file := file_set.add_file(path, source.len)
848851
file.index_lines(source)
849852
mut scan := scanner.new_scanner(prefs, .normal)
850853
scan.init(file, source)
851854
mut brace_depth := 0
855+
mut previous_tok := token.Token.unknown
852856
mut tok := scan.scan()
853857
for tok != .eof {
854858
if brace_depth == 0 && tok == .key_const {
859+
is_public := previous_tok == .key_pub
855860
tok = scan.scan()
856861
if tok == .lpar {
857862
tok = scan.scan()
@@ -868,7 +873,8 @@ fn collect_constant_names(source string, path string, module_name string, prefs
868873
continue
869874
}
870875
if nested_depth == 0 && at_declaration_start && tok == .name {
871-
fastc_register_constant(module_name, scan.lit, mut constants)
876+
fastc_register_constant(module_name, scan.lit, is_public, mut constants, mut
877+
public_constants)
872878
at_declaration_start = false
873879
}
874880
if tok in [.lpar, .lsbr, .lcbr] {
@@ -883,21 +889,26 @@ fn collect_constant_names(source string, path string, module_name string, prefs
883889
if tok != .name {
884890
return error('fastc parser does not support constant declaration in ${path}')
885891
}
886-
fastc_register_constant(module_name, scan.lit, mut constants)
892+
fastc_register_constant(module_name, scan.lit, is_public, mut constants, mut
893+
public_constants)
887894
continue
888895
}
889896
if tok == .lcbr {
890897
brace_depth++
891898
} else if tok == .rcbr && brace_depth > 0 {
892899
brace_depth--
893900
}
901+
previous_tok = tok
894902
tok = scan.scan()
895903
}
896904
}
897905

898-
fn fastc_register_constant(module_name string, name string, mut constants map[string]string) {
906+
fn fastc_register_constant(module_name string, name string, is_public bool, mut constants map[string]string, mut public_constants map[string]bool) {
899907
key := fastc_constant_key(module_name, name)
900908
constants[key] = fastc_c_constant_name(module_name, name)
909+
if is_public {
910+
public_constants[key] = true
911+
}
901912
}
902913

903914
fn collect_global_names(source string, path string, module_name string, prefs &pref.Preferences, mut globals map[string]string) ! {
@@ -941,30 +952,31 @@ fn collect_global_names(source string, path string, module_name string, prefs &p
941952
}
942953
}
943954

944-
fn fastc_generate_global_declarations(sources []FastcSourceFile, prefs &pref.Preferences, declared_types map[string]bool, declared_kinds map[string]FastcDeclaredTypeKind, struct_fields map[string]map[string]string, functions map[string]FastcFunctionSignature, constants map[string]string, constant_types map[string]string, globals map[string]string, mut global_types map[string]string) !FastcGlobalDeclarations {
955+
fn fastc_generate_global_declarations(sources []FastcSourceFile, prefs &pref.Preferences, declared_types map[string]bool, declared_kinds map[string]FastcDeclaredTypeKind, struct_fields map[string]map[string]string, functions map[string]FastcFunctionSignature, constants map[string]string, public_constants map[string]bool, constant_types map[string]string, globals map[string]string, mut global_types map[string]string) !FastcGlobalDeclarations {
945956
mut out := strings.new_builder(1024)
946957
mut initializers := strings.new_builder(1024)
947958
for source_file in sources {
948959
mut file_set := token.FileSet.new()
949960
mut file := file_set.add_file(source_file.path, source_file.source.len)
950961
file.index_lines(source_file.source)
951962
mut gen := Parser{
952-
prefs: unsafe { prefs }
953-
path: source_file.path
954-
module_name: source_file.header.module_name
955-
imports: source_file.header.imports
956-
declared_types: declared_types
957-
declared_kinds: declared_kinds
958-
struct_fields: struct_fields
959-
constants: constants
960-
globals: globals
961-
selfhost: prefs.building_v
962-
s: scanner.new_scanner(prefs, .normal)
963-
out: strings.new_builder(0)
964-
protos: strings.new_builder(0)
965-
functions: functions
966-
constant_types: constant_types
967-
global_types: global_types
963+
prefs: unsafe { prefs }
964+
path: source_file.path
965+
module_name: source_file.header.module_name
966+
imports: source_file.header.imports
967+
declared_types: declared_types
968+
declared_kinds: declared_kinds
969+
struct_fields: struct_fields
970+
constants: constants
971+
public_constants: public_constants
972+
globals: globals
973+
selfhost: prefs.building_v
974+
s: scanner.new_scanner(prefs, .normal)
975+
out: strings.new_builder(0)
976+
protos: strings.new_builder(0)
977+
functions: functions
978+
constant_types: constant_types
979+
global_types: global_types
968980
}
969981
gen.s.init(file, source_file.source)
970982
gen.next()
@@ -1053,27 +1065,28 @@ fn (mut g Parser) parse_global_declaration(mut out strings.Builder, mut initiali
10531065
g.skip_semicolons()
10541066
}
10551067

1056-
fn fastc_generate_constant_declarations(sources []FastcSourceFile, prefs &pref.Preferences, declared_types map[string]bool, declared_kinds map[string]FastcDeclaredTypeKind, struct_fields map[string]map[string]string, functions map[string]FastcFunctionSignature, constants map[string]string, mut constant_types map[string]string) !string {
1068+
fn fastc_generate_constant_declarations(sources []FastcSourceFile, prefs &pref.Preferences, declared_types map[string]bool, declared_kinds map[string]FastcDeclaredTypeKind, struct_fields map[string]map[string]string, functions map[string]FastcFunctionSignature, constants map[string]string, public_constants map[string]bool, mut constant_types map[string]string) !string {
10571069
mut out := strings.new_builder(4096)
10581070
for source_file in sources {
10591071
mut file_set := token.FileSet.new()
10601072
mut file := file_set.add_file(source_file.path, source_file.source.len)
10611073
file.index_lines(source_file.source)
10621074
mut gen := Parser{
1063-
prefs: unsafe { prefs }
1064-
path: source_file.path
1065-
module_name: source_file.header.module_name
1066-
imports: source_file.header.imports
1067-
declared_types: declared_types
1068-
declared_kinds: declared_kinds
1069-
struct_fields: struct_fields
1070-
constants: constants
1071-
selfhost: prefs.building_v
1072-
s: scanner.new_scanner(prefs, .normal)
1073-
out: strings.new_builder(256)
1074-
protos: strings.new_builder(0)
1075-
functions: functions
1076-
constant_types: constant_types
1075+
prefs: unsafe { prefs }
1076+
path: source_file.path
1077+
module_name: source_file.header.module_name
1078+
imports: source_file.header.imports
1079+
declared_types: declared_types
1080+
declared_kinds: declared_kinds
1081+
struct_fields: struct_fields
1082+
constants: constants
1083+
public_constants: public_constants
1084+
selfhost: prefs.building_v
1085+
s: scanner.new_scanner(prefs, .normal)
1086+
out: strings.new_builder(256)
1087+
protos: strings.new_builder(0)
1088+
functions: functions
1089+
constant_types: constant_types
10771090
}
10781091
gen.s.init(file, source_file.source)
10791092
gen.next()
@@ -3892,17 +3905,26 @@ fn (mut g Parser) parse_for() !bool {
38923905
g.next()
38933906
start := g.read_expression([token.Token.dotdot, token.Token.lcbr])!
38943907
start_expression_type := g.last_expression_type
3908+
start_expression := g.last_expression.clone()
38953909
if g.tok == .dotdot {
38963910
if item_is_mut || value_name != '' {
38973911
return g.unsupported('mutable or two-value range loop')
38983912
}
38993913
g.next()
39003914
end := g.read_expression([token.Token.lcbr])!
39013915
end_expression_type := g.last_expression_type
3916+
end_expression := g.last_expression.clone()
39023917
if !fastc_is_integer_expression_type(start_expression_type)
39033918
|| !fastc_is_integer_expression_type(end_expression_type) {
39043919
return g.unsupported('range bounds of types `${start_expression_type}` and `${end_expression_type}` must both be integers')
39053920
}
3921+
if start_value := fastc_integer_literal_value(start_expression) {
3922+
if end_value := fastc_integer_literal_value(end_expression) {
3923+
if start_value >= end_value {
3924+
return g.unsupported('empty range: `${start_expression[0].lit} .. ${end_expression[0].lit}` will never execute')
3925+
}
3926+
}
3927+
}
39063928
g.expect(.lcbr)!
39073929
start_name := g.temporary_name('range_start')
39083930
end_name := g.temporary_name('range_end')
@@ -4015,6 +4037,7 @@ fn (mut g Parser) parse_for() !bool {
40154037
}
40164038
if g.tok in [.decl_assign, .assign] {
40174039
is_declaration := g.tok == .decl_assign
4040+
mut assignment_type := ''
40184041
if is_declaration && name in g.locals {
40194042
return g.unsupported('redeclaration of `${name}`')
40204043
}
@@ -4025,16 +4048,28 @@ fn (mut g Parser) parse_for() !bool {
40254048
if !local.is_mut {
40264049
return g.unsupported('assignment to immutable loop variable `${name}`')
40274050
}
4051+
assignment_type = if local.is_reference {
4052+
local.typ.trim_right('*')
4053+
} else {
4054+
local.typ
4055+
}
40284056
}
40294057
g.next()
40304058
initial := g.read_expression([token.Token.semicolon])!
4059+
initial_expression_type := g.last_expression_type
40314060
initial_type := fastc_normalize_inferred_type(g.last_expression_type)
40324061
g.expect(.semicolon)!
40334062
if is_declaration {
40344063
g.locals[name] = FastcLocal{
40354064
is_mut: true
40364065
typ: initial_type
40374066
}
4067+
} else if initial_expression_type == '' || assignment_type == '' {
4068+
return g.unsupported('unverifiable assignment type for `${name}`')
4069+
} else if !fastc_call_types_are_compatible(initial_expression_type, assignment_type)
4070+
&& !(g.selfhost
4071+
&& g.selfhost_types_are_compatible(initial_expression_type, assignment_type)) {
4072+
return g.unsupported('assignment of type `${initial_expression_type}` to `${name}` of type `${assignment_type}`')
40384073
}
40394074
condition := g.read_expression([token.Token.semicolon])!
40404075
g.require_boolean_condition('for')!
@@ -5213,7 +5248,13 @@ fn (mut g Parser) read_expression_with_prefix(prefix string, stops []token.Token
52135248
module_separator := g.tok == .dot && previous_token == .name && (previous_lit in g.imports
52145249
|| previous_lit == 'C' || (g.selfhost && previous_lit !in g.locals
52155250
&& g.is_enum_type_name(previous_lit)))
5216-
mut piece := g.expression_token(previous_token, previous_lit)!
5251+
qualified_name_owner := if g.tok == .name && previous_token == .dot
5252+
&& expression_tokens.len >= 3 {
5253+
expression_tokens[expression_tokens.len - 3].lit
5254+
} else {
5255+
''
5256+
}
5257+
mut piece := g.expression_token(previous_token, previous_lit, qualified_name_owner)!
52175258
if g.tok == .name && previous_token == .dot {
52185259
mut method_lookahead := g.s
52195260
if method_lookahead.scan() == .lpar {
@@ -8892,10 +8933,10 @@ fn fastc_expression_list_items(tokens []FastcExpressionToken, start int, end int
88928933
return result
88938934
}
88948935

8895-
fn (g &Parser) expression_token(previous token.Token, previous_lit string) !string {
8936+
fn (g &Parser) expression_token(previous token.Token, previous_lit string, qualified_name_owner string) !string {
88968937
return match g.tok {
88978938
.name {
8898-
g.expression_name(previous)!
8939+
g.expression_name(previous, qualified_name_owner)!
88998940
}
89008941
.number {
89018942
if g.selfhost {
@@ -8978,7 +9019,15 @@ fn (g &Parser) nil_expression() !string {
89789019
return 'NULL'
89799020
}
89809021

8981-
fn (g &Parser) expression_name(previous token.Token) !string {
9022+
fn (g &Parser) expression_name(previous token.Token, qualified_name_owner string) !string {
9023+
if previous == .dot {
9024+
if imported_module := g.imports[qualified_name_owner] {
9025+
constant_key := fastc_constant_key(imported_module, g.lit)
9026+
if constant_key in g.constants && constant_key !in g.public_constants {
9027+
return g.unsupported('private constant `${g.lit}` from imported module `${imported_module}`')
9028+
}
9029+
}
9030+
}
89829031
g.validate_expression_name(g.lit, previous)!
89839032
return g.resolved_expression_name(g.lit, previous)
89849033
}
@@ -9944,6 +9993,14 @@ fn fastc_number_expression_type(literal string) string {
99449993
return 'integer literal'
99459994
}
99469995

9996+
fn fastc_integer_literal_value(tokens []FastcExpressionToken) ?i64 {
9997+
if tokens.len != 1 || tokens[0].tok != .number
9998+
|| fastc_number_expression_type(tokens[0].lit) != 'integer literal' {
9999+
return none
10000+
}
10001+
return tokens[0].lit.replace('_', '').i64()
10002+
}
10003+
994710004
fn fastc_common_arithmetic_type(left string, right string) string {
994810005
if left == right && fastc_is_numeric_expression_type(left) {
994910006
return left

0 commit comments

Comments
 (0)