cgen: initial option auto str support (#7004)
parent
6af082e70e
commit
879d238887
|
@ -23,16 +23,6 @@ struct OptionBase {
|
||||||
// derived Option_xxx types
|
// derived Option_xxx types
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (o OptionBase) str() string {
|
|
||||||
if o.ok && !o.is_none {
|
|
||||||
return 'Option{ valid }'
|
|
||||||
}
|
|
||||||
if o.is_none {
|
|
||||||
return 'Option{ none }'
|
|
||||||
}
|
|
||||||
return 'Option{ error: "${o.error}" }'
|
|
||||||
}
|
|
||||||
|
|
||||||
// `fn foo() ?Foo { return foo }` => `fn foo() ?Foo { return opt_ok(foo); }`
|
// `fn foo() ?Foo { return foo }` => `fn foo() ?Foo { return opt_ok(foo); }`
|
||||||
fn opt_ok2(data voidptr, mut option &OptionBase, size int) {
|
fn opt_ok2(data voidptr, mut option &OptionBase, size int) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
@ -305,7 +305,9 @@ pub fn (mut c Checker) symmetric_check(left table.Type, right table.Type) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (c &Checker) get_default_fmt(ftyp table.Type, typ table.Type) byte {
|
pub fn (c &Checker) get_default_fmt(ftyp table.Type, typ table.Type) byte {
|
||||||
if typ.is_float() {
|
if ftyp.has_flag(.optional) {
|
||||||
|
return `s`
|
||||||
|
} else if typ.is_float() {
|
||||||
return `g`
|
return `g`
|
||||||
} else if typ.is_signed() || typ.is_any_int() {
|
} else if typ.is_signed() || typ.is_any_int() {
|
||||||
return `d`
|
return `d`
|
||||||
|
@ -325,8 +327,7 @@ pub fn (c &Checker) get_default_fmt(ftyp table.Type, typ table.Type) byte {
|
||||||
}
|
}
|
||||||
if ftyp in [table.string_type, table.bool_type] ||
|
if ftyp in [table.string_type, table.bool_type] ||
|
||||||
sym.kind in
|
sym.kind in
|
||||||
[.enum_, .array, .array_fixed, .struct_, .map, .multi_return, .sum_type] || ftyp.has_flag(.optional) ||
|
[.enum_, .array, .array_fixed, .struct_, .map, .multi_return, .sum_type] || sym.has_method('str') {
|
||||||
sym.has_method('str') {
|
|
||||||
return `s`
|
return `s`
|
||||||
} else {
|
} else {
|
||||||
return `_`
|
return `_`
|
||||||
|
|
|
@ -1510,10 +1510,6 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
||||||
if fn_name in ['println', 'print'] && call_expr.args.len > 0 {
|
if fn_name in ['println', 'print'] && call_expr.args.len > 0 {
|
||||||
c.expected_type = table.string_type
|
c.expected_type = table.string_type
|
||||||
call_expr.args[0].typ = c.expr(call_expr.args[0].expr)
|
call_expr.args[0].typ = c.expr(call_expr.args[0].expr)
|
||||||
// check optional argument
|
|
||||||
if call_expr.args[0].typ.has_flag(.optional) {
|
|
||||||
c.error('cannot print optional type', call_expr.args[0].expr.position())
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
// TODO: optimize `struct T{} fn (t &T) str() string {return 'abc'} mut a := []&T{} a << &T{} println(a[0])`
|
// TODO: optimize `struct T{} fn (t &T) str() string {return 'abc'} mut a := []&T{} a << &T{} println(a[0])`
|
||||||
// It currently generates:
|
// It currently generates:
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
vlib/v/checker/tests/print_optional_type_err.vv:3:13: error: cannot print optional type
|
|
||||||
1 | import os
|
|
||||||
2 | fn main() {
|
|
||||||
3 | println(os.ls('.'))
|
|
||||||
| ~~~~~~~
|
|
||||||
4 | }
|
|
|
@ -1,4 +0,0 @@
|
||||||
import os
|
|
||||||
fn main() {
|
|
||||||
println(os.ls('.'))
|
|
||||||
}
|
|
|
@ -42,7 +42,7 @@ fn (mut g Gen) gen_str_for_type_with_styp(typ table.Type, styp string) string {
|
||||||
return str_fn_name_no_ptr
|
return str_fn_name_no_ptr
|
||||||
}
|
}
|
||||||
already_generated_key := '$styp:$str_fn_name'
|
already_generated_key := '$styp:$str_fn_name'
|
||||||
if !sym_has_str_method && already_generated_key !in g.str_types {
|
if !sym_has_str_method && already_generated_key !in g.str_types && !typ.has_flag(.optional) {
|
||||||
$if debugautostr ? {
|
$if debugautostr ? {
|
||||||
eprintln('> gen_str_for_type_with_styp: |typ: ${typ:5}, ${sym.name:20}|has_str: ${sym_has_str_method:5}|expects_ptr: ${str_method_expects_ptr:5}|nr_args: ${str_nr_args:1}|fn_name: ${str_fn_name:20}')
|
eprintln('> gen_str_for_type_with_styp: |typ: ${typ:5}, ${sym.name:20}|has_str: ${sym_has_str_method:5}|expects_ptr: ${str_method_expects_ptr:5}|nr_args: ${str_nr_args:1}|fn_name: ${str_fn_name:20}')
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,47 @@ fn (mut g Gen) gen_str_for_type_with_styp(typ table.Type, styp string) string {
|
||||||
}
|
}
|
||||||
return 'varg_$str_fn_name'
|
return 'varg_$str_fn_name'
|
||||||
}
|
}
|
||||||
|
if typ.has_flag(.optional) {
|
||||||
|
option_already_generated_key := 'option_$already_generated_key'
|
||||||
|
if option_already_generated_key !in g.str_types {
|
||||||
|
g.gen_str_for_option(typ, styp, str_fn_name)
|
||||||
|
g.str_types << option_already_generated_key
|
||||||
|
}
|
||||||
return str_fn_name
|
return str_fn_name
|
||||||
|
}
|
||||||
|
return str_fn_name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) gen_str_for_option(typ table.Type, styp string, str_fn_name string) {
|
||||||
|
parent_type := typ.clear_flag(.optional)
|
||||||
|
sym := g.table.get_type_symbol(parent_type)
|
||||||
|
sym_has_str_method, _, _ := sym.str_method_info()
|
||||||
|
sym_name := sym.name.replace('.', '__')
|
||||||
|
mut parent_str_fn_name := styp_to_str_fn_name(sym_name)
|
||||||
|
if !sym_has_str_method {
|
||||||
|
parent_styp := g.typ(parent_type)
|
||||||
|
parent_str_fn_name = g.gen_str_for_type_with_styp(parent_type, parent_styp)
|
||||||
|
}
|
||||||
|
g.type_definitions.writeln('string ${str_fn_name}($styp it); // auto')
|
||||||
|
g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) { return indent_${str_fn_name}(it, 0); }')
|
||||||
|
g.type_definitions.writeln('string indent_${str_fn_name}($styp it, int indent_count); // auto')
|
||||||
|
g.auto_str_funcs.writeln('string indent_${str_fn_name}($styp it, int indent_count) {')
|
||||||
|
g.auto_str_funcs.writeln('\tstring res;')
|
||||||
|
g.auto_str_funcs.writeln('\tif (it.is_none) {')
|
||||||
|
g.auto_str_funcs.writeln('\t\tres = tos_lit("none");')
|
||||||
|
g.auto_str_funcs.writeln('\t} else if (it.ok) {')
|
||||||
|
if typ.is_string() {
|
||||||
|
g.auto_str_funcs.writeln('\t\tres = _STR("\'%.*s\\000\'", 2, ${parent_str_fn_name}(*($sym_name*)it.data));')
|
||||||
|
} else if sym.kind == .struct_ && !sym_has_str_method {
|
||||||
|
g.auto_str_funcs.writeln('\t\tres = indent_${parent_str_fn_name}(*($sym_name*)it.data, indent_count);')
|
||||||
|
} else {
|
||||||
|
g.auto_str_funcs.writeln('\t\tres = ${parent_str_fn_name}(*($sym_name*)it.data);')
|
||||||
|
}
|
||||||
|
g.auto_str_funcs.writeln('\t} else {')
|
||||||
|
g.auto_str_funcs.writeln('\t\tres = _STR("error: \'%.*s\\000\'", 2, it.v_error);')
|
||||||
|
g.auto_str_funcs.writeln('\t}')
|
||||||
|
g.auto_str_funcs.writeln('\treturn _STR("Option(%.*s\\000)", 2, res);')
|
||||||
|
g.auto_str_funcs.writeln('}')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) gen_str_for_alias(info table.Alias, styp string, str_fn_name string) {
|
fn (mut g Gen) gen_str_for_alias(info table.Alias, styp string, str_fn_name string) {
|
||||||
|
|
|
@ -4742,11 +4742,6 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) ?bool {
|
||||||
g.write(')')
|
g.write(')')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if g.typ(typ).starts_with('Option') {
|
|
||||||
str_fn_name := 'OptionBase_str'
|
|
||||||
g.write('${str_fn_name}(*(OptionBase*)&')
|
|
||||||
g.expr(expr)
|
|
||||||
g.write(')')
|
|
||||||
} else {
|
} else {
|
||||||
str_fn_name := g.gen_str_for_type(typ)
|
str_fn_name := g.gen_str_for_type(typ)
|
||||||
g.write('${str_fn_name}(')
|
g.write('${str_fn_name}(')
|
||||||
|
|
|
@ -269,6 +269,8 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
|
||||||
} else if typ == table.bool_type {
|
} else if typ == table.bool_type {
|
||||||
g.expr(expr)
|
g.expr(expr)
|
||||||
g.write(' ? _SLIT("true") : _SLIT("false")')
|
g.write(' ? _SLIT("true") : _SLIT("false")')
|
||||||
|
} else if node.fmts[i] == `s` {
|
||||||
|
g.gen_expr_to_string(expr, typ)
|
||||||
} else if typ.is_number() || typ.is_pointer() || node.fmts[i] == `d` {
|
} else if typ.is_number() || typ.is_pointer() || node.fmts[i] == `d` {
|
||||||
if typ.is_signed() && node.fmts[i] in [`x`, `X`, `o`] {
|
if typ.is_signed() && node.fmts[i] in [`x`, `X`, `o`] {
|
||||||
// convert to unsigned first befors C's integer propagation strikes
|
// convert to unsigned first befors C's integer propagation strikes
|
||||||
|
@ -286,8 +288,6 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
|
||||||
} else {
|
} else {
|
||||||
g.expr(expr)
|
g.expr(expr)
|
||||||
}
|
}
|
||||||
} else if node.fmts[i] == `s` {
|
|
||||||
g.gen_expr_to_string(expr, typ)
|
|
||||||
} else {
|
} else {
|
||||||
g.expr(expr)
|
g.expr(expr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -290,3 +290,71 @@ fn test_multi_generic_struct() {
|
||||||
assert '$x' == 'MultiGenericStruct<TestStruct, TestStruct>{\n t: TestStruct{\n x: 0\n }\n x: TestStruct{\n x: 0\n }\n}'
|
assert '$x' == 'MultiGenericStruct<TestStruct, TestStruct>{\n t: TestStruct{\n x: 0\n }\n x: TestStruct{\n x: 0\n }\n}'
|
||||||
assert x.str() == 'MultiGenericStruct<TestStruct, TestStruct>{\n t: TestStruct{\n x: 0\n }\n x: TestStruct{\n x: 0\n }\n}'
|
assert x.str() == 'MultiGenericStruct<TestStruct, TestStruct>{\n t: TestStruct{\n x: 0\n }\n x: TestStruct{\n x: 0\n }\n}'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_option_err() ?string {
|
||||||
|
return error('this is an error')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_option_err() {
|
||||||
|
assert '$create_option_err()' == 'Option(error: \'this is an error\')'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_option_none() ?string {
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_option_none() {
|
||||||
|
assert '$create_option_none()' == 'Option(none)'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_option_string() ?string {
|
||||||
|
return 'this is a string'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_option_string() {
|
||||||
|
assert '$create_option_string()' == 'Option(\'this is a string\')'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_option_int() ?int {
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_option_int() {
|
||||||
|
assert '$create_option_int()' == 'Option(5)'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_option_array() ?[]int {
|
||||||
|
return [1, 2, 3]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_option_array() {
|
||||||
|
assert '$create_option_array()' == 'Option([1, 2, 3])'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_option_struct() ?TestStruct {
|
||||||
|
return TestStruct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_option_struct() {
|
||||||
|
assert '$create_option_struct()' == 'Option(TestStruct{\n x: 0\n})'
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OptionWrapper {
|
||||||
|
x ?TestStruct
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_struct_with_option() {
|
||||||
|
w := OptionWrapper{}
|
||||||
|
assert '$w' == 'OptionWrapper{\n x: Option(error: \'\')\n}'
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: doesn't work yet
|
||||||
|
struct OptionWrapperInt {
|
||||||
|
x ?int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_struct_with_option() {
|
||||||
|
w := OptionWrapperInt{}
|
||||||
|
assert '$w' == 'OptionWrapperInt{\n x: Option(error: \'\')\n}'
|
||||||
|
}
|
||||||
|
*/
|
Loading…
Reference in New Issue