From 035fd052d1f40079991110d5af1c2eca1769d399 Mon Sep 17 00:00:00 2001
From: yuyi <yuyi98@163.com>
Date: Tue, 4 May 2021 00:47:02 +0800
Subject: [PATCH] checker: check generic struct declaration (fix #9974) (#9978)

---
 vlib/v/checker/checker.v                      | 11 +++++++++
 .../tests/generics_struct_declaration_err.out |  7 ++++++
 .../tests/generics_struct_declaration_err.vv  | 24 +++++++++++++++++++
 3 files changed, 42 insertions(+)
 create mode 100644 vlib/v/checker/tests/generics_struct_declaration_err.out
 create mode 100644 vlib/v/checker/tests/generics_struct_declaration_err.vv

diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v
index 3b569657f5..02d52d7078 100644
--- a/vlib/v/checker/checker.v
+++ b/vlib/v/checker/checker.v
@@ -522,8 +522,12 @@ pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) {
 		c.check_valid_pascal_case(decl.name, 'struct name', decl.pos)
 	}
 	mut struct_sym := c.table.find_type(decl.name) or { ast.TypeSymbol{} }
+	mut has_generic_types := false
 	if mut struct_sym.info is ast.Struct {
 		for embed in decl.embeds {
+			if embed.typ.has_flag(.generic) {
+				has_generic_types = true
+			}
 			embed_sym := c.table.get_type_symbol(embed.typ)
 			if embed_sym.kind != .struct_ {
 				c.error('`$embed_sym.name` is not a struct', embed.pos)
@@ -541,6 +545,9 @@ pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) {
 		}
 		for i, field in decl.fields {
 			c.ensure_type_exists(field.typ, field.type_pos) or { return }
+			if field.typ.has_flag(.generic) {
+				has_generic_types = true
+			}
 			if decl.language == .v {
 				c.check_valid_snake_case(field.name, 'field name', field.pos)
 			}
@@ -592,6 +599,10 @@ pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) {
 				}
 			}
 		}
+		if decl.generic_types.len == 0 && has_generic_types {
+			c.error('generic struct declaration must specify the generic type names, e.g. Foo<T>',
+				decl.pos)
+		}
 	}
 }
 
diff --git a/vlib/v/checker/tests/generics_struct_declaration_err.out b/vlib/v/checker/tests/generics_struct_declaration_err.out
new file mode 100644
index 0000000000..bd3b2c3476
--- /dev/null
+++ b/vlib/v/checker/tests/generics_struct_declaration_err.out
@@ -0,0 +1,7 @@
+vlib/v/checker/tests/generics_struct_declaration_err.vv:5:1: error: generic struct declaration must specify the generic type names, e.g. Foo<T>
+    3 | }
+    4 |
+    5 | struct MyGenericChannelStruct {
+      | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    6 |     GenericChannelStruct<T>
+    7 |     msg string
diff --git a/vlib/v/checker/tests/generics_struct_declaration_err.vv b/vlib/v/checker/tests/generics_struct_declaration_err.vv
new file mode 100644
index 0000000000..4828ea315f
--- /dev/null
+++ b/vlib/v/checker/tests/generics_struct_declaration_err.vv
@@ -0,0 +1,24 @@
+struct GenericChannelStruct<T> {
+	ch chan T
+}
+
+struct MyGenericChannelStruct {
+	GenericChannelStruct<T>
+	msg string
+}
+
+struct Simple {
+	msg string
+}
+
+fn main() {
+	new_channel_struct<Simple>()
+}
+
+pub fn new_channel_struct<T>() GenericChannelStruct<T> {
+	d := GenericChannelStruct{
+		ch: chan T{}
+	}
+
+	return d
+}