parser: error on recursive struct (#7964)

pull/7748/head^2
Daniel Däschle 2021-01-09 01:33:36 +01:00 committed by GitHub
parent 141b0cb882
commit bbac95a438
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 55 additions and 13 deletions

View File

@ -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)

View File

@ -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')

View File

@ -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)

View File

@ -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 | }

View File

@ -0,0 +1,3 @@
struct Human {
child Human
}

View File

@ -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 | }

View File

@ -0,0 +1,7 @@
struct Child {
be Human
}
struct Human {
child Child
}

View File

@ -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
}