parser: fix generic detection of `foo < bar<T>()` (#11434)

pull/11441/head
Ruofan XU 2021-09-08 10:54:15 +08:00 committed by GitHub
parent cc8ee5fb84
commit 892971024e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 26 additions and 10 deletions

View File

@ -1871,13 +1871,14 @@ fn (p &Parser) is_typename(t token.Token) bool {
// heuristics to detect `func<T>()` from `var < expr` // heuristics to detect `func<T>()` from `var < expr`
// 1. `f<[]` is generic(e.g. `f<[]int>`) because `var < []` is invalid // 1. `f<[]` is generic(e.g. `f<[]int>`) because `var < []` is invalid
// 2. `f<map[` is generic(e.g. `f<map[string]string>) // 2. `f<map[` is generic(e.g. `f<map[string]string>)
// 3. `f<foo>` and `f<foo<` are generic because `v1 < foo > v2` and `v1 < foo < v2` are invalid syntax // 3. `f<foo>` is generic because `v1 < foo > v2` is invalid syntax
// 4. `f<Foo,` is generic when Foo is typename. // 4. `f<foo<bar` is generic when bar is not generic T (f<foo<T>(), in contrast, is not generic!)
// 5. `f<Foo,` is generic when Foo is typename.
// otherwise it is not generic because it may be multi-value (e.g. `return f < foo, 0`). // otherwise it is not generic because it may be multi-value (e.g. `return f < foo, 0`).
// 5. `f<mod.Foo>` is same as case 3 // 6. `f<mod.Foo>` is same as case 3
// 6. `f<mod.Foo,` is same as case 4 // 7. `f<mod.Foo,` is same as case 5
// 7. if there is a &, ignore the & and see if it is a type // 8. if there is a &, ignore the & and see if it is a type
// 10. otherwise, it's not generic // 9. otherwise, it's not generic
// see also test_generic_detection in vlib/v/tests/generics_test.v // see also test_generic_detection in vlib/v/tests/generics_test.v
fn (p &Parser) is_generic_call() bool { fn (p &Parser) is_generic_call() bool {
lit0_is_capital := p.tok.kind != .eof && p.tok.lit.len > 0 && p.tok.lit[0].is_capital() lit0_is_capital := p.tok.kind != .eof && p.tok.lit.len > 0 && p.tok.lit[0].is_capital()
@ -1911,9 +1912,10 @@ fn (p &Parser) is_generic_call() bool {
return true return true
} }
return match kind3 { return match kind3 {
.gt, .lt { true } // case 3 .gt { true } // case 3
.comma { p.is_typename(tok2) } // case 4 .lt { !(tok4.lit.len == 1 && tok4.lit[0].is_capital()) } // case 4
// case 5 and 6 .comma { p.is_typename(tok2) } // case 5
// case 6 and 7
.dot { kind4 == .name && (kind5 == .gt || (kind5 == .comma && p.is_typename(tok4))) } .dot { kind4 == .name && (kind5 == .gt || (kind5 == .comma && p.is_typename(tok4))) }
else { false } else { false }
} }

View File

@ -65,7 +65,7 @@ fn min<T>(tree Tree<T>) T {
1e100 1e100
} }
Node<T> { Node<T> {
if tree.value < (min<T>(tree.left)) { if tree.value < min<T>(tree.left) {
tree.value tree.value
} else { } else {
min<T>(tree.left) min<T>(tree.left)

View File

@ -68,6 +68,7 @@ fn test_postfix_expr() {
assert minus_one(i16(-7)) == -8 assert minus_one(i16(-7)) == -8
assert minus_one(int(-6)) == -7 assert minus_one(int(-6)) == -7
assert minus_one(i64(-5)) == -6 assert minus_one(i64(-5)) == -6
// the point is to see if it compiles, more than if the result // the point is to see if it compiles, more than if the result
// is correct, so 1e-6 isn't necessarily the right value to do this // is correct, so 1e-6 isn't necessarily the right value to do this
// but it's not important // but it's not important
@ -370,6 +371,7 @@ fn test_generic_fn_with_variadics() {
s := 'abc' s := 'abc'
i := 1 i := 1
abc := Abc{1, 2, 3} abc := Abc{1, 2, 3}
// these calls should all compile, and print the arguments, // these calls should all compile, and print the arguments,
// even though the arguments are all a different type and arity: // even though the arguments are all a different type and arity:
p(s) p(s)
@ -457,18 +459,28 @@ fn test_generic_init() {
a << 'a' a << 'a'
assert a.len == 1 assert a.len == 1
assert a[0] == 'a' assert a[0] == 'a'
// map init // map init
mut b := new<map[string]string>() mut b := new<map[string]string>()
assert b.len == 0 assert b.len == 0
b['b'] = 'b' b['b'] = 'b'
assert b.len == 1 assert b.len == 1
assert b['b'] == 'b' assert b['b'] == 'b'
// struct init // struct init
mut c := new<User>() mut c := new<User>()
c.name = 'c' c.name = 'c'
assert c.name == 'c' assert c.name == 'c'
} }
fn return_one<T>(rec int, useless T) T {
// foo < bar<T>() should work
if rec == 0 || 0 < return_one<T>(rec - 1, useless) {
return T(1)
}
return T(0)
}
fn test_generic_detection() { fn test_generic_detection() {
v1, v2 := -1, 1 v1, v2 := -1, 1
@ -482,9 +494,11 @@ fn test_generic_detection() {
assert multi_generic_args<int, string>(0, 's') assert multi_generic_args<int, string>(0, 's')
assert multi_generic_args<Foo1, Foo2>(Foo1{}, Foo2{}) assert multi_generic_args<Foo1, Foo2>(Foo1{}, Foo2{})
assert multi_generic_args<Foo<int>, Foo<int> >(Foo<int>{}, Foo<int>{}) assert multi_generic_args<Foo<int>, Foo<int> >(Foo<int>{}, Foo<int>{})
// TODO: assert multi_generic_args<Foo<int>, Foo<int>>(Foo1{}, Foo2{}) // TODO: assert multi_generic_args<Foo<int>, Foo<int>>(Foo1{}, Foo2{})
assert multi_generic_args<simplemodule.Data, int>(simplemodule.Data{}, 0) assert multi_generic_args<simplemodule.Data, int>(simplemodule.Data{}, 0)
assert multi_generic_args<int, simplemodule.Data>(0, simplemodule.Data{}) assert multi_generic_args<int, simplemodule.Data>(0, simplemodule.Data{})
assert multi_generic_args<[]int, int>([]int{}, 0) assert multi_generic_args<[]int, int>([]int{}, 0)
assert multi_generic_args<map[int]int, int>(map[int]int{}, 0) assert multi_generic_args<map[int]int, int>(map[int]int{}, 0)
assert 0 < return_one<int>(10, 0)
} }