builtin: prepare for error interfaces (#9043)
parent
c474106511
commit
be409b52e9
|
@ -3,74 +3,66 @@
|
||||||
// that can be found in the LICENSE file.
|
// that can be found in the LICENSE file.
|
||||||
module builtin
|
module builtin
|
||||||
|
|
||||||
struct OptionBase {
|
// IError holds information about an error instance
|
||||||
ok bool
|
pub interface IError {
|
||||||
is_none bool
|
msg string
|
||||||
error string
|
code int
|
||||||
ecode int
|
|
||||||
// Data is trailing after ecode
|
|
||||||
// and is not included in here but in the
|
|
||||||
// derived Option_xxx types
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// `fn foo() ?Foo { return foo }` => `fn foo() ?Foo { return opt_ok(foo); }`
|
// Error is the default implementation of IError, that is returned by e.g. `error()`
|
||||||
fn opt_ok2(data voidptr, mut option OptionBase, size int) {
|
pub struct Error {
|
||||||
|
pub:
|
||||||
|
msg string
|
||||||
|
code int
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Option3 {
|
||||||
|
state byte
|
||||||
|
err IError
|
||||||
|
}
|
||||||
|
|
||||||
|
[inline]
|
||||||
|
fn (e IError) str() string {
|
||||||
|
return e.msg
|
||||||
|
}
|
||||||
|
|
||||||
|
fn opt_ok3(data voidptr, mut option Option3, size int) {
|
||||||
unsafe {
|
unsafe {
|
||||||
*option = OptionBase{
|
*option = Option3{}
|
||||||
ok: true
|
// use err to get the end of Option3 and then memcpy into it
|
||||||
}
|
C.memcpy(byteptr(&option.err) + sizeof(IError), data, size)
|
||||||
// use ecode to get the end of OptionBase and then memcpy into it
|
|
||||||
C.memcpy(byteptr(&option.ecode) + sizeof(int), data, size)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option is the old option type used for bootstrapping
|
pub fn (o Option3) str() string {
|
||||||
struct Option {
|
if o.state == 0 {
|
||||||
ok bool
|
|
||||||
is_none bool
|
|
||||||
error string
|
|
||||||
ecode int
|
|
||||||
}
|
|
||||||
|
|
||||||
// str returns the string representation of the Option.
|
|
||||||
pub fn (o Option) str() string {
|
|
||||||
if o.ok && !o.is_none {
|
|
||||||
return 'Option{ ok }'
|
return 'Option{ ok }'
|
||||||
}
|
}
|
||||||
if o.is_none {
|
if o.state == 1 {
|
||||||
return 'Option{ none }'
|
return 'Option{ none }'
|
||||||
}
|
}
|
||||||
return 'Option{ error: "$o.error" }'
|
return 'Option{ err: "$o.err" }'
|
||||||
}
|
}
|
||||||
|
|
||||||
// opt_none is used internally when returning `none`.
|
[inline]
|
||||||
fn opt_none() Option {
|
pub fn error3(message string) IError {
|
||||||
return Option{
|
return &Error{
|
||||||
ok: false
|
msg: message
|
||||||
is_none: true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// error returns an optional containing the error given in `message`.
|
pub fn error_with_code3(message string, code int) IError {
|
||||||
// `if ouch { return error('an error occurred') }`
|
return &Error {
|
||||||
pub fn error(message string) Option {
|
msg: message
|
||||||
return Option{
|
code: code
|
||||||
ok: false
|
|
||||||
is_none: false
|
|
||||||
error: message
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// error_with_code returns an optional containing both error `message` and error `code`.
|
////////////////////////////////////////
|
||||||
// `if ouch { return error_with_code('an error occurred',1) }`
|
|
||||||
pub fn error_with_code(message string, code int) Option {
|
// these are just here temporarily to avoid breaking the compiler; they will be removed soon
|
||||||
return Option{
|
pub fn error(a string) Option2 { return {} }
|
||||||
ok: false
|
pub fn error_with_code(a string, b int) Option2 { return {} }
|
||||||
is_none: false
|
|
||||||
error: message
|
|
||||||
ecode: code
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Option2 is the base of V's new internal optional return system.
|
// Option2 is the base of V's new internal optional return system.
|
||||||
struct Option2 {
|
struct Option2 {
|
||||||
|
@ -81,13 +73,6 @@ struct Option2 {
|
||||||
// derived Option2_xxx types
|
// derived Option2_xxx types
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error holds information about an error instance
|
|
||||||
pub struct Error {
|
|
||||||
pub:
|
|
||||||
msg string
|
|
||||||
code int
|
|
||||||
}
|
|
||||||
|
|
||||||
[inline]
|
[inline]
|
||||||
fn (e Error) str() string {
|
fn (e Error) str() string {
|
||||||
// TODO: this should probably have a better str method,
|
// TODO: this should probably have a better str method,
|
||||||
|
@ -104,15 +89,14 @@ fn opt_ok(data voidptr, mut option Option2, size int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /*
|
|
||||||
pub fn (o Option2) str() string {
|
pub fn (o Option2) str() string {
|
||||||
if o.state == 0 {
|
if o.state == 0 {
|
||||||
return 'Option2{ ok }'
|
return 'Option{ ok }'
|
||||||
}
|
}
|
||||||
if o.state == 1 {
|
if o.state == 1 {
|
||||||
return 'Option2{ none }'
|
return 'Option{ none }'
|
||||||
}
|
}
|
||||||
return 'Option2{ err: "$o.err" }'
|
return 'Option{ err: "$o.err" }'
|
||||||
}
|
}
|
||||||
|
|
||||||
// error returns an optional containing the error given in `message`.
|
// error returns an optional containing the error given in `message`.
|
||||||
|
@ -137,4 +121,3 @@ pub fn error_with_code2(message string, code int) Option2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// */
|
|
||||||
|
|
|
@ -2387,7 +2387,7 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
|
||||||
return_stmt.types = got_types
|
return_stmt.types = got_types
|
||||||
// allow `none` & `error (Option)` return types for function that returns optional
|
// allow `none` & `error (Option)` return types for function that returns optional
|
||||||
if exp_is_optional
|
if exp_is_optional
|
||||||
&& got_types[0].idx() in [table.none_type_idx, table.error_type_idx, c.table.type_idxs['Option'], c.table.type_idxs['Option2']] {
|
&& got_types[0].idx() in [table.none_type_idx, table.error_type_idx, c.table.type_idxs['Option'], c.table.type_idxs['Option2'], c.table.type_idxs['Option3']] {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if expected_types.len > 0 && expected_types.len != got_types.len {
|
if expected_types.len > 0 && expected_types.len != got_types.len {
|
||||||
|
|
|
@ -552,9 +552,6 @@ fn (mut g Gen) base_type(t table.Type) string {
|
||||||
if nr_muls > 0 {
|
if nr_muls > 0 {
|
||||||
styp += strings.repeat(`*`, nr_muls)
|
styp += strings.repeat(`*`, nr_muls)
|
||||||
}
|
}
|
||||||
// if styp == 'Option' {
|
|
||||||
// return 'Option2'
|
|
||||||
// }
|
|
||||||
return styp
|
return styp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,7 +588,6 @@ fn (g &Gen) optional_type_text(styp string, base string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) register_optional(t table.Type) string {
|
fn (mut g Gen) register_optional(t table.Type) string {
|
||||||
// g.typedefs2.writeln('typedef Option $x;')
|
|
||||||
styp, base := g.optional_type_name(t)
|
styp, base := g.optional_type_name(t)
|
||||||
if styp !in g.optionals {
|
if styp !in g.optionals {
|
||||||
g.typedefs2.writeln('typedef struct $styp $styp;')
|
g.typedefs2.writeln('typedef struct $styp $styp;')
|
||||||
|
@ -736,6 +732,9 @@ fn (g &Gen) type_sidx(t table.Type) string {
|
||||||
//
|
//
|
||||||
pub fn (mut g Gen) write_typedef_types() {
|
pub fn (mut g Gen) write_typedef_types() {
|
||||||
for typ in g.table.types {
|
for typ in g.table.types {
|
||||||
|
if typ.name in c.builtins {
|
||||||
|
continue
|
||||||
|
}
|
||||||
match typ.kind {
|
match typ.kind {
|
||||||
.alias {
|
.alias {
|
||||||
parent := unsafe { &g.table.types[typ.parent_idx] }
|
parent := unsafe { &g.table.types[typ.parent_idx] }
|
||||||
|
@ -755,16 +754,7 @@ pub fn (mut g Gen) write_typedef_types() {
|
||||||
g.type_definitions.writeln('typedef array $typ.cname;')
|
g.type_definitions.writeln('typedef array $typ.cname;')
|
||||||
}
|
}
|
||||||
.interface_ {
|
.interface_ {
|
||||||
info := typ.info as table.Interface
|
g.write_interface_typesymbol_declaration(typ)
|
||||||
g.type_definitions.writeln('typedef struct {')
|
|
||||||
g.type_definitions.writeln('\tvoid* _object;')
|
|
||||||
g.type_definitions.writeln('\tint _interface_idx;')
|
|
||||||
for field in info.fields {
|
|
||||||
styp := g.typ(field.typ)
|
|
||||||
cname := c_name(field.name)
|
|
||||||
g.type_definitions.writeln('\t$styp* $cname;')
|
|
||||||
}
|
|
||||||
g.type_definitions.writeln('} ${c_name(typ.name)};')
|
|
||||||
}
|
}
|
||||||
.chan {
|
.chan {
|
||||||
if typ.name != 'chan' {
|
if typ.name != 'chan' {
|
||||||
|
@ -799,6 +789,19 @@ static inline void __${typ.cname}_pushval($typ.cname ch, $el_stype val) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (mut g Gen) write_interface_typesymbol_declaration(sym table.TypeSymbol) {
|
||||||
|
info := sym.info as table.Interface
|
||||||
|
g.type_definitions.writeln('typedef struct {')
|
||||||
|
g.type_definitions.writeln('\tvoid* _object;')
|
||||||
|
g.type_definitions.writeln('\tint _interface_idx;')
|
||||||
|
for field in info.fields {
|
||||||
|
styp := g.typ(field.typ)
|
||||||
|
cname := c_name(field.name)
|
||||||
|
g.type_definitions.writeln('\t$styp* $cname;')
|
||||||
|
}
|
||||||
|
g.type_definitions.writeln('} ${c_name(sym.name)};\n')
|
||||||
|
}
|
||||||
|
|
||||||
pub fn (mut g Gen) write_fn_typesymbol_declaration(sym table.TypeSymbol) {
|
pub fn (mut g Gen) write_fn_typesymbol_declaration(sym table.TypeSymbol) {
|
||||||
info := sym.info as table.FnType
|
info := sym.info as table.FnType
|
||||||
func := info.func
|
func := info.func
|
||||||
|
@ -908,7 +911,7 @@ fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) {
|
||||||
g.skip_stmt_pos = true
|
g.skip_stmt_pos = true
|
||||||
if stmt is ast.ExprStmt {
|
if stmt is ast.ExprStmt {
|
||||||
sym := g.table.get_type_symbol(stmt.typ)
|
sym := g.table.get_type_symbol(stmt.typ)
|
||||||
if sym.name in ['Option', 'Option2'] || stmt.expr is ast.None {
|
if sym.name in ['Option2', 'Option3'] || stmt.expr is ast.None {
|
||||||
tmp := g.new_tmp_var()
|
tmp := g.new_tmp_var()
|
||||||
g.write('Option2 $tmp = ')
|
g.write('Option2 $tmp = ')
|
||||||
g.expr(stmt.expr)
|
g.expr(stmt.expr)
|
||||||
|
@ -4336,7 +4339,7 @@ fn (mut g Gen) return_statement(node ast.Return) {
|
||||||
if fn_return_is_optional {
|
if fn_return_is_optional {
|
||||||
optional_none := node.exprs[0] is ast.None
|
optional_none := node.exprs[0] is ast.None
|
||||||
ftyp := g.typ(node.types[0])
|
ftyp := g.typ(node.types[0])
|
||||||
mut is_regular_option := ftyp in ['Option', 'Option2']
|
mut is_regular_option := ftyp in ['Option2', 'Option3']
|
||||||
if optional_none || is_regular_option {
|
if optional_none || is_regular_option {
|
||||||
tmp := g.new_tmp_var()
|
tmp := g.new_tmp_var()
|
||||||
g.write('Option2 $tmp = ')
|
g.write('Option2 $tmp = ')
|
||||||
|
@ -4453,7 +4456,7 @@ fn (mut g Gen) return_statement(node ast.Return) {
|
||||||
node.types[0].has_flag(.optional)
|
node.types[0].has_flag(.optional)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if fn_return_is_optional && !expr_type_is_opt && return_sym.name !in ['Option', 'Option2'] {
|
if fn_return_is_optional && !expr_type_is_opt && return_sym.name !in ['Option2', 'Option3'] {
|
||||||
styp := g.base_type(g.fn_decl.return_type)
|
styp := g.base_type(g.fn_decl.return_type)
|
||||||
opt_type := g.typ(g.fn_decl.return_type)
|
opt_type := g.typ(g.fn_decl.return_type)
|
||||||
// Create a tmp for this option
|
// Create a tmp for this option
|
||||||
|
@ -4608,9 +4611,6 @@ fn (mut g Gen) const_decl_init_later(mod string, name string, val string, typ ta
|
||||||
// Initialize more complex consts in `void _vinit/2{}`
|
// Initialize more complex consts in `void _vinit/2{}`
|
||||||
// (C doesn't allow init expressions that can't be resolved at compile time).
|
// (C doesn't allow init expressions that can't be resolved at compile time).
|
||||||
mut styp := g.typ(typ)
|
mut styp := g.typ(typ)
|
||||||
if styp == 'Option' {
|
|
||||||
styp = 'Option2'
|
|
||||||
}
|
|
||||||
cname := '_const_$name'
|
cname := '_const_$name'
|
||||||
g.definitions.writeln('$styp $cname; // inited later')
|
g.definitions.writeln('$styp $cname; // inited later')
|
||||||
if cname == '_const_os__args' {
|
if cname == '_const_os__args' {
|
||||||
|
@ -4996,7 +4996,7 @@ fn (mut g Gen) write_init_function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
builtins = ['string', 'array', 'KeyValue', 'DenseArray', 'map', 'Option', 'Error', 'Option2']
|
builtins = ['string', 'array', 'DenseArray', 'map', 'Error', 'IError', 'Option2', 'Option3']
|
||||||
)
|
)
|
||||||
|
|
||||||
fn (mut g Gen) write_builtin_types() {
|
fn (mut g Gen) write_builtin_types() {
|
||||||
|
@ -5004,7 +5004,12 @@ fn (mut g Gen) write_builtin_types() {
|
||||||
// builtin types need to be on top
|
// builtin types need to be on top
|
||||||
// everything except builtin will get sorted
|
// everything except builtin will get sorted
|
||||||
for builtin_name in c.builtins {
|
for builtin_name in c.builtins {
|
||||||
builtin_types << g.table.types[g.table.type_idxs[builtin_name]]
|
sym := g.table.types[g.table.type_idxs[builtin_name]]
|
||||||
|
if sym.kind == .interface_ {
|
||||||
|
g.write_interface_typesymbol_declaration(sym)
|
||||||
|
} else {
|
||||||
|
builtin_types << sym
|
||||||
|
}
|
||||||
}
|
}
|
||||||
g.write_types(builtin_types)
|
g.write_types(builtin_types)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue