cgen: initial option auto str support (#7004)
parent
6af082e70e
commit
879d238887
|
@ -23,16 +23,6 @@ struct OptionBase {
|
|||
// 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 opt_ok2(data voidptr, mut option &OptionBase, size int) {
|
||||
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 {
|
||||
if typ.is_float() {
|
||||
if ftyp.has_flag(.optional) {
|
||||
return `s`
|
||||
} else if typ.is_float() {
|
||||
return `g`
|
||||
} else if typ.is_signed() || typ.is_any_int() {
|
||||
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] ||
|
||||
sym.kind in
|
||||
[.enum_, .array, .array_fixed, .struct_, .map, .multi_return, .sum_type] || ftyp.has_flag(.optional) ||
|
||||
sym.has_method('str') {
|
||||
[.enum_, .array, .array_fixed, .struct_, .map, .multi_return, .sum_type] || sym.has_method('str') {
|
||||
return `s`
|
||||
} else {
|
||||
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 {
|
||||
c.expected_type = table.string_type
|
||||
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])`
|
||||
// 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
|
||||
}
|
||||
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 ? {
|
||||
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,8 +90,48 @@ fn (mut g Gen) gen_str_for_type_with_styp(typ table.Type, styp string) string {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
sym := g.table.get_type_symbol(info.parent_type)
|
||||
|
|
|
@ -4742,11 +4742,6 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) ?bool {
|
|||
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 {
|
||||
str_fn_name := g.gen_str_for_type(typ)
|
||||
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 {
|
||||
g.expr(expr)
|
||||
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` {
|
||||
if typ.is_signed() && node.fmts[i] in [`x`, `X`, `o`] {
|
||||
// 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 {
|
||||
g.expr(expr)
|
||||
}
|
||||
} else if node.fmts[i] == `s` {
|
||||
g.gen_expr_to_string(expr, typ)
|
||||
} else {
|
||||
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.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