From a640ddc102172f8bbac241ff4369b94b2b9ddde0 Mon Sep 17 00:00:00 2001 From: Felix Ehlers Date: Sat, 27 Dec 2025 12:52:21 +0100 Subject: [PATCH] checker: detect circular type references in sumtype declarations (fix #24511) (#26162) --- vlib/v/checker/checker.v | 33 +++++++++++++++++++ .../tests/sumtype_circular_reference_err.out | 11 +++++++ .../tests/sumtype_circular_reference_err.vv | 6 ++++ 3 files changed, 50 insertions(+) create mode 100644 vlib/v/checker/tests/sumtype_circular_reference_err.out create mode 100644 vlib/v/checker/tests/sumtype_circular_reference_err.vv diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index d25140824..5b5940b01 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -871,11 +871,44 @@ and use a reference to the sum type instead: `var := &${node.name}(${variant_nam if sym.name.trim_string_left(sym.mod + '.') == node.name { c.error('sum type cannot hold itself', variant.pos) + } else if sym.kind == .sum_type && sym.info is ast.SumType { + // Check for circular references through other sum types + mut visited := map[int]bool{} + visited[node.typ.idx()] = true + if c.sumtype_has_circular_ref(variant.typ, node.typ, mut visited) { + c.error('sum type `${node.name}` cannot be defined recursively', variant.pos) + } } names_used << variant_name } } +// Checks if the sum type `sum_typ` contains `target_typ` in its variants (directly or indirectly through other sum types) +fn (mut c Checker) sumtype_has_circular_ref(sum_typ ast.Type, target_typ ast.Type, mut visited map[int]bool) bool { + sum_sym := c.table.sym(sum_typ) + if sum_sym.kind != .sum_type || sum_sym.info !is ast.SumType { + return false + } + sum_info := sum_sym.info as ast.SumType + for variant in sum_info.variants { + if variant.idx() == target_typ.idx() { + return true + } + // Avoid infinite recursion by tracking visited types + if variant.idx() in visited { + continue + } + variant_sym := c.table.sym(variant) + if variant_sym.kind == .sum_type { + visited[variant.idx()] = true + if c.sumtype_has_circular_ref(variant, target_typ, mut visited) { + return true + } + } + } + return false +} + fn (mut c Checker) expand_iface_embeds(idecl &ast.InterfaceDecl, level int, iface_embeds []ast.InterfaceEmbedding) []ast.InterfaceEmbedding { // eprintln('> expand_iface_embeds: idecl.name: $idecl.name | level: $level | iface_embeds.len: $iface_embeds.len') if level > iface_level_cutoff_limit { diff --git a/vlib/v/checker/tests/sumtype_circular_reference_err.out b/vlib/v/checker/tests/sumtype_circular_reference_err.out new file mode 100644 index 000000000..cde78080f --- /dev/null +++ b/vlib/v/checker/tests/sumtype_circular_reference_err.out @@ -0,0 +1,11 @@ +vlib/v/checker/tests/sumtype_circular_reference_err.vv:1:14: error: sum type `Type6` cannot be defined recursively + 1 | type Type6 = Type7 | int + | ~~~~~ + 2 | type Type7 = Type6 | string + 3 | +vlib/v/checker/tests/sumtype_circular_reference_err.vv:2:14: error: sum type `Type7` cannot be defined recursively + 1 | type Type6 = Type7 | int + 2 | type Type7 = Type6 | string + | ~~~~~ + 3 | + 4 | struct Foo { diff --git a/vlib/v/checker/tests/sumtype_circular_reference_err.vv b/vlib/v/checker/tests/sumtype_circular_reference_err.vv new file mode 100644 index 000000000..a3931412a --- /dev/null +++ b/vlib/v/checker/tests/sumtype_circular_reference_err.vv @@ -0,0 +1,6 @@ +type Type6 = Type7 | int +type Type7 = Type6 | string + +struct Foo { + name Type7 +} -- 2.39.5