parser: check generic parameters error of generic struct declaration (#12418)

pull/12422/head
yuyi 2021-11-09 15:25:57 +08:00 committed by GitHub
parent 3f841edec1
commit 8f4180ea09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 84 additions and 58 deletions

View File

@ -38,7 +38,7 @@ pub fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr
if p.tok.kind == .lt {
// `foo<int>(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>()`
// T is unwrapped and registered in the checker.
@ -313,7 +313,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
}
}
// <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
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)

View File

@ -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<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())
// In case of `foo<T>()`
// 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()

View File

@ -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()

View File

@ -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 | }

View File

@ -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"))
}