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() { fn init_settings() {
mut s := &VpmSettings(0) mut s := &VpmSettings(unsafe { nil })
unsafe { unsafe {
s = settings s = settings
} }

View file

@ -6188,12 +6188,12 @@ fn my_callback(arg voidptr, howmany int, cvalues &&char, cnames &&char) int {
} }
fn main() { 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 // 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(c'users.db', &db)
// C.sqlite3_open(db_path.str, &db) // C.sqlite3_open(db_path.str, &db)
query := 'select count(*) from users' 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, // Note: You can also use the `.str` field of a V string,
// to get its C style zero terminated representation // to get its C style zero terminated representation
C.sqlite3_prepare_v2(db, &char(query.str), -1, &stmt, 0) 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) { pub fn (mut s System) update(dt f64) {
mut p := &Particle(0) mut p := &Particle(unsafe { nil })
mut moved := 0 mut moved := 0
for i := 0; i < s.pool.len; i++ { for i := 0; i < s.pool.len; i++ {
p = s.pool[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) { pub fn (mut s System) explode(x f32, y f32) {
mut reserve := 500 mut reserve := 500
center := vec.Vec2[f64]{x, y} center := vec.Vec2[f64]{x, y}
mut p := &Particle(0) mut p := &Particle(unsafe { nil })
mut moved := 0 mut moved := 0
for i := 0; i < s.bin.len && reserve > 0; i++ { for i := 0; i < s.bin.len && reserve > 0; i++ {
p = s.bin[i] p = s.bin[i]

View file

@ -70,7 +70,7 @@ fn new_node() &mapnode {
fn (mut m SortedMap) set(key string, value voidptr) { fn (mut m SortedMap) set(key string, value voidptr) {
mut node := m.root mut node := m.root
mut child_index := 0 mut child_index := 0
mut parent := &mapnode(0) mut parent := &mapnode(unsafe { nil })
for { for {
if node.len == max_len { if node.len == max_len {
if parent == unsafe { nil } { if parent == unsafe { nil } {
@ -228,7 +228,7 @@ fn (mut n mapnode) remove_key(k string) bool {
n.fill(idx) n.fill(idx)
} }
mut node := &mapnode(0) mut node := &mapnode(unsafe { nil })
if flag && idx > n.len { if flag && idx > n.len {
node = unsafe { &mapnode(n.children[idx - 1]) } node = unsafe { &mapnode(n.children[idx - 1]) }
} else { } 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 fn C.mysql_stmt_store_result(&C.MYSQL_STMT) int
pub struct Stmt { pub struct Stmt {
stmt &C.MYSQL_STMT = &C.MYSQL_STMT(0) stmt &C.MYSQL_STMT = &C.MYSQL_STMT(unsafe { nil })
query string query string
mut: mut:
binds []C.MYSQL_BIND binds []C.MYSQL_BIND

View file

@ -127,7 +127,7 @@ fn C.sqlite3_changes(&C.sqlite3) int
// connect Opens the connection with a database. // connect Opens the connection with a database.
pub fn connect(path string) !DB { pub fn connect(path string) !DB {
db := &C.sqlite3(0) db := &C.sqlite3(unsafe { nil })
code := C.sqlite3_open(&char(path.str), &db) code := C.sqlite3_open(&char(path.str), &db)
if code != 0 { if code != 0 {
return &SQLError{ 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` // 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 { pub fn (db &DB) q_int(query string) int {
stmt := &C.sqlite3_stmt(0) stmt := &C.sqlite3_stmt(unsafe { nil })
defer { defer {
C.sqlite3_finalize(stmt) 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` // 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 { pub fn (db &DB) q_string(query string) string {
stmt := &C.sqlite3_stmt(0) stmt := &C.sqlite3_stmt(unsafe { nil })
defer { defer {
C.sqlite3_finalize(stmt) 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 // Result codes: https://www.sqlite.org/rescode.html
[manualfree] [manualfree]
pub fn (db &DB) exec(query string) ([]Row, int) { pub fn (db &DB) exec(query string) ([]Row, int) {
stmt := &C.sqlite3_stmt(0) stmt := &C.sqlite3_stmt(unsafe { nil })
defer { defer {
C.sqlite3_finalize(stmt) 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. // 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 (...)` // e.g. for queries like these: `INSERT INTO ... VALUES (...)`
pub fn (db &DB) exec_none(query string) int { 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) C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
code := C.sqlite3_step(stmt) code := C.sqlite3_step(stmt)
C.sqlite3_finalize(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 // Only for V ORM
fn (db &DB) init_stmt(query string) (&C.sqlite3_stmt, int) { fn (db &DB) init_stmt(query string) (&C.sqlite3_stmt, int) {
// println('init_stmt("$query")') // 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) err := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
return stmt, err 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`. // 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. // 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 { 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 mut flags := 0

View file

@ -1355,7 +1355,7 @@ fn (mut dl Dlmalloc) segment_holding(ptr voidptr) &Segment {
} }
sp = sp.next sp = sp.next
} }
return &Segment(0) return &Segment(unsafe { nil })
} }
// realloc behaves as libc realloc, but operates within the given space // 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_socktype = int(typ)
hints.ai_flags = C.AI_PASSIVE hints.ai_flags = C.AI_PASSIVE
results := &C.addrinfo(0) results := &C.addrinfo(unsafe { nil })
sport := '${port}' 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 // infinite timeout is signaled by passing null as the timeout to
// select // select
if timeout == net.infinite_timeout { if timeout == net.infinite_timeout {
timeval_timeout = &C.timeval(0) timeval_timeout = &C.timeval(unsafe { nil })
} }
match test { 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 // infinite timeout is signaled by passing null as the timeout to
// select // select
if timeout == net.infinite_timeout { if timeout == net.infinite_timeout {
timeval_timeout = &C.timeval(0) timeval_timeout = &C.timeval(unsafe { nil })
} }
match test { match test {

View file

@ -5,7 +5,7 @@ struct Abc {
} }
fn test_printing_struct_with_reference_field_of_type_ssl_ctx() { 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) dump(a)
sa := a.str() sa := a.str()
assert sa.contains('&C.SSL_CTX(0x7b)') 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 // infinite timeout is signaled by passing null as the timeout to
// select // select
if timeout == net.infinite_timeout { if timeout == net.infinite_timeout {
timeval_timeout = &C.timeval(0) timeval_timeout = &C.timeval(unsafe { nil })
} }
match test { 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 // infinite timeout is signaled by passing null as the timeout to
// select // select
if timeout == unix.infinite_timeout { if timeout == unix.infinite_timeout {
timeval_timeout = &C.timeval(0) timeval_timeout = &C.timeval(unsafe { nil })
} }
match test { match test {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -102,7 +102,7 @@ pub fn new_table() &Table {
return t return t
} }
__global global_table = &Table(0) __global global_table = &Table(unsafe { nil })
pub fn set_global_table(t &Table) { pub fn set_global_table(t &Table) {
global_table = t 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 { if from_sym.kind == .alias {
from_type = (from_sym.info as ast.Alias).parent_type.derive_add_muls(from_type) 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 { if from_type == ast.voidptr_type_idx && !c.inside_unsafe {
// TODO make this an error // TODO make this an error
c.warn('cannot cast voidptr to a struct outside `unsafe`', node.pos) 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 { fn (mut c Checker) unsafe_expr(mut node ast.UnsafeExpr) ast.Type {
prev_unsafe := c.inside_unsafe
c.inside_unsafe = true c.inside_unsafe = true
t := c.expr(node.expr) t := c.expr(node.expr)
c.inside_unsafe = false c.inside_unsafe = prev_unsafe
return t 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() { pub fn (mut g JsGen) escape_namespace() {
g.ns = &Namespace(0) g.ns = &Namespace(unsafe { nil })
g.inside_builtin = false g.inside_builtin = false
} }

View file

@ -179,7 +179,7 @@ pub fn (mut p Parser) free_scanner() {
unsafe { unsafe {
if p.scanner != 0 { if p.scanner != 0 {
p.scanner.free() 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 // m.opt = options
opt := m.opt opt := m.opt
mut pc := &PkgConfig(0) mut pc := &PkgConfig(unsafe { nil })
mut res := m.res mut res := m.res
for arg in opt.args { for arg in opt.args {
mut pcdep := load(arg, options) or { mut pcdep := load(arg, options) or {

View file

@ -41,11 +41,11 @@ struct Foo {
type Alias = Foo type Alias = Foo
fn test_cast_to_alias_of_ref_struct() { fn test_cast_to_alias_of_ref_struct() {
foo := &Foo(0) foo := &Foo(unsafe { nil })
println(typeof(foo).name) println(typeof(foo).name)
assert typeof(foo).name == '&Foo' assert typeof(foo).name == '&Foo'
bar := &Alias(0) bar := &Alias(unsafe { nil })
println(typeof(bar).name) println(typeof(bar).name)
assert typeof(bar).name == '&Alias' 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} nodes: []&TrieNode{cap: 20}
} }
for _ in 0 .. 20 { for _ in 0 .. 20 {
km.nodes << &TrieNode(0) km.nodes << &TrieNode(unsafe { nil })
} }
for k, v in kw_map { for k, v in kw_map {
km.add_word(k, int(v)) km.add_word(k, int(v))

View file

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

View file

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

View file

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