parser: check generic parameters error of generic struct declaration (#12418)
parent
3f841edec1
commit
8f4180ea09
|
@ -38,7 +38,7 @@ pub fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr
|
||||||
if p.tok.kind == .lt {
|
if p.tok.kind == .lt {
|
||||||
// `foo<int>(10)`
|
// `foo<int>(10)`
|
||||||
p.expr_mod = ''
|
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())
|
concrete_list_pos = concrete_list_pos.extend(p.prev_tok.position())
|
||||||
// In case of `foo<T>()`
|
// In case of `foo<T>()`
|
||||||
// T is unwrapped and registered in the checker.
|
// T is unwrapped and registered in the checker.
|
||||||
|
@ -313,7 +313,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// <T>
|
// <T>
|
||||||
mut generic_names := p.parse_generic_names()
|
_, mut generic_names := p.parse_generic_types()
|
||||||
// generic names can be infer with receiver's generic names
|
// generic names can be infer with receiver's generic names
|
||||||
if is_method && rec.typ.has_flag(.generic) {
|
if is_method && rec.typ.has_flag(.generic) {
|
||||||
sym := p.table.get_type_symbol(rec.typ)
|
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
|
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 {
|
fn (mut p Parser) anon_fn() ast.AnonFn {
|
||||||
pos := p.tok.position()
|
pos := p.tok.position()
|
||||||
p.check(.key_fn)
|
p.check(.key_fn)
|
||||||
|
|
|
@ -2440,7 +2440,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
|
||||||
mut concrete_list_pos := p.tok.position()
|
mut concrete_list_pos := p.tok.position()
|
||||||
if is_generic_call {
|
if is_generic_call {
|
||||||
// `g.foo<int>(10)`
|
// `g.foo<int>(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())
|
concrete_list_pos = concrete_list_pos.extend(p.prev_tok.position())
|
||||||
// In case of `foo<T>()`
|
// In case of `foo<T>()`
|
||||||
// T is unwrapped and registered in the checker.
|
// 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
|
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{}
|
mut types := []ast.Type{}
|
||||||
if p.tok.kind != .lt {
|
if p.tok.kind != .lt {
|
||||||
return types
|
return types
|
||||||
|
@ -2551,6 +2601,11 @@ fn (mut p Parser) parse_generic_type_list() []ast.Type {
|
||||||
return types
|
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`
|
// `.green`
|
||||||
// `pref.BuildMode.default_mode`
|
// `pref.BuildMode.default_mode`
|
||||||
fn (mut p Parser) enum_val() ast.EnumVal {
|
fn (mut p Parser) enum_val() ast.EnumVal {
|
||||||
|
@ -3238,7 +3293,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
|
||||||
return ast.AliasTypeDecl{}
|
return ast.AliasTypeDecl{}
|
||||||
}
|
}
|
||||||
mut sum_variants := []ast.TypeNode{}
|
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())
|
decl_pos_with_generics := decl_pos.extend(p.prev_tok.position())
|
||||||
p.check(.assign)
|
p.check(.assign)
|
||||||
mut type_pos := p.tok.position()
|
mut type_pos := p.tok.position()
|
||||||
|
|
|
@ -47,7 +47,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
||||||
name_pos)
|
name_pos)
|
||||||
return ast.StructDecl{}
|
return ast.StructDecl{}
|
||||||
}
|
}
|
||||||
generic_types := p.parse_generic_type_list()
|
generic_types, _ := p.parse_generic_types()
|
||||||
no_body := p.tok.kind != .lcbr
|
no_body := p.tok.kind != .lcbr
|
||||||
if language == .v && no_body {
|
if language == .v && no_body {
|
||||||
p.error('`$p.tok.lit` lacks 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)
|
p.check_for_impure_v(language, name_pos)
|
||||||
modless_name := p.check_name()
|
modless_name := p.check_name()
|
||||||
interface_name := p.prepend_mod(modless_name).clone()
|
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')
|
// println('interface decl $interface_name')
|
||||||
p.check(.lcbr)
|
p.check(.lcbr)
|
||||||
pre_comments := p.eat_comments()
|
pre_comments := p.eat_comments()
|
||||||
|
|
|
@ -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<Hashable<K>, V> {
|
||||||
|
| ~~~~~~~~
|
||||||
|
2 | map_ map[int]V
|
||||||
|
3 | }
|
|
@ -0,0 +1,17 @@
|
||||||
|
struct GMap<Hashable<K>, V> {
|
||||||
|
map_ map[int]V
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t GMap<Hashable<K>, V>) contains(k K) bool {
|
||||||
|
return true // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t GMap<Hashable<K>, V>) set(k K, v V) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
x := GMap<string, string>{}
|
||||||
|
x.set("hello", "world")
|
||||||
|
println(x.contains("hello"))
|
||||||
|
}
|
Loading…
Reference in New Issue