parser: error on recursive struct (#7964)
parent
141b0cb882
commit
bbac95a438
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 | }
|
|
@ -0,0 +1,3 @@
|
|||
struct Human {
|
||||
child Human
|
||||
}
|
|
@ -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 | }
|
|
@ -0,0 +1,7 @@
|
|||
struct Child {
|
||||
be Human
|
||||
}
|
||||
|
||||
struct Human {
|
||||
child Child
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue