From 2fe20cd0920012b53770a352abf811ec93a29c67 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 29 Aug 2019 01:52:32 +0300 Subject: [PATCH] compiler: fix struct order bug --- compiler/cgen.v | 84 ++++++++++++------- compiler/main.v | 9 +- compiler/table.v | 60 ++++++++------ vlib/builtin/map.v | 202 ++++++++++++++++++++++----------------------- 4 files changed, 199 insertions(+), 156 deletions(-) diff --git a/compiler/cgen.v b/compiler/cgen.v index 499affdd7b..5c08d3bffb 100644 --- a/compiler/cgen.v +++ b/compiler/cgen.v @@ -14,7 +14,6 @@ struct CGen { typedefs []string type_aliases []string includes []string - //types []string thread_args []string thread_fns []string consts []string @@ -217,15 +216,6 @@ fn (p mut Parser) print_prof_counters() string { return res.join(';\n') } -/* -fn (p mut Parser) gen_type(s string) { - if !p.first_pass() { - return - } - p.cgen.types << s -} -*/ - fn (p mut Parser) gen_typedef(s string) { if !p.first_pass() { return @@ -297,24 +287,38 @@ fn platform_postfix_to_ifdefguard(name string) string { } // C struct definitions, ordered +// Sort the types, make sure types that are referenced by other types +// are added before them. fn (v mut V) c_type_definitions() string { - mut types := v.table.types - // Sort the types, make sure types that are referenced by other types - // are added before them. - for i in 0 .. types.len { - for j in 0 .. i { - t := types[i] - if types[j].contains_field_type(t.name) { - types[i] = types[j] - types[j] = t - continue - } - - } - } - // Generate C code - mut sb := strings.new_builder(10) + mut types := []Type // structs that need to be sorted + mut top_types := []Type // builtin types and types that only have primitive fields for t in v.table.types { + if !t.name[0].is_capital() { + top_types << t + continue + } + mut only_builtin_fields := true + for field in t.fields { + if field.typ[0].is_capital() { + only_builtin_fields = false + break + } + } + if only_builtin_fields { + top_types << t + continue + } + types << t + } + sort_structs(mut types) + // Generate C code + return types_to_c(top_types, v.table) + '\n/*----*/\n' + + types_to_c(types, v.table) +} + +fn types_to_c(types []Type, table &Table) string { + mut sb := strings.new_builder(10) + for t in types { if t.cat != .union_ && t.cat != .struct_ { continue } @@ -327,14 +331,38 @@ fn (v mut V) c_type_definitions() string { kind := if t.cat == .union_ {'union'} else {'struct'} sb.writeln('$kind $t.name {') for field in t.fields { - sb.writeln(v.table.cgen_name_type_pair(field.name, + sb.writeln(table.cgen_name_type_pair(field.name, field.typ) + ';') } sb.writeln('};\n') //if is_objc { - //p.gen_type('@end') + //sb.writeln('@end') //} } return sb.str() } +// pretty inefficient algo, works fine with N < 1000 (TODO optimize) +fn sort_structs(types mut []Type) { + mut cnt := 0 + for i := 0; i < types.len; i++ { + for j in 0 .. i { + t := types[i] + //t2 := types[j] + // check if any of the types before `t` reference `t` + if types[j].contains_field_type(t.name) { + //println('moving up: $t.name len=$types.len') + types.insert(j, t) + types.delete(i+1) + i = 0 // Start from scratch + cnt++ + if cnt > 500 { + println('infinite type loop (perhaps you have a recursive struct `$t.name`?)') + exit(1) + } + continue + } + } + } +} + diff --git a/compiler/main.v b/compiler/main.v index 993efb8dbc..9cd053e1ea 100644 --- a/compiler/main.v +++ b/compiler/main.v @@ -4,9 +4,11 @@ module main -import os -import time -import strings +import ( + os + time + strings +) const ( Version = '0.1.18' @@ -298,7 +300,6 @@ fn (v mut V) compile() { mut d := strings.new_builder(10000)// Avoid unnecessary allocations d.writeln(cgen.includes.join_lines()) d.writeln(cgen.typedefs.join_lines()) - //d.writeln(cgen.types.join_lines()) d.writeln(v.c_type_definitions()) d.writeln('\nstring _STR(const char*, ...);\n') d.writeln('\nstring _STR_TMP(const char*, ...);\n') diff --git a/compiler/table.v b/compiler/table.v index 98d7ec76ec..1432114472 100644 --- a/compiler/table.v +++ b/compiler/table.v @@ -7,29 +7,6 @@ module main import math import strings -struct Var { -mut: - typ string - name string - is_arg bool - is_const bool - args []Var // function args - attr string // [json] etc - is_mut bool - is_alloc bool - ptr bool - ref bool - parent_fn string // Variables can only be defined in functions - mod string // module where this var is stored - line_nr int - access_mod AccessMod - is_global bool // __global (translated from C only) - is_used bool - is_changed bool - scope_level int -} - - struct Table { mut: types []Type @@ -45,6 +22,9 @@ mut: obfuscate bool } + + + struct GenTable { fn_name string types []string @@ -77,6 +57,30 @@ enum TypeCategory { c_typedef } +struct Var { +mut: + typ string + name string + is_arg bool + is_const bool + args []Var // function args + attr string // [json] etc + is_mut bool + is_alloc bool + ptr bool + ref bool + parent_fn string // Variables can only be defined in functions + mod string // module where this var is stored + line_nr int + access_mod AccessMod + is_global bool // __global (translated from C only) + is_used bool + is_changed bool + scope_level int +} + + + struct Type { mut: mod string @@ -96,6 +100,13 @@ mut: gen_str bool // needs `.str()` method generation } +struct TypeNode { + mut: + next &TypeNode + typ Type +} + + // For debugging types fn (t Type) str() string { mut s := 'type "$t.name" {' @@ -928,6 +939,9 @@ fn (fit &FileImportTable) resolve_alias(alias string) string { } fn (t &Type) contains_field_type(typ string) bool { + if !t.name[0].is_capital() { + return false + } for field in t.fields { if field.typ == typ { return true diff --git a/vlib/builtin/map.v b/vlib/builtin/map.v index 221589e1ff..91d29f65ef 100644 --- a/vlib/builtin/map.v +++ b/vlib/builtin/map.v @@ -4,19 +4,19 @@ module builtin -import strings +import strings struct map { element_size int - root *Node -pub: - size int + root *mapnode +pub: + size int } -struct Node { - left *Node - right *Node - is_empty bool +struct mapnode { + left *mapnode + right *mapnode + is_empty bool key string val voidptr } @@ -24,108 +24,108 @@ struct Node { fn new_map(cap, elm_size int) map { res := map { element_size: elm_size - root: 0 + root: 0 } return res } -// `m := { 'one': 1, 'two': 2 }` +// `m := { 'one': 1, 'two': 2 }` fn new_map_init(cap, elm_size int, keys *string, vals voidptr) map { mut res := map { element_size: elm_size - root: 0 + root: 0 } for i in 0 .. cap { - res._set(keys[i], vals + i * elm_size) - } + res._set(keys[i], vals + i * elm_size) + } return res -} +} -fn new_node(key string, val voidptr, element_size int) *Node { - new_e := &Node { +fn new_node(key string, val voidptr, element_size int) *mapnode { + new_e := &mapnode { key: key val: malloc(element_size) left: 0 - right: 0 + right: 0 } C.memcpy(new_e.val, val, element_size) return new_e } -fn (m mut map) insert(n mut Node, key string, val voidptr) { +fn (m mut map) insert(n mut mapnode, key string, val voidptr) { if n.key == key { C.memcpy(n.val, val, m.element_size) - return - } + return + } if n.key > key { if isnil(n.left) { - n.left = new_node(key, val, m.element_size) - m.size++ - } else { - m.insert(mut n.left, key, val) - } - return - } + n.left = new_node(key, val, m.element_size) + m.size++ + } else { + m.insert(mut n.left, key, val) + } + return + } if isnil(n.right) { - n.right = new_node(key, val, m.element_size) - m.size++ - } else { - m.insert(mut n.right, key, val) - } -} + n.right = new_node(key, val, m.element_size) + m.size++ + } else { + m.insert(mut n.right, key, val) + } +} -fn (n & Node) find(key string, out voidptr, element_size int) bool{ +fn (n & mapnode) find(key string, out voidptr, element_size int) bool{ if n.key == key { C.memcpy(out, n.val, element_size) - return true - } + return true + } else if n.key > key { if isnil(n.left) { - return false - } else { - return n.left.find(key, out, element_size) - } - } + return false + } else { + return n.left.find(key, out, element_size) + } + } else { if isnil(n.right) { - return false - } else { - return n.right.find(key, out, element_size) - } + return false + } else { + return n.right.find(key, out, element_size) + } } -} +} -// same as `find`, but doesn't return a value. Used by `exists` -fn (n & Node) find2(key string, element_size int) bool{ +// same as `find`, but doesn't return a value. Used by `exists` +fn (n & mapnode) find2(key string, element_size int) bool{ if n.key == key { - return true - } + return true + } else if n.key > key { if isnil(n.left) { - return false - } else { - return n.left.find2(key, element_size) - } - } + return false + } else { + return n.left.find2(key, element_size) + } + } else { if isnil(n.right) { - return false - } else { - return n.right.find2(key, element_size) - } + return false + } else { + return n.right.find2(key, element_size) + } } -} +} fn (m mut map) _set(key string, val voidptr) { if isnil(m.root) { - m.root = new_node(key, val, m.element_size) - m.size++ - return - } - m.insert(mut m.root, key, val) + m.root = new_node(key, val, m.element_size) + m.size++ + return + } + m.insert(mut m.root, key, val) } -/* +/* fn (m map) bs(query string, start, end int, out voidptr) { // println('bs "$query" $start -> $end') mid := start + ((end - start) / 2) @@ -150,23 +150,23 @@ fn (m map) bs(query string, start, end int, out voidptr) { } m.bs(query, mid, end, out) } -*/ +*/ -fn preorder_keys(node &Node, keys mut []string, key_i int) int { +fn preorder_keys(node &mapnode, keys mut []string, key_i int) int { mut i := key_i if !node.is_empty { mut a := *keys a[i] = node.key i++ } - if !isnil(node.left) { + if !isnil(node.left) { i = preorder_keys(node.left, mut keys, i) - } - if !isnil(node.right) { + } + if !isnil(node.right) { i = preorder_keys(node.right, mut keys, i) } return i -} +} pub fn (m mut map) keys() []string { mut keys := [''; m.size] @@ -179,44 +179,44 @@ pub fn (m mut map) keys() []string { fn (m map) get(key string, out voidptr) bool { if isnil(m.root) { - return false - } - return m.root.find(key, out, m.element_size) + return false + } + return m.root.find(key, out, m.element_size) } -pub fn (n mut Node) delete(key string, element_size int) { +pub fn (n mut mapnode) delete(key string, element_size int) { if n.key == key { C.memset(n.val, 0, element_size) - n.is_empty = true - return - } + n.is_empty = true + return + } else if n.key > key { if isnil(n.left) { - return - } else { - n.left.delete(key, element_size) - } - } + return + } else { + n.left.delete(key, element_size) + } + } else { if isnil(n.right) { - return - } else { - n.right.delete(key, element_size) - } - } -} + return + } else { + n.right.delete(key, element_size) + } + } +} -pub fn (m mut map) delete(key string) { - m.root.delete(key, m.element_size) - m.size-- -} +pub fn (m mut map) delete(key string) { + m.root.delete(key, m.element_size) + m.size-- +} pub fn (m map) exists(key string) bool { - panic('map.exists(key) was removed from the language. Use `key in map` instead.') + panic('map.exists(key) was removed from the language. Use `key in map` instead.') } fn (m map) _exists(key string) bool { - return !isnil(m.root) && m.root.find2(key, m.element_size) + return !isnil(m.root) && m.root.find2(key, m.element_size) } pub fn (m map) print() { @@ -246,10 +246,10 @@ pub fn (m map_string) str() string { return '{}' } mut sb := strings.new_builder(50) - sb.writeln('{') - for key, val in m { - sb.writeln(' "$key" => "$val"') + sb.writeln('{') + for key, val in m { + sb.writeln(' "$key" => "$val"') } - sb.writeln('}') - return sb.str() + sb.writeln('}') + return sb.str() }