diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index a6b1d9d1a5..7edab448a1 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -38,7 +38,7 @@ pub fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr if p.tok.kind == .lt { // `foo(10)` p.expr_mod = '' - concrete_types = p.parse_generic_type_list() + concrete_types = p.parse_concrete_types() concrete_list_pos = concrete_list_pos.extend(p.prev_tok.position()) // In case of `foo()` // T is unwrapped and registered in the checker. @@ -313,7 +313,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl { } } // - mut generic_names := p.parse_generic_names() + _, mut generic_names := p.parse_generic_types() // generic names can be infer with receiver's generic names if is_method && rec.typ.has_flag(.generic) { sym := p.table.get_type_symbol(rec.typ) @@ -608,57 +608,6 @@ fn (mut p Parser) fn_receiver(mut params []ast.Param, mut rec ReceiverParsingInf return } -fn (mut p Parser) parse_generic_names() []string { - mut param_names := []string{} - if p.tok.kind != .lt { - return param_names - } - p.check(.lt) - mut first_done := false - mut count := 0 - for p.tok.kind !in [.gt, .eof] { - if first_done { - p.check(.comma) - } - name := p.tok.lit - if name.len > 0 && !name[0].is_capital() { - p.error('generic parameter needs to be uppercase') - } - if name.len > 1 { - p.error('generic parameter name needs to be exactly one char') - } - if !util.is_generic_type_name(p.tok.lit) { - p.error('`$p.tok.lit` is a reserved name and cannot be used for generics') - } - if name in param_names { - p.error('duplicated generic parameter `$name`') - } - if count > 8 { - p.error('cannot have more than 9 generic parameters') - } - p.check(.name) - param_names << name - if p.table.find_type_idx(name) == 0 { - p.table.register_type_symbol(ast.TypeSymbol{ - name: name - cname: util.no_dots(name) - mod: p.mod - kind: .any - is_public: true - }) - } - first_done = true - count++ - } - p.check(.gt) - return param_names -} - -// is_generic_name returns true if the current token is a generic name. -fn (p Parser) is_generic_name() bool { - return p.tok.kind == .name && util.is_generic_type_name(p.tok.lit) -} - fn (mut p Parser) anon_fn() ast.AnonFn { pos := p.tok.position() p.check(.key_fn) diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 788d7b8ca1..d36243c20c 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -2440,7 +2440,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { mut concrete_list_pos := p.tok.position() if is_generic_call { // `g.foo(10)` - concrete_types = p.parse_generic_type_list() + concrete_types = p.parse_concrete_types() concrete_list_pos = concrete_list_pos.extend(p.prev_tok.position()) // In case of `foo()` // T is unwrapped and registered in the checker. @@ -2533,7 +2533,57 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { return sel_expr } -fn (mut p Parser) parse_generic_type_list() []ast.Type { +fn (mut p Parser) parse_generic_types() ([]ast.Type, []string) { + mut types := []ast.Type{} + mut param_names := []string{} + if p.tok.kind != .lt { + return types, param_names + } + p.check(.lt) + mut first_done := false + mut count := 0 + for p.tok.kind !in [.gt, .eof] { + if first_done { + p.check(.comma) + } + name := p.tok.lit + if name.len > 0 && !name[0].is_capital() { + p.error('generic parameter needs to be uppercase') + } + if name.len > 1 { + p.error('generic parameter name needs to be exactly one char') + } + if !util.is_generic_type_name(p.tok.lit) { + p.error('`$p.tok.lit` is a reserved name and cannot be used for generics') + } + if name in param_names { + p.error('duplicated generic parameter `$name`') + } + if count > 8 { + p.error('cannot have more than 9 generic parameters') + } + p.check(.name) + param_names << name + + mut idx := p.table.find_type_idx(name) + if idx == 0 { + idx = p.table.register_type_symbol(ast.TypeSymbol{ + name: name + cname: util.no_dots(name) + mod: p.mod + kind: .any + is_public: true + }) + } + types << ast.new_type(idx).set_flag(.generic) + first_done = true + count++ + } + p.check(.gt) + return types, param_names +} + +fn (mut p Parser) parse_concrete_types() []ast.Type { mut types := []ast.Type{} if p.tok.kind != .lt { return types @@ -2551,6 +2601,11 @@ fn (mut p Parser) parse_generic_type_list() []ast.Type { return types } +// is_generic_name returns true if the current token is a generic name. +fn (p Parser) is_generic_name() bool { + return p.tok.kind == .name && util.is_generic_type_name(p.tok.lit) +} + // `.green` // `pref.BuildMode.default_mode` fn (mut p Parser) enum_val() ast.EnumVal { @@ -3238,7 +3293,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl { return ast.AliasTypeDecl{} } mut sum_variants := []ast.TypeNode{} - generic_types := p.parse_generic_type_list() + generic_types, _ := p.parse_generic_types() decl_pos_with_generics := decl_pos.extend(p.prev_tok.position()) p.check(.assign) mut type_pos := p.tok.position() diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index 35a83c611a..d4b7105f4f 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -47,7 +47,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl { name_pos) return ast.StructDecl{} } - generic_types := p.parse_generic_type_list() + generic_types, _ := p.parse_generic_types() no_body := p.tok.kind != .lcbr if language == .v && no_body { p.error('`$p.tok.lit` lacks body') @@ -451,7 +451,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { p.check_for_impure_v(language, name_pos) modless_name := p.check_name() interface_name := p.prepend_mod(modless_name).clone() - generic_types := p.parse_generic_type_list() + generic_types, _ := p.parse_generic_types() // println('interface decl $interface_name') p.check(.lcbr) pre_comments := p.eat_comments() diff --git a/vlib/v/parser/tests/generic_struct_type_decl_err.out b/vlib/v/parser/tests/generic_struct_type_decl_err.out new file mode 100644 index 0000000000..dd9f09c4a1 --- /dev/null +++ b/vlib/v/parser/tests/generic_struct_type_decl_err.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/generic_struct_type_decl_err.vv:1:13: error: generic parameter name needs to be exactly one char + 1 | struct GMap, V> { + | ~~~~~~~~ + 2 | map_ map[int]V + 3 | } diff --git a/vlib/v/parser/tests/generic_struct_type_decl_err.vv b/vlib/v/parser/tests/generic_struct_type_decl_err.vv new file mode 100644 index 0000000000..79f5f92f48 --- /dev/null +++ b/vlib/v/parser/tests/generic_struct_type_decl_err.vv @@ -0,0 +1,17 @@ +struct GMap, V> { + map_ map[int]V +} + +fn (t GMap, V>) contains(k K) bool { + return true // TODO +} + +fn (t GMap, V>) set(k K, v V) { + // TODO +} + +fn main() { + x := GMap{} + x.set("hello", "world") + println(x.contains("hello")) +}