mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
ast, checker, cgen: enable unsafe { nil }
with reference to interface fields in structs (fix #16198) (#16199)
This commit is contained in:
parent
53c6e46a51
commit
fef4dd94e9
@ -1530,7 +1530,7 @@ pub fn (t Table) does_type_implement_interface(typ Type, inter_typ Type) bool {
|
|||||||
}
|
}
|
||||||
// verify fields
|
// verify fields
|
||||||
for ifield in inter_sym.info.fields {
|
for ifield in inter_sym.info.fields {
|
||||||
if ifield.typ == voidptr_type {
|
if ifield.typ == voidptr_type || ifield.typ == nil_type {
|
||||||
// Allow `voidptr` fields in interfaces for now. (for example
|
// Allow `voidptr` fields in interfaces for now. (for example
|
||||||
// to enable .db check in vweb)
|
// to enable .db check in vweb)
|
||||||
if t.struct_has_field(sym, ifield.name) {
|
if t.struct_has_field(sym, ifield.name) {
|
||||||
@ -1549,7 +1549,9 @@ pub fn (t Table) does_type_implement_interface(typ Type, inter_typ Type) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
inter_sym.info.types << typ
|
if typ != voidptr_type && typ != nil_type && !inter_sym.info.types.contains(typ) {
|
||||||
|
inter_sym.info.types << typ
|
||||||
|
}
|
||||||
if !inter_sym.info.types.contains(voidptr_type) {
|
if !inter_sym.info.types.contains(voidptr_type) {
|
||||||
inter_sym.info.types << voidptr_type
|
inter_sym.info.types << voidptr_type
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ pub fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool {
|
|||||||
if expected == ast.byteptr_type {
|
if expected == ast.byteptr_type {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if expected == ast.voidptr_type {
|
if expected == ast.voidptr_type || expected == ast.nil_type {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (expected == ast.bool_type && (got.is_any_kind_of_pointer() || got.is_int()))
|
if (expected == ast.bool_type && (got.is_any_kind_of_pointer() || got.is_int()))
|
||||||
@ -112,7 +112,8 @@ pub fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool {
|
|||||||
if exp_idx == got_idx {
|
if exp_idx == got_idx {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if exp_idx == ast.voidptr_type_idx || exp_idx == ast.byteptr_type_idx
|
if exp_idx == ast.voidptr_type_idx || exp_idx == ast.nil_type_idx
|
||||||
|
|| exp_idx == ast.byteptr_type_idx
|
||||||
|| (expected.is_ptr() && expected.deref().idx() == ast.u8_type_idx) {
|
|| (expected.is_ptr() && expected.deref().idx() == ast.u8_type_idx) {
|
||||||
if got.is_ptr() || got.is_pointer() {
|
if got.is_ptr() || got.is_pointer() {
|
||||||
return true
|
return true
|
||||||
@ -125,7 +126,8 @@ pub fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if got_idx == ast.voidptr_type_idx || got_idx == ast.byteptr_type_idx
|
if got_idx == ast.voidptr_type_idx || got_idx == ast.nil_type_idx
|
||||||
|
|| got_idx == ast.byteptr_type_idx
|
||||||
|| (got_idx == ast.u8_type_idx && got.is_ptr()) {
|
|| (got_idx == ast.u8_type_idx && got.is_ptr()) {
|
||||||
if expected.is_ptr() || expected.is_pointer() {
|
if expected.is_ptr() || expected.is_pointer() {
|
||||||
return true
|
return true
|
||||||
|
@ -850,7 +850,7 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to
|
|||||||
inter_sym.methods
|
inter_sym.methods
|
||||||
}
|
}
|
||||||
// voidptr is an escape hatch, it should be allowed to be passed
|
// voidptr is an escape hatch, it should be allowed to be passed
|
||||||
if utyp != ast.voidptr_type {
|
if utyp != ast.voidptr_type && utyp != ast.nil_type {
|
||||||
// Verify methods
|
// Verify methods
|
||||||
for imethod in imethods {
|
for imethod in imethods {
|
||||||
method := c.table.find_method_with_embeds(typ_sym, imethod.name) or {
|
method := c.table.find_method_with_embeds(typ_sym, imethod.name) or {
|
||||||
@ -900,7 +900,7 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// voidptr is an escape hatch, it should be allowed to be passed
|
// voidptr is an escape hatch, it should be allowed to be passed
|
||||||
if utyp != ast.voidptr_type {
|
if utyp != ast.voidptr_type && utyp != ast.nil_type {
|
||||||
// >> Hack to allow old style custom error implementations
|
// >> Hack to allow old style custom error implementations
|
||||||
// TODO: remove once deprecation period for `IError` methods has ended
|
// TODO: remove once deprecation period for `IError` methods has ended
|
||||||
if inter_sym.idx == ast.error_type_idx
|
if inter_sym.idx == ast.error_type_idx
|
||||||
@ -913,7 +913,12 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inter_sym.info.types << utyp
|
if utyp != ast.voidptr_type && utyp != ast.nil_type && !inter_sym.info.types.contains(utyp) {
|
||||||
|
inter_sym.info.types << utyp
|
||||||
|
}
|
||||||
|
if !inter_sym.info.types.contains(ast.voidptr_type) {
|
||||||
|
inter_sym.info.types << ast.voidptr_type
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -224,7 +224,7 @@ fn (mut c Checker) eval_comptime_const_expr(expr ast.Expr, nlevel int) ?ast.Comp
|
|||||||
if expr.typ == ast.f64_type {
|
if expr.typ == ast.f64_type {
|
||||||
return cast_expr_value.f64() or { return none }
|
return cast_expr_value.f64() or { return none }
|
||||||
}
|
}
|
||||||
if expr.typ == ast.voidptr_type {
|
if expr.typ == ast.voidptr_type || expr.typ == ast.nil_type {
|
||||||
ptrvalue := cast_expr_value.voidptr() or { return none }
|
ptrvalue := cast_expr_value.voidptr() or { return none }
|
||||||
return ast.ComptTimeConstValue(ptrvalue)
|
return ast.ComptTimeConstValue(ptrvalue)
|
||||||
}
|
}
|
||||||
|
@ -1031,8 +1031,9 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
|
|||||||
// it can lead to codegen errors (except for 'magic' functions like `json.encode` that,
|
// it can lead to codegen errors (except for 'magic' functions like `json.encode` that,
|
||||||
// the compiler has special codegen support for), so it should be opt in, that is it
|
// the compiler has special codegen support for), so it should be opt in, that is it
|
||||||
// shoould require an explicit voidptr(x) cast (and probably unsafe{} ?) .
|
// shoould require an explicit voidptr(x) cast (and probably unsafe{} ?) .
|
||||||
if call_arg.typ != param.typ
|
if call_arg.typ != param.typ && (param.typ == ast.voidptr_type
|
||||||
&& (param.typ == ast.voidptr_type || final_param_sym.idx == ast.voidptr_type_idx)
|
|| final_param_sym.idx == ast.voidptr_type_idx
|
||||||
|
|| param.typ == ast.nil_type || final_param_sym.idx == ast.nil_type_idx)
|
||||||
&& !call_arg.typ.is_any_kind_of_pointer() && func.language == .v
|
&& !call_arg.typ.is_any_kind_of_pointer() && func.language == .v
|
||||||
&& !call_arg.expr.is_lvalue() && func.name != 'json.encode' && !c.pref.translated
|
&& !call_arg.expr.is_lvalue() && func.name != 'json.encode' && !c.pref.translated
|
||||||
&& !c.file.is_translated {
|
&& !c.file.is_translated {
|
||||||
@ -1110,7 +1111,8 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Allow voidptrs for everything
|
// Allow voidptrs for everything
|
||||||
if param_type == ast.voidptr_type_idx || arg_typ == ast.voidptr_type_idx {
|
if param_type == ast.voidptr_type_idx || param_type == ast.nil_type_idx
|
||||||
|
|| arg_typ == ast.voidptr_type_idx || arg_typ == ast.nil_type_idx {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if param_type.is_any_kind_of_pointer() && arg_typ.is_any_kind_of_pointer() {
|
if param_type.is_any_kind_of_pointer() && arg_typ.is_any_kind_of_pointer() {
|
||||||
@ -1151,7 +1153,7 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
|
|||||||
// Warn about automatic (de)referencing, which will be removed soon.
|
// Warn about automatic (de)referencing, which will be removed soon.
|
||||||
if func.language != .c && !c.inside_unsafe && arg_typ.nr_muls() != param.typ.nr_muls()
|
if func.language != .c && !c.inside_unsafe && arg_typ.nr_muls() != param.typ.nr_muls()
|
||||||
&& !(call_arg.is_mut && param.is_mut) && !(!call_arg.is_mut && !param.is_mut)
|
&& !(call_arg.is_mut && param.is_mut) && !(!call_arg.is_mut && !param.is_mut)
|
||||||
&& param.typ !in [ast.byteptr_type, ast.charptr_type, ast.voidptr_type] {
|
&& param.typ !in [ast.byteptr_type, ast.charptr_type, ast.voidptr_type, ast.nil_type] {
|
||||||
c.warn('automatic referencing/dereferencing is deprecated and will be removed soon (got: $arg_typ.nr_muls() references, expected: $param.typ.nr_muls() references)',
|
c.warn('automatic referencing/dereferencing is deprecated and will be removed soon (got: $arg_typ.nr_muls() references, expected: $param.typ.nr_muls() references)',
|
||||||
call_arg.pos)
|
call_arg.pos)
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
|
|||||||
if !c.inside_unsafe && !node.left.is_auto_deref_var() && !node.right.is_auto_deref_var() {
|
if !c.inside_unsafe && !node.left.is_auto_deref_var() && !node.right.is_auto_deref_var() {
|
||||||
c.warn('pointer arithmetic is only allowed in `unsafe` blocks', left_right_pos)
|
c.warn('pointer arithmetic is only allowed in `unsafe` blocks', left_right_pos)
|
||||||
}
|
}
|
||||||
if left_type == ast.voidptr_type && !c.pref.translated {
|
if (left_type == ast.voidptr_type || left_type == ast.nil_type) && !c.pref.translated {
|
||||||
c.error('`$node.op` cannot be used with `voidptr`', left_pos)
|
c.error('`$node.op` cannot be used with `voidptr`', left_pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,9 +91,10 @@ pub fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
|
|||||||
c.check_expr_opt_call(field.default_expr, default_expr_type)
|
c.check_expr_opt_call(field.default_expr, default_expr_type)
|
||||||
}
|
}
|
||||||
struct_sym.info.fields[i].default_expr_typ = default_expr_type
|
struct_sym.info.fields[i].default_expr_typ = default_expr_type
|
||||||
|
interface_implemented := sym.kind == .interface_
|
||||||
|
&& c.type_implements(default_expr_type, field.typ, field.pos)
|
||||||
c.check_expected(default_expr_type, field.typ) or {
|
c.check_expected(default_expr_type, field.typ) or {
|
||||||
if sym.kind == .interface_
|
if sym.kind == .interface_ && interface_implemented {
|
||||||
&& c.type_implements(default_expr_type, field.typ, field.pos) {
|
|
||||||
if !default_expr_type.is_ptr() && !default_expr_type.is_pointer()
|
if !default_expr_type.is_ptr() && !default_expr_type.is_pointer()
|
||||||
&& !c.inside_unsafe {
|
&& !c.inside_unsafe {
|
||||||
if c.table.sym(default_expr_type).kind != .interface_ {
|
if c.table.sym(default_expr_type).kind != .interface_ {
|
||||||
|
@ -54,6 +54,13 @@ vlib/v/checker/tests/pointer_ops.vv:15:3: error: invalid operator `%` to `&Foo`
|
|||||||
| ~~~~~~~
|
| ~~~~~~~
|
||||||
16 | }
|
16 | }
|
||||||
17 | }
|
17 | }
|
||||||
|
vlib/v/checker/tests/pointer_ops.vv:22:7: error: `+` cannot be used with `voidptr`
|
||||||
|
20 | unsafe {
|
||||||
|
21 | mut p := nil
|
||||||
|
22 | _ = p + 1
|
||||||
|
| ^
|
||||||
|
23 | p++
|
||||||
|
24 | p += 3
|
||||||
vlib/v/checker/tests/pointer_ops.vv:23:4: error: invalid operation: ++ (non-numeric type `voidptr`)
|
vlib/v/checker/tests/pointer_ops.vv:23:4: error: invalid operation: ++ (non-numeric type `voidptr`)
|
||||||
21 | mut p := nil
|
21 | mut p := nil
|
||||||
22 | _ = p + 1
|
22 | _ = p + 1
|
||||||
@ -68,6 +75,13 @@ vlib/v/checker/tests/pointer_ops.vv:24:3: error: operator `+=` not defined on le
|
|||||||
| ^
|
| ^
|
||||||
25 | _ = p - 1
|
25 | _ = p - 1
|
||||||
26 | p--
|
26 | p--
|
||||||
|
vlib/v/checker/tests/pointer_ops.vv:25:7: error: `-` cannot be used with `voidptr`
|
||||||
|
23 | p++
|
||||||
|
24 | p += 3
|
||||||
|
25 | _ = p - 1
|
||||||
|
| ^
|
||||||
|
26 | p--
|
||||||
|
27 | p -= 3
|
||||||
vlib/v/checker/tests/pointer_ops.vv:26:4: error: invalid operation: -- (non-numeric type `voidptr`)
|
vlib/v/checker/tests/pointer_ops.vv:26:4: error: invalid operation: -- (non-numeric type `voidptr`)
|
||||||
24 | p += 3
|
24 | p += 3
|
||||||
25 | _ = p - 1
|
25 | _ = p - 1
|
||||||
|
@ -366,7 +366,7 @@ pub fn (mut e Eval) expr(expr ast.Expr, expecting ast.Type) Object {
|
|||||||
e.error('unknown cast: ${e.table.sym(expr.expr_type).str()} to ${e.table.sym(expr.typ).str()}')
|
e.error('unknown cast: ${e.table.sym(expr.expr_type).str()} to ${e.table.sym(expr.typ).str()}')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if expr.typ == ast.voidptr_type_idx {
|
} else if expr.typ == ast.voidptr_type_idx || expr.typ == ast.nil_type_idx {
|
||||||
match y {
|
match y {
|
||||||
char, Int {
|
char, Int {
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -540,7 +540,7 @@ pub fn (mut e Eval) expr(expr ast.Expr, expecting ast.Type) Object {
|
|||||||
|
|
||||||
fn (e Eval) type_to_size(typ ast.Type) u64 {
|
fn (e Eval) type_to_size(typ ast.Type) u64 {
|
||||||
match typ {
|
match typ {
|
||||||
ast.voidptr_type_idx, ast.byteptr_type_idx, ast.charptr_type_idx {
|
ast.voidptr_type_idx, ast.nil_type_idx, ast.byteptr_type_idx, ast.charptr_type_idx {
|
||||||
return u64(if e.pref.m64 {
|
return u64(if e.pref.m64 {
|
||||||
64
|
64
|
||||||
} else {
|
} else {
|
||||||
|
@ -766,7 +766,7 @@ fn (g &Gen) type_to_fmt(typ ast.Type) StrIntpType {
|
|||||||
if typ == ast.char_type_idx {
|
if typ == ast.char_type_idx {
|
||||||
return .si_c
|
return .si_c
|
||||||
}
|
}
|
||||||
if typ in ast.voidptr_types || typ in ast.byteptr_types {
|
if typ in ast.voidptr_types || typ == ast.nil_type || typ in ast.byteptr_types {
|
||||||
return .si_p
|
return .si_p
|
||||||
}
|
}
|
||||||
if typ in ast.charptr_types {
|
if typ in ast.charptr_types {
|
||||||
|
@ -2406,6 +2406,7 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
|
|||||||
}
|
}
|
||||||
// Generic dereferencing logic
|
// Generic dereferencing logic
|
||||||
neither_void := ast.voidptr_type !in [got_type, expected_type]
|
neither_void := ast.voidptr_type !in [got_type, expected_type]
|
||||||
|
&& ast.nil_type !in [got_type, expected_type]
|
||||||
if expected_type.has_flag(.shared_f) && !got_type_raw.has_flag(.shared_f)
|
if expected_type.has_flag(.shared_f) && !got_type_raw.has_flag(.shared_f)
|
||||||
&& !expected_type.has_flag(.optional) && !expected_type.has_flag(.result) {
|
&& !expected_type.has_flag(.optional) && !expected_type.has_flag(.result) {
|
||||||
shared_styp := exp_styp[0..exp_styp.len - 1] // `shared` implies ptr, so eat one `*`
|
shared_styp := exp_styp[0..exp_styp.len - 1] // `shared` implies ptr, so eat one `*`
|
||||||
@ -5956,7 +5957,7 @@ fn (mut g Gen) interface_table() string {
|
|||||||
} else {
|
} else {
|
||||||
// the field is embedded in another struct
|
// the field is embedded in another struct
|
||||||
cast_struct.write_string('\t\t.$cname = ($field_styp*)((char*)x')
|
cast_struct.write_string('\t\t.$cname = ($field_styp*)((char*)x')
|
||||||
if st == ast.voidptr_type {
|
if st == ast.voidptr_type || st == ast.nil_type {
|
||||||
cast_struct.write_string('/*.... ast.voidptr_type */')
|
cast_struct.write_string('/*.... ast.voidptr_type */')
|
||||||
} else {
|
} else {
|
||||||
if st_sym.kind == .struct_ {
|
if st_sym.kind == .struct_ {
|
||||||
@ -6004,7 +6005,7 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x)
|
|||||||
if g.pref.build_mode != .build_module {
|
if g.pref.build_mode != .build_module {
|
||||||
methods_struct.writeln('\t{')
|
methods_struct.writeln('\t{')
|
||||||
}
|
}
|
||||||
if st == ast.voidptr_type {
|
if st == ast.voidptr_type || st == ast.nil_type {
|
||||||
for mname, _ in methodidx {
|
for mname, _ in methodidx {
|
||||||
if g.pref.build_mode != .build_module {
|
if g.pref.build_mode != .build_module {
|
||||||
methods_struct.writeln('\t\t._method_${c_name(mname)} = (void*) 0,')
|
methods_struct.writeln('\t\t._method_${c_name(mname)} = (void*) 0,')
|
||||||
@ -6116,7 +6117,8 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x)
|
|||||||
// .speak = Cat_speak_Interface_Animal_method_wrapper
|
// .speak = Cat_speak_Interface_Animal_method_wrapper
|
||||||
method_call += iwpostfix
|
method_call += iwpostfix
|
||||||
}
|
}
|
||||||
if g.pref.build_mode != .build_module && st != ast.voidptr_type {
|
if g.pref.build_mode != .build_module && st != ast.voidptr_type
|
||||||
|
&& st != ast.nil_type {
|
||||||
methods_struct.writeln('\t\t._method_${c_name(method.name)} = (void*) $method_call,')
|
methods_struct.writeln('\t\t._method_${c_name(method.name)} = (void*) $method_call,')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -436,7 +436,7 @@ fn (mut g Gen) struct_init_field(sfield ast.StructInitField, language ast.Langua
|
|||||||
}
|
}
|
||||||
g.write('}')
|
g.write('}')
|
||||||
} else {
|
} else {
|
||||||
if sfield.typ != ast.nil_type
|
if sfield.typ != ast.voidptr_type && sfield.typ != ast.nil_type
|
||||||
&& (sfield.expected_type.is_ptr() && !sfield.expected_type.has_flag(.shared_f))
|
&& (sfield.expected_type.is_ptr() && !sfield.expected_type.has_flag(.shared_f))
|
||||||
&& !(sfield.typ.is_ptr() || sfield.typ.is_pointer()) && !sfield.typ.is_number() {
|
&& !(sfield.typ.is_ptr() || sfield.typ.is_pointer()) && !sfield.typ.is_number() {
|
||||||
g.write('/* autoref */&')
|
g.write('/* autoref */&')
|
||||||
|
@ -651,7 +651,7 @@ fn (g &JsGen) type_to_fmt(typ ast.Type) StrIntpType {
|
|||||||
if typ == ast.char_type_idx {
|
if typ == ast.char_type_idx {
|
||||||
return .si_c
|
return .si_c
|
||||||
}
|
}
|
||||||
if typ in ast.voidptr_types || typ in ast.byteptr_types {
|
if typ in ast.voidptr_types || typ == ast.nil_type || typ in ast.byteptr_types {
|
||||||
return .si_p
|
return .si_p
|
||||||
}
|
}
|
||||||
if typ in ast.charptr_types {
|
if typ in ast.charptr_types {
|
||||||
|
@ -72,3 +72,32 @@ fn test_interface_fn_pointer_fields() {
|
|||||||
nf := NofunInterface(Nofun{my_fn})
|
nf := NofunInterface(Nofun{my_fn})
|
||||||
assert nf.foo(123) == 246
|
assert nf.foo(123) == 246
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For issue: 16198 errors when the reference interface type field of the struct is nil(init, assign...)
|
||||||
|
interface Speaker {
|
||||||
|
speak() string
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Wolf {}
|
||||||
|
|
||||||
|
fn (w Wolf) speak() string {
|
||||||
|
return 'woof'
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Foo {
|
||||||
|
mut:
|
||||||
|
speaker &Speaker = unsafe { nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_set_nil_to_ref_interface_type_fields() {
|
||||||
|
mut foo := Foo{
|
||||||
|
speaker: unsafe { nil }
|
||||||
|
}
|
||||||
|
assert true
|
||||||
|
|
||||||
|
foo.speaker = unsafe { nil }
|
||||||
|
assert true
|
||||||
|
|
||||||
|
foo.speaker = &Wolf{}
|
||||||
|
assert foo.speaker.speak() == 'woof'
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user