diff --git a/vlib/builtin/array_test.v b/vlib/builtin/array_test.v index 9abc9b7a7e..8cc0da76cd 100644 --- a/vlib/builtin/array_test.v +++ b/vlib/builtin/array_test.v @@ -4,7 +4,7 @@ const ( ) fn test_pointer() { - mut arr := []*int + mut arr := []&int a := 1 b := 2 c := 3 @@ -14,6 +14,10 @@ fn test_pointer() { assert *arr[0] == 1 arr[1] = &c assert *arr[1] == 3 + mut d_arr := [arr] // [][]&int + d_arr << arr + assert *d_arr[0][1] == 3 + assert *d_arr[1][0] == 1 } fn test_ints() { diff --git a/vlib/builtin/map_test.v b/vlib/builtin/map_test.v index a772be5baf..ce4689a9d3 100644 --- a/vlib/builtin/map_test.v +++ b/vlib/builtin/map_test.v @@ -132,13 +132,13 @@ fn test_various_map_value() { m13['test'] = rune(0) assert m13['test'] == rune(0) - //mut m14 := map[string]voidptr - //m14['test'] = voidptr(0) - //assert m14['test'] == voidptr(0) + mut m14 := map[string]voidptr + m14['test'] = voidptr(0) + assert m14['test'] == voidptr(0) - //mut m15 := map[string]byteptr - //m15['test'] = byteptr(0) - //assert m15['test'] == byteptr(0) + mut m15 := map[string]byteptr + m15['test'] = byteptr(0) + assert m15['test'] == byteptr(0) mut m16 := map[string]i64 m16['test'] = i64(0) @@ -147,6 +147,10 @@ fn test_various_map_value() { mut m17 := map[string]u64 m17['test'] = u64(0) assert m17['test'] == u64(0) + + mut m18 := map[string]&int + m18['test'] = &int(0) + assert m18['test'] == &int(0) } diff --git a/vlib/compiler/compile_errors.v b/vlib/compiler/compile_errors.v index 670771ff4e..2628d40d31 100644 --- a/vlib/compiler/compile_errors.v +++ b/vlib/compiler/compile_errors.v @@ -191,7 +191,7 @@ fn (p mut Parser) print_error_context() { fn normalized_error(s string) string { // Print `[]int` instead of `array_int` in errors - mut res := s.replace('array_', '[]').replace('__', '.').replace('Option_', '?').replace('main.', '') + mut res := s.replace('array_', '[]').replace('__', '.').replace('Option_', '?').replace('main.', '').replace('ptr_', '&') if res.contains('_V_MulRet_') { res = res.replace('_V_MulRet_', '(').replace('_V_', ', ') res = res[..res.len - 1] + ')"' diff --git a/vlib/compiler/comptime.v b/vlib/compiler/comptime.v index f49a49f730..b8c8377c6f 100644 --- a/vlib/compiler/comptime.v +++ b/vlib/compiler/comptime.v @@ -321,7 +321,7 @@ fn (p mut Parser) gen_array_str(typ Type) { is_public: true receiver_typ: typ.name }) - elm_type := typ.name[6..] + elm_type := parse_pointer(typ.name[6..]) elm_type2 := p.table.find_type(elm_type) is_array := elm_type.starts_with('array_') if is_array { @@ -416,7 +416,7 @@ fn (p mut Parser) gen_array_filter(str_typ string, method_ph int) { } array_int b = tmp2; */ - val_type := str_typ[6..] + val_type := parse_pointer(str_typ[6..]) p.open_scope() p.register_var(Var{ name: 'it' @@ -456,7 +456,7 @@ fn (p mut Parser) gen_array_map(str_typ string, method_ph int) string { } array_int b = tmp2; */ - val_type := str_typ[6..] + val_type := parse_pointer(str_typ[6..]) p.open_scope() p.register_var(Var{ name: 'it' @@ -477,7 +477,7 @@ fn (p mut Parser) gen_array_map(str_typ string, method_ph int) string { p.gen(tmp) // TODO why does this `gen()` work? p.check(.rpar) p.close_scope() - return 'array_' + map_type + return 'array_' + stringify_pointer(map_type) } fn (p mut Parser) comptime_if_block(name string) { diff --git a/vlib/compiler/expression.v b/vlib/compiler/expression.v index 93149a65ff..56b9dbc46f 100644 --- a/vlib/compiler/expression.v +++ b/vlib/compiler/expression.v @@ -413,7 +413,7 @@ fn (p mut Parser) name_expr() string { f.typ = p.gen_handle_option_or_else(f.typ, '', fn_call_ph) } else if !p.is_var_decl && !is_or_else && !p.inside_return_expr && f.typ.starts_with('Option_') { - opt_type := f.typ[7..] + opt_type := f.typ[7..].replace('ptr_', '&') p.error('unhandled option type: `?$opt_type`') } // dot after a function call: `get_user().age` @@ -450,7 +450,7 @@ fn (p mut Parser) expression() string { // a << 7 => int tmp = 7; array_push(&a, &tmp); // _PUSH(&a, expression(), tmp, string) tmp := p.get_tmp() - tmp_typ := typ[6..].replace('_ptr', '*') // skip "array_" + tmp_typ := parse_pointer(typ[6..]) // skip "array_" p.check_space(.left_shift) // Get the value we are pushing p.gen(', (') diff --git a/vlib/compiler/fn.v b/vlib/compiler/fn.v index ed4e00c5e5..d16cb9f781 100644 --- a/vlib/compiler/fn.v +++ b/vlib/compiler/fn.v @@ -1353,7 +1353,7 @@ fn (p mut Parser) extract_type_inst(f &Fn, args_ []string) TypeInst { // replace a generic type using TypeInst fn replace_generic_type(gen_type string, ti &TypeInst) string { - mut typ := gen_type.replace('map_', '').replace('varg_', '').trim_right('*') + mut typ := gen_type.replace('map_', '').replace('varg_', '').trim_right('*').replace('ptr_', '') for typ.starts_with('array_') { typ = typ[6..] } diff --git a/vlib/compiler/for.v b/vlib/compiler/for.v index 0776065290..b71d79f8a7 100644 --- a/vlib/compiler/for.v +++ b/vlib/compiler/for.v @@ -92,12 +92,12 @@ fn (p mut Parser) for_st() { p.gen_for_varg_header(i, expr, typ, val) } else if is_arr { - typ = typ[6..].replace('_ptr', '*') + typ = parse_pointer(typ[6..]) p.gen_for_header(i, tmp, typ, val) } else if is_map { i_var_type = 'string' - typ = typ[4..] + typ = parse_pointer(typ[4..]) p.gen_for_map_header(i, tmp, typ, val, typ) } else if is_str { @@ -178,7 +178,7 @@ fn (p mut Parser) for_st() { p.gen_for_range_header(i, range_end, tmp, typ, val) } else if is_arr { - typ = typ[6..].replace('_ptr', '*') // all after `array_` + typ = parse_pointer(typ[6..]) // all after `array_` p.gen_for_header(i, tmp, typ, val) } else if is_str { diff --git a/vlib/compiler/gen_c.v b/vlib/compiler/gen_c.v index f153355c90..c757819ac2 100644 --- a/vlib/compiler/gen_c.v +++ b/vlib/compiler/gen_c.v @@ -96,7 +96,7 @@ fn (p mut Parser) gen_handle_option_or_else(_typ, name string, fn_call_ph int) s is_assign := name.len > 0 tmp := p.get_tmp() p.cgen.set_placeholder(fn_call_ph, '$typ $tmp = ') - typ = typ[7..] + typ = parse_pointer(typ[7..]) p.genln(';') or_tok_idx := p.token_idx p.check(.key_orelse) @@ -324,7 +324,7 @@ fn (p mut Parser) gen_method_call(receiver &Var, receiver_type string, cgen_name if ftyp == 'void*' { if receiver_type.starts_with('array_') { // array_int => int - cast = receiver_type.all_after('array_') + cast = parse_pointer(receiver_type.all_after('array_')) cast = '*($cast*) ' } else { @@ -611,7 +611,7 @@ fn (p mut Parser) cast(typ string) { fn type_default(typ string) string { if typ.starts_with('array_') { - return 'new_array(0, 1, sizeof( ${typ[6..]} ))' + return 'new_array(0, 1, sizeof( ${parse_pointer(typ[6..])} ))' } // Always set pointers to 0 if typ.ends_with('*') { diff --git a/vlib/compiler/get_type.v b/vlib/compiler/get_type.v index 0154006d74..efdd7cbeee 100644 --- a/vlib/compiler/get_type.v +++ b/vlib/compiler/get_type.v @@ -102,7 +102,7 @@ fn (p mut Parser) get_type2() Type { p.error('maps only support string keys for now') } p.check(.rsbr) - val_type := p.get_type() // p.check_name() + val_type := stringify_pointer(p.get_type()) // p.check_name() typ = 'map_$val_type' p.register_map(typ) return Type{ @@ -190,6 +190,7 @@ fn (p mut Parser) get_type2() Type { if arr_level > 0 { // p.log('ARR TYPE="$typ" run=$p.pass') // We come across "[]User" etc ? + typ = stringify_pointer(typ) for i := 0; i < arr_level; i++ { typ = 'array_$typ' } @@ -197,6 +198,7 @@ fn (p mut Parser) get_type2() Type { } p.next() if is_question { + typ = stringify_pointer(typ) typ = 'Option_$typ' p.table.register_type_with_parent(typ, 'Option') } @@ -223,3 +225,21 @@ fn (p mut Parser) get_type2() Type { } } +fn parse_pointer(_typ string) string { + if !_typ.starts_with('ptr_') { + return _typ + } + mut typ := _typ.clone() + for typ.starts_with('ptr_') { + typ = typ[4..] + '*' + } + return typ +} + +fn stringify_pointer(typ string) string { + if !typ.ends_with('*') { + return typ + } + count := typ.count('*') + return 'ptr_'.repeat(count) + typ.trim_right('*') +} diff --git a/vlib/compiler/if_match.v b/vlib/compiler/if_match.v index 2da1ee58d0..6024c48ce1 100644 --- a/vlib/compiler/if_match.v +++ b/vlib/compiler/if_match.v @@ -215,7 +215,7 @@ fn (p mut Parser) if_statement(is_expr bool, elif_depth int) string { p.error('`if x := opt() {` syntax requires a function that returns an optional value') } p.is_var_decl = false - typ := option_type[7..] + typ := parse_pointer(option_type[7..]) // Option_User tmp = get_user(1); // if (tmp.ok) { // User user = *(User*)tmp.data; diff --git a/vlib/compiler/parser.v b/vlib/compiler/parser.v index 4ca350d9ba..4c03399f58 100644 --- a/vlib/compiler/parser.v +++ b/vlib/compiler/parser.v @@ -1019,7 +1019,7 @@ fn (p mut Parser) get_type() string { } p.check(.rsbr) val_type := p.get_type() // p.check_name() - typ = 'map_$val_type' + typ = 'map_${stringify_pointer(val_type)}' p.register_map(typ) return typ } @@ -1123,13 +1123,13 @@ fn (p mut Parser) get_type() string { // p.log('ARR TYPE="$typ" run=$p.pass') // We come across "[]User" etc ? for i := 0; i < arr_level; i++ { - typ = 'array_$typ' + typ = 'array_${stringify_pointer(typ)}' } p.register_array(typ) } p.next() if is_question { - typ = 'Option_$typ' + typ = 'Option_${stringify_pointer(typ)}' p.table.register_type_with_parent(typ, 'Option') } // Because the code uses * to see if it's a pointer @@ -1511,13 +1511,13 @@ fn ($v.name mut $v.typ) ${p.cur_fn.name}(...) { p.error_with_token_index('${fn_name}() $err_used_as_value', p.token_idx - 2) } // Allow `num = 4` where `num` is an `?int` - if p.assigned_type.starts_with('Option_') && expr_type == p.assigned_type['Option_'.len..] { + if p.assigned_type.starts_with('Option_') && expr_type == parse_pointer(p.assigned_type['Option_'.len..]) { expr := p.cgen.cur_line[pos..] left := p.cgen.cur_line[..pos] - typ := expr_type.replace('Option_', '') + typ := parse_pointer(expr_type.replace('Option_', '')) p.cgen.resetln(left + 'opt_ok(($typ[]){ $expr }, sizeof($typ))') } - else if expr_type.starts_with('Option_') && p.assigned_type == expr_type['Option_'.len..] && p.tok == .key_orelse { + else if expr_type.starts_with('Option_') && p.assigned_type == parse_pointer(expr_type['Option_'.len..]) && p.tok == .key_orelse { line := p.cgen.cur_line vname := line[..pos].replace('=', '') // TODO cgen line hack if idx:=line.index('='){ @@ -1976,7 +1976,7 @@ fn (p mut Parser) dot(str_typ_ string, method_ph int) string { } if !typ.is_c && !p.is_c_fn_call && !has_field && !has_method && !p.first_pass() { if typ.name.starts_with('Option_') { - opt_type := typ.name[7..] + opt_type := typ.name[7..].replace('ptr_', '&') p.error('unhandled option type: `?$opt_type`') } // println('error in dot():') @@ -2064,7 +2064,7 @@ pub: method.typ = p.gen_handle_option_or_else(method.typ, '', method_ph) } else if !p.is_var_decl && !is_or_else && !p.inside_return_expr && method.typ.starts_with('Option_') { - opt_type := method.typ[7..] + opt_type := method.typ[7..].replace('ptr_', '&') p.error('unhandled option type: `?$opt_type`') } // Methods returning `array` should return `array_string` etc @@ -2073,7 +2073,7 @@ pub: } // Array methods returning `voidptr` (like `last()`) should return element type if method.typ == 'void*' && typ.name.starts_with('array_') { - return typ.name[6..] + return parse_pointer(typ.name[6..]) } // if false && p.tok == .lsbr { // if is_indexer { @@ -2177,7 +2177,7 @@ fn (p mut Parser) index_expr(typ_ string, fn_ph int) string { } if is_arr { if is_arr0 { - typ = typ[6..].replace('_ptr', '*') + typ = parse_pointer(typ[6..]) } p.gen_array_at(typ, is_arr0, fn_ph) } @@ -2187,6 +2187,7 @@ fn (p mut Parser) index_expr(typ_ string, fn_ph int) string { // can only do that later once we know whether there's an "=" or not if is_map { typ = typ.replace('map_', '') + typ = parse_pointer(typ) if typ == 'map' { typ = 'void*' } @@ -2212,7 +2213,7 @@ fn (p mut Parser) index_expr(typ_ string, fn_ph int) string { } if p.tok == .dotdot { if is_arr { - typ = 'array_' + typ + typ = 'array_' + stringify_pointer(typ) } else if is_str { typ = 'string' @@ -2292,7 +2293,7 @@ fn (p mut Parser) index_expr(typ_ string, fn_ph int) string { // } // `m[key]`. no =, just a getter else if (is_map || is_arr || (is_str && !p.builtin_mod)) && is_indexer { - typ = typ.replace('_ptr', '*') + typ = parse_pointer(typ) p.index_get(typ, fn_ph, IndexConfig{ is_arr: is_arr is_map: is_map @@ -2349,7 +2350,7 @@ fn (p mut Parser) indot_expr() string { if !is_arr && !is_map { p.error('`in` requires an array/map') } - if is_arr && arr_typ[6..] != typ { + if is_arr && parse_pointer(arr_typ[6..]) != typ { p.error('bad element type: `$typ` in `$arr_typ`') } if is_map && typ != 'string' { @@ -2471,7 +2472,7 @@ fn (p mut Parser) map_init() string { p.fgen_nl() } p.gen('new_map_init($i, sizeof($val_type), ' + '(string[$i]){ $keys_gen }, ($val_type [$i]){ $vals_gen } )') - typ := 'map_$val_type' + typ := 'map_${stringify_pointer(val_type)}' p.register_map(typ) return typ } @@ -2486,7 +2487,7 @@ fn (p mut Parser) map_init() string { // if !p.table.known_type(val_type) { // p.error('map init unknown type "$val_type"') // } - typ := 'map_$val_type' + typ := 'map_${stringify_pointer(val_type)}' p.register_map(typ) p.gen('new_map(1, sizeof($val_type))') if p.tok == .lcbr { @@ -2593,16 +2594,17 @@ fn (p mut Parser) array_init() string { p.check(.rsbr) // type after `]`? (e.g. "[]string") exp_array := p.expected_type.starts_with('array_') - if p.tok != .name && p.tok != .mul && p.tok != .lsbr && i == 0 && !exp_array { + if p.tok != .name && p.tok != .mul && p.tok != .lsbr && p.tok != .amp && i == 0 && !exp_array { p.error('specify array type: `[]typ` instead of `[]`') } - if i == 0 && (p.tok == .name || p.tok == .mul) && p.tokens[p.token_idx - 2].line_nr == p.tokens[p.token_idx - 1].line_nr { + if i == 0 && (p.tok == .name || p.tok == .mul || p.tok == .amp) && p.tokens[p.token_idx - 2].line_nr == p.tokens[p.token_idx - 1].line_nr { // TODO // vals.len == 0 { if exp_array { - p.error('no need to specify the full array type here, use `[]` instead of `[]${p.expected_type[6..]}`') + type_expected := p.expected_type[6..].replace('ptr_', '&') + p.error('no need to specify the full array type here, use `[]` instead of `[]$type_expected`') } - typ = p.get_type().replace('*', '_ptr') + typ = p.get_type() } else if exp_array && i == 0 { // allow `known_array = []` @@ -2633,9 +2635,9 @@ fn (p mut Parser) array_init() string { // if ptr { // typ += '_ptr" // } - real := typ.replace('_ptr', '*') + real := parse_pointer(typ) p.gen_array_init(real, no_alloc, new_arr_ph, i) - typ = 'array_$typ' + typ = 'array_${stringify_pointer(typ)}' p.register_array(typ) return typ } @@ -2749,10 +2751,10 @@ fn (p mut Parser) return_st() { // Automatically wrap an object inside an option if the function // returns an option: // `return val` => `return opt_ok(val)` - if p.cur_fn.typ.ends_with(expr_type) && !is_none && p.cur_fn.typ.starts_with('Option_') { + if p.cur_fn.typ.ends_with(stringify_pointer(expr_type)) && !is_none && p.cur_fn.typ.starts_with('Option_') { tmp := p.get_tmp() ret := p.cgen.cur_line[ph..] - typ := expr_type.replace('Option_', '') + typ := parse_pointer(expr_type.replace('Option_', '')) p.cgen.resetln('$expr_type $tmp = OPTION_CAST($expr_type)($ret);') p.genln(deferred_text) p.gen('return opt_ok(&$tmp, sizeof($typ))') diff --git a/vlib/compiler/struct.v b/vlib/compiler/struct.v index 687bc2508f..6f6076304a 100644 --- a/vlib/compiler/struct.v +++ b/vlib/compiler/struct.v @@ -428,7 +428,7 @@ fn (p mut Parser) struct_init(typ_ string) string { // init map fields if field_typ.starts_with('map_') { p.gen_struct_field_init(sanitized_name) - p.gen_empty_map(field_typ[4..]) + p.gen_empty_map(parse_pointer(field_typ[4..])) inited_fields << sanitized_name if i != t.fields.len - 1 { p.gen(',') diff --git a/vlib/compiler/table.v b/vlib/compiler/table.v index 612a9e0a3b..d36adfba69 100644 --- a/vlib/compiler/table.v +++ b/vlib/compiler/table.v @@ -679,7 +679,7 @@ fn (p mut Parser) check_types2(got_, expected_ string, throw bool) bool { return true } // Expected type "Option_os__File", got "os__File" - if expected.starts_with('Option_') && expected.ends_with(got) { + if expected.starts_with('Option_') && expected.ends_with(stringify_pointer(got)) { return true } // NsColor* return 0 diff --git a/vlib/compiler/tests/option_test.v b/vlib/compiler/tests/option_test.v index fdebb08903..026f38d1fb 100644 --- a/vlib/compiler/tests/option_test.v +++ b/vlib/compiler/tests/option_test.v @@ -124,3 +124,23 @@ fn test_opt_field() { val := t.opt or { return } assert val == 5 } + +fn opt_ptr(a &int) ?&int { + if isnil(a) { + return none + } + return a +} + +fn test_opt_ptr() { + a := 3 + r1 := opt_ptr(&a) or { + &int(0) + } + assert r1 == &a + r2 := opt_ptr(&int(0)) or { + return + } + println('`$r2` should be none') + assert false +}