checker: fix generic structs init (#10134)
							parent
							
								
									906b207e58
								
							
						
					
					
						commit
						492d264d08
					
				| 
						 | 
				
			
			@ -609,6 +609,56 @@ pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (mut c Checker) unwrap_generics_struct_init(struct_type ast.Type) ast.Type {
 | 
			
		||||
	ts := c.table.get_type_symbol(struct_type)
 | 
			
		||||
	if ts.info is ast.Struct {
 | 
			
		||||
		if ts.info.is_generic {
 | 
			
		||||
			mut nrt := '$ts.name<'
 | 
			
		||||
			mut c_nrt := '${ts.name}_T_'
 | 
			
		||||
			for i in 0 .. ts.info.generic_types.len {
 | 
			
		||||
				gts := c.table.get_type_symbol(c.unwrap_generic(ts.info.generic_types[i]))
 | 
			
		||||
				nrt += gts.name
 | 
			
		||||
				c_nrt += gts.name
 | 
			
		||||
				if i != ts.info.generic_types.len - 1 {
 | 
			
		||||
					nrt += ','
 | 
			
		||||
					c_nrt += '_'
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			nrt += '>'
 | 
			
		||||
			idx := c.table.type_idxs[nrt]
 | 
			
		||||
			if idx != 0 && c.table.type_symbols[idx].kind != .placeholder {
 | 
			
		||||
				return ast.new_type(idx).derive(struct_type).clear_flag(.generic)
 | 
			
		||||
			} else {
 | 
			
		||||
				mut fields := ts.info.fields.clone()
 | 
			
		||||
				if ts.info.generic_types.len == c.cur_concrete_types.len {
 | 
			
		||||
					generic_names := ts.info.generic_types.map(c.table.get_type_symbol(it).name)
 | 
			
		||||
					for i in 0 .. fields.len {
 | 
			
		||||
						if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ,
 | 
			
		||||
							generic_names, c.cur_concrete_types, true)
 | 
			
		||||
						{
 | 
			
		||||
							fields[i].typ = t_typ
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					mut info := ts.info
 | 
			
		||||
					info.is_generic = false
 | 
			
		||||
					info.concrete_types = c.cur_concrete_types.clone()
 | 
			
		||||
					info.parent_type = struct_type
 | 
			
		||||
					info.fields = fields
 | 
			
		||||
					stru_idx := c.table.register_type_symbol(ast.TypeSymbol{
 | 
			
		||||
						kind: .struct_
 | 
			
		||||
						name: nrt
 | 
			
		||||
						cname: util.no_dots(c_nrt)
 | 
			
		||||
						mod: c.mod
 | 
			
		||||
						info: info
 | 
			
		||||
					})
 | 
			
		||||
					return ast.new_type(stru_idx).derive(struct_type).clear_flag(.generic)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return struct_type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
 | 
			
		||||
	// typ := c.table.find_type(struct_init.typ.typ.name) or {
 | 
			
		||||
	// c.error('unknown struct: $struct_init.typ.typ.name', struct_init.pos)
 | 
			
		||||
| 
						 | 
				
			
			@ -627,7 +677,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
 | 
			
		|||
			struct_init.typ = c.expected_type
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	utyp := c.unwrap_generic(struct_init.typ)
 | 
			
		||||
	utyp := c.unwrap_generics_struct_init(struct_init.typ)
 | 
			
		||||
	c.ensure_type_exists(utyp, struct_init.pos) or {}
 | 
			
		||||
	type_sym := c.table.get_type_symbol(utyp)
 | 
			
		||||
	if !c.inside_unsafe && type_sym.kind == .sum_type {
 | 
			
		||||
| 
						 | 
				
			
			@ -757,7 +807,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
 | 
			
		|||
				if is_embed {
 | 
			
		||||
					expected_type = embed_type
 | 
			
		||||
					c.expected_type = expected_type
 | 
			
		||||
					expr_type = c.expr(field.expr)
 | 
			
		||||
					expr_type = c.unwrap_generic(c.expr(field.expr))
 | 
			
		||||
					expr_type_sym := c.table.get_type_symbol(expr_type)
 | 
			
		||||
					if expr_type != ast.void_type && expr_type_sym.kind != .placeholder {
 | 
			
		||||
						c.check_expected(expr_type, embed_type) or {
 | 
			
		||||
| 
						 | 
				
			
			@ -772,7 +822,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
 | 
			
		|||
					field_type_sym := c.table.get_type_symbol(info_field.typ)
 | 
			
		||||
					expected_type = info_field.typ
 | 
			
		||||
					c.expected_type = expected_type
 | 
			
		||||
					expr_type = c.expr(field.expr)
 | 
			
		||||
					expr_type = c.unwrap_generic(c.expr(field.expr))
 | 
			
		||||
					if !info_field.typ.has_flag(.optional) {
 | 
			
		||||
						expr_type = c.check_expr_opt_call(field.expr, expr_type)
 | 
			
		||||
					}
 | 
			
		||||
| 
						 | 
				
			
			@ -882,7 +932,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
 | 
			
		|||
			c.error('expression is not an lvalue', struct_init.update_expr.position())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return struct_init.typ
 | 
			
		||||
	return utyp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (mut c Checker) check_div_mod_by_zero(expr ast.Expr, op_kind token.Kind) {
 | 
			
		||||
| 
						 | 
				
			
			@ -2930,7 +2980,14 @@ pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) ast.Typ
 | 
			
		|||
// TODO: non deferred
 | 
			
		||||
pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
 | 
			
		||||
	c.expected_type = c.table.cur_fn.return_type
 | 
			
		||||
	expected_type := c.unwrap_generic(c.expected_type)
 | 
			
		||||
	mut expected_type := c.unwrap_generic(c.expected_type)
 | 
			
		||||
	if expected_type.has_flag(.generic) && c.table.get_type_symbol(expected_type).kind == .struct_ {
 | 
			
		||||
		if t_typ := c.table.resolve_generic_to_concrete(expected_type, c.table.cur_fn.generic_names,
 | 
			
		||||
			c.cur_concrete_types, true)
 | 
			
		||||
		{
 | 
			
		||||
			expected_type = t_typ
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	expected_type_sym := c.table.get_type_symbol(expected_type)
 | 
			
		||||
	if return_stmt.exprs.len > 0 && c.table.cur_fn.return_type == ast.void_type {
 | 
			
		||||
		c.error('unexpected argument, current function does not return anything', return_stmt.exprs[0].position())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +0,0 @@
 | 
			
		|||
vlib/v/checker/tests/check_generic_int_init.vv:2:9: error: type `int` is private
 | 
			
		||||
    1 | fn test<T>() T {
 | 
			
		||||
    2 |     return T{}
 | 
			
		||||
      |            ~~~
 | 
			
		||||
    3 | }
 | 
			
		||||
    4 |
 | 
			
		||||
| 
						 | 
				
			
			@ -1,7 +0,0 @@
 | 
			
		|||
fn test<T>() T {
 | 
			
		||||
	return T{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
	_ := test<int>()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -12,3 +12,10 @@ vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv:32:1: error: g
 | 
			
		|||
      | ~~~~~~~~~~~~~~
 | 
			
		||||
   33 |     println("hi")
 | 
			
		||||
   34 | }
 | 
			
		||||
vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv:21:14: error: cannot use `Generic<Concrete>` as `Generic<T>` in argument 1 to `g_worker`
 | 
			
		||||
   19 |     }
 | 
			
		||||
   20 |
 | 
			
		||||
   21 |     go g_worker(g)
 | 
			
		||||
      |                 ^
 | 
			
		||||
   22 |
 | 
			
		||||
   23 |     return g
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,3 +5,9 @@ vlib/v/checker/tests/generics_fn_return_generic_struct_err.vv:13:32: error: retu
 | 
			
		|||
      |                                ~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
   14 |     d := GenericChannelStruct{
 | 
			
		||||
   15 |         ch: chan T{}
 | 
			
		||||
vlib/v/checker/tests/generics_fn_return_generic_struct_err.vv:17:9: error: cannot use `GenericChannelStruct<Simple>` as type `GenericChannelStruct` in return argument
 | 
			
		||||
   15 |         ch: chan T{}
 | 
			
		||||
   16 |     }
 | 
			
		||||
   17 |     return d
 | 
			
		||||
      |            ^
 | 
			
		||||
   18 | }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
struct List<T> {
 | 
			
		||||
mut:
 | 
			
		||||
	count u32
 | 
			
		||||
	first &ListNode<T>
 | 
			
		||||
	last  &ListNode<T>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ListNode<T> {
 | 
			
		||||
mut:
 | 
			
		||||
	val  T
 | 
			
		||||
	next &ListNode<T> = 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn create<T>(arr []T) &List<T> {
 | 
			
		||||
	assert arr.len > 0
 | 
			
		||||
	mut n := &ListNode{
 | 
			
		||||
		val: arr[0]
 | 
			
		||||
		next: 0
 | 
			
		||||
	}
 | 
			
		||||
	mut l := &List{
 | 
			
		||||
		first: n
 | 
			
		||||
		last: n
 | 
			
		||||
		count: 1
 | 
			
		||||
	}
 | 
			
		||||
	return l
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_generics_with_generic_structs_init() {
 | 
			
		||||
	n := create([1, 2, 3])
 | 
			
		||||
	println(n)
 | 
			
		||||
	assert n.count == 1
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue