cgen: initial option auto str support (#7004)

pull/7059/head
Daniel Däschle 2020-12-01 04:00:23 +01:00 committed by GitHub
parent 6af082e70e
commit 879d238887
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 115 additions and 35 deletions

View File

@ -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 {

View File

@ -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 `_`

View File

@ -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:

View File

@ -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 | }

View File

@ -1,4 +0,0 @@
import os
fn main() {
println(os.ls('.'))
}

View File

@ -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) {

View File

@ -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}(')

View File

@ -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)
} }

View File

@ -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}'
}
*/