1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

checker: disallow struct int to ptr outside unsafe (#17923)

This commit is contained in:
Swastik Baranwal 2023-04-13 11:08:21 +05:30 committed by GitHub
parent 92cb7468ce
commit 3d99f1f2c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 124 additions and 47 deletions

View File

@ -704,7 +704,7 @@ const (
)
fn init_settings() {
mut s := &VpmSettings(0)
mut s := &VpmSettings(unsafe { nil })
unsafe {
s = settings
}

View File

@ -6188,12 +6188,12 @@ fn my_callback(arg voidptr, howmany int, cvalues &&char, cnames &&char) int {
}
fn main() {
db := &C.sqlite3(0) // this means `sqlite3* db = 0`
db := &C.sqlite3(unsafe { nil }) // this means `sqlite3* db = 0`
// passing a string literal to a C function call results in a C string, not a V string
C.sqlite3_open(c'users.db', &db)
// C.sqlite3_open(db_path.str, &db)
query := 'select count(*) from users'
stmt := &C.sqlite3_stmt(0)
stmt := &C.sqlite3_stmt(unsafe { nil })
// Note: You can also use the `.str` field of a V string,
// to get its C style zero terminated representation
C.sqlite3_prepare_v2(db, &char(query.str), -1, &stmt, 0)

View File

@ -28,7 +28,7 @@ pub fn (mut s System) init(sc SystemConfig) {
}
pub fn (mut s System) update(dt f64) {
mut p := &Particle(0)
mut p := &Particle(unsafe { nil })
mut moved := 0
for i := 0; i < s.pool.len; i++ {
p = s.pool[i]
@ -70,7 +70,7 @@ pub fn (mut s System) reset() {
pub fn (mut s System) explode(x f32, y f32) {
mut reserve := 500
center := vec.Vec2[f64]{x, y}
mut p := &Particle(0)
mut p := &Particle(unsafe { nil })
mut moved := 0
for i := 0; i < s.bin.len && reserve > 0; i++ {
p = s.bin[i]

View File

@ -70,7 +70,7 @@ fn new_node() &mapnode {
fn (mut m SortedMap) set(key string, value voidptr) {
mut node := m.root
mut child_index := 0
mut parent := &mapnode(0)
mut parent := &mapnode(unsafe { nil })
for {
if node.len == max_len {
if parent == unsafe { nil } {
@ -228,7 +228,7 @@ fn (mut n mapnode) remove_key(k string) bool {
n.fill(idx)
}
mut node := &mapnode(0)
mut node := &mapnode(unsafe { nil })
if flag && idx > n.len {
node = unsafe { &mapnode(n.children[idx - 1]) }
} else {

View File

@ -63,7 +63,7 @@ fn C.mysql_stmt_next_result(&C.MYSQL_STMT) int
fn C.mysql_stmt_store_result(&C.MYSQL_STMT) int
pub struct Stmt {
stmt &C.MYSQL_STMT = &C.MYSQL_STMT(0)
stmt &C.MYSQL_STMT = &C.MYSQL_STMT(unsafe { nil })
query string
mut:
binds []C.MYSQL_BIND

View File

@ -127,7 +127,7 @@ fn C.sqlite3_changes(&C.sqlite3) int
// connect Opens the connection with a database.
pub fn connect(path string) !DB {
db := &C.sqlite3(0)
db := &C.sqlite3(unsafe { nil })
code := C.sqlite3_open(&char(path.str), &db)
if code != 0 {
return &SQLError{
@ -182,7 +182,7 @@ pub fn (db &DB) get_affected_rows_count() int {
// q_int returns a single integer value, from the first column of the result of executing `query`
pub fn (db &DB) q_int(query string) int {
stmt := &C.sqlite3_stmt(0)
stmt := &C.sqlite3_stmt(unsafe { nil })
defer {
C.sqlite3_finalize(stmt)
}
@ -195,7 +195,7 @@ pub fn (db &DB) q_int(query string) int {
// q_string returns a single string value, from the first column of the result of executing `query`
pub fn (db &DB) q_string(query string) string {
stmt := &C.sqlite3_stmt(0)
stmt := &C.sqlite3_stmt(unsafe { nil })
defer {
C.sqlite3_finalize(stmt)
}
@ -210,7 +210,7 @@ pub fn (db &DB) q_string(query string) string {
// Result codes: https://www.sqlite.org/rescode.html
[manualfree]
pub fn (db &DB) exec(query string) ([]Row, int) {
stmt := &C.sqlite3_stmt(0)
stmt := &C.sqlite3_stmt(unsafe { nil })
defer {
C.sqlite3_finalize(stmt)
}
@ -278,7 +278,7 @@ pub fn (db &DB) error_message(code int, query string) IError {
// Use it, in case you don't expect any row results, but still want a result code.
// e.g. for queries like these: `INSERT INTO ... VALUES (...)`
pub fn (db &DB) exec_none(query string) int {
stmt := &C.sqlite3_stmt(0)
stmt := &C.sqlite3_stmt(unsafe { nil })
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
code := C.sqlite3_step(stmt)
C.sqlite3_finalize(stmt)

View File

@ -8,7 +8,7 @@ fn C.sqlite3_bind_text(&C.sqlite3_stmt, int, &char, int, voidptr) int
// Only for V ORM
fn (db &DB) init_stmt(query string) (&C.sqlite3_stmt, int) {
// println('init_stmt("$query")')
stmt := &C.sqlite3_stmt(0)
stmt := &C.sqlite3_stmt(unsafe { nil })
err := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
return stmt, err
}

View File

@ -149,7 +149,7 @@ pub enum OpenModeFlag {
// connect_full Opens connection to sqlite database. It gives more control than `open`.
// Flags give control over readonly and create decisions. Specific VFS can be chosen.
pub fn connect_full(path string, mode_flags []OpenModeFlag, vfs_name string) !DB {
db := &C.sqlite3(0)
db := &C.sqlite3(unsafe { nil })
mut flags := 0

View File

@ -1355,7 +1355,7 @@ fn (mut dl Dlmalloc) segment_holding(ptr voidptr) &Segment {
}
sp = sp.next
}
return &Segment(0)
return &Segment(unsafe { nil })
}
// realloc behaves as libc realloc, but operates within the given space

View File

@ -194,7 +194,7 @@ pub fn resolve_ipaddrs(addr string, family AddrFamily, typ SocketType) ![]Addr {
hints.ai_socktype = int(typ)
hints.ai_flags = C.AI_PASSIVE
results := &C.addrinfo(0)
results := &C.addrinfo(unsafe { nil })
sport := '${port}'

View File

@ -70,7 +70,7 @@ fn @select(handle int, test Select, timeout time.Duration) !bool {
// infinite timeout is signaled by passing null as the timeout to
// select
if timeout == net.infinite_timeout {
timeval_timeout = &C.timeval(0)
timeval_timeout = &C.timeval(unsafe { nil })
}
match test {

View File

@ -379,7 +379,7 @@ fn @select(handle int, test Select, timeout time.Duration) !bool {
// infinite timeout is signaled by passing null as the timeout to
// select
if timeout == net.infinite_timeout {
timeval_timeout = &C.timeval(0)
timeval_timeout = &C.timeval(unsafe { nil })
}
match test {

View File

@ -5,7 +5,7 @@ struct Abc {
}
fn test_printing_struct_with_reference_field_of_type_ssl_ctx() {
a := Abc{&C.SSL_CTX(123)}
a := unsafe { Abc{&C.SSL_CTX(123)} }
dump(a)
sa := a.str()
assert sa.contains('&C.SSL_CTX(0x7b)')

View File

@ -436,7 +436,7 @@ fn @select(handle int, test Select, timeout time.Duration) !bool {
// infinite timeout is signaled by passing null as the timeout to
// select
if timeout == net.infinite_timeout {
timeval_timeout = &C.timeval(0)
timeval_timeout = &C.timeval(unsafe { nil })
}
match test {

View File

@ -42,7 +42,7 @@ fn @select(handle int, test Select, timeout time.Duration) !bool {
// infinite timeout is signaled by passing null as the timeout to
// select
if timeout == unix.infinite_timeout {
timeval_timeout = &C.timeval(0)
timeval_timeout = &C.timeval(unsafe { nil })
}
match test {

View File

@ -272,7 +272,7 @@ pub fn ls(path string) ![]string {
if isnil(dir) {
return error('ls() couldnt open dir "${path}"')
}
mut ent := &C.dirent(0)
mut ent := &C.dirent(unsafe { nil })
// mut ent := &C.dirent{!}
for {
ent = C.readdir(dir)

View File

@ -126,7 +126,7 @@ fn C.sqlite3_changes(&C.sqlite3) int
// connect Opens the connection with a database.
pub fn connect(path string) !DB {
db := &C.sqlite3(0)
db := &C.sqlite3(unsafe { nil })
code := C.sqlite3_open(&char(path.str), &db)
if code != 0 {
return &SQLError{
@ -181,7 +181,7 @@ pub fn (db &DB) get_affected_rows_count() int {
// Returns a single cell with value int.
pub fn (db &DB) q_int(query string) int {
stmt := &C.sqlite3_stmt(0)
stmt := &C.sqlite3_stmt(unsafe { nil })
defer {
C.sqlite3_finalize(stmt)
}
@ -194,7 +194,7 @@ pub fn (db &DB) q_int(query string) int {
// Returns a single cell with value string.
pub fn (db &DB) q_string(query string) string {
stmt := &C.sqlite3_stmt(0)
stmt := &C.sqlite3_stmt(unsafe { nil })
defer {
C.sqlite3_finalize(stmt)
}
@ -209,7 +209,7 @@ pub fn (db &DB) q_string(query string) string {
// Result codes: https://www.sqlite.org/rescode.html
[manualfree]
pub fn (db &DB) exec(query string) ([]Row, int) {
stmt := &C.sqlite3_stmt(0)
stmt := &C.sqlite3_stmt(unsafe { nil })
defer {
C.sqlite3_finalize(stmt)
}
@ -276,7 +276,7 @@ pub fn (db &DB) error_message(code int, query string) IError {
// In case you don't expect any row results, but still want a result code.
// e.g. INSERT INTO ... VALUES (...)
pub fn (db &DB) exec_none(query string) int {
stmt := &C.sqlite3_stmt(0)
stmt := &C.sqlite3_stmt(unsafe { nil })
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
code := C.sqlite3_step(stmt)
C.sqlite3_finalize(stmt)

View File

@ -149,7 +149,7 @@ pub enum OpenModeFlag {
// connect_full Opens connection to sqlite database. It gives more control than `open`.
// Flags give control over readonly and create decisions. Specific VFS can be chosen.
pub fn connect_full(path string, mode_flags []OpenModeFlag, vfs_name string) !DB {
db := &C.sqlite3(0)
db := &C.sqlite3(unsafe { nil })
mut flags := 0

View File

@ -11,7 +11,7 @@ mut:
read_all_bytes bool = true
}
const ctx_ptr = &Context(0)
const ctx_ptr = &Context(unsafe { nil })
// init initializes the terminal console with Config `cfg`.
pub fn init(cfg Config) &Context {

View File

@ -8,7 +8,7 @@ import time
const buf_size = 64
const ctx_ptr = &Context(0)
const ctx_ptr = &Context(unsafe { nil })
const stdin_at_startup = u32(0)

View File

@ -255,7 +255,7 @@ fn (mut ctx Context) parse_events() {
if nr_iters > 100 {
ctx.shift(1)
}
mut event := &Event(0)
mut event := &Event(unsafe { nil })
if ctx.read_buf[0] == 0x1b {
e, len := escape_sequence(ctx.read_buf.bytestr())
event = e
@ -420,7 +420,7 @@ fn escape_sequence(buf_ string) (&Event, int) {
if buf.len > 2 && buf[1] == `<` {
split := buf[2..].split(';')
if split.len < 3 {
return &Event(0), 0
return &Event(unsafe { nil }), 0
}
typ, x, y := split[0].int(), split[1].int(), split[2].int()

View File

@ -4,7 +4,7 @@ import toml.to
import toml.ast
const empty_toml_document = toml.Doc{
ast: &ast.Root(0)
ast: &ast.Root(unsafe { nil })
}
const (

View File

@ -102,7 +102,7 @@ pub fn new_table() &Table {
return t
}
__global global_table = &Table(0)
__global global_table = &Table(unsafe { nil })
pub fn set_global_table(t &Table) {
global_table = t

View File

@ -2836,6 +2836,30 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
if from_sym.kind == .alias {
from_type = (from_sym.info as ast.Alias).parent_type.derive_add_muls(from_type)
}
if mut node.expr is ast.IntegerLiteral {
if node.expr.val.int() == 0 && !c.pref.translated && !c.file.is_translated {
c.error('cannot null cast a struct pointer, use &${to_sym.name}(unsafe { nil })',
node.pos)
} else if !c.inside_unsafe && !c.pref.translated && !c.file.is_translated {
c.error('cannot cast int to a struct pointer outside `unsafe`', node.pos)
}
} else if mut node.expr is ast.Ident {
match mut node.expr.obj {
ast.GlobalField, ast.ConstField, ast.Var {
if mut node.expr.obj.expr is ast.IntegerLiteral {
if node.expr.obj.expr.val.int() == 0 && !c.pref.translated
&& !c.file.is_translated {
c.error('cannot null cast a struct pointer, use &${to_sym.name}(unsafe { nil })',
node.pos)
} else if !c.inside_unsafe && !c.pref.translated && !c.file.is_translated {
c.error('cannot cast int to a struct pointer outside `unsafe`',
node.pos)
}
}
}
else {}
}
}
if from_type == ast.voidptr_type_idx && !c.inside_unsafe {
// TODO make this an error
c.warn('cannot cast voidptr to a struct outside `unsafe`', node.pos)
@ -3611,9 +3635,10 @@ fn (mut c Checker) lock_expr(mut node ast.LockExpr) ast.Type {
}
fn (mut c Checker) unsafe_expr(mut node ast.UnsafeExpr) ast.Type {
prev_unsafe := c.inside_unsafe
c.inside_unsafe = true
t := c.expr(node.expr)
c.inside_unsafe = false
c.inside_unsafe = prev_unsafe
return t
}

View File

@ -0,0 +1,18 @@
vlib/v/checker/tests/struct_ptr_cast_int_outside_unsafe_err.vv:6:5: error: cannot cast int to a struct pointer outside `unsafe`
4 | a := 1
5 |
6 | _ = &Context(a)
| ~~~~~~~~~~~
7 | _ = &Context(b)
8 | _ = &Context(1)
vlib/v/checker/tests/struct_ptr_cast_int_outside_unsafe_err.vv:7:5: error: cannot cast int to a struct pointer outside `unsafe`
5 |
6 | _ = &Context(a)
7 | _ = &Context(b)
| ~~~~~~~~~~~
8 | _ = &Context(1)
vlib/v/checker/tests/struct_ptr_cast_int_outside_unsafe_err.vv:8:5: error: cannot cast int to a struct pointer outside `unsafe`
6 | _ = &Context(a)
7 | _ = &Context(b)
8 | _ = &Context(1)
| ~~~~~~~~~~~

View File

@ -0,0 +1,8 @@
struct Context {}
const b = 1
a := 1
_ = &Context(a)
_ = &Context(b)
_ = &Context(1)

View File

@ -0,0 +1,18 @@
vlib/v/checker/tests/struct_ptr_cast_zero_err.vv:6:5: error: cannot null cast a struct pointer, use &Context(unsafe { nil })
4 | b := 0
5 |
6 | _ = &Context(0)
| ~~~~~~~~~~~
7 | _ = &Context(a)
8 | _ = &Context(b)
vlib/v/checker/tests/struct_ptr_cast_zero_err.vv:7:5: error: cannot null cast a struct pointer, use &Context(unsafe { nil })
5 |
6 | _ = &Context(0)
7 | _ = &Context(a)
| ~~~~~~~~~~~
8 | _ = &Context(b)
vlib/v/checker/tests/struct_ptr_cast_zero_err.vv:8:5: error: cannot null cast a struct pointer, use &Context(unsafe { nil })
6 | _ = &Context(0)
7 | _ = &Context(a)
8 | _ = &Context(b)
| ~~~~~~~~~~~

View File

@ -0,0 +1,8 @@
struct Context {}
const a = 0
b := 0
_ = &Context(0)
_ = &Context(a)
_ = &Context(b)

View File

@ -428,7 +428,7 @@ pub fn (mut g JsGen) enter_namespace(name string) {
}
pub fn (mut g JsGen) escape_namespace() {
g.ns = &Namespace(0)
g.ns = &Namespace(unsafe { nil })
g.inside_builtin = false
}

View File

@ -179,7 +179,7 @@ pub fn (mut p Parser) free_scanner() {
unsafe {
if p.scanner != 0 {
p.scanner.free()
p.scanner = &scanner.Scanner(0)
p.scanner = &scanner.Scanner(nil)
}
}
}

View File

@ -78,7 +78,7 @@ pub fn (mut m Main) run() !string {
}
// m.opt = options
opt := m.opt
mut pc := &PkgConfig(0)
mut pc := &PkgConfig(unsafe { nil })
mut res := m.res
for arg in opt.args {
mut pcdep := load(arg, options) or {

View File

@ -41,11 +41,11 @@ struct Foo {
type Alias = Foo
fn test_cast_to_alias_of_ref_struct() {
foo := &Foo(0)
foo := &Foo(unsafe { nil })
println(typeof(foo).name)
assert typeof(foo).name == '&Foo'
bar := &Alias(0)
bar := &Alias(unsafe { nil })
println(typeof(bar).name)
assert typeof(bar).name == '&Alias'
}

View File

@ -67,7 +67,7 @@ pub fn new_keywords_matcher_trie[T](kw_map map[string]T) KeywordsMatcherTrie {
nodes: []&TrieNode{cap: 20}
}
for _ in 0 .. 20 {
km.nodes << &TrieNode(0)
km.nodes << &TrieNode(unsafe { nil })
}
for k, v in kw_map {
km.add_word(k, int(v))

View File

@ -297,7 +297,7 @@ mut:
[unsafe]
pub fn cached_read_source_file(path string) !string {
mut static cache := &SourceCache(0)
mut static cache := &SourceCache(unsafe { nil })
if cache == unsafe { nil } {
cache = &SourceCache{}
}
@ -308,7 +308,7 @@ pub fn cached_read_source_file(path string) !string {
if path.len == 0 {
unsafe { cache.sources.free() }
unsafe { free(cache) }
cache = &SourceCache(0)
cache = &SourceCache(unsafe { nil })
return error('memory source file cache cleared')
}

View File

@ -267,7 +267,7 @@ struct SimpleTcpClientConfig {
}
fn simple_tcp_client(config SimpleTcpClientConfig) !string {
mut client := &net.TcpConn(0)
mut client := &net.TcpConn(unsafe { nil })
mut tries := 0
for tries < config.retries {
tries++
@ -308,7 +308,7 @@ ${config.content}'
}
fn simple_tcp_client_post_json(config SimpleTcpClientConfig) !string {
mut client := &net.TcpConn(0)
mut client := &net.TcpConn(unsafe { nil })
mut tries := 0
for tries < config.retries {
tries++

View File

@ -266,7 +266,7 @@ struct SimpleTcpClientConfig {
}
fn simple_tcp_client(config SimpleTcpClientConfig) !string {
mut client := &net.TcpConn(0)
mut client := &net.TcpConn(unsafe { nil })
mut tries := 0
for tries < config.retries {
tries++