Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cgen: make possible to initialize sumtype with default type (first variant) #22039

Merged
merged 13 commits into from
Aug 16, 2024
4 changes: 2 additions & 2 deletions vlib/v/ast/ast.v
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ pub const int_type_name = $if new_int ? {
'int'
}

pub type Expr = AnonFn
pub type Expr = NodeError
| AnonFn
| ArrayDecompose
| ArrayInit
| AsCast
Expand Down Expand Up @@ -53,7 +54,6 @@ pub type Expr = AnonFn
| MapInit
| MatchExpr
| Nil
| NodeError
| None
| OffsetOf
| OrExpr
Expand Down
12 changes: 10 additions & 2 deletions vlib/v/ast/types.v
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import v.token

pub type Type = int

pub type TypeInfo = Aggregate
pub struct UnknownTypeInfo {}

pub type TypeInfo = UnknownTypeInfo
| Aggregate
| Alias
| Array
| ArrayFixed
Expand Down Expand Up @@ -929,12 +932,17 @@ pub fn (t &TypeSymbol) is_empty_struct_array() bool {
if t.info is ArrayFixed {
elem_sym := global_table.final_sym(t.info.elem_type)
if elem_sym.info is Struct {
return elem_sym.info.fields.len == 0
return elem_sym.info.is_empty_struct()
}
}
return false
}

@[inline]
pub fn (t &Struct) is_empty_struct() bool {
return t.fields.len == 0 && t.embeds.len == 0
}

