From 45b7a0e6e2c9d12405819fdde486750101e13ccb Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Sat, 2 Nov 2024 05:53:08 -0300 Subject: [PATCH] checker, cgen: fix comptime `T.methods` with generic types and interface checking with `is` operator (fix #22721) (#22724) --- vlib/v/ast/table.v | 12 +++ vlib/v/ast/types.v | 25 ++++++ vlib/v/checker/comptime.v | 8 +- vlib/v/gen/c/comptime.v | 4 +- .../comptime_methods_generic_struct_test.v | 80 +++++++++++++++++++ 5 files changed, 122 insertions(+), 7 deletions(-) create mode 100644 vlib/v/tests/comptime/comptime_methods_generic_struct_test.v diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index fdf08d2e8..f45f55896 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1564,6 +1564,18 @@ pub fn (t &Table) does_type_implement_interface(typ Type, inter_typ Type) bool { } continue } + match sym.info { + SumType, Struct, Interface { + if method := sym.find_method_with_generic_parent(imethod.name) { + msg := t.is_same_method(imethod, method) + if msg.len > 0 { + return false + } + continue + } + } + else {} + } return false } // verify fields diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index a780a1c61..f6ed74087 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -1942,3 +1942,28 @@ pub fn (i Interface) get_methods() []string { } return [] } + +pub fn (t &TypeSymbol) get_methods() []Fn { + mut methods := t.methods.filter(it.attrs.len == 0) // methods without attrs first + mut methods_with_attrs := t.methods.filter(it.attrs.len > 0) // methods with attrs second + match t.info { + Struct, Interface, SumType { + if t.info.parent_type.has_flag(.generic) { + parent_sym := global_table.sym(t.info.parent_type) + match parent_sym.info { + Struct, Interface, SumType { + parent_methods := parent_sym.methods + if parent_methods.len > 0 { + methods << parent_methods.filter(it.attrs.len == 0) + methods_with_attrs << parent_methods.filter(it.attrs.len > 0) + } + } + else {} + } + } + } + else {} + } + methods << methods_with_attrs + return methods +} diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index cbbccb886..986d54d22 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -311,10 +311,7 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) { return } } else if node.kind == .methods { - mut methods := sym.methods.filter(it.attrs.len == 0) // methods without attrs first - methods_with_attrs := sym.methods.filter(it.attrs.len > 0) // methods with attrs second - methods << methods_with_attrs - + methods := sym.get_methods() for method in methods { c.push_new_comptime_info() c.comptime.inside_comptime_for = true @@ -774,6 +771,9 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr, pos token.Pos) ComptimeBr sym := c.table.sym(cond.right.typ) if sym.kind != .interface { c.expr(mut cond.left) + } else { + return c.check_compatible_types((cond.left as ast.TypeNode).typ, + cond.right) } return .unknown } else if cond.left is ast.TypeNode && mut cond.right is ast.ComptimeType { diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index ac7bcf99c..b22057bd5 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -825,9 +825,7 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) { mut i := 0 if node.kind == .methods { - mut methods := sym.methods.filter(it.attrs.len == 0) // methods without attrs first - methods_with_attrs := sym.methods.filter(it.attrs.len > 0) // methods with attrs second - methods << methods_with_attrs + methods := sym.get_methods() if methods.len > 0 { g.writeln('FunctionData ${node.val_var} = {0};') } diff --git a/vlib/v/tests/comptime/comptime_methods_generic_struct_test.v b/vlib/v/tests/comptime/comptime_methods_generic_struct_test.v new file mode 100644 index 000000000..1cc64c27c --- /dev/null +++ b/vlib/v/tests/comptime/comptime_methods_generic_struct_test.v @@ -0,0 +1,80 @@ +interface El { + tag() string + content() ![]u8 +} + +struct Aa { + a string +} + +fn (a Aa) tag() string { + return 'a' +} + +fn (a Aa) content() ![]u8 { + return a.a.bytes() +} + +struct Bb { + b string +} + +fn (b Bb) tag() string { + return 'b' +} + +fn (b Bb) content() ![]u8 { + return b.b.bytes() +} + +struct Choi[T] implements El { + src []T +} + +fn (c Choi[T]) tag() string { + return 'seq' +} + +fn (c Choi[T]) content() ![]u8 { + mut out := []u8{} + for el in c.src { + out << el.tag().bytes() + out << el.content()! + } + return out +} + +fn is_gelement[T]() int { + mut val := 0 + $for meth in T.methods { + $if meth.name == 'tag' { + val += 1 + } + $if meth.name == 'content' { + val += 1 + } + } + return val +} + +fn is_element[T]() bool { + $if T is El { + return true + } + return false +} + +struct Per { + a Aa + b Bb + c Choi[Aa] + d string +} + +fn test_main() { + assert dump(is_element[Aa]()) == true + assert dump(is_element[Bb]()) == true + assert dump(is_gelement[Bb]()) == 2 + assert dump(is_element[Choi[Aa]]()) == true + assert dump(is_gelement[Choi[Bb]]()) == 2 +} -- 2.39.5