diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 274dcdd99c..c3dca795da 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -374,7 +374,7 @@ pub fn (mut c Checker) interface_decl(decl ast.InterfaceDecl) { } } -pub fn (mut c Checker) struct_decl(decl ast.StructDecl) { +pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) { if decl.language == .v && !c.is_builtin_mod { c.check_valid_pascal_case(decl.name, 'struct name', decl.pos) } @@ -2962,7 +2962,7 @@ fn (mut c Checker) stmt(node ast.Stmt) { c.sql_stmt(mut node) } ast.StructDecl { - c.struct_decl(node) + c.struct_decl(mut node) } ast.TypeDecl { c.type_decl(node) diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index ffa766bddc..44dc7a0487 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -4927,6 +4927,8 @@ fn (g &Gen) sort_structs(typesa []table.TypeSymbol) []table.TypeSymbol { // sort graph dep_graph_sorted := dep_graph.resolve() if !dep_graph_sorted.acyclic { + // this should no longer be called since it's catched in the parser + // TODO: should it be removed? verror('cgen.sort_structs(): the following structs form a dependency cycle:\n' + dep_graph_sorted.display_cycles() + '\nyou can solve this by making one or both of the dependant struct fields references, eg: field &MyStruct' + '\nif you feel this is an error, please create a new issue here: https://github.com/vlang/v/issues and tag @joe-conigliaro') diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index 5341dd0d24..28fb9b6b92 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -74,7 +74,16 @@ fn (mut p Parser) struct_decl() ast.StructDecl { p.error_with_pos('struct names must have more than one character', name_pos) return ast.StructDecl{} } - // println('struct decl $name') + mut orig_name := name + if language == .c { + name = 'C.$name' + orig_name = name + } else if language == .js { + name = 'JS.$name' + orig_name = name + } else { + name = p.prepend_mod(name) + } mut ast_fields := []ast.StructField{} mut fields := []table.Field{} mut embed_types := []table.Type{} @@ -213,7 +222,6 @@ fn (mut p Parser) struct_decl() ast.StructDecl { type_pos = p.prev_tok.position() field_pos = field_start_pos.extend(type_pos) } - // println(p.tok.position()) // Comments after type (same line) comments << p.eat_comments() if p.tok.kind == .lsbr { @@ -226,8 +234,6 @@ fn (mut p Parser) struct_decl() ast.StructDecl { if p.tok.kind == .assign { // Default value p.next() - // default_expr = p.tok.lit - // p.expr(0) default_expr = p.expr(0) match mut default_expr { ast.EnumVal { default_expr.typ = typ } @@ -275,13 +281,6 @@ fn (mut p Parser) struct_decl() ast.StructDecl { last_line = p.tok.line_nr p.check(.rcbr) } - if language == .c { - name = 'C.$name' - } else if language == .js { - name = 'JS.$name' - } else { - name = p.prepend_mod(name) - } t := table.TypeSymbol{ kind: .struct_ language: language @@ -299,6 +298,10 @@ fn (mut p Parser) struct_decl() ast.StructDecl { } is_public: is_pub } + if p.table.has_deep_child_no_ref(&t, name) { + p.error_with_pos('invalid recursive struct `$orig_name`', name_pos) + return ast.StructDecl{} + } mut ret := 0 // println('reg type symbol $name mod=$p.mod') ret = p.table.register_type_symbol(t) diff --git a/vlib/v/parser/tests/invalid_recursive_struct1_err.out b/vlib/v/parser/tests/invalid_recursive_struct1_err.out new file mode 100644 index 0000000000..8cac75806f --- /dev/null +++ b/vlib/v/parser/tests/invalid_recursive_struct1_err.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/invalid_recursive_struct1_err.vv:1:8: error: invalid recursive struct `Human` + 1 | struct Human { + | ~~~~~ + 2 | child Human + 3 | } \ No newline at end of file diff --git a/vlib/v/parser/tests/invalid_recursive_struct1_err.vv b/vlib/v/parser/tests/invalid_recursive_struct1_err.vv new file mode 100644 index 0000000000..649ca81a80 --- /dev/null +++ b/vlib/v/parser/tests/invalid_recursive_struct1_err.vv @@ -0,0 +1,3 @@ +struct Human { + child Human +} diff --git a/vlib/v/parser/tests/invalid_recursive_struct2_err.out b/vlib/v/parser/tests/invalid_recursive_struct2_err.out new file mode 100644 index 0000000000..d784e50305 --- /dev/null +++ b/vlib/v/parser/tests/invalid_recursive_struct2_err.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/invalid_recursive_struct2_err.vv:5:8: error: invalid recursive struct `Human` + 3 | } + 4 | + 5 | struct Human { + | ~~~~~ + 6 | child Child + 7 | } \ No newline at end of file diff --git a/vlib/v/parser/tests/invalid_recursive_struct2_err.vv b/vlib/v/parser/tests/invalid_recursive_struct2_err.vv new file mode 100644 index 0000000000..0088c8eb64 --- /dev/null +++ b/vlib/v/parser/tests/invalid_recursive_struct2_err.vv @@ -0,0 +1,7 @@ +struct Child { + be Human +} + +struct Human { + child Child +} diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index 0938f5c93c..c997ef2fe7 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -740,3 +740,18 @@ pub fn (table &Table) known_type_names() []string { } return res } + +// has_deep_child_no_ref returns true if type is struct and has any child or nested child with the type of the given name +// the given name consists of module and name (`mod.Name`) +// it doesn't care about childs that are references +pub fn (table &Table) has_deep_child_no_ref(ts &TypeSymbol, name string) bool { + if ts.info is Struct { + for _, field in ts.info.fields { + sym := table.get_type_symbol(field.typ) + if !field.typ.is_ptr() && (sym.name == name || table.has_deep_child_no_ref(sym, name)) { + return true + } + } + } + return false +}