diff --git a/vlib/builtin/option.v b/vlib/builtin/option.v index 95f0c01945..0d4e075169 100644 --- a/vlib/builtin/option.v +++ b/vlib/builtin/option.v @@ -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 { diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index 688384ac4e..0b635815c0 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -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 `_` diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index c40121599f..3cd8b3f5b8 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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: diff --git a/vlib/v/checker/tests/print_optional_type_err.out b/vlib/v/checker/tests/print_optional_type_err.out deleted file mode 100644 index 26a9f68c40..0000000000 --- a/vlib/v/checker/tests/print_optional_type_err.out +++ /dev/null @@ -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 | } diff --git a/vlib/v/checker/tests/print_optional_type_err.vv b/vlib/v/checker/tests/print_optional_type_err.vv deleted file mode 100644 index 738398aa5f..0000000000 --- a/vlib/v/checker/tests/print_optional_type_err.vv +++ /dev/null @@ -1,4 +0,0 @@ -import os -fn main() { - println(os.ls('.')) -} diff --git a/vlib/v/gen/auto_str_methods.v b/vlib/v/gen/auto_str_methods.v index 2f2e4fdcb4..9d2daac00a 100644 --- a/vlib/v/gen/auto_str_methods.v +++ b/vlib/v/gen/auto_str_methods.v @@ -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,9 +90,49 @@ 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) sym_has_str_method, _, _ := sym.str_method_info() diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 612509dca0..0c4881e541 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -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}(') diff --git a/vlib/v/gen/str.v b/vlib/v/gen/str.v index 67c6e1efa3..7a5b5b9ba7 100644 --- a/vlib/v/gen/str.v +++ b/vlib/v/gen/str.v @@ -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) } diff --git a/vlib/v/tests/str_gen_test.v b/vlib/v/tests/str_gen_test.v index 7908b11b75..0cba2baf57 100644 --- a/vlib/v/tests/str_gen_test.v +++ b/vlib/v/tests/str_gen_test.v @@ -290,3 +290,71 @@ fn test_multi_generic_struct() { assert '$x' == 'MultiGenericStruct{\n t: TestStruct{\n x: 0\n }\n x: TestStruct{\n x: 0\n }\n}' assert x.str() == 'MultiGenericStruct{\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}' +} +*/ \ No newline at end of file