cgen: sort const array init order (fix #14748) (#14749)

master
yuyi 2022-06-14 02:09:24 +08:00 committed by GitHub
parent 3535927bcd
commit 5efa67906c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 122 additions and 33 deletions

View File

@ -14,7 +14,11 @@ pub mut:
// They have their own specialized CI runner. // They have their own specialized CI runner.
const github_job = os.getenv('GITHUB_JOB') const github_job = os.getenv('GITHUB_JOB')
const should_skip = github_job != '' && github_job != 'websocket_tests' const should_skip = get_should_skip()
fn get_should_skip() bool {
return github_job != '' && github_job != 'websocket_tests'
}
// tests with internal ws servers // tests with internal ws servers
fn test_ws_ipv6() { fn test_ws_ipv6() {

View File

@ -505,16 +505,32 @@ pub const (
int_literal_type = new_type(int_literal_type_idx) int_literal_type = new_type(int_literal_type_idx)
thread_type = new_type(thread_type_idx) thread_type = new_type(thread_type_idx)
error_type = new_type(error_type_idx) error_type = new_type(error_type_idx)
charptr_types = [charptr_type, new_type(char_type_idx).set_nr_muls(1)] charptr_types = new_charptr_types()
byteptr_types = [byteptr_type, new_type(byte_type_idx).set_nr_muls(1)] byteptr_types = new_byteptr_types()
voidptr_types = [voidptr_type, new_type(voidptr_type_idx).set_nr_muls(1)] voidptr_types = new_voidptr_types()
cptr_types = merge_types(voidptr_types, byteptr_types, charptr_types) cptr_types = merge_types(voidptr_types, byteptr_types, charptr_types)
) )
fn new_charptr_types() []Type {
return [ast.charptr_type, new_type(ast.char_type_idx).set_nr_muls(1)]
}
fn new_byteptr_types() []Type {
return [ast.byteptr_type, new_type(ast.byte_type_idx).set_nr_muls(1)]
}
fn new_voidptr_types() []Type {
return [ast.voidptr_type, new_type(ast.voidptr_type_idx).set_nr_muls(1)]
}
pub fn merge_types(params ...[]Type) []Type { pub fn merge_types(params ...[]Type) []Type {
mut res := []Type{cap: params.len} mut res := []Type{cap: params.len}
for types in params { for types in params {
res << types for t in types {
if t !in res {
res << t
}
}
} }
return res return res
} }

View File

@ -219,6 +219,7 @@ struct GlobalConstDef {
init string // init later (in _vinit) init string // init later (in _vinit)
dep_names []string // the names of all the consts, that this const depends on dep_names []string // the names of all the consts, that this const depends on
order int // -1 for simple defines, string literals, anonymous function names, extern declarations etc order int // -1 for simple defines, string literals, anonymous function names, extern declarations etc
line int // the line number in the source code, where the const was defined; used to resolve order ambiguities
} }
pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
@ -4425,9 +4426,11 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
mod: field.mod mod: field.mod
def: '$styp $const_name = $val; // fixed array const' def: '$styp $const_name = $val; // fixed array const'
dep_names: g.dependent_var_names(field_expr) dep_names: g.dependent_var_names(field_expr)
// line: field.pos.line_nr
} }
} else { } else {
g.const_decl_init_later(field.mod, name, field.expr, field.typ, false) g.const_decl_init_later(field.pos.line_nr, field.mod, name, field.expr,
field.typ, false)
} }
} }
ast.StringLiteral { ast.StringLiteral {
@ -4437,15 +4440,18 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
def: 'string $const_name; // a string literal, inited later' def: 'string $const_name; // a string literal, inited later'
init: '\t$const_name = $val;' init: '\t$const_name = $val;'
order: -1 order: -1
// line: field.pos.line_nr
} }
} }
ast.CallExpr { ast.CallExpr {
if field.expr.return_type.has_flag(.optional) { if field.expr.return_type.has_flag(.optional) {
g.inside_const_optional = true g.inside_const_optional = true
unwrap_option := field.expr.or_block.kind != .absent unwrap_option := field.expr.or_block.kind != .absent
g.const_decl_init_later(field.mod, name, field.expr, field.typ, unwrap_option) g.const_decl_init_later(field.pos.line_nr, field.mod, name, field.expr,
field.typ, unwrap_option)
} else { } else {
g.const_decl_init_later(field.mod, name, field.expr, field.typ, false) g.const_decl_init_later(field.pos.line_nr, field.mod, name, field.expr,
field.typ, false)
} }
g.inside_const_optional = false g.inside_const_optional = false
} }
@ -4463,8 +4469,8 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
use_cache_mode := g.pref.build_mode == .build_module || g.pref.use_cache use_cache_mode := g.pref.build_mode == .build_module || g.pref.use_cache
if !use_cache_mode { if !use_cache_mode {
if ct_value := field.comptime_expr_value() { if ct_value := field.comptime_expr_value() {
if g.const_decl_precomputed(field.mod, name, field.name, ct_value, if g.const_decl_precomputed(field.pos.line_nr, field.mod, name,
field.typ) field.name, ct_value, field.typ)
{ {
continue continue
} }
@ -4473,16 +4479,18 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
if field.is_simple_define_const() { if field.is_simple_define_const() {
// "Simple" expressions are not going to need multiple statements, // "Simple" expressions are not going to need multiple statements,
// only the ones which are inited later, so it's safe to use expr_string // only the ones which are inited later, so it's safe to use expr_string
g.const_decl_simple_define(field.mod, field.name, g.expr_string(field_expr)) g.const_decl_simple_define(field.pos.line_nr, field.mod, field.name,
g.expr_string(field_expr))
} else { } else {
g.const_decl_init_later(field.mod, name, field.expr, field.typ, false) g.const_decl_init_later(field.pos.line_nr, field.mod, name, field.expr,
field.typ, false)
} }
} }
} }
} }
} }
fn (mut g Gen) const_decl_precomputed(mod string, name string, field_name string, ct_value ast.ComptTimeConstValue, typ ast.Type) bool { fn (mut g Gen) const_decl_precomputed(line_nr int, mod string, name string, field_name string, ct_value ast.ComptTimeConstValue, typ ast.Type) bool {
mut styp := g.typ(typ) mut styp := g.typ(typ)
cname := if g.pref.translated && !g.is_builtin_mod { name } else { '_const_$name' } cname := if g.pref.translated && !g.is_builtin_mod { name } else { '_const_$name' }
$if trace_const_precomputed ? { $if trace_const_precomputed ? {
@ -4490,13 +4498,13 @@ fn (mut g Gen) const_decl_precomputed(mod string, name string, field_name string
} }
match ct_value { match ct_value {
i8 { i8 {
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str()) g.const_decl_write_precomputed(line_nr, mod, styp, cname, field_name, ct_value.str())
} }
i16 { i16 {
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str()) g.const_decl_write_precomputed(line_nr, mod, styp, cname, field_name, ct_value.str())
} }
int { int {
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str()) g.const_decl_write_precomputed(line_nr, mod, styp, cname, field_name, ct_value.str())
} }
i64 { i64 {
if typ == ast.i64_type { if typ == ast.i64_type {
@ -4508,32 +4516,35 @@ fn (mut g Gen) const_decl_precomputed(mod string, name string, field_name string
// with -cstrict. Add checker errors for overflows instead, // with -cstrict. Add checker errors for overflows instead,
// so V can catch them earlier, instead of relying on the // so V can catch them earlier, instead of relying on the
// C compiler for that. // C compiler for that.
g.const_decl_simple_define(mod, name, ct_value.str()) g.const_decl_simple_define(line_nr, mod, name, ct_value.str())
return true return true
} }
if typ == ast.u64_type { if typ == ast.u64_type {
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str() + 'U') g.const_decl_write_precomputed(line_nr, mod, styp, cname, field_name,
ct_value.str() + 'U')
} else { } else {
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str()) g.const_decl_write_precomputed(line_nr, mod, styp, cname, field_name,
ct_value.str())
} }
} }
u8 { u8 {
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str()) g.const_decl_write_precomputed(line_nr, mod, styp, cname, field_name, ct_value.str())
} }
u16 { u16 {
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str()) g.const_decl_write_precomputed(line_nr, mod, styp, cname, field_name, ct_value.str())
} }
u32 { u32 {
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str()) g.const_decl_write_precomputed(line_nr, mod, styp, cname, field_name, ct_value.str())
} }
u64 { u64 {
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str() + 'U') g.const_decl_write_precomputed(line_nr, mod, styp, cname, field_name, ct_value.str() +
'U')
} }
f32 { f32 {
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str()) g.const_decl_write_precomputed(line_nr, mod, styp, cname, field_name, ct_value.str())
} }
f64 { f64 {
g.const_decl_write_precomputed(mod, styp, cname, field_name, ct_value.str()) g.const_decl_write_precomputed(line_nr, mod, styp, cname, field_name, ct_value.str())
} }
rune { rune {
rune_code := u32(ct_value) rune_code := u32(ct_value)
@ -4542,14 +4553,16 @@ fn (mut g Gen) const_decl_precomputed(mod string, name string, field_name string
return false return false
} }
escval := util.smart_quote(u8(rune_code).ascii_str(), false) escval := util.smart_quote(u8(rune_code).ascii_str(), false)
g.const_decl_write_precomputed(mod, styp, cname, field_name, "'$escval'") g.const_decl_write_precomputed(line_nr, mod, styp, cname, field_name,
"'$escval'")
} else { } else {
g.const_decl_write_precomputed(mod, styp, cname, field_name, u32(ct_value).str()) g.const_decl_write_precomputed(line_nr, mod, styp, cname, field_name,
u32(ct_value).str())
} }
} }
string { string {
escaped_val := util.smart_quote(ct_value, false) escaped_val := util.smart_quote(ct_value, false)
// g.const_decl_write_precomputed(styp, cname, '_SLIT("$escaped_val")') // g.const_decl_write_precomputed(line_nr, styp, cname, '_SLIT("$escaped_val")')
// TODO: ^ the above for strings, cause: // TODO: ^ the above for strings, cause:
// `error C2099: initializer is not a constant` errors in MSVC, // `error C2099: initializer is not a constant` errors in MSVC,
// so fall back to the delayed initialisation scheme: // so fall back to the delayed initialisation scheme:
@ -4570,14 +4583,14 @@ fn (mut g Gen) const_decl_precomputed(mod string, name string, field_name string
return true return true
} }
fn (mut g Gen) const_decl_write_precomputed(mod string, styp string, cname string, field_name string, ct_value string) { fn (mut g Gen) const_decl_write_precomputed(line_nr int, mod string, styp string, cname string, field_name string, ct_value string) {
g.global_const_defs[util.no_dots(field_name)] = GlobalConstDef{ g.global_const_defs[util.no_dots(field_name)] = GlobalConstDef{
mod: mod mod: mod
def: '$styp $cname = $ct_value; // precomputed' def: '$styp $cname = $ct_value; // precomputed'
} }
} }
fn (mut g Gen) const_decl_simple_define(mod string, name string, val string) { fn (mut g Gen) const_decl_simple_define(line_nr int, mod string, name string, val string) {
// Simple expressions should use a #define // Simple expressions should use a #define
// so that we don't pollute the binary with unnecessary global vars // so that we don't pollute the binary with unnecessary global vars
// Do not do this when building a module, otherwise the consts // Do not do this when building a module, otherwise the consts
@ -4607,7 +4620,7 @@ fn (mut g Gen) const_decl_simple_define(mod string, name string, val string) {
} }
} }
fn (mut g Gen) const_decl_init_later(mod string, name string, expr ast.Expr, typ ast.Type, unwrap_option bool) { fn (mut g Gen) const_decl_init_later(line_nr int, mod string, name string, expr ast.Expr, typ ast.Type, unwrap_option bool) {
// Initialize more complex consts in `void _vinit/2{}` // Initialize more complex consts in `void _vinit/2{}`
// (C doesn't allow init expressions that can't be resolved at compile time). // (C doesn't allow init expressions that can't be resolved at compile time).
mut styp := g.typ(typ) mut styp := g.typ(typ)
@ -4628,10 +4641,24 @@ fn (mut g Gen) const_decl_init_later(mod string, name string, expr ast.Expr, typ
init.writeln(g.expr_string_surround('\t$cname = ', expr, ';')) init.writeln(g.expr_string_surround('\t$cname = ', expr, ';'))
} }
} }
mut order := 0
mut line := 0
if expr is ast.CallExpr {
// TODO: analyse the function *bodies* too, to know if and what consts are used in them,
// in order to fill in dep_names properly, and to detect initialisation cycles statically
// in the checker.
// For now, just keep the source code order for `const c = f()`,
// so that programs importing `rand` and `cmd/tools/vshader.v` could run properly:
order = 1
line = line_nr
}
g.global_const_defs[util.no_dots(name)] = GlobalConstDef{ g.global_const_defs[util.no_dots(name)] = GlobalConstDef{
mod: mod mod: mod
def: '$styp $cname; // inited later' def: '$styp $cname; // inited later'
init: init.str() init: init.str()
dep_names: g.dependent_var_names(expr)
order: order
line: line
} }
if g.is_autofree { if g.is_autofree {
sym := g.table.sym(typ) sym := g.table.sym(typ)
@ -4689,6 +4716,7 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) {
mod: node.mod mod: node.mod
def: '$fn_type_name = ${g.table.sym(field.typ).name}; // global2' def: '$fn_type_name = ${g.table.sym(field.typ).name}; // global2'
order: -1 order: -1
// line: node.pos.line_nr
} }
continue continue
} }
@ -4703,6 +4731,7 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) {
mod: node.mod mod: node.mod
def: def_builder.str() def: def_builder.str()
order: -1 order: -1
// line: node.pos.line_nr
} }
continue continue
} }
@ -4735,6 +4764,7 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) {
def: def_builder.str() def: def_builder.str()
init: init init: init
dep_names: g.dependent_var_names(field.expr) dep_names: g.dependent_var_names(field.expr)
// line: node.pos.line_nr
} }
} }
} }
@ -5112,13 +5142,22 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) {
} }
fn (mut g Gen) sort_globals_consts() { fn (mut g Gen) sort_globals_consts() {
util.timing_start(@METHOD)
defer {
util.timing_measure(@METHOD)
}
g.sorted_global_const_names.clear() g.sorted_global_const_names.clear()
mut orders := map[i64]bool{}
mut dep_graph := depgraph.new_dep_graph() mut dep_graph := depgraph.new_dep_graph()
for var_name, var_info in g.global_const_defs { for var_name, var_info in g.global_const_defs {
dep_graph.add_with_value(var_name, var_info.dep_names, var_info.order) final_order := (var_info.order + 2) * 10000 + var_info.line
dep_graph.add_with_value(var_name, var_info.dep_names, final_order)
orders[final_order] = true
} }
dep_graph_sorted := dep_graph.resolve() dep_graph_sorted := dep_graph.resolve()
for order in [-1, 0] { mut all_orders := orders.keys()
all_orders.sort()
for order in all_orders {
for node in dep_graph_sorted.nodes { for node in dep_graph_sorted.nodes {
if node.value == order { if node.value == order {
g.sorted_global_const_names << node.name g.sorted_global_const_names << node.name

View File

@ -0,0 +1,10 @@
const (
cst3 = [[[cst1, cst2]]]
cst1 = [11]
cst2 = [22]
)
fn test_const_array_init_order() {
println(cst3)
assert cst3 == [[[[11], [22]]]]
}

View File

@ -0,0 +1,20 @@
import os
const (
shdc_exe_name = 'sokol-shdc.exe'
tool_name = os.file_name(os.executable())
cache_dir = os.join_path(os.cache_dir(), 'v', tool_name)
shdc = shdc_exe()
)
fn shdc_exe() string {
return os.join_path(cache_dir, shdc_exe_name)
}
fn test_const_order() {
// Ensures that cache_dir is initialised *first*, and *then* shdc
// even though `shdc` is initialised with the "simpler" call expression `shdc_exe()`,
// that seemingly does not use any other consts:
assert cache_dir.len > 1
assert shdc != '/sokol-shdc.exe'
}