Skip to content

Commit

Permalink
v: define a default sumtype value (based on the first variant type) (v…
Browse files Browse the repository at this point in the history
  • Loading branch information
felipensp authored and medvednikov committed Aug 17, 2024
1 parent 0b8ded4 commit 243ca4c
Show file tree
Hide file tree
Showing 11 changed files with 124 additions and 45 deletions.
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 @@ -1775,7 +1775,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 @@ -7213,6 +7213,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 @@ -7237,7 +7256,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 @@ -7301,7 +7323,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 @@ -7316,14 +7338,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'
assert json.encode(StructType[SumTypes]{ val: '' }) == '{"val":""}'
assert json.encode(StructType[SumTypes]{ val: 'a' }) == '{"val":"a"}'

Expand Down

0 comments on commit 243ca4c

Please sign in to comment.