v: initial support for generic interfaces and sumtypes (#10795)
parent
7687b28d8d
commit
6e942bf4c2
|
@ -264,15 +264,16 @@ pub:
|
|||
|
||||
pub struct InterfaceDecl {
|
||||
pub:
|
||||
name string
|
||||
typ Type
|
||||
name_pos token.Position
|
||||
language Language
|
||||
field_names []string
|
||||
is_pub bool
|
||||
mut_pos int // mut:
|
||||
pos token.Position
|
||||
pre_comments []Comment
|
||||
name string
|
||||
typ Type
|
||||
name_pos token.Position
|
||||
language Language
|
||||
field_names []string
|
||||
is_pub bool
|
||||
mut_pos int // mut:
|
||||
pos token.Position
|
||||
pre_comments []Comment
|
||||
generic_types []Type
|
||||
pub mut:
|
||||
methods []FnDecl
|
||||
fields []StructField
|
||||
|
@ -965,11 +966,12 @@ pub:
|
|||
// New implementation of sum types
|
||||
pub struct SumTypeDecl {
|
||||
pub:
|
||||
name string
|
||||
is_pub bool
|
||||
pos token.Position
|
||||
comments []Comment
|
||||
typ Type
|
||||
name string
|
||||
is_pub bool
|
||||
pos token.Position
|
||||
comments []Comment
|
||||
typ Type
|
||||
generic_types []Type
|
||||
pub mut:
|
||||
variants []TypeNode
|
||||
}
|
||||
|
|
|
@ -70,7 +70,6 @@ pub struct Fn {
|
|||
pub:
|
||||
is_variadic bool
|
||||
language Language
|
||||
generic_names []string
|
||||
is_pub bool
|
||||
is_deprecated bool // `[deprecated] fn abc(){}`
|
||||
is_noreturn bool // `[noreturn] fn abc(){}`
|
||||
|
@ -90,6 +89,7 @@ pub mut:
|
|||
source_fn voidptr // set in the checker, while processing fn declarations
|
||||
usages int
|
||||
//
|
||||
generic_names []string
|
||||
attrs []Attr // all fn attributes
|
||||
is_conditional bool // true for `[if abc]fn(){}`
|
||||
ctdefine_idx int // the index of the attribute, containing the compile time define [if mytag]
|
||||
|
@ -1180,6 +1180,9 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
|
|||
return none
|
||||
}
|
||||
typ := concrete_types[index]
|
||||
if typ == 0 {
|
||||
return none
|
||||
}
|
||||
return typ.derive_add_muls(generic_type).clear_flag(.generic)
|
||||
}
|
||||
match mut sym.info {
|
||||
|
@ -1273,7 +1276,7 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
|
|||
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
|
||||
}
|
||||
}
|
||||
Struct {
|
||||
Struct, Interface, SumType {
|
||||
if sym.info.is_generic {
|
||||
mut nrt := '$sym.name<'
|
||||
for i in 0 .. sym.info.generic_types.len {
|
||||
|
|
|
@ -760,6 +760,11 @@ pub mut:
|
|||
fields []StructField
|
||||
methods []Fn
|
||||
ifaces []Type
|
||||
// generic interface support
|
||||
is_generic bool
|
||||
generic_types []Type
|
||||
concrete_types []Type
|
||||
parent_type Type
|
||||
}
|
||||
|
||||
pub struct Enum {
|
||||
|
@ -846,6 +851,11 @@ pub:
|
|||
pub mut:
|
||||
fields []StructField
|
||||
found_fields bool
|
||||
// generic sumtype support
|
||||
is_generic bool
|
||||
generic_types []Type
|
||||
concrete_types []Type
|
||||
parent_type Type
|
||||
}
|
||||
|
||||
// human readable type name
|
||||
|
@ -964,17 +974,21 @@ pub fn (t &Table) type_to_str_using_aliases(typ Type, import_aliases map[string]
|
|||
}
|
||||
res += ')'
|
||||
}
|
||||
.struct_ {
|
||||
.struct_, .interface_, .sum_type {
|
||||
if typ.has_flag(.generic) {
|
||||
info := sym.info as Struct
|
||||
res += '<'
|
||||
for i, gtyp in info.generic_types {
|
||||
res += t.get_type_symbol(gtyp).name
|
||||
if i != info.generic_types.len - 1 {
|
||||
res += ', '
|
||||
match sym.info {
|
||||
Struct, Interface, SumType {
|
||||
res += '<'
|
||||
for i, gtyp in sym.info.generic_types {
|
||||
res += t.get_type_symbol(gtyp).name
|
||||
if i != sym.info.generic_types.len - 1 {
|
||||
res += ', '
|
||||
}
|
||||
}
|
||||
res += '>'
|
||||
}
|
||||
else {}
|
||||
}
|
||||
res += '>'
|
||||
} else {
|
||||
res = t.shorten_user_defined_typenames(res, import_aliases)
|
||||
}
|
||||
|
@ -1004,7 +1018,7 @@ pub fn (t &Table) type_to_str_using_aliases(typ Type, import_aliases map[string]
|
|||
res = 'thread ' + t.type_to_str_using_aliases(rtype, import_aliases)
|
||||
}
|
||||
}
|
||||
.alias, .any, .sum_type, .interface_, .size_t, .aggregate, .placeholder, .enum_ {
|
||||
.alias, .any, .size_t, .aggregate, .placeholder, .enum_ {
|
||||
res = t.shorten_user_defined_typenames(res, import_aliases)
|
||||
}
|
||||
}
|
||||
|
@ -1123,6 +1137,47 @@ pub fn (t &TypeSymbol) find_method(name string) ?Fn {
|
|||
return none
|
||||
}
|
||||
|
||||
pub fn (t &TypeSymbol) find_method_with_generic_parent(name string) ?Fn {
|
||||
if m := t.find_method(name) {
|
||||
return m
|
||||
}
|
||||
mut table := global_table
|
||||
match t.info {
|
||||
Struct, Interface, SumType {
|
||||
if t.info.parent_type.has_flag(.generic) {
|
||||
parent_sym := table.get_type_symbol(t.info.parent_type)
|
||||
if x := parent_sym.find_method(name) {
|
||||
match parent_sym.info {
|
||||
Struct, Interface, SumType {
|
||||
mut method := x
|
||||
generic_names := parent_sym.info.generic_types.map(table.get_type_symbol(it).name)
|
||||
if rt := table.resolve_generic_to_concrete(method.return_type,
|
||||
generic_names, t.info.concrete_types)
|
||||
{
|
||||
method.return_type = rt
|
||||
}
|
||||
method.params = method.params.clone()
|
||||
for mut param in method.params {
|
||||
if pt := table.resolve_generic_to_concrete(param.typ,
|
||||
generic_names, t.info.concrete_types)
|
||||
{
|
||||
param.typ = pt
|
||||
}
|
||||
}
|
||||
method.generic_names.clear()
|
||||
return method
|
||||
}
|
||||
else {}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
pub fn (t &TypeSymbol) str_method_info() (bool, bool, int) {
|
||||
mut has_str_method := false
|
||||
mut expects_ptr := false
|
||||
|
|
|
@ -80,7 +80,7 @@ pub fn (mut b Builder) front_stages(v_files []string) ? {
|
|||
|
||||
pub fn (mut b Builder) middle_stages() ? {
|
||||
util.timing_start('CHECK')
|
||||
b.checker.generic_struct_insts_to_concrete()
|
||||
b.checker.generic_insts_to_concrete()
|
||||
b.checker.check_files(b.parsed_files)
|
||||
util.timing_measure('CHECK')
|
||||
b.print_warnings_and_errors()
|
||||
|
|
|
@ -584,22 +584,24 @@ pub fn (mut c Checker) infer_fn_generic_types(f ast.Fn, mut call_expr ast.CallEx
|
|||
// resolve generic struct receiver
|
||||
if i == 0 && call_expr.is_method && param.typ.has_flag(.generic) {
|
||||
sym := c.table.get_type_symbol(call_expr.receiver_type)
|
||||
if sym.kind == .struct_ {
|
||||
info := sym.info as ast.Struct
|
||||
if c.table.cur_fn.generic_names.len > 0 { // in generic fn
|
||||
if gt_name in c.table.cur_fn.generic_names
|
||||
&& c.table.cur_fn.generic_names.len == c.table.cur_concrete_types.len {
|
||||
idx := c.table.cur_fn.generic_names.index(gt_name)
|
||||
typ = c.table.cur_concrete_types[idx]
|
||||
}
|
||||
} else { // in non-generic fn
|
||||
receiver_generic_names := info.generic_types.map(c.table.get_type_symbol(it).name)
|
||||
if gt_name in receiver_generic_names
|
||||
&& info.generic_types.len == info.concrete_types.len {
|
||||
idx := receiver_generic_names.index(gt_name)
|
||||
typ = info.concrete_types[idx]
|
||||
match sym.info {
|
||||
ast.Struct, ast.Interface, ast.SumType {
|
||||
if c.table.cur_fn.generic_names.len > 0 { // in generic fn
|
||||
if gt_name in c.table.cur_fn.generic_names
|
||||
&& c.table.cur_fn.generic_names.len == c.table.cur_concrete_types.len {
|
||||
idx := c.table.cur_fn.generic_names.index(gt_name)
|
||||
typ = c.table.cur_concrete_types[idx]
|
||||
}
|
||||
} else { // in non-generic fn
|
||||
receiver_generic_names := sym.info.generic_types.map(c.table.get_type_symbol(it).name)
|
||||
if gt_name in receiver_generic_names
|
||||
&& sym.info.generic_types.len == sym.info.concrete_types.len {
|
||||
idx := receiver_generic_names.index(gt_name)
|
||||
typ = sym.info.concrete_types[idx]
|
||||
}
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
arg_i := if i != 0 && call_expr.is_method { i - 1 } else { i }
|
||||
|
@ -667,12 +669,20 @@ pub fn (mut c Checker) infer_fn_generic_types(f ast.Fn, mut call_expr ast.CallEx
|
|||
}
|
||||
} else if param.typ.has_flag(.variadic) {
|
||||
to_set = c.table.mktyp(arg.typ)
|
||||
} else if arg_sym.kind == .struct_ {
|
||||
info := arg_sym.info as ast.Struct
|
||||
generic_names := info.generic_types.map(c.table.get_type_symbol(it).name)
|
||||
if gt_name in generic_names && info.generic_types.len == info.concrete_types.len {
|
||||
} else if arg_sym.kind in [.struct_, .interface_, .sum_type] {
|
||||
mut generic_types := []ast.Type{}
|
||||
mut concrete_types := []ast.Type{}
|
||||
match mut arg_sym.info {
|
||||
ast.Struct, ast.Interface, ast.SumType {
|
||||
generic_types = arg_sym.info.generic_types
|
||||
concrete_types = arg_sym.info.concrete_types
|
||||
}
|
||||
else {}
|
||||
}
|
||||
generic_names := generic_types.map(c.table.get_type_symbol(it).name)
|
||||
if gt_name in generic_names && generic_types.len == concrete_types.len {
|
||||
idx := generic_names.index(gt_name)
|
||||
typ = info.concrete_types[idx]
|
||||
typ = concrete_types[idx]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -636,12 +636,19 @@ pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) {
|
|||
}
|
||||
}
|
||||
|
||||
fn (mut c Checker) unwrap_generic_struct(struct_type ast.Type, generic_names []string, concrete_types []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.cname}_T_'
|
||||
fn (mut c Checker) unwrap_generic_type(typ ast.Type, generic_names []string, concrete_types []ast.Type) ast.Type {
|
||||
mut final_concrete_types := []ast.Type{}
|
||||
mut fields := []ast.StructField{}
|
||||
mut nrt := ''
|
||||
mut c_nrt := ''
|
||||
ts := c.table.get_type_symbol(typ)
|
||||
match mut ts.info {
|
||||
ast.Struct, ast.Interface, ast.SumType {
|
||||
if !ts.info.is_generic {
|
||||
return typ
|
||||
}
|
||||
nrt = '$ts.name<'
|
||||
c_nrt = '${ts.cname}_T_'
|
||||
for i in 0 .. ts.info.generic_types.len {
|
||||
if ct := c.table.resolve_generic_to_concrete(ts.info.generic_types[i],
|
||||
generic_names, concrete_types)
|
||||
|
@ -658,15 +665,15 @@ fn (mut c Checker) unwrap_generic_struct(struct_type ast.Type, generic_names []s
|
|||
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)
|
||||
return ast.new_type(idx).derive(typ).clear_flag(.generic)
|
||||
} else {
|
||||
// fields type translate to concrete type
|
||||
mut fields := ts.info.fields.clone()
|
||||
fields = ts.info.fields.clone()
|
||||
for i in 0 .. fields.len {
|
||||
if fields[i].typ.has_flag(.generic) {
|
||||
sym := c.table.get_type_symbol(fields[i].typ)
|
||||
if sym.kind == .struct_ && fields[i].typ.idx() != struct_type.idx() {
|
||||
fields[i].typ = c.unwrap_generic_struct(fields[i].typ, generic_names,
|
||||
if sym.kind == .struct_ && fields[i].typ.idx() != typ.idx() {
|
||||
fields[i].typ = c.unwrap_generic_type(fields[i].typ, generic_names,
|
||||
concrete_types)
|
||||
} else {
|
||||
if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ,
|
||||
|
@ -678,35 +685,84 @@ fn (mut c Checker) unwrap_generic_struct(struct_type ast.Type, generic_names []s
|
|||
}
|
||||
}
|
||||
// update concrete types
|
||||
mut info_concrete_types := []ast.Type{}
|
||||
for i in 0 .. ts.info.generic_types.len {
|
||||
if t_typ := c.table.resolve_generic_to_concrete(ts.info.generic_types[i],
|
||||
generic_names, concrete_types)
|
||||
{
|
||||
info_concrete_types << t_typ
|
||||
final_concrete_types << t_typ
|
||||
}
|
||||
}
|
||||
mut info := ts.info
|
||||
info.is_generic = false
|
||||
info.concrete_types = info_concrete_types
|
||||
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)
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
return struct_type
|
||||
match mut ts.info {
|
||||
ast.Struct {
|
||||
mut info := ts.info
|
||||
info.is_generic = false
|
||||
info.concrete_types = final_concrete_types
|
||||
info.parent_type = typ
|
||||
info.fields = fields
|
||||
new_idx := c.table.register_type_symbol(
|
||||
kind: .struct_
|
||||
name: nrt
|
||||
cname: util.no_dots(c_nrt)
|
||||
mod: c.mod
|
||||
info: info
|
||||
)
|
||||
return ast.new_type(new_idx).derive(typ).clear_flag(.generic)
|
||||
}
|
||||
ast.Interface {
|
||||
// resolve generic types inside methods
|
||||
mut imethods := ts.info.methods.clone()
|
||||
for mut method in imethods {
|
||||
if t := c.table.resolve_generic_to_concrete(method.return_type, generic_names,
|
||||
concrete_types)
|
||||
{
|
||||
method.return_type = t
|
||||
}
|
||||
for mut param in method.params {
|
||||
if t := c.table.resolve_generic_to_concrete(param.typ, generic_names,
|
||||
concrete_types)
|
||||
{
|
||||
param.typ = t
|
||||
}
|
||||
}
|
||||
}
|
||||
mut all_methods := ts.methods
|
||||
for imethod in imethods {
|
||||
for mut method in all_methods {
|
||||
if imethod.name == method.name {
|
||||
method = imethod
|
||||
}
|
||||
}
|
||||
}
|
||||
mut info := ts.info
|
||||
info.is_generic = false
|
||||
info.concrete_types = final_concrete_types
|
||||
info.parent_type = typ
|
||||
info.fields = fields
|
||||
info.methods = imethods
|
||||
new_idx := c.table.register_type_symbol(
|
||||
kind: .interface_
|
||||
name: nrt
|
||||
cname: util.no_dots(c_nrt)
|
||||
mod: c.mod
|
||||
info: info
|
||||
)
|
||||
mut ts_copy := c.table.get_type_symbol(new_idx)
|
||||
for method in all_methods {
|
||||
ts_copy.register_method(method)
|
||||
}
|
||||
return ast.new_type(new_idx).derive(typ).clear_flag(.generic)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
// generic struct instantiations to concrete types
|
||||
pub fn (mut c Checker) generic_struct_insts_to_concrete() {
|
||||
pub fn (mut c Checker) generic_insts_to_concrete() {
|
||||
for mut typ in c.table.type_symbols {
|
||||
if typ.kind == .generic_struct_inst {
|
||||
info := typ.info as ast.GenericStructInst
|
||||
|
@ -715,32 +771,126 @@ pub fn (mut c Checker) generic_struct_insts_to_concrete() {
|
|||
typ.kind = .placeholder
|
||||
continue
|
||||
}
|
||||
mut parent_info := parent.info as ast.Struct
|
||||
mut fields := parent_info.fields.clone()
|
||||
if parent_info.generic_types.len == info.concrete_types.len {
|
||||
generic_names := parent_info.generic_types.map(c.table.get_type_symbol(it).name)
|
||||
for i in 0 .. fields.len {
|
||||
if fields[i].typ.has_flag(.generic) {
|
||||
sym := c.table.get_type_symbol(fields[i].typ)
|
||||
if sym.kind == .struct_ && fields[i].typ.idx() != info.parent_idx {
|
||||
fields[i].typ = c.unwrap_generic_struct(fields[i].typ, generic_names,
|
||||
info.concrete_types)
|
||||
} else {
|
||||
match parent.info {
|
||||
ast.Struct {
|
||||
mut parent_info := parent.info as ast.Struct
|
||||
mut fields := parent_info.fields.clone()
|
||||
if parent_info.generic_types.len == info.concrete_types.len {
|
||||
generic_names := parent_info.generic_types.map(c.table.get_type_symbol(it).name)
|
||||
for i in 0 .. fields.len {
|
||||
if fields[i].typ.has_flag(.generic) {
|
||||
sym := c.table.get_type_symbol(fields[i].typ)
|
||||
if sym.kind == .struct_ && fields[i].typ.idx() != info.parent_idx {
|
||||
fields[i].typ = c.unwrap_generic_type(fields[i].typ,
|
||||
generic_names, info.concrete_types)
|
||||
} else {
|
||||
if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ,
|
||||
generic_names, info.concrete_types)
|
||||
{
|
||||
fields[i].typ = t_typ
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
parent_info.is_generic = false
|
||||
parent_info.concrete_types = info.concrete_types.clone()
|
||||
parent_info.fields = fields
|
||||
parent_info.parent_type = ast.new_type(info.parent_idx).set_flag(.generic)
|
||||
typ.info = ast.Struct{
|
||||
...parent_info
|
||||
is_generic: false
|
||||
concrete_types: info.concrete_types.clone()
|
||||
fields: fields
|
||||
parent_type: ast.new_type(info.parent_idx).set_flag(.generic)
|
||||
}
|
||||
typ.is_public = true
|
||||
typ.kind = parent.kind
|
||||
}
|
||||
}
|
||||
ast.Interface {
|
||||
mut parent_info := parent.info as ast.Interface
|
||||
if parent_info.generic_types.len == info.concrete_types.len {
|
||||
mut fields := parent_info.fields.clone()
|
||||
generic_names := parent_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, info.concrete_types)
|
||||
{
|
||||
fields[i].typ = t_typ
|
||||
}
|
||||
}
|
||||
mut imethods := parent_info.methods.clone()
|
||||
for mut method in imethods {
|
||||
method.generic_names.clear()
|
||||
if pt := c.table.resolve_generic_to_concrete(method.return_type,
|
||||
generic_names, info.concrete_types)
|
||||
{
|
||||
method.return_type = pt
|
||||
}
|
||||
method.params = method.params.clone()
|
||||
for mut param in method.params {
|
||||
if pt := c.table.resolve_generic_to_concrete(param.typ,
|
||||
generic_names, info.concrete_types)
|
||||
{
|
||||
param.typ = pt
|
||||
}
|
||||
}
|
||||
typ.register_method(method)
|
||||
}
|
||||
mut all_methods := parent.methods
|
||||
for imethod in imethods {
|
||||
for mut method in all_methods {
|
||||
if imethod.name == method.name {
|
||||
method = imethod
|
||||
}
|
||||
}
|
||||
}
|
||||
typ.info = ast.Interface{
|
||||
...parent_info
|
||||
is_generic: false
|
||||
concrete_types: info.concrete_types.clone()
|
||||
fields: fields
|
||||
methods: imethods
|
||||
parent_type: ast.new_type(info.parent_idx).set_flag(.generic)
|
||||
}
|
||||
typ.is_public = true
|
||||
typ.kind = parent.kind
|
||||
typ.methods = all_methods
|
||||
}
|
||||
}
|
||||
parent_info.is_generic = false
|
||||
parent_info.concrete_types = info.concrete_types.clone()
|
||||
parent_info.fields = fields
|
||||
parent_info.parent_type = ast.new_type(info.parent_idx).set_flag(.generic)
|
||||
typ.is_public = true
|
||||
typ.kind = .struct_
|
||||
typ.info = parent_info
|
||||
ast.SumType {
|
||||
mut parent_info := parent.info as ast.SumType
|
||||
if parent_info.generic_types.len == info.concrete_types.len {
|
||||
mut fields := parent_info.fields.clone()
|
||||
mut variants := parent_info.variants.clone()
|
||||
generic_names := parent_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, info.concrete_types)
|
||||
{
|
||||
fields[i].typ = t_typ
|
||||
}
|
||||
}
|
||||
for i in 0 .. variants.len {
|
||||
if t_typ := c.table.resolve_generic_to_concrete(variants[i],
|
||||
generic_names, info.concrete_types)
|
||||
{
|
||||
variants[i] = t_typ
|
||||
}
|
||||
}
|
||||
typ.info = ast.SumType{
|
||||
...parent_info
|
||||
is_generic: false
|
||||
concrete_types: info.concrete_types.clone()
|
||||
fields: fields
|
||||
variants: variants
|
||||
parent_type: ast.new_type(info.parent_idx).set_flag(.generic)
|
||||
}
|
||||
typ.is_public = true
|
||||
typ.kind = parent.kind
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -778,7 +928,7 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
|
|||
return ast.void_type
|
||||
}
|
||||
}
|
||||
unwrapped_struct_type := c.unwrap_generic_struct(node.typ, c.table.cur_fn.generic_names,
|
||||
unwrapped_struct_type := c.unwrap_generic_type(node.typ, c.table.cur_fn.generic_names,
|
||||
c.table.cur_concrete_types)
|
||||
c.ensure_type_exists(unwrapped_struct_type, node.pos) or {}
|
||||
type_sym := c.table.get_type_symbol(unwrapped_struct_type)
|
||||
|
@ -2118,7 +2268,7 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
|
|||
}
|
||||
// resolve return generics struct to concrete type
|
||||
if method.generic_names.len > 0 && method.return_type.has_flag(.generic) {
|
||||
call_expr.return_type = c.unwrap_generic_struct(method.return_type, method.generic_names,
|
||||
call_expr.return_type = c.unwrap_generic_type(method.return_type, method.generic_names,
|
||||
concrete_types)
|
||||
} else {
|
||||
call_expr.return_type = method.return_type
|
||||
|
@ -2697,7 +2847,7 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
|
|||
concrete_types = call_expr.concrete_types
|
||||
}
|
||||
if func.generic_names.len > 0 {
|
||||
for i, call_arg in call_expr.args {
|
||||
for i, mut call_arg in call_expr.args {
|
||||
param := if func.is_variadic && i >= func.params.len - 1 {
|
||||
func.params[func.params.len - 1]
|
||||
} else {
|
||||
|
@ -2711,7 +2861,18 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
|
|||
if unwrap_typ := c.table.resolve_generic_to_concrete(param.typ, func.generic_names,
|
||||
concrete_types)
|
||||
{
|
||||
c.check_expected_call_arg(c.unwrap_generic(typ), unwrap_typ, call_expr.language) or {
|
||||
utyp := c.unwrap_generic(typ)
|
||||
unwrap_sym := c.table.get_type_symbol(unwrap_typ)
|
||||
if unwrap_sym.kind == .interface_ {
|
||||
if c.type_implements(utyp, unwrap_typ, call_arg.expr.position()) {
|
||||
if !utyp.is_ptr() && !utyp.is_pointer() && !c.inside_unsafe
|
||||
&& c.table.get_type_symbol(utyp).kind != .interface_ {
|
||||
c.mark_as_referenced(mut &call_arg.expr, true)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
c.check_expected_call_arg(utyp, unwrap_typ, call_expr.language) or {
|
||||
c.error('$err.msg in argument ${i + 1} to `$fn_name`', call_arg.pos)
|
||||
}
|
||||
}
|
||||
|
@ -2720,7 +2881,7 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
|
|||
}
|
||||
// resolve return generics struct to concrete type
|
||||
if func.generic_names.len > 0 && func.return_type.has_flag(.generic) {
|
||||
call_expr.return_type = c.unwrap_generic_struct(func.return_type, func.generic_names,
|
||||
call_expr.return_type = c.unwrap_generic_type(func.return_type, func.generic_names,
|
||||
concrete_types)
|
||||
} else {
|
||||
call_expr.return_type = func.return_type
|
||||
|
@ -2782,13 +2943,92 @@ fn semicolonize(main string, details string) string {
|
|||
return '$main; $details'
|
||||
}
|
||||
|
||||
fn (mut c Checker) resolve_generic_interface(typ ast.Type, interface_type ast.Type, pos token.Position) ast.Type {
|
||||
utyp := c.unwrap_generic(typ)
|
||||
typ_sym := c.table.get_type_symbol(utyp)
|
||||
mut inter_sym := c.table.get_type_symbol(interface_type)
|
||||
if mut inter_sym.info is ast.Interface {
|
||||
if inter_sym.info.is_generic {
|
||||
mut inferred_types := []ast.Type{}
|
||||
for ifield in inter_sym.info.fields {
|
||||
if ifield.typ.has_flag(.generic) {
|
||||
if field := c.table.find_field_with_embeds(typ_sym, ifield.name) {
|
||||
if field.typ !in inferred_types {
|
||||
inferred_types << field.typ
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for imethod in inter_sym.info.methods {
|
||||
method := typ_sym.find_method(imethod.name) or {
|
||||
typ_sym.find_method_with_generic_parent(imethod.name) or { ast.Fn{} }
|
||||
}
|
||||
if imethod.return_type.has_flag(.generic) {
|
||||
if method.return_type !in inferred_types {
|
||||
inferred_types << method.return_type
|
||||
}
|
||||
}
|
||||
for i, iparam in imethod.params {
|
||||
param := method.params[i] or { ast.Param{} }
|
||||
if iparam.typ.has_flag(.generic) {
|
||||
if param.typ !in inferred_types {
|
||||
inferred_types << param.typ
|
||||
}
|
||||
}
|
||||
}
|
||||
if inferred_types !in c.table.fn_generic_types[imethod.name] {
|
||||
c.table.fn_generic_types[imethod.name] << inferred_types
|
||||
}
|
||||
}
|
||||
if inferred_types.len == 0 {
|
||||
c.error('cannot infer generic types for ${c.table.type_to_str(interface_type)}',
|
||||
pos)
|
||||
return ast.void_type
|
||||
}
|
||||
if inferred_types.len > 1 {
|
||||
c.error('cannot infer generic types for ${c.table.type_to_str(interface_type)}: got conflicting type information',
|
||||
pos)
|
||||
return ast.void_type
|
||||
}
|
||||
inferred_type := inferred_types[0]
|
||||
if inferred_type !in inter_sym.info.concrete_types {
|
||||
inter_sym.info.concrete_types << inferred_type
|
||||
}
|
||||
generic_names := inter_sym.info.generic_types.map(c.table.get_type_name(it))
|
||||
return c.unwrap_generic_type(interface_type, generic_names, inter_sym.info.concrete_types)
|
||||
}
|
||||
}
|
||||
return interface_type
|
||||
}
|
||||
|
||||
fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos token.Position) bool {
|
||||
$if debug_interface_type_implements ? {
|
||||
eprintln('> type_implements typ: $typ.debug() | inter_typ: $interface_type.debug()')
|
||||
eprintln('> type_implements typ: $typ.debug() (`${c.table.type_to_str(typ)}`) | inter_typ: $interface_type.debug() (`${c.table.type_to_str(interface_type)}`)')
|
||||
}
|
||||
utyp := c.unwrap_generic(typ)
|
||||
typ_sym := c.table.get_type_symbol(utyp)
|
||||
mut inter_sym := c.table.get_type_symbol(interface_type)
|
||||
if mut inter_sym.info is ast.Interface {
|
||||
mut generic_type := interface_type
|
||||
mut generic_info := inter_sym.info
|
||||
if inter_sym.info.parent_type.has_flag(.generic) {
|
||||
parent_sym := c.table.get_type_symbol(inter_sym.info.parent_type)
|
||||
if parent_sym.info is ast.Interface {
|
||||
generic_type = inter_sym.info.parent_type
|
||||
generic_info = parent_sym.info
|
||||
}
|
||||
}
|
||||
mut inferred_type := interface_type
|
||||
if generic_info.is_generic {
|
||||
inferred_type = c.resolve_generic_interface(typ, generic_type, pos)
|
||||
if inferred_type == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if inter_sym.info.is_generic {
|
||||
return c.type_implements(typ, inferred_type, pos)
|
||||
}
|
||||
}
|
||||
// do not check the same type more than once
|
||||
if mut inter_sym.info is ast.Interface {
|
||||
for t in inter_sym.info.types {
|
||||
|
@ -2815,9 +3055,17 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to
|
|||
} else {
|
||||
inter_sym.methods
|
||||
}
|
||||
// Verify methods
|
||||
for imethod in imethods {
|
||||
if method := typ_sym.find_method(imethod.name) {
|
||||
// voidptr is an escape hatch, it should be allowed to be passed
|
||||
if utyp != ast.voidptr_type {
|
||||
// Verify methods
|
||||
for imethod in imethods {
|
||||
method := typ_sym.find_method(imethod.name) or {
|
||||
typ_sym.find_method_with_generic_parent(imethod.name) or {
|
||||
c.error("`$styp` doesn't implement method `$imethod.name` of interface `$inter_sym.name`",
|
||||
pos)
|
||||
continue
|
||||
}
|
||||
}
|
||||
msg := c.table.is_same_method(imethod, method)
|
||||
if msg.len > 0 {
|
||||
sig := c.table.fn_signature(imethod, skip_receiver: true)
|
||||
|
@ -2826,12 +3074,6 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to
|
|||
pos)
|
||||
return false
|
||||
}
|
||||
continue
|
||||
}
|
||||
// voidptr is an escape hatch, it should be allowed to be passed
|
||||
if utyp != ast.voidptr_type {
|
||||
c.error("`$styp` doesn't implement method `$imethod.name` of interface `$inter_sym.name`",
|
||||
pos)
|
||||
}
|
||||
}
|
||||
// Verify fields
|
||||
|
@ -6114,7 +6356,7 @@ fn (mut c Checker) smartcast_if_conds(node ast.Expr, mut scope ast.Scope) {
|
|||
c.smartcast_if_conds(node.right, mut scope)
|
||||
} else if node.op == .key_is {
|
||||
right_expr := node.right
|
||||
right_type := match right_expr {
|
||||
mut right_type := match right_expr {
|
||||
ast.TypeNode {
|
||||
right_expr.typ
|
||||
}
|
||||
|
@ -6126,9 +6368,10 @@ fn (mut c Checker) smartcast_if_conds(node ast.Expr, mut scope ast.Scope) {
|
|||
ast.Type(0)
|
||||
}
|
||||
}
|
||||
right_type = c.unwrap_generic(right_type)
|
||||
if right_type != ast.Type(0) {
|
||||
left_sym := c.table.get_type_symbol(node.left_type)
|
||||
expr_type := c.expr(node.left)
|
||||
expr_type := c.unwrap_generic(c.expr(node.left))
|
||||
if left_sym.kind == .interface_ {
|
||||
c.type_implements(right_type, expr_type, node.pos)
|
||||
} else if !c.check_types(right_type, expr_type) {
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
vlib/v/checker/tests/generic_sumtype_invalid_variant.vv:5:7: error: `MultiGeneric<bool,int,string>` has no variant `u64`
|
||||
3 | fn main() {
|
||||
4 | mut m := MultiGeneric<bool, int, string>(true)
|
||||
5 | if m is u64 {
|
||||
| ~~
|
||||
6 | println('hi')
|
||||
7 | }
|
||||
vlib/v/checker/tests/generic_sumtype_invalid_variant.vv:8:7: error: `MultiGeneric<bool,int,string>` has no variant `X`
|
||||
6 | println('hi')
|
||||
7 | }
|
||||
8 | if m is X {
|
||||
| ~~
|
||||
9 | println('hi again')
|
||||
10 | }
|
|
@ -0,0 +1,11 @@
|
|||
type MultiGeneric<X, Y, Z> = X | Y | Z
|
||||
|
||||
fn main() {
|
||||
mut m := MultiGeneric<bool, int, string>(true)
|
||||
if m is u64 {
|
||||
println('hi')
|
||||
}
|
||||
if m is X {
|
||||
println('hi again')
|
||||
}
|
||||
}
|
|
@ -1187,7 +1187,14 @@ pub fn (mut f Fmt) interface_decl(node ast.InterfaceDecl) {
|
|||
f.write('interface ')
|
||||
f.write_language_prefix(node.language)
|
||||
name := node.name.after('.')
|
||||
f.write('$name {')
|
||||
f.write(name)
|
||||
if node.generic_types.len > 0 {
|
||||
f.write('<')
|
||||
gtypes := node.generic_types.map(f.table.type_to_str(it)).join(', ')
|
||||
f.write(gtypes)
|
||||
f.write('>')
|
||||
}
|
||||
f.write(' {')
|
||||
if node.fields.len > 0 || node.methods.len > 0 || node.pos.line_nr < node.pos.last_line {
|
||||
f.writeln('')
|
||||
}
|
||||
|
@ -1370,7 +1377,15 @@ pub fn (mut f Fmt) sum_type_decl(node ast.SumTypeDecl) {
|
|||
if node.is_pub {
|
||||
f.write('pub ')
|
||||
}
|
||||
f.write('type $node.name = ')
|
||||
f.write('type $node.name')
|
||||
if node.generic_types.len > 0 {
|
||||
f.write('<')
|
||||
gtypes := node.generic_types.map(f.table.type_to_str(it)).join(', ')
|
||||
f.write(gtypes)
|
||||
f.write('>')
|
||||
}
|
||||
f.write(' = ')
|
||||
|
||||
mut sum_type_names := []string{}
|
||||
for t in node.variants {
|
||||
sum_type_names << f.table.type_to_str_using_aliases(t.typ, f.mod2alias)
|
||||
|
|
|
@ -117,7 +117,7 @@ fn (mut g Gen) gen_assert_single_expr(expr ast.Expr, typ ast.Type) {
|
|||
}
|
||||
}
|
||||
ast.TypeNode {
|
||||
sym := g.table.get_type_symbol(typ)
|
||||
sym := g.table.get_type_symbol(g.unwrap_generic(typ))
|
||||
g.write(ctoslit('$sym.name'))
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -20,7 +20,7 @@ fn (mut g Gen) gen_sumtype_equality_fn(left_type ast.Type) string {
|
|||
fn_builder.writeln('\tif (a._typ != b._typ) { return false; }')
|
||||
for typ in info.variants {
|
||||
variant := g.unwrap(typ)
|
||||
fn_builder.writeln('\tif (a._typ == $typ) {')
|
||||
fn_builder.writeln('\tif (a._typ == $variant.typ) {')
|
||||
name := '_$variant.sym.cname'
|
||||
if variant.sym.kind == .string {
|
||||
fn_builder.writeln('\t\treturn string__eq(*a.$name, *b.$name);')
|
||||
|
|
|
@ -324,6 +324,11 @@ fn (mut g Gen) gen_str_for_interface(info ast.Interface, styp string, str_fn_nam
|
|||
if styp.ends_with('*') {
|
||||
clean_interface_v_type_name = '&' + clean_interface_v_type_name.replace('*', '')
|
||||
}
|
||||
if clean_interface_v_type_name.contains('_T_') {
|
||||
clean_interface_v_type_name =
|
||||
clean_interface_v_type_name.replace('Array_', '[]').replace('_T_', '<').replace('_', ', ') +
|
||||
'>'
|
||||
}
|
||||
clean_interface_v_type_name = util.strip_main_name(clean_interface_v_type_name)
|
||||
fn_builder.writeln('static string indent_${str_fn_name}($styp x, int indent_count) { /* gen_str_for_interface */')
|
||||
for typ in info.types {
|
||||
|
@ -378,6 +383,11 @@ fn (mut g Gen) gen_str_for_union_sum_type(info ast.SumType, styp string, str_fn_
|
|||
if styp.ends_with('*') {
|
||||
clean_sum_type_v_type_name = '&' + clean_sum_type_v_type_name.replace('*', '')
|
||||
}
|
||||
if clean_sum_type_v_type_name.contains('_T_') {
|
||||
clean_sum_type_v_type_name =
|
||||
clean_sum_type_v_type_name.replace('Array_', '[]').replace('_T_', '<').replace('_', ', ') +
|
||||
'>'
|
||||
}
|
||||
clean_sum_type_v_type_name = util.strip_main_name(clean_sum_type_v_type_name)
|
||||
fn_builder.writeln('\tswitch(x._typ) {')
|
||||
for typ in info.variants {
|
||||
|
|
|
@ -519,6 +519,9 @@ pub fn (mut g Gen) write_typeof_functions() {
|
|||
for typ in g.table.type_symbols {
|
||||
if typ.kind == .sum_type {
|
||||
sum_info := typ.info as ast.SumType
|
||||
if sum_info.is_generic {
|
||||
continue
|
||||
}
|
||||
g.writeln('static char * v_typeof_sumtype_${typ.cname}(int sidx) { /* $typ.name */ ')
|
||||
if g.pref.build_mode == .build_module {
|
||||
g.writeln('\t\tif( sidx == _v_type_idx_${typ.cname}() ) return "${util.strip_main_name(typ.name)}";')
|
||||
|
@ -541,6 +544,9 @@ pub fn (mut g Gen) write_typeof_functions() {
|
|||
g.writeln('}')
|
||||
} else if typ.kind == .interface_ {
|
||||
inter_info := typ.info as ast.Interface
|
||||
if inter_info.is_generic {
|
||||
continue
|
||||
}
|
||||
g.writeln('static char * v_typeof_interface_${typ.cname}(int sidx) { /* $typ.name */ ')
|
||||
for t in inter_info.types {
|
||||
subtype := g.table.get_type_symbol(t)
|
||||
|
@ -739,23 +745,27 @@ fn (mut g Gen) cc_type(typ ast.Type, is_prefix_struct bool) string {
|
|||
sym := g.table.get_type_symbol(g.unwrap_generic(typ))
|
||||
mut styp := sym.cname
|
||||
// TODO: this needs to be removed; cgen shouldn't resolve generic types (job of checker)
|
||||
if mut sym.info is ast.Struct {
|
||||
if sym.info.is_generic {
|
||||
mut sgtyps := '_T'
|
||||
for gt in sym.info.generic_types {
|
||||
gts := g.table.get_type_symbol(g.unwrap_generic(gt))
|
||||
sgtyps += '_$gts.cname'
|
||||
match mut sym.info {
|
||||
ast.Struct, ast.Interface, ast.SumType {
|
||||
if sym.info.is_generic {
|
||||
mut sgtyps := '_T'
|
||||
for gt in sym.info.generic_types {
|
||||
gts := g.table.get_type_symbol(g.unwrap_generic(gt))
|
||||
sgtyps += '_$gts.cname'
|
||||
}
|
||||
styp += sgtyps
|
||||
}
|
||||
styp += sgtyps
|
||||
}
|
||||
} else if mut sym.info is ast.MultiReturn {
|
||||
// TODO: this doesn't belong here, but makes it working for now
|
||||
mut cname := 'multi_return'
|
||||
for mr_typ in sym.info.types {
|
||||
mr_type_sym := g.table.get_type_symbol(g.unwrap_generic(mr_typ))
|
||||
cname += '_$mr_type_sym.cname'
|
||||
ast.MultiReturn {
|
||||
// TODO: this doesn't belong here, but makes it working for now
|
||||
mut cname := 'multi_return'
|
||||
for mr_typ in sym.info.types {
|
||||
mr_type_sym := g.table.get_type_symbol(g.unwrap_generic(mr_typ))
|
||||
cname += '_$mr_type_sym.cname'
|
||||
}
|
||||
return cname
|
||||
}
|
||||
return cname
|
||||
else {}
|
||||
}
|
||||
if is_prefix_struct && styp.starts_with('C__') {
|
||||
styp = styp[3..]
|
||||
|
@ -889,7 +899,10 @@ pub fn (mut g Gen) write_alias_typesymbol_declaration(sym ast.TypeSymbol) {
|
|||
|
||||
pub fn (mut g Gen) write_interface_typesymbol_declaration(sym ast.TypeSymbol) {
|
||||
info := sym.info as ast.Interface
|
||||
struct_name := c_name(sym.name)
|
||||
if info.is_generic {
|
||||
return
|
||||
}
|
||||
struct_name := c_name(sym.cname)
|
||||
g.type_definitions.writeln('typedef struct $struct_name $struct_name;')
|
||||
g.type_definitions.writeln('struct $struct_name {')
|
||||
g.type_definitions.writeln('\tunion {')
|
||||
|
@ -1852,20 +1865,27 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
|
|||
g.write('.msg))')
|
||||
return
|
||||
}
|
||||
if exp_sym.kind == .interface_ && got_type_raw.idx() != expected_type.idx()
|
||||
if exp_sym.info is ast.Interface && got_type_raw.idx() != expected_type.idx()
|
||||
&& !expected_type.has_flag(.optional) {
|
||||
if expr is ast.StructInit && !got_type.is_ptr() {
|
||||
g.inside_cast_in_heap++
|
||||
got_styp := g.cc_type(got_type.to_ptr(), true)
|
||||
exp_styp := g.cc_type(expected_type, true)
|
||||
fname := 'I_${got_styp}_to_Interface_$exp_styp'
|
||||
// TODO: why does cc_type even add this in the first place?
|
||||
exp_styp := exp_sym.cname
|
||||
mut fname := 'I_${got_styp}_to_Interface_$exp_styp'
|
||||
if exp_sym.info.is_generic {
|
||||
fname = g.generic_fn_name(exp_sym.info.concrete_types, fname, false)
|
||||
}
|
||||
g.call_cfn_for_casting_expr(fname, expr, expected_is_ptr, exp_styp, true,
|
||||
got_styp)
|
||||
g.inside_cast_in_heap--
|
||||
} else {
|
||||
got_styp := g.cc_type(got_type, true)
|
||||
exp_styp := g.cc_type(expected_type, true)
|
||||
fname := 'I_${got_styp}_to_Interface_$exp_styp'
|
||||
exp_styp := exp_sym.cname
|
||||
mut fname := '/*$exp_sym*/I_${got_styp}_to_Interface_$exp_styp'
|
||||
if exp_sym.info.is_generic {
|
||||
fname = g.generic_fn_name(exp_sym.info.concrete_types, fname, false)
|
||||
}
|
||||
g.call_cfn_for_casting_expr(fname, expr, expected_is_ptr, exp_styp, got_is_ptr,
|
||||
got_styp)
|
||||
}
|
||||
|
@ -3421,8 +3441,9 @@ fn (mut g Gen) expr(node ast.Expr) {
|
|||
// match sum Type
|
||||
// g.write('/* Type */')
|
||||
// type_idx := node.typ.idx()
|
||||
sym := g.table.get_type_symbol(node.typ)
|
||||
sidx := g.type_sidx(node.typ)
|
||||
typ := g.unwrap_generic(node.typ)
|
||||
sym := g.table.get_type_symbol(typ)
|
||||
sidx := g.type_sidx(typ)
|
||||
// g.write('$type_idx /* $sym.name */')
|
||||
g.write('$sidx /* $sym.name */')
|
||||
}
|
||||
|
@ -3839,7 +3860,7 @@ fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var str
|
|||
} else if sym.kind == .interface_ {
|
||||
if branch.exprs[sumtype_index] is ast.TypeNode {
|
||||
typ := branch.exprs[sumtype_index] as ast.TypeNode
|
||||
branch_sym := g.table.get_type_symbol(typ.typ)
|
||||
branch_sym := g.table.get_type_symbol(g.unwrap_generic(typ.typ))
|
||||
g.write('${dot_or_ptr}_typ == _${sym.cname}_${branch_sym.cname}_index')
|
||||
} else if branch.exprs[sumtype_index] is ast.None && sym.name == 'IError' {
|
||||
g.write('${dot_or_ptr}_typ == _IError_None___index')
|
||||
|
@ -4248,7 +4269,7 @@ fn (mut g Gen) ident(node ast.Ident) {
|
|||
}
|
||||
}
|
||||
for i, typ in v.smartcasts {
|
||||
cast_sym := g.table.get_type_symbol(typ)
|
||||
cast_sym := g.table.get_type_symbol(g.unwrap_generic(typ))
|
||||
mut is_ptr := false
|
||||
if i == 0 {
|
||||
g.write(name)
|
||||
|
@ -5452,6 +5473,9 @@ fn (mut g Gen) write_types(types []ast.TypeSymbol) {
|
|||
}
|
||||
}
|
||||
ast.SumType {
|
||||
if typ.info.is_generic {
|
||||
continue
|
||||
}
|
||||
g.typedefs.writeln('typedef struct $name $name;')
|
||||
g.type_definitions.writeln('')
|
||||
g.type_definitions.writeln('// Union sum type $name = ')
|
||||
|
@ -6135,6 +6159,9 @@ fn (mut g Gen) interface_table() string {
|
|||
continue
|
||||
}
|
||||
inter_info := ityp.info as ast.Interface
|
||||
if inter_info.is_generic {
|
||||
continue
|
||||
}
|
||||
// interface_name is for example Speaker
|
||||
interface_name := ityp.cname
|
||||
// generate a struct that references interface methods
|
||||
|
@ -6242,13 +6269,41 @@ static inline $interface_name I_${cctype}_to_Interface_${interface_name}($cctype
|
|||
}
|
||||
}
|
||||
}
|
||||
for _, method in st_sym.methods {
|
||||
mut methods := st_sym.methods
|
||||
match st_sym.info {
|
||||
ast.Struct, ast.Interface, ast.SumType {
|
||||
if st_sym.info.parent_type.has_flag(.generic) {
|
||||
parent_sym := g.table.get_type_symbol(st_sym.info.parent_type)
|
||||
for method in parent_sym.methods {
|
||||
if method.name in methodidx {
|
||||
methods << st_sym.find_method_with_generic_parent(method.name) or {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
for method in methods {
|
||||
mut name := method.name
|
||||
if inter_info.parent_type.has_flag(.generic) {
|
||||
parent_sym := g.table.get_type_symbol(inter_info.parent_type)
|
||||
match mut parent_sym.info {
|
||||
ast.Struct, ast.Interface, ast.SumType {
|
||||
name = g.generic_fn_name(parent_sym.info.concrete_types, method.name,
|
||||
false)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
|
||||
if method.name !in methodidx {
|
||||
// a method that is not part of the interface should be just skipped
|
||||
continue
|
||||
}
|
||||
// .speak = Cat_speak
|
||||
mut method_call := '${cctype}_$method.name'
|
||||
mut method_call := '${cctype}_$name'
|
||||
if !method.params[0].typ.is_ptr() {
|
||||
// inline void Cat_speak_Interface_Animal_method_wrapper(Cat c) { return Cat_speak(*c); }
|
||||
iwpostfix := '_Interface_${interface_name}_method_wrapper'
|
||||
|
|
|
@ -146,7 +146,7 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
|
|||
for concrete_types in g.table.fn_generic_types[node.name] {
|
||||
if g.pref.is_verbose {
|
||||
syms := concrete_types.map(g.table.get_type_symbol(it))
|
||||
the_type := syms.map(node.name).join(', ')
|
||||
the_type := syms.map(it.name).join(', ')
|
||||
println('gen fn `$node.name` for type `$the_type`')
|
||||
}
|
||||
g.table.cur_concrete_types = concrete_types
|
||||
|
|
|
@ -1856,11 +1856,7 @@ fn (p &Parser) is_typename(t token.Token) bool {
|
|||
// 10. 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 := if p.tok.kind != .eof && p.tok.lit.len > 0 {
|
||||
p.tok.lit[0].is_capital()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
lit0_is_capital := p.tok.kind != .eof && p.tok.lit.len > 0 && p.tok.lit[0].is_capital()
|
||||
if lit0_is_capital || p.peek_tok.kind != .lt {
|
||||
return false
|
||||
}
|
||||
|
@ -1901,6 +1897,45 @@ fn (p &Parser) is_generic_call() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
const valid_tokens_inside_types = [token.Kind.lsbr, .rsbr, .name, .dot, .comma, .key_fn, .lt]
|
||||
|
||||
fn (mut p Parser) is_generic_cast() bool {
|
||||
if !p.tok.can_start_type(ast.builtin_type_names) {
|
||||
return false
|
||||
}
|
||||
mut i := 0
|
||||
mut level := 0
|
||||
mut lt_count := 0
|
||||
for {
|
||||
i++
|
||||
tok := p.peek_token(i)
|
||||
|
||||
if tok.kind == .lt {
|
||||
lt_count++
|
||||
level++
|
||||
} else if tok.kind == .gt {
|
||||
level--
|
||||
}
|
||||
if lt_count > 0 && level == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if i > 20 || tok.kind !in parser.valid_tokens_inside_types {
|
||||
return false
|
||||
}
|
||||
}
|
||||
next_tok := p.peek_token(i + 1)
|
||||
// `next_tok` is the token following the closing `>` of the generic type: MyType<int>{
|
||||
// ^
|
||||
// if `next_tok` is a left paren, then the full expression looks something like
|
||||
// `Foo<string>(` or `Foo<mod.Type>(`, which are valid type casts - return true
|
||||
if next_tok.kind == .lpar {
|
||||
return true
|
||||
}
|
||||
// any other token is not a valid generic cast, however
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn (mut p Parser) name_expr() ast.Expr {
|
||||
prev_tok_kind := p.prev_tok.kind
|
||||
mut node := ast.empty_expr()
|
||||
|
@ -2041,6 +2076,8 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
|||
false
|
||||
}
|
||||
is_optional := p.tok.kind == .question
|
||||
is_generic_call := p.is_generic_call()
|
||||
is_generic_cast := p.is_generic_cast()
|
||||
// p.warn('name expr $p.tok.lit $p.peek_tok.str()')
|
||||
same_line := p.tok.line_nr == p.peek_tok.line_nr
|
||||
// `(` must be on same line as name token otherwise it's a ParExpr
|
||||
|
@ -2053,8 +2090,8 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
|||
p.defer_vars << ident
|
||||
}
|
||||
}
|
||||
} else if p.peek_tok.kind == .lpar
|
||||
|| (is_optional && p.peek_token(2).kind == .lpar) || p.is_generic_call() {
|
||||
} else if p.peek_tok.kind == .lpar || is_generic_call || is_generic_cast
|
||||
|| (is_optional && p.peek_token(2).kind == .lpar) {
|
||||
// foo(), foo<int>() or type() cast
|
||||
mut name := if is_optional { p.peek_tok.lit } else { p.tok.lit }
|
||||
if mod.len > 0 {
|
||||
|
@ -2064,7 +2101,7 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
|||
// type cast. TODO: finish
|
||||
// if name in ast.builtin_type_names {
|
||||
if (!known_var && (name in p.table.type_idxs || name_w_mod in p.table.type_idxs)
|
||||
&& name !in ['C.stat', 'C.sigaction']) || is_mod_cast
|
||||
&& name !in ['C.stat', 'C.sigaction']) || is_mod_cast || is_generic_cast
|
||||
|| (language == .v && name[0].is_capital()) {
|
||||
// MainLetter(x) is *always* a cast, as long as it is not `C.`
|
||||
// TODO handle C.stat()
|
||||
|
@ -3077,6 +3114,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
|
|||
return ast.AliasTypeDecl{}
|
||||
}
|
||||
mut sum_variants := []ast.TypeNode{}
|
||||
generic_types := p.parse_generic_type_list()
|
||||
p.check(.assign)
|
||||
mut type_pos := p.tok.position()
|
||||
mut comments := []ast.Comment{}
|
||||
|
@ -3132,6 +3170,8 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
|
|||
mod: p.mod
|
||||
info: ast.SumType{
|
||||
variants: variant_types
|
||||
is_generic: generic_types.len > 0
|
||||
generic_types: generic_types
|
||||
}
|
||||
is_public: is_pub
|
||||
})
|
||||
|
@ -3141,6 +3181,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl {
|
|||
typ: typ
|
||||
is_pub: is_pub
|
||||
variants: sum_variants
|
||||
generic_types: generic_types
|
||||
pos: decl_pos
|
||||
comments: comments
|
||||
}
|
||||
|
|
|
@ -47,18 +47,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
name_pos)
|
||||
return ast.StructDecl{}
|
||||
}
|
||||
mut generic_types := []ast.Type{}
|
||||
if p.tok.kind == .lt {
|
||||
p.next()
|
||||
for {
|
||||
generic_types << p.parse_type()
|
||||
if p.tok.kind != .comma {
|
||||
break
|
||||
}
|
||||
p.next()
|
||||
}
|
||||
p.check(.gt)
|
||||
}
|
||||
generic_types := p.parse_generic_type_list()
|
||||
no_body := p.tok.kind != .lcbr
|
||||
if language == .v && no_body {
|
||||
p.error('`$p.tok.lit` lacks body')
|
||||
|
@ -456,6 +445,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
|||
p.check_for_impure_v(language, name_pos)
|
||||
modless_name := p.check_name()
|
||||
interface_name := p.prepend_mod(modless_name).clone()
|
||||
generic_types := p.parse_generic_type_list()
|
||||
// println('interface decl $interface_name')
|
||||
p.check(.lcbr)
|
||||
pre_comments := p.eat_comments({})
|
||||
|
@ -473,6 +463,8 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
|||
mod: p.mod
|
||||
info: ast.Interface{
|
||||
types: []
|
||||
is_generic: generic_types.len > 0
|
||||
generic_types: generic_types
|
||||
}
|
||||
)
|
||||
if reg_idx == -1 {
|
||||
|
@ -622,6 +614,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
|||
is_pub: is_pub
|
||||
pos: pos
|
||||
pre_comments: pre_comments
|
||||
generic_types: generic_types
|
||||
mut_pos: mut_pos
|
||||
name_pos: name_pos
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
interface Gettable<T> {
|
||||
get() T
|
||||
}
|
||||
|
||||
struct Animal<T> {
|
||||
metadata T
|
||||
}
|
||||
|
||||
fn (a Animal<T>) get<T>() T {
|
||||
return a.metadata
|
||||
}
|
||||
|
||||
// different struct implementing the same interface:
|
||||
struct Mineral<T> {
|
||||
value T
|
||||
}
|
||||
|
||||
fn (m Mineral<T>) get<T>() T {
|
||||
return m.value
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
fn extract<T>(xs []Gettable<T>) []T {
|
||||
return xs.map(it.get())
|
||||
}
|
||||
|
||||
fn extract_basic<T>(xs Gettable<T>) T {
|
||||
return xs.get()
|
||||
}
|
||||
|
||||
fn test_extract() {
|
||||
a := Animal<int>{123}
|
||||
b := Animal<int>{456}
|
||||
c := Mineral<int>{789}
|
||||
|
||||
arr := [Gettable<int>(a), Gettable<int>(b), Gettable<int>(c)]
|
||||
assert typeof(arr).name == '[]Gettable<int>'
|
||||
|
||||
x := extract<int>(arr)
|
||||
assert x == [123, 456, 789]
|
||||
}
|
||||
|
||||
// fn test_extract_multiple_instance_types() {
|
||||
// a := Animal<string>{'123'}
|
||||
// b := Animal<string>{'456'}
|
||||
// c := Mineral<string>{'789'}
|
||||
|
||||
// arr := [Gettable<string>(a), Gettable<string>(b), Gettable<string>(c)]
|
||||
// assert typeof(arr).name == '[]Gettable<string>'
|
||||
|
||||
// x := extract<string>(arr)
|
||||
// assert x == ['123', '456', '789']
|
||||
// }
|
||||
|
||||
fn test_extract_basic() {
|
||||
a := Animal<int>{123}
|
||||
b := Animal<int>{456}
|
||||
c := Mineral<int>{789}
|
||||
|
||||
aa := extract_basic(a)
|
||||
bb := extract_basic(b)
|
||||
cc := extract_basic(c)
|
||||
assert '$aa | $bb | $cc' == '123 | 456 | 789'
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
struct None {}
|
||||
|
||||
// not named `Option` to avoid conflicts with the built-in type:
|
||||
type MyOption<T> = Error | None | T
|
||||
|
||||
fn unwrap_if<T>(o MyOption<T>) T {
|
||||
if o is T {
|
||||
return o
|
||||
}
|
||||
panic('no value')
|
||||
}
|
||||
|
||||
fn unwrap_match<T>(o MyOption<T>) string {
|
||||
match o {
|
||||
None {
|
||||
return 'none'
|
||||
}
|
||||
Error {
|
||||
return 'none'
|
||||
}
|
||||
T {
|
||||
return 'value'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_generic_sumtype_unwrapping() {
|
||||
y := MyOption<bool>(false)
|
||||
|
||||
assert unwrap_if(y) == false
|
||||
assert unwrap_match(y) == 'value'
|
||||
}
|
||||
|
||||
fn test_generic_sumtype_auto_str() {
|
||||
x := MyOption<string>('hi')
|
||||
y := MyOption<bool>(None{})
|
||||
assert '$x, $y' == "MyOption<string>('hi'), MyOption<bool>(None{})"
|
||||
}
|
||||
|
||||
struct Foo<T> {
|
||||
x T
|
||||
}
|
||||
|
||||
struct Bar<T> {
|
||||
x T
|
||||
}
|
||||
|
||||
type MyType<T> = Bar<T> | Foo<T>
|
||||
|
||||
fn test_generic_struct_members() {
|
||||
// TODO: this is currently needed to properly resolve that variant's type:
|
||||
_ = Bar<string>{''}
|
||||
|
||||
f := Foo<string>{'hi'}
|
||||
t := MyType<string>(f)
|
||||
assert t.type_name() == 'Foo<string>'
|
||||
// accessing a field common to all variants, just like with a normal sumtype:
|
||||
assert t.x == 'hi'
|
||||
}
|
||||
|
||||
type MultiGeneric<X, Y, Z> = X | Y | Z
|
||||
|
||||
fn test_multi_generic_type() {
|
||||
mut m := MultiGeneric<bool, int, string>(1234)
|
||||
m = 'hi'
|
||||
match m {
|
||||
bool {
|
||||
assert false
|
||||
}
|
||||
int {
|
||||
assert false
|
||||
}
|
||||
string {
|
||||
return
|
||||
}
|
||||
}
|
||||
assert false
|
||||
}
|
|
@ -479,9 +479,9 @@ pub fn (kind Kind) is_infix() bool {
|
|||
|
||||
// Pass ast.builtin_type_names
|
||||
// Note: can't import table here due to circular module dependency
|
||||
pub fn (tok &Token) can_start_type(builtin_type_names []string) bool {
|
||||
pub fn (tok &Token) can_start_type(builtin_types []string) bool {
|
||||
match tok.kind {
|
||||
.name { return tok.lit[0].is_capital() || tok.lit in builtin_type_names }
|
||||
.name { return (tok.lit.len > 0 && tok.lit[0].is_capital()) || tok.lit in builtin_types }
|
||||
// Note: return type (T1, T2) should be handled elsewhere
|
||||
.amp, .key_fn, .lsbr, .question { return true }
|
||||
else {}
|
||||
|
|
Loading…
Reference in New Issue