vweb: make thread safe; checker: `$if T is Interface {`
parent
e310513a5f
commit
dc034d9b16
|
@ -22,7 +22,7 @@ fn main() {
|
||||||
println('failed to git pull. uncommitted changes?')
|
println('failed to git pull. uncommitted changes?')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// println('running fast')
|
// println('running ./fast')
|
||||||
resp := os.execute('./fast')
|
resp := os.execute('./fast')
|
||||||
if resp.exit_code < 0 {
|
if resp.exit_code < 0 {
|
||||||
println(resp.output)
|
println(resp.output)
|
||||||
|
@ -38,6 +38,6 @@ fn main() {
|
||||||
os.system('git push origin gh-pages')
|
os.system('git push origin gh-pages')
|
||||||
os.chdir('..')
|
os.chdir('..')
|
||||||
}
|
}
|
||||||
time.sleep(60 * time.second)
|
time.sleep(180 * time.second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,8 +117,7 @@ fn frame(user_data voidptr) {
|
||||||
app.last = t
|
app.last = t
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(ev &C.sapp_event, user_data voidptr) {
|
fn event(ev &C.sapp_event, mut app App) {
|
||||||
mut app := &App(user_data)
|
|
||||||
if ev.@type == .mouse_move {
|
if ev.@type == .mouse_move {
|
||||||
app.ps.explode(ev.mouse_x, ev.mouse_y)
|
app.ps.explode(ev.mouse_x, ev.mouse_y)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ mut:
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println('vweb example')
|
println('vweb example')
|
||||||
vweb.run<App>(port)
|
vweb.run(&App{}, port)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut app App) init_server() {
|
pub fn (mut app App) init_server() {
|
||||||
|
|
|
@ -714,6 +714,7 @@ pub mut:
|
||||||
is_expr bool
|
is_expr bool
|
||||||
typ Type
|
typ Type
|
||||||
has_else bool
|
has_else bool
|
||||||
|
// implements bool // comptime $if implements interface
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IfBranch {
|
pub struct IfBranch {
|
||||||
|
|
|
@ -28,6 +28,7 @@ pub mut:
|
||||||
panic_handler FnPanicHandler = default_table_panic_handler
|
panic_handler FnPanicHandler = default_table_panic_handler
|
||||||
panic_userdata voidptr = voidptr(0) // can be used to pass arbitrary data to panic_handler;
|
panic_userdata voidptr = voidptr(0) // can be used to pass arbitrary data to panic_handler;
|
||||||
panic_npanics int
|
panic_npanics int
|
||||||
|
cur_fn &FnDecl // previously stored in Checker.cur_fn and Gen.cur_fn
|
||||||
}
|
}
|
||||||
|
|
||||||
[unsafe]
|
[unsafe]
|
||||||
|
@ -150,6 +151,7 @@ mut:
|
||||||
pub fn new_table() &Table {
|
pub fn new_table() &Table {
|
||||||
mut t := &Table{
|
mut t := &Table{
|
||||||
type_symbols: []TypeSymbol{cap: 64000}
|
type_symbols: []TypeSymbol{cap: 64000}
|
||||||
|
cur_fn: 0
|
||||||
}
|
}
|
||||||
t.register_builtin_type_symbols()
|
t.register_builtin_type_symbols()
|
||||||
t.is_fmt = true
|
t.is_fmt = true
|
||||||
|
@ -326,9 +328,9 @@ fn (t &Table) register_aggregate_field(mut sym TypeSymbol, name string) ?StructF
|
||||||
return new_field
|
return new_field
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (t &Table) struct_has_field(s &TypeSymbol, name string) bool {
|
pub fn (t &Table) struct_has_field(struct_ &TypeSymbol, name string) bool {
|
||||||
// println('struct_has_field($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx')
|
// println('struct_has_field($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx')
|
||||||
if _ := t.find_field(s, name) {
|
if _ := t.find_field(struct_, name) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -985,6 +987,30 @@ pub fn (t &Table) has_deep_child_no_ref(ts &TypeSymbol, name string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// complete_interface_check does a MxN check for all M interfaces vs all N types, to determine what types implement what interfaces.
|
||||||
|
// It short circuits most checks when an interface can not possibly be implemented by a type.
|
||||||
|
pub fn (mut table Table) complete_interface_check() {
|
||||||
|
util.timing_start(@METHOD)
|
||||||
|
defer {
|
||||||
|
util.timing_measure(@METHOD)
|
||||||
|
}
|
||||||
|
for tk, mut tsym in table.type_symbols {
|
||||||
|
if tsym.kind != .struct_ {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
info := tsym.info as Struct
|
||||||
|
for _, mut idecl in table.interfaces {
|
||||||
|
if idecl.methods.len > tsym.methods.len {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if idecl.fields.len > info.fields.len {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
table.does_type_implement_interface(tk, idecl.typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// bitsize_to_type returns a type corresponding to the bit_size
|
// bitsize_to_type returns a type corresponding to the bit_size
|
||||||
// Examples:
|
// Examples:
|
||||||
//
|
//
|
||||||
|
@ -1020,6 +1046,66 @@ pub fn (mut t Table) bitsize_to_type(bit_size int) Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut table Table) does_type_implement_interface(typ Type, inter_typ Type) bool {
|
||||||
|
// TODO: merge with c.type_implements, which also does error reporting in addition
|
||||||
|
// to checking.
|
||||||
|
utyp := typ
|
||||||
|
if utyp.idx() == inter_typ.idx() {
|
||||||
|
// same type -> already casted to the interface
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if inter_typ.idx() == error_type_idx && utyp.idx() == none_type_idx {
|
||||||
|
// `none` "implements" the Error interface
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
typ_sym := table.get_type_symbol(utyp)
|
||||||
|
if typ_sym.language != .v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
mut inter_sym := table.get_type_symbol(inter_typ)
|
||||||
|
if typ_sym.kind == .interface_ && inter_sym.kind == .interface_ {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// do not check the same type more than once
|
||||||
|
if mut inter_sym.info is Interface {
|
||||||
|
for t in inter_sym.info.types {
|
||||||
|
if t.idx() == utyp.idx() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
imethods := if inter_sym.kind == .interface_ {
|
||||||
|
(inter_sym.info as Interface).methods
|
||||||
|
} else {
|
||||||
|
inter_sym.methods
|
||||||
|
}
|
||||||
|
for imethod in imethods {
|
||||||
|
if method := typ_sym.find_method(imethod.name) {
|
||||||
|
msg := table.is_same_method(imethod, method)
|
||||||
|
if msg.len > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if mut inter_sym.info is Interface {
|
||||||
|
for ifield in inter_sym.info.fields {
|
||||||
|
if field := table.find_field_with_embeds(typ_sym, ifield.name) {
|
||||||
|
if ifield.typ != field.typ {
|
||||||
|
return false
|
||||||
|
} else if ifield.is_mut && !(field.is_mut || field.is_global) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
inter_sym.info.types << utyp
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// resolve_generic_to_concrete resolves generics to real types T => int.
|
// resolve_generic_to_concrete resolves generics to real types T => int.
|
||||||
// Even map[string]map[string]T can be resolved.
|
// Even map[string]map[string]T can be resolved.
|
||||||
// This is used for resolving the generic return type of CallExpr white `unwrap_generic` is used to resolve generic usage in FnDecl.
|
// This is used for resolving the generic return type of CallExpr white `unwrap_generic` is used to resolve generic usage in FnDecl.
|
||||||
|
@ -1162,50 +1248,13 @@ pub fn (mut t Table) generic_struct_insts_to_concrete() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// complete_interface_check does a MxN check for all M interfaces vs all N types, to determine what types implement what interfaces.
|
pub fn (mut table Table) type_implements_interface(utyp Type, interface_type Type) bool {
|
||||||
// It short circuits most checks when an interface can not possibly be implemented by a type.
|
$if debug_interface_type_implements ? {
|
||||||
pub fn (mut table Table) complete_interface_check() {
|
eprintln('> Table.type_implements_inteface typ: $utyp.debug() | interface_typ: $interface_type.debug()')
|
||||||
util.timing_start(@METHOD)
|
|
||||||
defer {
|
|
||||||
util.timing_measure(@METHOD)
|
|
||||||
}
|
|
||||||
for tk, mut tsym in table.type_symbols {
|
|
||||||
if tsym.kind != .struct_ {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
info := tsym.info as Struct
|
|
||||||
for _, mut idecl in table.interfaces {
|
|
||||||
if idecl.methods.len > tsym.methods.len {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if idecl.fields.len > info.fields.len {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
table.does_type_implement_interface(tk, idecl.typ)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut table Table) does_type_implement_interface(typ Type, inter_typ Type) bool {
|
|
||||||
// TODO: merge with c.type_implements, which also does error reporting in addition
|
|
||||||
// to checking.
|
|
||||||
utyp := typ
|
|
||||||
if utyp.idx() == inter_typ.idx() {
|
|
||||||
// same type -> already casted to the interface
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if inter_typ.idx() == error_type_idx && utyp.idx() == none_type_idx {
|
|
||||||
// `none` "implements" the Error interface
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
// utyp := c.unwrap_generic(typ)
|
||||||
typ_sym := table.get_type_symbol(utyp)
|
typ_sym := table.get_type_symbol(utyp)
|
||||||
if typ_sym.language != .v {
|
mut inter_sym := table.get_type_symbol(interface_type)
|
||||||
return false
|
|
||||||
}
|
|
||||||
mut inter_sym := table.get_type_symbol(inter_typ)
|
|
||||||
if typ_sym.kind == .interface_ && inter_sym.kind == .interface_ {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// do not check the same type more than once
|
// do not check the same type more than once
|
||||||
if mut inter_sym.info is Interface {
|
if mut inter_sym.info is Interface {
|
||||||
for t in inter_sym.info.types {
|
for t in inter_sym.info.types {
|
||||||
|
@ -1214,32 +1263,70 @@ fn (mut table Table) does_type_implement_interface(typ Type, inter_typ Type) boo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// styp := table.type_to_str(utyp)
|
||||||
|
if utyp.idx() == interface_type.idx() {
|
||||||
|
// same type -> already casted to the interface
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if interface_type.idx() == error_type_idx && utyp.idx() == none_type_idx {
|
||||||
|
// `none` "implements" the Error interface
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if typ_sym.kind == .interface_ && inter_sym.kind == .interface_ {
|
||||||
|
return false
|
||||||
|
// error('cannot implement interface `$inter_sym.name` with a different interface `$styp`',
|
||||||
|
// pos)
|
||||||
|
}
|
||||||
imethods := if inter_sym.kind == .interface_ {
|
imethods := if inter_sym.kind == .interface_ {
|
||||||
(inter_sym.info as Interface).methods
|
(inter_sym.info as Interface).methods
|
||||||
} else {
|
} else {
|
||||||
inter_sym.methods
|
inter_sym.methods
|
||||||
}
|
}
|
||||||
|
// Verify methods
|
||||||
for imethod in imethods {
|
for imethod in imethods {
|
||||||
if method := typ_sym.find_method(imethod.name) {
|
if method := typ_sym.find_method(imethod.name) {
|
||||||
msg := table.is_same_method(imethod, method)
|
msg := table.is_same_method(imethod, method)
|
||||||
if msg.len > 0 {
|
if msg.len > 0 {
|
||||||
|
// sig := table.fn_signature(imethod, skip_receiver: true)
|
||||||
|
// add_error_detail('$inter_sym.name has `$sig`')
|
||||||
|
// c.error('`$styp` incorrectly implements method `$imethod.name` of interface `$inter_sym.name`: $msg',
|
||||||
|
// pos)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// c.error("`$styp` doesn't implement method `$imethod.name` of interface `$inter_sym.name`",
|
||||||
|
// pos)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
// Verify fields
|
||||||
if mut inter_sym.info is Interface {
|
if mut inter_sym.info is Interface {
|
||||||
for ifield in inter_sym.info.fields {
|
for ifield in inter_sym.info.fields {
|
||||||
|
if ifield.typ == voidptr_type {
|
||||||
|
// Allow `voidptr` fields in interfaces for now. (for example
|
||||||
|
// to enable .db check in vweb)
|
||||||
|
if table.struct_has_field(typ_sym, ifield.name) {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
if field := table.find_field_with_embeds(typ_sym, ifield.name) {
|
if field := table.find_field_with_embeds(typ_sym, ifield.name) {
|
||||||
if ifield.typ != field.typ {
|
if ifield.typ != field.typ {
|
||||||
|
// exp := table.type_to_str(ifield.typ)
|
||||||
|
// got := table.type_to_str(field.typ)
|
||||||
|
// c.error('`$styp` incorrectly implements field `$ifield.name` of interface `$inter_sym.name`, expected `$exp`, got `$got`',
|
||||||
|
// pos)
|
||||||
return false
|
return false
|
||||||
} else if ifield.is_mut && !(field.is_mut || field.is_global) {
|
} else if ifield.is_mut && !(field.is_mut || field.is_global) {
|
||||||
|
// c.error('`$styp` incorrectly implements interface `$inter_sym.name`, field `$ifield.name` must be mutable',
|
||||||
|
// pos)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return false
|
// c.error("`$styp` doesn't implement field `$ifield.name` of interface `$inter_sym.name`",
|
||||||
|
// pos)
|
||||||
}
|
}
|
||||||
inter_sym.info.types << utyp
|
inter_sym.info.types << utyp
|
||||||
}
|
}
|
||||||
|
|
|
@ -741,7 +741,7 @@ pub mut:
|
||||||
|
|
||||||
pub struct Interface {
|
pub struct Interface {
|
||||||
pub mut:
|
pub mut:
|
||||||
types []Type
|
types []Type // all types that implement this interface
|
||||||
fields []StructField
|
fields []StructField
|
||||||
methods []Fn
|
methods []Fn
|
||||||
ifaces []Type
|
ifaces []Type
|
||||||
|
|
|
@ -467,7 +467,8 @@ pub fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) ast.Typ
|
||||||
node.need_fmts[i] = fmt != c.get_default_fmt(ftyp, typ)
|
node.need_fmts[i] = fmt != c.get_default_fmt(ftyp, typ)
|
||||||
}
|
}
|
||||||
// check recursive str
|
// check recursive str
|
||||||
if c.cur_fn.is_method && c.cur_fn.name == 'str' && c.cur_fn.receiver.name == expr.str() {
|
if c.table.cur_fn.is_method && c.table.cur_fn.name == 'str'
|
||||||
|
&& c.table.cur_fn.receiver.name == expr.str() {
|
||||||
c.error('cannot call `str()` method recursively', expr.position())
|
c.error('cannot call `str()` method recursively', expr.position())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -527,7 +528,7 @@ pub fn (mut c Checker) infer_fn_generic_types(f ast.Fn, mut call_expr ast.CallEx
|
||||||
mut param_elem_sym := c.table.get_type_symbol(param_elem_info.elem_type)
|
mut param_elem_sym := c.table.get_type_symbol(param_elem_info.elem_type)
|
||||||
for {
|
for {
|
||||||
if arg_elem_sym.kind == .array && param_elem_sym.kind == .array
|
if arg_elem_sym.kind == .array && param_elem_sym.kind == .array
|
||||||
&& param_elem_sym.name !in c.cur_fn.generic_names {
|
&& param_elem_sym.name !in c.table.cur_fn.generic_names {
|
||||||
arg_elem_info = arg_elem_sym.info as ast.Array
|
arg_elem_info = arg_elem_sym.info as ast.Array
|
||||||
arg_elem_sym = c.table.get_type_symbol(arg_elem_info.elem_type)
|
arg_elem_sym = c.table.get_type_symbol(arg_elem_info.elem_type)
|
||||||
param_elem_info = param_elem_sym.info as ast.Array
|
param_elem_info = param_elem_sym.info as ast.Array
|
||||||
|
|
|
@ -47,15 +47,15 @@ pub mut:
|
||||||
notices []errors.Notice
|
notices []errors.Notice
|
||||||
error_lines []int // to avoid printing multiple errors for the same line
|
error_lines []int // to avoid printing multiple errors for the same line
|
||||||
expected_type ast.Type
|
expected_type ast.Type
|
||||||
expected_or_type ast.Type // fn() or { 'this type' } eg. string. expected or block type
|
expected_or_type ast.Type // fn() or { 'this type' } eg. string. expected or block type
|
||||||
cur_fn &ast.FnDecl // current function
|
// cur_fn &ast.FnDecl // current function
|
||||||
const_decl string
|
const_decl string
|
||||||
const_deps []string
|
const_deps []string
|
||||||
const_names []string
|
const_names []string
|
||||||
global_names []string
|
global_names []string
|
||||||
locked_names []string // vars that are currently locked
|
locked_names []string // vars that are currently locked
|
||||||
rlocked_names []string // vars that are currently read-locked
|
rlocked_names []string // vars that are currently read-locked
|
||||||
in_for_count int // if checker is currently in a for loop
|
in_for_count int // if checker is currently in a for loop
|
||||||
// checked_ident string // to avoid infinite checker loops
|
// checked_ident string // to avoid infinite checker loops
|
||||||
returns bool
|
returns bool
|
||||||
scope_returns bool
|
scope_returns bool
|
||||||
|
@ -99,7 +99,6 @@ pub fn new_checker(table &ast.Table, pref &pref.Preferences) Checker {
|
||||||
return Checker{
|
return Checker{
|
||||||
table: table
|
table: table
|
||||||
pref: pref
|
pref: pref
|
||||||
cur_fn: 0
|
|
||||||
timers: util.new_timers(timers_should_print)
|
timers: util.new_timers(timers_should_print)
|
||||||
match_exhaustive_cutoff_limit: pref.checker_match_exhaustive_cutoff_limit
|
match_exhaustive_cutoff_limit: pref.checker_match_exhaustive_cutoff_limit
|
||||||
}
|
}
|
||||||
|
@ -666,7 +665,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type {
|
||||||
'it cannot be initialized with `$type_sym.name{}`', struct_init.pos)
|
'it cannot be initialized with `$type_sym.name{}`', struct_init.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if type_sym.name.len == 1 && c.cur_fn.generic_names.len == 0 {
|
if type_sym.name.len == 1 && c.table.cur_fn.generic_names.len == 0 {
|
||||||
c.error('unknown struct `$type_sym.name`', struct_init.pos)
|
c.error('unknown struct `$type_sym.name`', struct_init.pos)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -1531,10 +1530,10 @@ pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) ast.Type {
|
||||||
c.expected_or_type = call_expr.return_type.clear_flag(.optional)
|
c.expected_or_type = call_expr.return_type.clear_flag(.optional)
|
||||||
c.stmts(call_expr.or_block.stmts)
|
c.stmts(call_expr.or_block.stmts)
|
||||||
c.expected_or_type = ast.void_type
|
c.expected_or_type = ast.void_type
|
||||||
if call_expr.or_block.kind == .propagate && !c.cur_fn.return_type.has_flag(.optional)
|
if call_expr.or_block.kind == .propagate && !c.table.cur_fn.return_type.has_flag(.optional)
|
||||||
&& !c.inside_const {
|
&& !c.inside_const {
|
||||||
if !c.cur_fn.is_main {
|
if !c.table.cur_fn.is_main {
|
||||||
c.error('to propagate the optional call, `$c.cur_fn.name` must return an optional',
|
c.error('to propagate the optional call, `$c.table.cur_fn.name` must return an optional',
|
||||||
call_expr.or_block.pos)
|
call_expr.or_block.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1916,7 +1915,7 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
|
||||||
c.warn('method `${left_type_sym.name}.$method_name` must be called from an `unsafe` block',
|
c.warn('method `${left_type_sym.name}.$method_name` must be called from an `unsafe` block',
|
||||||
call_expr.pos)
|
call_expr.pos)
|
||||||
}
|
}
|
||||||
if !c.cur_fn.is_deprecated && method.is_deprecated {
|
if !c.table.cur_fn.is_deprecated && method.is_deprecated {
|
||||||
c.deprecate_fnmethod('method', '${left_type_sym.name}.$method.name', method,
|
c.deprecate_fnmethod('method', '${left_type_sym.name}.$method.name', method,
|
||||||
call_expr)
|
call_expr)
|
||||||
}
|
}
|
||||||
|
@ -2160,7 +2159,7 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
|
||||||
concrete_types << concrete_type
|
concrete_types << concrete_type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !isnil(c.cur_fn) && c.cur_concrete_types.len == 0 && has_generic {
|
if !isnil(c.table.cur_fn) && c.cur_concrete_types.len == 0 && has_generic {
|
||||||
c.error('generic fn using generic types cannot be called outside of generic fn',
|
c.error('generic fn using generic types cannot be called outside of generic fn',
|
||||||
call_expr.pos)
|
call_expr.pos)
|
||||||
}
|
}
|
||||||
|
@ -2312,7 +2311,7 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
|
||||||
&& func.mod != c.mod {
|
&& func.mod != c.mod {
|
||||||
c.error('function `$func.name` is private', call_expr.pos)
|
c.error('function `$func.name` is private', call_expr.pos)
|
||||||
}
|
}
|
||||||
if !isnil(c.cur_fn) && !c.cur_fn.is_deprecated && func.is_deprecated {
|
if !isnil(c.table.cur_fn) && !c.table.cur_fn.is_deprecated && func.is_deprecated {
|
||||||
c.deprecate_fnmethod('function', func.name, func, call_expr)
|
c.deprecate_fnmethod('function', func.name, func, call_expr)
|
||||||
}
|
}
|
||||||
if func.is_unsafe && !c.inside_unsafe
|
if func.is_unsafe && !c.inside_unsafe
|
||||||
|
@ -2562,13 +2561,13 @@ fn semicolonize(main string, details string) string {
|
||||||
return '$main; $details'
|
return '$main; $details'
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut c Checker) type_implements(typ ast.Type, inter_typ ast.Type, pos token.Position) bool {
|
fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos token.Position) bool {
|
||||||
$if debug_interface_type_implements ? {
|
$if debug_interface_type_implements ? {
|
||||||
eprintln('> type_implements typ: $typ.debug() | inter_typ: $inter_typ.debug()')
|
eprintln('> type_implements typ: $typ.debug() | inter_typ: $inter_typ.debug()')
|
||||||
}
|
}
|
||||||
utyp := c.unwrap_generic(typ)
|
utyp := c.unwrap_generic(typ)
|
||||||
typ_sym := c.table.get_type_symbol(utyp)
|
typ_sym := c.table.get_type_symbol(utyp)
|
||||||
mut inter_sym := c.table.get_type_symbol(inter_typ)
|
mut inter_sym := c.table.get_type_symbol(interface_type)
|
||||||
// do not check the same type more than once
|
// do not check the same type more than once
|
||||||
if mut inter_sym.info is ast.Interface {
|
if mut inter_sym.info is ast.Interface {
|
||||||
for t in inter_sym.info.types {
|
for t in inter_sym.info.types {
|
||||||
|
@ -2578,11 +2577,11 @@ fn (mut c Checker) type_implements(typ ast.Type, inter_typ ast.Type, pos token.P
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
styp := c.table.type_to_str(utyp)
|
styp := c.table.type_to_str(utyp)
|
||||||
if utyp.idx() == inter_typ.idx() {
|
if utyp.idx() == interface_type.idx() {
|
||||||
// same type -> already casted to the interface
|
// same type -> already casted to the interface
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if inter_typ.idx() == ast.error_type_idx && utyp.idx() == ast.none_type_idx {
|
if interface_type.idx() == ast.error_type_idx && utyp.idx() == ast.none_type_idx {
|
||||||
// `none` "implements" the Error interface
|
// `none` "implements" the Error interface
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -2595,6 +2594,7 @@ fn (mut c Checker) type_implements(typ ast.Type, inter_typ ast.Type, pos token.P
|
||||||
} else {
|
} else {
|
||||||
inter_sym.methods
|
inter_sym.methods
|
||||||
}
|
}
|
||||||
|
// Verify methods
|
||||||
for imethod in imethods {
|
for imethod in imethods {
|
||||||
if method := typ_sym.find_method(imethod.name) {
|
if method := typ_sym.find_method(imethod.name) {
|
||||||
msg := c.table.is_same_method(imethod, method)
|
msg := c.table.is_same_method(imethod, method)
|
||||||
|
@ -2610,6 +2610,7 @@ fn (mut c Checker) type_implements(typ ast.Type, inter_typ ast.Type, pos token.P
|
||||||
c.error("`$styp` doesn't implement method `$imethod.name` of interface `$inter_sym.name`",
|
c.error("`$styp` doesn't implement method `$imethod.name` of interface `$inter_sym.name`",
|
||||||
pos)
|
pos)
|
||||||
}
|
}
|
||||||
|
// Verify fields
|
||||||
if mut inter_sym.info is ast.Interface {
|
if mut inter_sym.info is ast.Interface {
|
||||||
for ifield in inter_sym.info.fields {
|
for ifield in inter_sym.info.fields {
|
||||||
if field := c.table.find_field_with_embeds(typ_sym, ifield.name) {
|
if field := c.table.find_field_with_embeds(typ_sym, ifield.name) {
|
||||||
|
@ -2662,9 +2663,9 @@ pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type ast.Type) ast
|
||||||
|
|
||||||
pub fn (mut c Checker) check_or_expr(or_expr ast.OrExpr, ret_type ast.Type, expr_return_type ast.Type) {
|
pub fn (mut c Checker) check_or_expr(or_expr ast.OrExpr, ret_type ast.Type, expr_return_type ast.Type) {
|
||||||
if or_expr.kind == .propagate {
|
if or_expr.kind == .propagate {
|
||||||
if !c.cur_fn.return_type.has_flag(.optional) && c.cur_fn.name != 'main.main'
|
if !c.table.cur_fn.return_type.has_flag(.optional) && c.table.cur_fn.name != 'main.main'
|
||||||
&& !c.inside_const {
|
&& !c.inside_const {
|
||||||
c.error('to propagate the optional call, `$c.cur_fn.name` must return an optional',
|
c.error('to propagate the optional call, `$c.table.cur_fn.name` must return an optional',
|
||||||
or_expr.pos)
|
or_expr.pos)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -2763,7 +2764,7 @@ pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) ast.Typ
|
||||||
match mut selector_expr.expr {
|
match mut selector_expr.expr {
|
||||||
ast.Ident {
|
ast.Ident {
|
||||||
name := selector_expr.expr.name
|
name := selector_expr.expr.name
|
||||||
valid_generic := util.is_generic_type_name(name) && name in c.cur_fn.generic_names
|
valid_generic := util.is_generic_type_name(name) && name in c.table.cur_fn.generic_names
|
||||||
if valid_generic {
|
if valid_generic {
|
||||||
name_type = ast.Type(c.table.find_type_idx(name)).set_flag(.generic)
|
name_type = ast.Type(c.table.find_type_idx(name)).set_flag(.generic)
|
||||||
}
|
}
|
||||||
|
@ -2901,10 +2902,10 @@ pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) ast.Typ
|
||||||
|
|
||||||
// TODO: non deferred
|
// TODO: non deferred
|
||||||
pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
|
pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
|
||||||
c.expected_type = c.cur_fn.return_type
|
c.expected_type = c.table.cur_fn.return_type
|
||||||
expected_type := c.unwrap_generic(c.expected_type)
|
expected_type := c.unwrap_generic(c.expected_type)
|
||||||
expected_type_sym := c.table.get_type_symbol(expected_type)
|
expected_type_sym := c.table.get_type_symbol(expected_type)
|
||||||
if return_stmt.exprs.len > 0 && c.cur_fn.return_type == ast.void_type {
|
if return_stmt.exprs.len > 0 && c.table.cur_fn.return_type == ast.void_type {
|
||||||
c.error('unexpected argument, current function does not return anything', return_stmt.exprs[0].position())
|
c.error('unexpected argument, current function does not return anything', return_stmt.exprs[0].position())
|
||||||
return
|
return
|
||||||
} else if return_stmt.exprs.len == 0 && !(c.expected_type == ast.void_type
|
} else if return_stmt.exprs.len == 0 && !(c.expected_type == ast.void_type
|
||||||
|
@ -2979,7 +2980,7 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
|
||||||
if return_stmt.exprs[i].is_auto_deref_var() {
|
if return_stmt.exprs[i].is_auto_deref_var() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.error('fn `$c.cur_fn.name` expects you to return a non reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_typ)}` instead',
|
c.error('fn `$c.table.cur_fn.name` expects you to return a non reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_typ)}` instead',
|
||||||
pos)
|
pos)
|
||||||
}
|
}
|
||||||
if (exp_type.is_ptr() || exp_type.is_pointer())
|
if (exp_type.is_ptr() || exp_type.is_pointer())
|
||||||
|
@ -2988,7 +2989,7 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
|
||||||
if return_stmt.exprs[i].is_auto_deref_var() {
|
if return_stmt.exprs[i].is_auto_deref_var() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.error('fn `$c.cur_fn.name` expects you to return a reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_typ)}` instead',
|
c.error('fn `$c.table.cur_fn.name` expects you to return a reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_typ)}` instead',
|
||||||
pos)
|
pos)
|
||||||
}
|
}
|
||||||
if exp_type.is_ptr() && got_typ.is_ptr() {
|
if exp_type.is_ptr() && got_typ.is_ptr() {
|
||||||
|
@ -3839,8 +3840,8 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
}
|
}
|
||||||
ast.DeferStmt {
|
ast.DeferStmt {
|
||||||
if node.idx_in_fn < 0 {
|
if node.idx_in_fn < 0 {
|
||||||
node.idx_in_fn = c.cur_fn.defer_stmts.len
|
node.idx_in_fn = c.table.cur_fn.defer_stmts.len
|
||||||
c.cur_fn.defer_stmts << unsafe { &node }
|
c.table.cur_fn.defer_stmts << unsafe { &node }
|
||||||
}
|
}
|
||||||
c.stmts(node.stmts)
|
c.stmts(node.stmts)
|
||||||
}
|
}
|
||||||
|
@ -3891,7 +3892,7 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
c.warn('`goto` requires `unsafe` (consider using labelled break/continue)',
|
c.warn('`goto` requires `unsafe` (consider using labelled break/continue)',
|
||||||
node.pos)
|
node.pos)
|
||||||
}
|
}
|
||||||
if node.name !in c.cur_fn.label_names {
|
if node.name !in c.table.cur_fn.label_names {
|
||||||
c.error('unknown label `$node.name`', node.pos)
|
c.error('unknown label `$node.name`', node.pos)
|
||||||
}
|
}
|
||||||
// TODO: check label doesn't bypass variable declarations
|
// TODO: check label doesn't bypass variable declarations
|
||||||
|
@ -4424,8 +4425,8 @@ fn (mut c Checker) stmts(stmts []ast.Stmt) {
|
||||||
|
|
||||||
pub fn (mut c Checker) unwrap_generic(typ ast.Type) ast.Type {
|
pub fn (mut c Checker) unwrap_generic(typ ast.Type) ast.Type {
|
||||||
if typ.has_flag(.generic) {
|
if typ.has_flag(.generic) {
|
||||||
if t_typ := c.table.resolve_generic_to_concrete(typ, c.cur_fn.generic_names, c.cur_concrete_types,
|
if t_typ := c.table.resolve_generic_to_concrete(typ, c.table.cur_fn.generic_names,
|
||||||
false)
|
c.cur_concrete_types, false)
|
||||||
{
|
{
|
||||||
return t_typ
|
return t_typ
|
||||||
}
|
}
|
||||||
|
@ -4453,11 +4454,11 @@ pub fn (mut c Checker) expr(node ast.Expr) ast.Type {
|
||||||
}
|
}
|
||||||
ast.AnonFn {
|
ast.AnonFn {
|
||||||
c.inside_anon_fn = true
|
c.inside_anon_fn = true
|
||||||
keep_fn := c.cur_fn
|
keep_fn := c.table.cur_fn
|
||||||
c.cur_fn = unsafe { &node.decl }
|
c.table.cur_fn = unsafe { &node.decl }
|
||||||
c.stmts(node.decl.stmts)
|
c.stmts(node.decl.stmts)
|
||||||
c.fn_decl(mut node.decl)
|
c.fn_decl(mut node.decl)
|
||||||
c.cur_fn = keep_fn
|
c.table.cur_fn = keep_fn
|
||||||
c.inside_anon_fn = false
|
c.inside_anon_fn = false
|
||||||
return node.typ
|
return node.typ
|
||||||
}
|
}
|
||||||
|
@ -4939,23 +4940,23 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
|
||||||
fn (mut c Checker) at_expr(mut node ast.AtExpr) ast.Type {
|
fn (mut c Checker) at_expr(mut node ast.AtExpr) ast.Type {
|
||||||
match node.kind {
|
match node.kind {
|
||||||
.fn_name {
|
.fn_name {
|
||||||
node.val = c.cur_fn.name.all_after_last('.')
|
node.val = c.table.cur_fn.name.all_after_last('.')
|
||||||
}
|
}
|
||||||
.method_name {
|
.method_name {
|
||||||
fname := c.cur_fn.name.all_after_last('.')
|
fname := c.table.cur_fn.name.all_after_last('.')
|
||||||
if c.cur_fn.is_method {
|
if c.table.cur_fn.is_method {
|
||||||
node.val = c.table.type_to_str(c.cur_fn.receiver.typ).all_after_last('.') + '.' +
|
node.val = c.table.type_to_str(c.table.cur_fn.receiver.typ).all_after_last('.') +
|
||||||
fname
|
'.' + fname
|
||||||
} else {
|
} else {
|
||||||
node.val = fname
|
node.val = fname
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.mod_name {
|
.mod_name {
|
||||||
node.val = c.cur_fn.mod
|
node.val = c.table.cur_fn.mod
|
||||||
}
|
}
|
||||||
.struct_name {
|
.struct_name {
|
||||||
if c.cur_fn.is_method {
|
if c.table.cur_fn.is_method {
|
||||||
node.val = c.table.type_to_str(c.cur_fn.receiver.typ).all_after_last('.')
|
node.val = c.table.type_to_str(c.table.cur_fn.receiver.typ).all_after_last('.')
|
||||||
} else {
|
} else {
|
||||||
node.val = ''
|
node.val = ''
|
||||||
}
|
}
|
||||||
|
@ -5748,6 +5749,11 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
||||||
comptime_field_name = left.expr.str()
|
comptime_field_name = left.expr.str()
|
||||||
c.comptime_fields_type[comptime_field_name] = got_type
|
c.comptime_fields_type[comptime_field_name] = got_type
|
||||||
is_comptime_type_is_expr = true
|
is_comptime_type_is_expr = true
|
||||||
|
} else if branch.cond.right is ast.TypeNode && left is ast.TypeNode {
|
||||||
|
// is interface
|
||||||
|
checked_type := c.unwrap_generic((left as ast.TypeNode).typ)
|
||||||
|
should_skip = !c.table.type_implements_interface(checked_type,
|
||||||
|
got_type)
|
||||||
} else if left is ast.TypeNode {
|
} else if left is ast.TypeNode {
|
||||||
is_comptime_type_is_expr = true
|
is_comptime_type_is_expr = true
|
||||||
left_type := c.unwrap_generic(left.typ)
|
left_type := c.unwrap_generic(left.typ)
|
||||||
|
@ -5932,8 +5938,8 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
||||||
return node.typ
|
return node.typ
|
||||||
}
|
}
|
||||||
|
|
||||||
// comp_if_branch checks the condition of a compile-time `if` branch. It returns a `bool` that
|
// comp_if_branch checks the condition of a compile-time `if` branch. It returns `true`
|
||||||
// saying whether that branch's contents should be skipped (targets a different os for example)
|
// if that branch's contents should be skipped (targets a different os for example)
|
||||||
fn (mut c Checker) comp_if_branch(cond ast.Expr, pos token.Position) bool {
|
fn (mut c Checker) comp_if_branch(cond ast.Expr, pos token.Position) bool {
|
||||||
// TODO: better error messages here
|
// TODO: better error messages here
|
||||||
match cond {
|
match cond {
|
||||||
|
@ -5971,12 +5977,20 @@ fn (mut c Checker) comp_if_branch(cond ast.Expr, pos token.Position) bool {
|
||||||
return l && r // skip (return true) only if both should be skipped
|
return l && r // skip (return true) only if both should be skipped
|
||||||
}
|
}
|
||||||
.key_is, .not_is {
|
.key_is, .not_is {
|
||||||
if cond.left is ast.SelectorExpr || cond.left is ast.TypeNode {
|
if cond.left is ast.TypeNode && cond.right is ast.TypeNode {
|
||||||
// $if method.@type is string
|
// `$if Foo is Interface {`
|
||||||
|
type_node := cond.right as ast.TypeNode
|
||||||
|
sym := c.table.get_type_symbol(type_node.typ)
|
||||||
|
if sym.kind != .interface_ {
|
||||||
|
c.error('`$sym.name` is not an interface', cond.right.position())
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
} else if cond.left is ast.SelectorExpr || cond.left is ast.TypeNode {
|
||||||
|
// `$if method.@type is string`
|
||||||
c.expr(cond.left)
|
c.expr(cond.left)
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
c.error('invalid `\$if` condition: expected a type or selector expression',
|
c.error('invalid `\$if` condition: expected a type or a selector expression or an interface check',
|
||||||
cond.left.position())
|
cond.left.position())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6868,7 +6882,7 @@ fn (mut c Checker) post_process_generic_fns() {
|
||||||
for concrete_types in c.table.fn_generic_types[node.name] {
|
for concrete_types in c.table.fn_generic_types[node.name] {
|
||||||
c.cur_concrete_types = concrete_types
|
c.cur_concrete_types = concrete_types
|
||||||
c.fn_decl(mut node)
|
c.fn_decl(mut node)
|
||||||
if node.name in ['vweb.run_app', 'vweb.run'] {
|
if node.name == 'vweb.run' {
|
||||||
for ct in concrete_types {
|
for ct in concrete_types {
|
||||||
if ct !in c.vweb_gen_types {
|
if ct !in c.vweb_gen_types {
|
||||||
c.vweb_gen_types << ct
|
c.vweb_gen_types << ct
|
||||||
|
@ -7033,7 +7047,8 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.expected_type = ast.void_type
|
c.expected_type = ast.void_type
|
||||||
c.cur_fn = unsafe { node }
|
c.table.cur_fn = unsafe { node }
|
||||||
|
// c.table.cur_fn = node
|
||||||
// Add return if `fn(...) ? {...}` have no return at end
|
// Add return if `fn(...) ? {...}` have no return at end
|
||||||
if node.return_type != ast.void_type && node.return_type.has_flag(.optional)
|
if node.return_type != ast.void_type && node.return_type.has_flag(.optional)
|
||||||
&& (node.stmts.len == 0 || node.stmts[node.stmts.len - 1] !is ast.Return) {
|
&& (node.stmts.len == 0 || node.stmts[node.stmts.len - 1] !is ast.Return) {
|
||||||
|
|
|
@ -19,7 +19,7 @@ vlib/v/checker/tests/unknown_comptime_expr.vv:13:6: error: undefined ident: `huh
|
||||||
| ~~~
|
| ~~~
|
||||||
14 | $if s is int {}
|
14 | $if s is int {}
|
||||||
15 | $if s.i is 5 {}
|
15 | $if s.i is 5 {}
|
||||||
vlib/v/checker/tests/unknown_comptime_expr.vv:14:6: error: invalid `$if` condition: expected a type or selector expression
|
vlib/v/checker/tests/unknown_comptime_expr.vv:14:6: error: invalid `$if` condition: expected a type or selector expression or an interface check
|
||||||
12 | s := S1{}
|
12 | s := S1{}
|
||||||
13 | $if huh.typ is T {}
|
13 | $if huh.typ is T {}
|
||||||
14 | $if s is int {}
|
14 | $if s is int {}
|
||||||
|
|
|
@ -43,6 +43,5 @@ pub fn (mut app App) index() {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
port := 8181
|
port := 8181
|
||||||
mut app := App{}
|
vweb.run<App>(&App{}, port)
|
||||||
vweb.run_app<App>(mut app, port)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,5 @@ pub fn (mut app App) index() vweb.Result {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
mut app := App{}
|
vweb.run<App>(&App{}, 8181)
|
||||||
vweb.run_app<App>(mut app, 8181)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,22 +120,22 @@ mut:
|
||||||
is_builtin_mod bool
|
is_builtin_mod bool
|
||||||
hotcode_fn_names []string
|
hotcode_fn_names []string
|
||||||
embedded_files []ast.EmbeddedFile
|
embedded_files []ast.EmbeddedFile
|
||||||
cur_fn ast.FnDecl
|
// cur_fn ast.FnDecl
|
||||||
cur_concrete_types []ast.Type // current concrete types, e.g. <int, string>
|
cur_concrete_types []ast.Type // current concrete types, e.g. <int, string>
|
||||||
sql_i int
|
sql_i int
|
||||||
sql_stmt_name string
|
sql_stmt_name string
|
||||||
sql_bind_name string
|
sql_bind_name string
|
||||||
sql_idents []string
|
sql_idents []string
|
||||||
sql_idents_types []ast.Type
|
sql_idents_types []ast.Type
|
||||||
sql_left_type ast.Type
|
sql_left_type ast.Type
|
||||||
sql_table_name string
|
sql_table_name string
|
||||||
sql_fkey string
|
sql_fkey string
|
||||||
sql_parent_id string
|
sql_parent_id string
|
||||||
sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
|
sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
|
||||||
inside_vweb_tmpl bool
|
inside_vweb_tmpl bool
|
||||||
inside_return bool
|
inside_return bool
|
||||||
inside_or_block bool
|
inside_or_block bool
|
||||||
strs_to_free0 []string // strings.Builder
|
strs_to_free0 []string // strings.Builder
|
||||||
// strs_to_free []string // strings.Builder
|
// strs_to_free []string // strings.Builder
|
||||||
inside_call bool
|
inside_call bool
|
||||||
has_main bool
|
has_main bool
|
||||||
|
@ -6369,38 +6369,40 @@ fn (mut g Gen) interface_table() string {
|
||||||
}
|
}
|
||||||
already_generated_mwrappers[interface_index_name] = current_iinidx
|
already_generated_mwrappers[interface_index_name] = current_iinidx
|
||||||
current_iinidx++
|
current_iinidx++
|
||||||
// eprintln('>>> current_iinidx: ${current_iinidx-iinidx_minimum_base} | interface_index_name: $interface_index_name')
|
if ityp.name != 'vweb.DbInterface' { // TODO remove this
|
||||||
sb.writeln('$staticprefix $interface_name I_${cctype}_to_Interface_${interface_name}($cctype* x);')
|
// eprintln('>>> current_iinidx: ${current_iinidx-iinidx_minimum_base} | interface_index_name: $interface_index_name')
|
||||||
mut cast_struct := strings.new_builder(100)
|
sb.writeln('$staticprefix $interface_name I_${cctype}_to_Interface_${interface_name}($cctype* x);')
|
||||||
cast_struct.writeln('($interface_name) {')
|
mut cast_struct := strings.new_builder(100)
|
||||||
cast_struct.writeln('\t\t._$cctype = x,')
|
cast_struct.writeln('($interface_name) {')
|
||||||
cast_struct.writeln('\t\t._typ = $interface_index_name,')
|
cast_struct.writeln('\t\t._$cctype = x,')
|
||||||
for field in inter_info.fields {
|
cast_struct.writeln('\t\t._typ = $interface_index_name,')
|
||||||
cname := c_name(field.name)
|
for field in inter_info.fields {
|
||||||
field_styp := g.typ(field.typ)
|
cname := c_name(field.name)
|
||||||
if _ := st_sym.find_field(field.name) {
|
field_styp := g.typ(field.typ)
|
||||||
cast_struct.writeln('\t\t.$cname = ($field_styp*)((char*)x + __offsetof_ptr(x, $cctype, $cname)),')
|
if _ := st_sym.find_field(field.name) {
|
||||||
} else {
|
cast_struct.writeln('\t\t.$cname = ($field_styp*)((char*)x + __offsetof_ptr(x, $cctype, $cname)),')
|
||||||
// the field is embedded in another struct
|
} else {
|
||||||
cast_struct.write_string('\t\t.$cname = ($field_styp*)((char*)x')
|
// the field is embedded in another struct
|
||||||
for embed_type in st_sym.struct_info().embeds {
|
cast_struct.write_string('\t\t.$cname = ($field_styp*)((char*)x')
|
||||||
embed_sym := g.table.get_type_symbol(embed_type)
|
for embed_type in st_sym.struct_info().embeds {
|
||||||
if _ := embed_sym.find_field(field.name) {
|
embed_sym := g.table.get_type_symbol(embed_type)
|
||||||
cast_struct.write_string(' + __offsetof_ptr(x, $cctype, $embed_sym.embed_name()) + __offsetof_ptr(x, $embed_sym.cname, $cname)')
|
if _ := embed_sym.find_field(field.name) {
|
||||||
break
|
cast_struct.write_string(' + __offsetof_ptr(x, $cctype, $embed_sym.embed_name()) + __offsetof_ptr(x, $embed_sym.cname, $cname)')
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
cast_struct.writeln('),')
|
||||||
}
|
}
|
||||||
cast_struct.writeln('),')
|
|
||||||
}
|
}
|
||||||
}
|
cast_struct.write_string('\t}')
|
||||||
cast_struct.write_string('\t}')
|
cast_struct_str := cast_struct.str()
|
||||||
cast_struct_str := cast_struct.str()
|
|
||||||
|
|
||||||
cast_functions.writeln('
|
cast_functions.writeln('
|
||||||
// Casting functions for converting "$cctype" to interface "$interface_name"
|
// Casting functions for converting "$cctype" to interface "$interface_name"
|
||||||
$staticprefix inline $interface_name I_${cctype}_to_Interface_${interface_name}($cctype* x) {
|
$staticprefix inline $interface_name I_${cctype}_to_Interface_${interface_name}($cctype* x) {
|
||||||
return $cast_struct_str;
|
return $cast_struct_str;
|
||||||
}')
|
}')
|
||||||
|
}
|
||||||
|
|
||||||
if g.pref.build_mode != .build_module {
|
if g.pref.build_mode != .build_module {
|
||||||
methods_struct.writeln('\t{')
|
methods_struct.writeln('\t{')
|
||||||
|
|
|
@ -192,6 +192,7 @@ fn (mut g Gen) comp_if(node ast.IfExpr) {
|
||||||
if !node.is_expr && !node.has_else && node.branches.len == 1 {
|
if !node.is_expr && !node.has_else && node.branches.len == 1 {
|
||||||
if node.branches[0].stmts.len == 0 {
|
if node.branches[0].stmts.len == 0 {
|
||||||
// empty ifdef; result of target OS != conditional => skip
|
// empty ifdef; result of target OS != conditional => skip
|
||||||
|
g.write('/*empty $ if*/')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !g.pref.output_cross_c {
|
if !g.pref.output_cross_c {
|
||||||
|
@ -217,7 +218,10 @@ fn (mut g Gen) comp_if(node ast.IfExpr) {
|
||||||
} else {
|
} else {
|
||||||
''
|
''
|
||||||
}
|
}
|
||||||
mut comp_if_stmts_skip := false
|
mut comp_if_stmts_skip := false // don't write any statements if the condition is false
|
||||||
|
// (so that for example windows calls don't get generated inside `$if macos` which
|
||||||
|
// will lead to compilation errors)
|
||||||
|
|
||||||
for i, branch in node.branches {
|
for i, branch in node.branches {
|
||||||
start_pos := g.out.len
|
start_pos := g.out.len
|
||||||
if i == node.branches.len - 1 && node.has_else {
|
if i == node.branches.len - 1 && node.has_else {
|
||||||
|
@ -273,6 +277,10 @@ fn (mut g Gen) comp_if(node ast.IfExpr) {
|
||||||
g.writeln('#endif')
|
g.writeln('#endif')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// returning `false` means the statements inside the $if can be skipped
|
||||||
|
*/
|
||||||
|
// returns the value of the bool comptime expression
|
||||||
fn (mut g Gen) comp_if_cond(cond ast.Expr) bool {
|
fn (mut g Gen) comp_if_cond(cond ast.Expr) bool {
|
||||||
match cond {
|
match cond {
|
||||||
ast.BoolLiteral {
|
ast.BoolLiteral {
|
||||||
|
@ -310,7 +318,38 @@ fn (mut g Gen) comp_if_cond(cond ast.Expr) bool {
|
||||||
mut name := ''
|
mut name := ''
|
||||||
mut exp_type := ast.Type(0)
|
mut exp_type := ast.Type(0)
|
||||||
got_type := (cond.right as ast.TypeNode).typ
|
got_type := (cond.right as ast.TypeNode).typ
|
||||||
if left is ast.SelectorExpr {
|
// Handle `$if x is Interface {`
|
||||||
|
// mut matches_interface := 'false'
|
||||||
|
if left is ast.TypeNode && cond.right is ast.TypeNode {
|
||||||
|
// `$if Foo is Interface {`
|
||||||
|
interface_sym := g.table.get_type_symbol(got_type)
|
||||||
|
if interface_sym.info is ast.Interface {
|
||||||
|
// q := g.table.get_type_symbol(interface_sym.info.types[0])
|
||||||
|
checked_type := g.unwrap_generic((left as ast.TypeNode).typ)
|
||||||
|
// TODO PERF this check is run twice (also in the checker)
|
||||||
|
// store the result in a field
|
||||||
|
is_true := g.table.type_implements_interface(checked_type,
|
||||||
|
got_type)
|
||||||
|
// true // exp_type in interface_sym.info.types
|
||||||
|
if cond.op == .key_is {
|
||||||
|
if is_true {
|
||||||
|
g.write('1')
|
||||||
|
} else {
|
||||||
|
g.write('0')
|
||||||
|
}
|
||||||
|
return is_true
|
||||||
|
} else if cond.op == .not_is {
|
||||||
|
if is_true {
|
||||||
|
g.write('0')
|
||||||
|
} else {
|
||||||
|
g.write('1')
|
||||||
|
}
|
||||||
|
return !is_true
|
||||||
|
}
|
||||||
|
// matches_interface = '/*iface:$got_type $exp_type*/ true'
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
} else if left is ast.SelectorExpr {
|
||||||
name = '${left.expr}.$left.field_name'
|
name = '${left.expr}.$left.field_name'
|
||||||
exp_type = g.comptime_var_type_map[name]
|
exp_type = g.comptime_var_type_map[name]
|
||||||
} else if left is ast.TypeNode {
|
} else if left is ast.TypeNode {
|
||||||
|
@ -323,7 +362,7 @@ fn (mut g Gen) comp_if_cond(cond ast.Expr) bool {
|
||||||
g.write('$exp_type == $got_type')
|
g.write('$exp_type == $got_type')
|
||||||
return exp_type == got_type
|
return exp_type == got_type
|
||||||
} else {
|
} else {
|
||||||
g.write('$exp_type !=$got_type')
|
g.write('$exp_type != $got_type')
|
||||||
return exp_type != got_type
|
return exp_type != got_type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ fn (mut g Gen) process_fn_decl(node ast.FnDecl) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) gen_fn_decl(node ast.FnDecl, skip bool) {
|
fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
|
||||||
// TODO For some reason, build fails with autofree with this line
|
// TODO For some reason, build fails with autofree with this line
|
||||||
// as it's only informative, comment it for now
|
// as it's only informative, comment it for now
|
||||||
// g.gen_attrs(it.attrs)
|
// g.gen_attrs(it.attrs)
|
||||||
|
@ -143,11 +143,14 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl, skip bool) {
|
||||||
g.cur_concrete_types = []
|
g.cur_concrete_types = []
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cur_fn_save := g.cur_fn
|
cur_fn_save := g.table.cur_fn
|
||||||
defer {
|
defer {
|
||||||
g.cur_fn = cur_fn_save
|
g.table.cur_fn = cur_fn_save
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
// TODO remove unsafe
|
||||||
|
g.table.cur_fn = node
|
||||||
}
|
}
|
||||||
g.cur_fn = node
|
|
||||||
fn_start_pos := g.out.len
|
fn_start_pos := g.out.len
|
||||||
g.write_v_source_line_info(node.pos)
|
g.write_v_source_line_info(node.pos)
|
||||||
msvc_attrs := g.write_fn_attrs(node.attrs)
|
msvc_attrs := g.write_fn_attrs(node.attrs)
|
||||||
|
@ -474,8 +477,8 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
|
||||||
|
|
||||||
pub fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type {
|
pub fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type {
|
||||||
if typ.has_flag(.generic) {
|
if typ.has_flag(.generic) {
|
||||||
if t_typ := g.table.resolve_generic_to_concrete(typ, g.cur_fn.generic_names, g.cur_concrete_types,
|
if t_typ := g.table.resolve_generic_to_concrete(typ, g.table.cur_fn.generic_names,
|
||||||
false)
|
g.cur_concrete_types, false)
|
||||||
{
|
{
|
||||||
return t_typ
|
return t_typ
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,9 @@ fn test_macho() {
|
||||||
mut g := native.Gen{
|
mut g := native.Gen{
|
||||||
pref: &pref.Preferences{}
|
pref: &pref.Preferences{}
|
||||||
out_name: 'test.bin'
|
out_name: 'test.bin'
|
||||||
table: &ast.Table{}
|
table: &ast.Table{
|
||||||
|
cur_fn: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
g.generate_macho_header()
|
g.generate_macho_header()
|
||||||
g.generate_macho_footer()
|
g.generate_macho_footer()
|
||||||
|
|
|
@ -30,16 +30,17 @@ fn main() {
|
||||||
assert timeout > 0
|
assert timeout > 0
|
||||||
go exit_after_timeout(timeout)
|
go exit_after_timeout(timeout)
|
||||||
//
|
//
|
||||||
mut app := App{
|
mut app := &App{
|
||||||
port: http_port
|
port: http_port
|
||||||
timeout: timeout
|
timeout: timeout
|
||||||
}
|
}
|
||||||
vweb.run_app<App>(mut app, http_port)
|
eprintln('>> webserver: started on http://127.0.0.1:$app.port/ , with maximum runtime of $app.timeout milliseconds.')
|
||||||
|
// vweb.run<App>(mut app, http_port)
|
||||||
|
vweb.run(mut app, http_port)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut app App) init_server() {
|
// pub fn (mut app App) init_server() {
|
||||||
eprintln('>> webserver: started on http://127.0.0.1:$app.port/ , with maximum runtime of $app.timeout milliseconds.')
|
//}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut app App) index() vweb.Result {
|
pub fn (mut app App) index() vweb.Result {
|
||||||
return app.text('Welcome to VWeb')
|
return app.text('Welcome to VWeb')
|
||||||
|
|
|
@ -284,27 +284,49 @@ pub fn (ctx &Context) get_header(key string) string {
|
||||||
return ctx.req.header.get_custom(key) or { '' }
|
return ctx.req.header.get_custom(key) or { '' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
pub fn run<T>(port int) {
|
pub fn run<T>(port int) {
|
||||||
mut app := T{}
|
mut x := &T{}
|
||||||
run_app<T>(mut app, port)
|
run_app(mut x, port)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
interface DbInterface {
|
||||||
|
db voidptr
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_app<T>(mut app T, port int) {
|
// run_app
|
||||||
|
pub fn run<T>(global_app &T, port int) {
|
||||||
|
// x := global_app.clone()
|
||||||
|
// mut global_app := &T{}
|
||||||
|
// mut app := &T{}
|
||||||
|
// run_app<T>(mut app, port)
|
||||||
|
|
||||||
mut l := net.listen_tcp(port) or { panic('failed to listen') }
|
mut l := net.listen_tcp(port) or { panic('failed to listen') }
|
||||||
println('[Vweb] Running app on http://localhost:$port')
|
println('[Vweb] Running app on http://localhost:$port')
|
||||||
app.Context = Context{
|
// app.Context = Context{
|
||||||
conn: 0
|
// conn: 0
|
||||||
}
|
//}
|
||||||
app.init_server()
|
// app.init_server()
|
||||||
$for method in T.methods {
|
// global_app.init_server()
|
||||||
$if method.return_type is Result {
|
//$for method in T.methods {
|
||||||
// check routes for validity
|
//$if method.return_type is Result {
|
||||||
}
|
// check routes for validity
|
||||||
}
|
//}
|
||||||
|
//}
|
||||||
for {
|
for {
|
||||||
|
// Create a new app object for each connection, copy global data like db connections
|
||||||
|
mut request_app := T{}
|
||||||
|
$if T is DbInterface {
|
||||||
|
request_app.db = global_app.db
|
||||||
|
} $else {
|
||||||
|
// println('vweb no db')
|
||||||
|
}
|
||||||
|
// request_app.Context = Context{
|
||||||
|
// conn: 0
|
||||||
|
//}
|
||||||
mut conn := l.accept() or { panic('accept() failed') }
|
mut conn := l.accept() or { panic('accept() failed') }
|
||||||
// TODO: running handle_conn concurrently results in a race-condition
|
handle_conn<T>(mut conn, mut request_app)
|
||||||
handle_conn<T>(mut conn, mut app)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue