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`
// 1. `f<[]` is generic(e.g. `f<[]int>`) because `var < []` is invalid
// 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
// 4. `f<Foo,` is generic when Foo is typename.
// 3. `f<foo>` is generic because `v1 < foo > v2` is invalid syntax
// 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`).
// 5. `f<mod.Foo>` is same as case 3
// 6. `f<mod.Foo,` is same as case 4
// 7. if there is a &, ignore the & and see if it is a type
// 10. otherwise, it's not generic
// 6. `f<mod.Foo>` is same as case 3
// 7. `f<mod.Foo,` is same as case 5
// 8. if there is a &, ignore the & and see if it is a type
// 9. otherwise, it's not generic
// see also test_generic_detection in vlib/v/tests/generics_test.v
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()
@ -1911,9 +1912,10 @@ fn (p &Parser) is_generic_call() bool {
return true
}
return match kind3 {
.gt, .lt { true } // case 3
.comma { p.is_typename(tok2) } // case 4
// case 5 and 6
.gt { true } // case 3
.lt { !(tok4.lit.len == 1 && tok4.lit[0].is_capital()) } // case 4
.comma { p.is_typename(tok2) } // case 5
// case 6 and 7
.dot { kind4 == .name && (kind5 == .gt || (kind5 == .comma && p.is_typename(tok4))) }
else { false }
}

View File

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

View File

@ -68,6 +68,7 @@ fn test_postfix_expr() {
assert minus_one(i16(-7)) == -8
assert minus_one(int(-6)) == -7
assert minus_one(i64(-5)) == -6
// 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
// but it's not important
@ -370,6 +371,7 @@ fn test_generic_fn_with_variadics() {
s := 'abc'
i := 1
abc := Abc{1, 2, 3}
// these calls should all compile, and print the arguments,
// even though the arguments are all a different type and arity:
p(s)
@ -457,18 +459,28 @@ fn test_generic_init() {
a << 'a'
assert a.len == 1
assert a[0] == 'a'
// map init
mut b := new<map[string]string>()
assert b.len == 0
b['b'] = 'b'
assert b.len == 1
assert b['b'] == 'b'
// struct init
mut c := new<User>()
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() {
v1, v2 := -1, 1
@ -482,9 +494,11 @@ fn test_generic_detection() {
assert multi_generic_args<int, string>(0, 's')
assert multi_generic_args<Foo1, Foo2>(Foo1{}, Foo2{})
assert multi_generic_args<Foo<int>, Foo<int> >(Foo<int>{}, Foo<int>{})
// 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<int, simplemodule.Data>(0, simplemodule.Data{})
assert multi_generic_args<[]int, int>([]int{}, 0)
assert multi_generic_args<map[int]int, int>(map[int]int{}, 0)
assert 0 < return_one<int>(10, 0)
}