Val
(Valentin)
1
I want to model a struct that wraps access to a SQLite database as a non-copyable struct. Opening or creating a database can fail, so naturally, I want the init to be able to throw errors. However, this causes the following error: "Conditional initialization or destruction of noncopyable types is not supported; this variable must be consistently in an initialized or uninitialized state through every code path". This error doesn't explicitly forbid throwing initializations, and I couldn't find anything in the corresponding evolution proposal. Is there a way to make this work?
1 Like
Joe_Groff
(Joe Groff)
2
It ought to be supported, but there is a known issue that causes this error to be raised. Let me see if I can look up the workaround.
3 Likes
glessard
(Guillaume Lessard)
3
I remember seeing this, but my attempt to reproduce it right now is not working. Do you have an example that triggers it?
Val
(Valentin)
4
Here's a somewhat minimal sample. The error goes away if you comment out the last two lines of the init.
struct SQLite: ~Copyable {
let dbPath: String
let db: OpaquePointer
init(dbPath: String) throws {
self.dbPath = dbPath
var db: OpaquePointer? = nil
try sqlite {
sqlite3_open_v2(
dbPath,
&db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX,
nil
)
}
self.db = db!
try sqlite { sqlite3_busy_timeout(db, 5 * 1000 /* 5s */) }
try sqlite { sqlite3_extended_errcode(db) }
}
}
func sqlite(_ fn: () -> Int32) throws {
let result = fn()
if result != SQLITE_OK {
throw SQLiteError.generic(result)
}
}
enum SQLiteError: Error, Equatable {
case generic(Int32)
}
1 Like
glessard
(Guillaume Lessard)
5
Can you move the stored property initializations to the end of the function? In that way all throwing sites would occur before any part of the struct is initialized.
Interestingly, it's the interleaving of throwing sites and property storing that is the issue. This fails:
init(dbPath: String) throws {
self.dbPath = dbPath
let db: OpaquePointer? = nil
try sqlite { 1 }
self.db = db!
try sqlite { 2 }
try sqlite { 3 }
}
But this works:
init(dbPath: String) throws {
self.dbPath = dbPath
let db: OpaquePointer? = nil
try sqlite { 1 }
try sqlite { 2 }
try sqlite { 3 }
self.db = db!
}
1 Like
Val
(Valentin)
6
That fixed it. Thanks! Perhaps the error message could hint at this. Maybe even with notes pointing to the property initializations.
glessard
(Guillaume Lessard)
7
I'm pretty sure there's a compiler bug there =)
1 Like