builder: restore module import cycle detection/topological reorder

pull/4439/head
Delyan Angelov 2020-04-16 12:29:36 +03:00
parent 04db2d02b8
commit 8a1248b2e7
17 changed files with 117 additions and 32 deletions

View File

@ -272,6 +272,7 @@ jobs:
.\make.bat -msvc
- name: Fixed tests
run: |
./v -cg cmd\tools\vtest-fixed.v
./v test-fixed
# - name: Test
# run: |

View File

@ -4,7 +4,7 @@
module builtin
#include <dbghelp.h>
// dbghelp.h is already included in cheaders.v
#flag windows -l dbghelp
pub struct SymbolInfo {

View File

@ -465,6 +465,7 @@ pub:
pub struct HashStmt {
pub:
val string
mod string
}
// filter(), map()

View File

@ -14,6 +14,7 @@ import (
v.gen
v.gen.js
v.gen.x64
v.depgraph
)
pub struct Builder {
@ -49,7 +50,9 @@ pub fn new_builder(pref &pref.Preferences) Builder {
// parse all deps from already parsed files
pub fn (b mut Builder) parse_imports() {
mut done_imports := []string
for i in 0 .. b.parsed_files.len {
// NB: b.parsed_files is appended in the loop,
// so we can not use the shorter `for in` form.
for i := 0; i<b.parsed_files.len; i++ {
ast_file := b.parsed_files[i]
for _, imp in ast_file.imports {
mod := imp.mod
@ -80,6 +83,60 @@ pub fn (b mut Builder) parse_imports() {
done_imports << mod
}
}
b.resolve_deps()
}
pub fn (b mut Builder) resolve_deps() {
graph := b.import_graph()
deps_resolved := graph.resolve()
if !deps_resolved.acyclic {
eprintln('import cycle detected between the following modules: \n' + deps_resolved.display_cycles())
// TODO: error, when v itself does not have v.table -> v.ast -> v.table cycles anymore
return
}
if b.pref.is_verbose {
eprintln('------ resolved dependencies graph: ------')
eprintln(deps_resolved.display())
eprintln('------------------------------------------')
}
mut mods := []string
for node in deps_resolved.nodes {
mods << node.name
}
if b.pref.is_verbose {
eprintln('------ imported modules: ------')
eprintln(mods.str())
eprintln('-------------------------------')
}
mut reordered_parsed_files := []ast.File
for m in mods {
for pf in b.parsed_files {
if m == pf.mod.name {
reordered_parsed_files << pf
//eprintln('pf.mod.name: $pf.mod.name | pf.path: $pf.path')
}
}
}
b.parsed_files = reordered_parsed_files
}
// graph of all imported modules
pub fn (b &Builder) import_graph() &depgraph.DepGraph {
mut builtins := util.builtin_module_parts
builtins << 'builtin'
mut graph := depgraph.new_dep_graph()
for p in b.parsed_files {
mut deps := []string
if p.mod.name !in builtins {
deps << 'builtin'
}
for _, m in p.imports {
deps << m.mod
}
graph.add(p.mod.name, deps)
}
return graph
}
pub fn (b Builder) v_files_from_dir(dir string) []string {

View File

@ -1,13 +1,14 @@
vlib/v/checker/tests/inout/enum_err.v:2:13: error: default value for enum has to be an integer
1| enum Color {
2| green = 'green',
vlib/v/checker/tests/inout/enum_err.v:4:13: error: default value for enum has to be an integer
2|
3| enum Color {
4| green = 'green',
~~~~~~~
3| yellow = 1+1,
4| blue,
vlib/v/checker/tests/inout/enum_err.v:3:14: error: default value for enum has to be an integer
1| enum Color {
2| green = 'green',
3| yellow = 1+1,
5| yellow = 1+1,
6| blue,
vlib/v/checker/tests/inout/enum_err.v:5:14: error: default value for enum has to be an integer
3| enum Color {
4| green = 'green',
5| yellow = 1+1,
~~~
4| blue,
5| }
6| blue,
7| }

View File

@ -1,5 +1,11 @@
module main
enum Color {
green = 'green',
yellow = 1+1,
blue,
}
fn main(){
println('hello')
}

View File

@ -1,3 +1,5 @@
struct abc {
}
fn main(){}

View File

@ -1,6 +1,6 @@
vlib/v/checker/tests/inout/unknown_field.v:7:12: error: unknown field `checker.Test.sdd`
vlib/v/checker/tests/inout/unknown_field.v:7:12: error: unknown field `Test.sdd`
5| fn main() {
6| t := Test{}
7| println(t.sdd)
~~~
8| }
8| }

View File

@ -1,4 +1,4 @@
module checker
module main
struct Test {}

View File

@ -1,6 +1,6 @@
vlib/v/checker/tests/inout/unknown_method.v:7:12: error: unknown method: checker.Test.sdd
vlib/v/checker/tests/inout/unknown_method.v:7:12: error: unknown method: Test.sdd
5| fn main() {
6| t := Test{}
7| println(t.sdd())
~~~~~
8| }
8| }

View File

@ -1,4 +1,4 @@
module checker
module main
struct Test {}

View File

@ -1,4 +1,4 @@
module checker
module main
fn main() {
mut a := 'aa'

View File

@ -25,13 +25,14 @@ fn foo(t token.Token) {
struct Gen {
out strings.Builder
cheaders strings.Builder
includes strings.Builder // all C #includes required by V modules
typedefs strings.Builder
typedefs2 strings.Builder
definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
inits strings.Builder // contents of `void _vinit(){}`
gowrappers strings.Builder // all go callsite wrappers
stringliterals strings.Builder // all string literals (they depend on tos3() beeing defined
includes strings.Builder
table &table.Table
pref &pref.Preferences
mut:
@ -76,13 +77,14 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
// println('start cgen2')
var g := Gen{
out: strings.new_builder(1000)
cheaders: strings.new_builder(8192)
includes: strings.new_builder(100)
typedefs: strings.new_builder(100)
typedefs2: strings.new_builder(100)
definitions: strings.new_builder(100)
gowrappers: strings.new_builder(100)
stringliterals: strings.new_builder(100)
inits: strings.new_builder(100)
includes: strings.new_builder(100)
table: table
pref: pref
fn_decl: 0
@ -124,8 +126,15 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
}
//
g.finish()
return g.hashes() + g.includes.str() + g.typedefs.str() + g.typedefs2.str() + g.definitions.str() +
g.gowrappers.str() + g.stringliterals.str() + g.out.str()
return g.hashes() +
'\n// V typedefs:\n' + g.typedefs.str() +
'\n// V typedefs2:\n' + g.typedefs2.str() +
'\n// V cheaders:\n' + g.cheaders.str() +
'\n// V includes:\n' + g.includes.str() +
'\n// V definitions:\n' + g.definitions.str() +
'\n// V gowrappers:\n' + g.gowrappers.str() +
'\n// V stringliterals:\n' + g.stringliterals.str() +
'\n// V out\n' + g.out.str()
}
pub fn (g Gen) hashes() string {
@ -135,10 +144,10 @@ pub fn (g Gen) hashes() string {
}
pub fn (g mut Gen) init() {
g.definitions.writeln('// Generated by the V compiler')
g.definitions.writeln('#include <inttypes.h>') // int64_t etc
g.definitions.writeln(c_builtin_types)
g.definitions.writeln(c_headers)
g.cheaders.writeln('// Generated by the V compiler')
g.cheaders.writeln('#include <inttypes.h>') // int64_t etc
g.cheaders.writeln(c_builtin_types)
g.cheaders.writeln(c_headers)
g.definitions.writeln('\nstring _STR(const char*, ...);\n')
g.definitions.writeln('\nstring _STR_TMP(const char*, ...);\n')
g.write_builtin_types()
@ -478,7 +487,11 @@ fn (g mut Gen) stmt(node ast.Stmt) {
ast.HashStmt {
// #include etc
typ := it.val.all_before(' ')
if typ in ['include', 'define'] {
if typ == 'include' {
g.includes.writeln('// added by module `$it.mod`:')
g.includes.writeln('#$it.val')
}
if typ == 'define' {
g.definitions.writeln('#$it.val')
}
}

View File

@ -67,7 +67,6 @@ const (
// c_headers
#include <stdio.h> // TODO remove all these includes, define all function signatures and types manually
#include <stdlib.h>
#include <float.h>
//#include "fns.h"
#include <signal.h>

View File

@ -52,6 +52,7 @@ fn (p mut Parser) hash() ast.HashStmt {
}
return ast.HashStmt{
val: val
mod: p.mod
}
}

View File

@ -7,6 +7,7 @@ import v.ast
import v.table
import v.scanner
import v.token
import v.util
pub fn (p mut Parser) call_expr(is_c bool, is_js bool, mod string) ast.CallExpr {
first_pos := p.tok.position()
@ -227,8 +228,7 @@ fn (p mut Parser) fn_decl() ast.FnDecl {
is_js: is_js
no_body: no_body
pos: pos
is_builtin: p.builtin_mod || p.mod in ['math', 'strconv', 'strconv.ftoa', 'hash.wyhash',
'math.bits', 'strings']
is_builtin: p.builtin_mod || p.mod in util.builtin_module_parts
}
}

View File

@ -12,6 +12,10 @@ pub const (
v_version = '0.1.26'
)
pub const (
builtin_module_parts = ['math.bits' /* needed by strconv.ftoa */, 'strconv', 'strconv.ftoa', 'hash.wyhash', 'strings']
)
// vhash() returns the build string C.V_COMMIT_HASH . See cmd/tools/gen_vc.v .
pub fn vhash() string {
mut buf := [50]byte