diff --git a/cmd/tools/vast/vast.v b/cmd/tools/vast/vast.v index 6019e0fbb7..9482bfa689 100644 --- a/cmd/tools/vast/vast.v +++ b/cmd/tools/vast/vast.v @@ -784,8 +784,9 @@ fn (t Tree) alias_type_decl(node ast.AliasTypeDecl) &Node { mut obj := new_object() obj.add_terse('ast_type', t.string_node('AliasTypeDecl')) obj.add_terse('name', t.string_node(node.name)) - obj.add_terse('is_pub', t.bool_node(node.is_pub)) + obj.add_terse('typ', t.type_node(node.typ)) obj.add_terse('parent_type', t.type_node(node.parent_type)) + obj.add_terse('is_pub', t.bool_node(node.is_pub)) obj.add('comments', t.array_node_comment(node.comments)) obj.add('pos', t.pos(node.pos)) return obj diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 33347fb6ab..70bbc5951a 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -1183,6 +1183,7 @@ pub struct AliasTypeDecl { pub: name string is_pub bool + typ Type parent_type Type pos token.Pos type_pos token.Pos diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 723a066c1e..b12602ac9f 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -435,32 +435,88 @@ fn (mut c Checker) alias_type_decl(node ast.AliasTypeDecl) { c.check_valid_pascal_case(node.name, 'type alias', node.pos) } c.ensure_type_exists(node.parent_type, node.type_pos) or { return } - mut typ_sym := c.table.sym(node.parent_type) - if typ_sym.kind in [.placeholder, .int_literal, .float_literal] { - c.error('unknown type `${typ_sym.name}`', node.type_pos) - } else if typ_sym.kind == .alias { - orig_sym := c.table.sym((typ_sym.info as ast.Alias).parent_type) - c.error('type `${typ_sym.str()}` is an alias, use the original alias type `${orig_sym.name}` instead', - node.type_pos) - } else if typ_sym.kind == .chan { - c.error('aliases of `chan` types are not allowed.', node.type_pos) - } else if typ_sym.kind == .function { - orig_sym := c.table.type_to_str(node.parent_type) - c.error('type `${typ_sym.str()}` is an alias, use the original alias type `${orig_sym}` instead', - node.type_pos) - } else if typ_sym.kind == .struct_ { - if mut typ_sym.info is ast.Struct { - // check if the generic param types have been defined - for ct in typ_sym.info.concrete_types { - ct_sym := c.table.sym(ct) - if ct_sym.kind == .placeholder { - c.error('unknown type `${ct_sym.name}`', node.type_pos) + mut parent_typ_sym := c.table.sym(node.parent_type) + match parent_typ_sym.kind { + .placeholder, .int_literal, .float_literal { + c.error('unknown aliased type `${parent_typ_sym.name}`', node.type_pos) + } + .alias { + orig_sym := c.table.sym((parent_typ_sym.info as ast.Alias).parent_type) + c.error('type `${parent_typ_sym.str()}` is an alias, use the original alias type `${orig_sym.name}` instead', + node.type_pos) + } + .chan { + c.error('aliases of `chan` types are not allowed', node.type_pos) + } + .thread { + c.error('aliases of `thread` types are not allowed', node.type_pos) + } + .multi_return { + c.error('aliases of function multi return types are not allowed', node.type_pos) + } + .void { + c.error('aliases of the void type are not allowed', node.type_pos) + } + .function { + orig_sym := c.table.type_to_str(node.parent_type) + c.error('type `${parent_typ_sym.str()}` is an alias, use the original alias type `${orig_sym}` instead', + node.type_pos) + } + .struct_ { + if mut parent_typ_sym.info is ast.Struct { + // check if the generic param types have been defined + for ct in parent_typ_sym.info.concrete_types { + ct_sym := c.table.sym(ct) + if ct_sym.kind == .placeholder { + c.error('unknown type `${ct_sym.name}`', node.type_pos) + } } } } + .array { + c.check_alias_vs_element_type_of_parent(node, (parent_typ_sym.info as ast.Array).elem_type, + 'array') + } + .array_fixed { + c.check_alias_vs_element_type_of_parent(node, (parent_typ_sym.info as ast.ArrayFixed).elem_type, + 'fixed array') + } + .map { + info := parent_typ_sym.info as ast.Map + c.check_alias_vs_element_type_of_parent(node, info.key_type, 'map key') + c.check_alias_vs_element_type_of_parent(node, info.value_type, 'map value') + } + .sum_type { + // TODO: decide whether the following should be allowed. Note that it currently works, + // while `type Sum = int | Sum` is explicitly disallowed: + // type Sum = int | Alias + // type Alias = Sum + } + // The rest of the parent symbol kinds are also allowed, since they are either primitive types, + // that in turn do not allow recursion, or are abstract enough so that they can not be checked at comptime: + else {} + /* + .voidptr, .byteptr, .charptr {} + .char, .rune, .bool {} + .string, .enum_, .none_, .any {} + .i8, .i16, .int, .i64, .isize {} + .u8, .u16, .u32, .u64, .usize {} + .f32, .f64 {} + .interface_ {} + .generic_inst {} + .aggregate {} + */ } } +fn (mut c Checker) check_alias_vs_element_type_of_parent(node ast.AliasTypeDecl, element_type_of_parent ast.Type, label string) { + if node.typ.idx() != element_type_of_parent.idx() { + return + } + c.error('recursive declarations of aliases are not allowed - the alias `${node.name}` is used in the ${label}', + node.type_pos) +} + fn (mut c Checker) fn_type_decl(node ast.FnTypeDecl) { c.check_valid_pascal_case(node.name, 'fn type', node.pos) typ_sym := c.table.sym(node.typ) @@ -4230,6 +4286,12 @@ fn (mut c Checker) ensure_generic_type_specify_type_names(typ ast.Type, pos toke } sym := c.table.final_sym(typ) + if c.ensure_generic_type_level > 38 { + dump(typ) + dump(sym.kind) + dump(pos) + dump(c.ensure_generic_type_level) + } match sym.kind { .function { fn_info := sym.info as ast.FnType diff --git a/vlib/v/checker/tests/recursive_alias_type_err.out b/vlib/v/checker/tests/recursive_alias_type_err.out new file mode 100644 index 0000000000..af733bcfd1 --- /dev/null +++ b/vlib/v/checker/tests/recursive_alias_type_err.out @@ -0,0 +1,50 @@ +vlib/v/checker/tests/recursive_alias_type_err.vv:1:24: error: recursive declarations of aliases are not allowed - the alias `VTableTypeArray` is used in the array + 1 | type VTableTypeArray = []VTableTypeArray + | ~~~~~~~~~~~~~~~~~ + 2 | type VTableTypeArrayPointer = []&VTableTypeArrayPointer + 3 | +vlib/v/checker/tests/recursive_alias_type_err.vv:2:31: error: recursive declarations of aliases are not allowed - the alias `VTableTypeArrayPointer` is used in the array + 1 | type VTableTypeArray = []VTableTypeArray + 2 | type VTableTypeArrayPointer = []&VTableTypeArrayPointer + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + 3 | + 4 | type VTableTypeArrayFixed = [5]VTableTypeArrayFixed +vlib/v/checker/tests/recursive_alias_type_err.vv:4:29: error: recursive declarations of aliases are not allowed - the alias `VTableTypeArrayFixed` is used in the fixed array + 2 | type VTableTypeArrayPointer = []&VTableTypeArrayPointer + 3 | + 4 | type VTableTypeArrayFixed = [5]VTableTypeArrayFixed + | ~~~~~~~~~~~~~~~~~~~~~~~ + 5 | type VTableTypeArrayFixedPointer = [5]&VTableTypeArrayFixedPointer + 6 | +vlib/v/checker/tests/recursive_alias_type_err.vv:5:36: error: recursive declarations of aliases are not allowed - the alias `VTableTypeArrayFixedPointer` is used in the fixed array + 3 | + 4 | type VTableTypeArrayFixed = [5]VTableTypeArrayFixed + 5 | type VTableTypeArrayFixedPointer = [5]&VTableTypeArrayFixedPointer + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 6 | + 7 | type VTableTypeMap = map[string]VTableTypeMap +vlib/v/checker/tests/recursive_alias_type_err.vv:7:22: error: recursive declarations of aliases are not allowed - the alias `VTableTypeMap` is used in the map value + 5 | type VTableTypeArrayFixedPointer = [5]&VTableTypeArrayFixedPointer + 6 | + 7 | type VTableTypeMap = map[string]VTableTypeMap + | ~~~~~~~~~~~~~~~~~~~~~~~~ + 8 | type VTableTypeMapOfPointer = map[string]&VTableTypeMapOfPointer + 9 | +vlib/v/checker/tests/recursive_alias_type_err.vv:8:31: error: recursive declarations of aliases are not allowed - the alias `VTableTypeMapOfPointer` is used in the map value + 6 | + 7 | type VTableTypeMap = map[string]VTableTypeMap + 8 | type VTableTypeMapOfPointer = map[string]&VTableTypeMapOfPointer + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 9 | + 10 | type VTableTypeMapKey = map[VTableTypeMapKey]int +vlib/v/checker/tests/recursive_alias_type_err.vv:10:25: error: recursive declarations of aliases are not allowed - the alias `VTableTypeMapKey` is used in the map key + 8 | type VTableTypeMapOfPointer = map[string]&VTableTypeMapOfPointer + 9 | + 10 | type VTableTypeMapKey = map[VTableTypeMapKey]int + | ~~~~~~~~~~~~~~~~~~~~~~~~ + 11 | type VTableTypeMapOfPointerKey = map[&VTableTypeMapOfPointerKey]int +vlib/v/checker/tests/recursive_alias_type_err.vv:11:34: error: recursive declarations of aliases are not allowed - the alias `VTableTypeMapOfPointerKey` is used in the map key + 9 | + 10 | type VTableTypeMapKey = map[VTableTypeMapKey]int + 11 | type VTableTypeMapOfPointerKey = map[&VTableTypeMapOfPointerKey]int + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/vlib/v/checker/tests/recursive_alias_type_err.vv b/vlib/v/checker/tests/recursive_alias_type_err.vv new file mode 100644 index 0000000000..97d86662e4 --- /dev/null +++ b/vlib/v/checker/tests/recursive_alias_type_err.vv @@ -0,0 +1,11 @@ +type VTableTypeArray = []VTableTypeArray +type VTableTypeArrayPointer = []&VTableTypeArrayPointer + +type VTableTypeArrayFixed = [5]VTableTypeArrayFixed +type VTableTypeArrayFixedPointer = [5]&VTableTypeArrayFixedPointer + +type VTableTypeMap = map[string]VTableTypeMap +type VTableTypeMapOfPointer = map[string]&VTableTypeMapOfPointer + +type VTableTypeMapKey = map[VTableTypeMapKey]int +type VTableTypeMapOfPointerKey = map[&VTableTypeMapOfPointerKey]int diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 42b757876d..559d6bfbcf 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -4000,6 +4000,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl { return ast.AliasTypeDecl{ name: name is_pub: is_pub + typ: idx parent_type: parent_type type_pos: type_pos.extend(type_end_pos) pos: decl_pos