pub fn (t &TypeSymbol) is_array_fixed() bool {
if t.info is ArrayFixed {
return true
Expand Down
3 changes: 0 additions & 3 deletions vlib/v/checker/struct.v
Original file line number Diff line number Diff line change
Expand Up @@ -473,9 +473,6 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
c.ensure_type_exists(node.typ, node.pos)
}
type_sym := c.table.sym(node.typ)
if !c.inside_unsafe && type_sym.kind == .sum_type {
c.note('direct sum type init (`x := SumType{}`) will be removed soon', node.pos)
}
// Make sure the first letter is capital, do not allow e.g. `x := string{}`,
// but `x := T{}` is ok.
if !c.is_builtin_mod && !c.inside_unsafe && type_sym.language == .v
Expand Down
4 changes: 3 additions & 1 deletion vlib/v/fmt/fmt.v
Original file line number Diff line number Diff line change
Expand Up @@ -1757,7 +1757,9 @@ pub fn (mut f Fmt) sum_type_decl(node ast.SumTypeDecl) {
variants << Variant{f.table.type_to_str_using_aliases(variant.typ, f.mod2alias), i}
f.mark_types_import_as_used(variant.typ)
}
variants.sort(a.name < b.name)
// The first variant is now used as the default variant when doing `a:= Sumtype{}`, i.e. a change in semantics.
// Sorting is disabled, because it is no longer a cosmetic change - it can change the default variant.
// variants.sort(a.name < b.name)

mut separator := ' | '
mut is_multiline := false
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/fmt/tests/types_expected.vv
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ type Float = f32 | f64

// Callback represents all the possible callbacks
pub type Callback = ConnectedCallback // connected callback
| LoggedOnCallback // logged on callback
| DisconnectedCallback // disconnected callback
| LoggedOffCallback // logged off callback
| LoggedOnCallback // logged on callback

struct ConnectedCallback {}

Expand Down
39 changes: 29 additions & 10 deletions vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -7204,6 +7204,25 @@ fn c_fn_name(name_ string) string {
return name
}

fn (mut g Gen) type_default_sumtype(typ_ ast.Type, sym ast.TypeSymbol) string {
first_typ := g.unwrap_generic((sym.info as ast.SumType).variants[0])
first_sym := g.table.sym(first_typ)
first_styp := g.typ(first_typ)
first_field := g.get_sumtype_variant_name(first_typ, first_sym)
default_str := if first_typ.has_flag(.option) {
'(${first_styp}){ .state=2, .err=_const_none__, .data={EMPTY_STRUCT_INITIALIZATION} }'
} else if first_sym.info is ast.Struct && first_sym.info.is_empty_struct() {
'{EMPTY_STRUCT_INITIALIZATION}'
} else {
g.type_default(first_typ)
}
if default_str[0] == `{` {
return '(${g.typ(typ_)}){._${first_field}=HEAP(${first_styp}, ((${first_styp})${default_str})),._typ=${int(first_typ)}}'
} else {
return '(${g.typ(typ_)}){._${first_field}=HEAP(${first_styp}, (${default_str})),._typ=${int(first_typ)}}'
}
}

fn (mut g Gen) type_default(typ_ ast.Type) string {
typ := g.unwrap_generic(typ_)
if typ.has_option_or_result() {
Expand All @@ -7228,7 +7247,10 @@ fn (mut g Gen) type_default(typ_ ast.Type) string {
}
return '{0}'
}
.interface_, .sum_type, .multi_return, .thread {
.sum_type {
return '{0}' // g.type_default_sumtype(typ, sym)
}
.interface_, .multi_return, .thread {
return '{0}'
}
.alias {
Expand Down Expand Up @@ -7292,7 +7314,7 @@ fn (mut g Gen) type_default(typ_ ast.Type) string {
for field in info.fields {
field_sym := g.table.sym(field.typ)
if field.has_default_expr
|| field_sym.kind in [.array, .map, .string, .bool, .alias, .i8, .i16, .int, .i64, .u8, .u16, .u32, .u64, .f32, .f64, .char, .voidptr, .byteptr, .charptr, .struct_, .chan] {
|| field_sym.kind in [.array, .map, .string, .bool, .alias, .i8, .i16, .int, .i64, .u8, .u16, .u32, .u64, .f32, .f64, .char, .voidptr, .byteptr, .charptr, .struct_, .chan, .sum_type] {
if sym.language == .c && !field.has_default_expr {
continue
}
Expand All @@ -7307,14 +7329,11 @@ fn (mut g Gen) type_default(typ_ ast.Type) string {
}
init_str += '.${field_name} = ${expr_str},'
} else {
mut zero_str := g.type_default(field.typ)
if zero_str == '{0}' {
if field_sym.info is ast.Struct && field_sym.language == .v {
if field_sym.info.fields.len == 0
&& field_sym.info.embeds.len == 0 {
zero_str = '{EMPTY_STRUCT_INITIALIZATION}'
}
}
zero_str := if field_sym.language == .v && field_sym.info is ast.Struct
&& field_sym.info.is_empty_struct() {
'{EMPTY_STRUCT_INITIALIZATION}'
} else {
g.type_default(field.typ)
}
init_str += '.${field_name} = ${zero_str},'
}
Expand Down
22 changes: 7 additions & 15 deletions vlib/v/gen/c/struct.v
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ fn (mut g Gen) struct_init(node ast.StructInit) {
return
}
mut sym := g.table.final_sym(g.unwrap_generic(node.typ))
if sym.kind == .sum_type {
g.write(g.type_default_sumtype(g.unwrap_generic(node.typ), sym))
return
}
is_amp := g.is_amp
is_multiline := node.init_fields.len > 5
g.is_amp = false // reset the flag immediately so that other struct inits in this expr are handled correctly
Expand Down Expand Up @@ -443,6 +447,9 @@ fn (mut g Gen) zero_struct_field(field ast.StructField) bool {
} else if field.typ.has_flag(.option) {
g.gen_option_error(field.typ, ast.None{})
return true
} else if sym.info is ast.SumType {
g.write(g.type_default_sumtype(field.typ, sym))
return true
} else if sym.info is ast.ArrayFixed {
g.write('{')
for i in 0 .. sym.info.size {
Expand All @@ -462,21 +469,6 @@ fn (mut g Gen) zero_struct_field(field ast.StructField) bool {
return true
}

fn (mut g Gen) is_empty_struct(t Type) bool {
sym := t.unaliased_sym
match sym.info {
ast.Struct {
if sym.info.fields.len > 0 || sym.info.embeds.len > 0 {
return false
}
return true
}
else {
return false
}
}
}

fn (mut g Gen) struct_decl(s ast.Struct, name string, is_anon bool) {
if s.is_generic {
return
Expand Down
40 changes: 32 additions & 8 deletions vlib/v/gen/c/testdata/sumtype_pass_by_reference.out
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
&Expr(ParExpr{
expr: Expr(InfixExpr{
left: unknown sum type value
right: unknown sum type value
left: Expr(InfixExpr{
left: unknown sum type value
right: unknown sum type value
})
right: Expr(InfixExpr{
left: unknown sum type value
right: unknown sum type value
})
})
})
&Expr(ParExpr{
expr: Expr(InfixExpr{
left: unknown sum type value
right: unknown sum type value
left: Expr(InfixExpr{
left: unknown sum type value
right: unknown sum type value
})
right: Expr(InfixExpr{
left: unknown sum type value
right: unknown sum type value
})
})
})
&Expr(InfixExpr{
left: unknown sum type value
right: unknown sum type value
left: Expr(InfixExpr{
left: unknown sum type value
right: unknown sum type value
})
right: Expr(InfixExpr{
left: unknown sum type value
right: unknown sum type value
})
})
&Expr(InfixExpr{
left: unknown sum type value
right: unknown sum type value
left: Expr(InfixExpr{
left: unknown sum type value
right: unknown sum type value
})
right: Expr(InfixExpr{
left: unknown sum type value
right: unknown sum type value
})
})
37 changes: 37 additions & 0 deletions vlib/v/tests/sumtype_init_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
type MyType = int | string
type MyType2 = bool | int | string
type MyType3 = string | u64
type MyType4 = ?string | bool

struct Test {
a MyType
b MyType2
c MyType3
d MyType4
}

fn test_struct() {
t := Test{}
assert dump(t.a) == MyType(0)
assert dump(t.b) == MyType2(false)
assert dump(t.c) == MyType3('')
assert dump(t.d) == MyType4(?string(none))
}

fn test_main() {
x := MyType{}
println(x)
assert x == MyType(0)

y := MyType2{}
println(y)
assert y == MyType2(false)

w := MyType3{}
println(w)
assert w == MyType3('')

z := MyType4{}
println(z)
assert z == MyType4(?string(none))
}
4 changes: 2 additions & 2 deletions vlib/v/tests/sumtype_str_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ fn test_in_struct() {

fn test_unknown_value() {
c := Container{}
assert '${c}' == 'Container{\n st: unknown sum type value\n}'
assert c.str() == 'Container{\n st: unknown sum type value\n}'
assert '${c}' == "Container{\n st: ST(Abc{\n foo: 0\n bar: false\n str: ''\n })\n}"
assert c.str() == "Container{\n st: ST(Abc{\n foo: 0\n bar: false\n str: ''\n })\n}"
}

fn test_nested_in_struct() {
Expand Down
2 changes: 1 addition & 1 deletion vlib/x/json2/tests/encode_struct_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ fn test_pointer() {
}

fn test_sumtypes() {
assert json.encode(StructType[SumTypes]{}) == '{}' // is_none := val.$(field.name).str() == 'unknown sum type value'
assert json.encode(StructType[SumTypes]{}) == '{"val":{"val":""}}' // is_none := val.$(field.name).str() == 'unknown sum type value'
spytheman marked this conversation as resolved.
Show resolved Hide resolved
assert json.encode(StructType[SumTypes]{ val: '' }) == '{"val":""}'
assert json.encode(StructType[SumTypes]{ val: 'a' }) == '{"val":"a"}'

Expand Down
Loading