|
|
|
@ -1,7 +1,6 @@
|
|
|
|
|
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by an MIT license
|
|
|
|
|
// that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
module compiler
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
@ -56,8 +55,11 @@ mut:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
EmptyFn = Fn{}
|
|
|
|
|
MainFn = Fn{ name: 'main' }
|
|
|
|
|
EmptyFn = Fn{
|
|
|
|
|
}
|
|
|
|
|
MainFn = Fn{
|
|
|
|
|
name: 'main'
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
pub fn (a []TypeInst) str() string {
|
|
|
|
@ -165,7 +167,11 @@ fn (p &Parser) known_var_check_new_var(name string) bool {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn (p mut Parser) register_var(v Var) {
|
|
|
|
|
mut new_var := {v | idx: p.var_idx, scope_level: p.cur_fn.scope_level}
|
|
|
|
|
mut new_var := {
|
|
|
|
|
v |
|
|
|
|
|
idx:p.var_idx,
|
|
|
|
|
scope_level:p.cur_fn.scope_level
|
|
|
|
|
}
|
|
|
|
|
if v.line_nr == 0 {
|
|
|
|
|
new_var.token_idx = p.cur_tok_index()
|
|
|
|
|
new_var.line_nr = p.cur_tok().line_nr
|
|
|
|
@ -189,13 +195,15 @@ fn (p mut Parser) clear_vars() {
|
|
|
|
|
}
|
|
|
|
|
p.local_vars = []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Function signatures are added to the top of the .c file in the first run.
|
|
|
|
|
fn (p mut Parser) fn_decl() {
|
|
|
|
|
p.clear_vars() // clear local vars every time a new fn is started
|
|
|
|
|
defer { p.fgen_nl() p.fgen_nl() }
|
|
|
|
|
defer {
|
|
|
|
|
p.fgen_nl()
|
|
|
|
|
p.fgen_nl()
|
|
|
|
|
}
|
|
|
|
|
fn_start_idx := p.cur_tok_index()
|
|
|
|
|
// If we are in the first pass, create a new function.
|
|
|
|
|
// In the second pass fetch the one we created.
|
|
|
|
@ -208,10 +216,12 @@ fn (p mut Parser) fn_decl() {
|
|
|
|
|
else {
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
is_pub := p.tok == .key_pub
|
|
|
|
|
mut f := Fn{
|
|
|
|
|
mod: p.mod
|
|
|
|
|
is_public: is_pub || p.is_vh // functions defined in .vh are always public
|
|
|
|
|
|
|
|
|
|
is_unsafe: p.attr == 'unsafe_fn'
|
|
|
|
|
is_deprecated: p.attr == 'deprecated'
|
|
|
|
|
comptime_define: if p.attr.starts_with('if ') {p.attr[3..]}else {''}
|
|
|
|
@ -228,7 +238,6 @@ fn (p mut Parser) fn_decl() {
|
|
|
|
|
// p.gen('/* returns $p.returns */')
|
|
|
|
|
p.next()
|
|
|
|
|
p.fspace()
|
|
|
|
|
|
|
|
|
|
// Method receiver
|
|
|
|
|
mut receiver_typ := ''
|
|
|
|
|
if p.tok == .lpar {
|
|
|
|
@ -253,8 +262,7 @@ fn (p mut Parser) fn_decl() {
|
|
|
|
|
p.error('invalid receiver type `$receiver_typ` (`$receiver_typ` is an interface)')
|
|
|
|
|
}
|
|
|
|
|
// Don't allow modifying types from a different module
|
|
|
|
|
if !p.first_pass() && !p.builtin_mod && t.mod != p.mod &&
|
|
|
|
|
!p.is_vgen // let vgen define methods like .str() on types defined in other modules
|
|
|
|
|
if !p.first_pass() && !p.builtin_mod && t.mod != p.mod && !p.is_vgen // let vgen define methods like .str() on types defined in other modules
|
|
|
|
|
{
|
|
|
|
|
// println('T.mod=$T.mod')
|
|
|
|
|
// println('p.mod=$p.mod')
|
|
|
|
@ -325,9 +333,7 @@ fn (p mut Parser) fn_decl() {
|
|
|
|
|
// full mod function name
|
|
|
|
|
// `os.exit()` ==> `os__exit()`
|
|
|
|
|
// if !is_c && !p.builtin_mod && receiver_typ.len == 0 {
|
|
|
|
|
if !is_c && !has_receiver &&
|
|
|
|
|
(!p.builtin_mod || (p.builtin_mod && f.name == 'init'))
|
|
|
|
|
{
|
|
|
|
|
if !is_c && !has_receiver && (!p.builtin_mod || (p.builtin_mod && f.name == 'init')) {
|
|
|
|
|
f.name = p.prepend_mod(f.name)
|
|
|
|
|
}
|
|
|
|
|
if p.first_pass() && receiver_typ.len == 0 {
|
|
|
|
@ -345,7 +351,8 @@ fn (p mut Parser) fn_decl() {
|
|
|
|
|
if p.generic_dispatch.inst.size > 0 {
|
|
|
|
|
f.dispatch_of = p.generic_dispatch
|
|
|
|
|
rename_generic_fn_instance(mut f, f.dispatch_of)
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
f.is_generic = true
|
|
|
|
|
}
|
|
|
|
|
p.next()
|
|
|
|
@ -358,7 +365,9 @@ fn (p mut Parser) fn_decl() {
|
|
|
|
|
p.error('redeclaration of type parameter `$type_par`')
|
|
|
|
|
}
|
|
|
|
|
f.type_pars << type_par
|
|
|
|
|
if p.tok == .gt { break }
|
|
|
|
|
if p.tok == .gt {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
p.check(.comma)
|
|
|
|
|
}
|
|
|
|
|
p.check(.gt)
|
|
|
|
@ -367,7 +376,8 @@ fn (p mut Parser) fn_decl() {
|
|
|
|
|
// Args (...)
|
|
|
|
|
p.fn_args(mut f)
|
|
|
|
|
if is_op {
|
|
|
|
|
if f.args.len != 1 + 1 { // +1 is for the receiver
|
|
|
|
|
if f.args.len != 1 + 1 {
|
|
|
|
|
// +1 is for the receiver
|
|
|
|
|
p.error('operator overloading methods must have only 1 argument')
|
|
|
|
|
}
|
|
|
|
|
if f.args[0].typ != f.args[1].typ {
|
|
|
|
@ -460,18 +470,19 @@ fn (p mut Parser) fn_decl() {
|
|
|
|
|
}
|
|
|
|
|
// println('added generic method r:$rcv.name f:$f.name')
|
|
|
|
|
p.add_method(rcv.name, f)
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
p.table.register_fn(f)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
p.set_current_fn(EmptyFn)
|
|
|
|
|
p.skip_fn_body()
|
|
|
|
|
return
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
p.gen_fn_decl(f, typ, str_args)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if is_fn_header {
|
|
|
|
|
p.genln('$typ $fn_name_cgen ($str_args);')
|
|
|
|
|
p.fgen_nl()
|
|
|
|
@ -527,7 +538,6 @@ fn (p mut Parser) fn_decl() {
|
|
|
|
|
// p.genln('// live_function body start')
|
|
|
|
|
p.genln('pthread_mutex_lock(&live_fn_mutex);')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if f.name in ['main__main', 'main', 'WinMain'] {
|
|
|
|
|
if p.pref.is_test {
|
|
|
|
|
p.error_with_token_index('tests cannot have function `main`', f.fn_name_token_idx)
|
|
|
|
@ -585,6 +595,7 @@ fn (p mut Parser) fn_decl() {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
p.genln('}')
|
|
|
|
|
if !p.builtin_mod && p.mod != 'os' {
|
|
|
|
|
p.check_unused_and_mut_vars()
|
|
|
|
@ -623,13 +634,7 @@ fn (p mut Parser) skip_fn_body() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn (p &Parser) get_linkage_prefix() string {
|
|
|
|
|
return if p.pref.ccompiler == 'msvc' && p.attr == 'live' && p.pref.is_so {
|
|
|
|
|
'__declspec(dllexport) '
|
|
|
|
|
} else if p.attr == 'inline' {
|
|
|
|
|
'static inline '
|
|
|
|
|
} else {
|
|
|
|
|
''
|
|
|
|
|
}
|
|
|
|
|
return if p.pref.ccompiler == 'msvc' && p.attr == 'live' && p.pref.is_so {'__declspec(dllexport) '}else if p.attr == 'inline' {'static inline '}else {''}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn (p mut Parser) check_unused_and_mut_vars() {
|
|
|
|
@ -637,15 +642,10 @@ fn (p mut Parser) check_unused_and_mut_vars() {
|
|
|
|
|
if var.name == '' {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if !var.is_used && !p.pref.is_repl && !var.is_arg &&
|
|
|
|
|
!p.pref.translated && var.name != 'tmpl_res' && p.mod != 'vweb'
|
|
|
|
|
{
|
|
|
|
|
if !var.is_used && !p.pref.is_repl && !var.is_arg && !p.pref.translated && var.name != 'tmpl_res' && p.mod != 'vweb' {
|
|
|
|
|
p.production_error_with_token_index('`$var.name` declared and not used', var.token_idx)
|
|
|
|
|
}
|
|
|
|
|
if !var.is_changed && var.is_mut && !p.pref.is_repl &&
|
|
|
|
|
!p.pref.translated && var.typ != 'T*' &&
|
|
|
|
|
p.mod != 'ui' && var.typ != 'App*'
|
|
|
|
|
{
|
|
|
|
|
if !var.is_changed && var.is_mut && !p.pref.is_repl && !p.pref.translated && var.typ != 'T*' && p.mod != 'ui' && var.typ != 'App*' {
|
|
|
|
|
p.error_with_token_index('`$var.name` is declared as mutable, but it was never changed', var.token_idx)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -698,12 +698,10 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type
|
|
|
|
|
}
|
|
|
|
|
did_gen_something = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !did_gen_something {
|
|
|
|
|
// Msvc doesnt like empty struct
|
|
|
|
|
arg_struct += 'EMPTY_STRUCT_DECLARATION;'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
arg_struct += '} $arg_struct_name ;'
|
|
|
|
|
// Also register the wrapper, so we can use the original function without modifying it
|
|
|
|
|
fn_name = p.table.fn_gen_name(f)
|
|
|
|
@ -733,7 +731,6 @@ fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type
|
|
|
|
|
p.genln('int $tmp2 = pthread_create(& $thread_name, NULL, (void *)$wrapper_name, $parg);')
|
|
|
|
|
}
|
|
|
|
|
p.check(.rpar)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn (p mut Parser) verify_fn_before_call(f &Fn) {
|
|
|
|
@ -765,7 +762,8 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s
|
|
|
|
|
if f.is_c && !p.builtin_mod {
|
|
|
|
|
if f.name == 'free' {
|
|
|
|
|
p.error('use `free()` instead of `C.free()`')
|
|
|
|
|
} else if f.name == 'malloc' {
|
|
|
|
|
}
|
|
|
|
|
else if f.name == 'malloc' {
|
|
|
|
|
p.error('use `malloc()` instead of `C.malloc()`')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -777,7 +775,8 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s
|
|
|
|
|
for {
|
|
|
|
|
if p.tokens[i].tok == .gt {
|
|
|
|
|
p.error('explicit type arguments are not allowed; remove `<...>`')
|
|
|
|
|
} else if p.tokens[i].tok == .lpar {
|
|
|
|
|
}
|
|
|
|
|
else if p.tokens[i].tok == .lpar {
|
|
|
|
|
// probably a typo, do not concern the user with the above error message
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
@ -787,12 +786,10 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s
|
|
|
|
|
// if p.pref.is_prof {
|
|
|
|
|
// p.cur_fn.called_fns << cgen_name
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// If we have a method placeholder,
|
|
|
|
|
// we need to preappend "method(receiver, ...)"
|
|
|
|
|
if f.is_method {
|
|
|
|
|
receiver := f.args.first()
|
|
|
|
|
|
|
|
|
|
mut receiver_is_interface := false
|
|
|
|
|
if receiver.typ.ends_with('er') {
|
|
|
|
|
// I absolutely love this syntax
|
|
|
|
@ -820,39 +817,33 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s
|
|
|
|
|
if receiver.is_mut && !p.expr_var.is_mut {
|
|
|
|
|
// println('$method_call recv=$receiver.name recv_mut=$receiver.is_mut')
|
|
|
|
|
if p.expr_var.is_for_var {
|
|
|
|
|
p.error('`$p.expr_var.name` is immutable, `for` variables' +
|
|
|
|
|
' always are')
|
|
|
|
|
} else {
|
|
|
|
|
p.error('`$p.expr_var.name` is immutable, `for` variables' + ' always are')
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
p.error('`$p.expr_var.name` is immutable, declare it with `mut`')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !p.expr_var.is_changed && receiver.is_mut {
|
|
|
|
|
p.mark_var_changed(p.expr_var)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !receiver_is_interface {
|
|
|
|
|
p.gen_method_call(receiver, receiver_type, cgen_name, f.typ, method_ph)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// Normal function call
|
|
|
|
|
p.gen('$cgen_name (')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// `foo<Bar>()`
|
|
|
|
|
// if f is generic, the name is changed to a suitable instance in dispatch_generic_fn_instance()
|
|
|
|
|
// we then replace `cgen_name` with the instance's name
|
|
|
|
|
generic := f.is_generic
|
|
|
|
|
p.fn_call_args(mut f)
|
|
|
|
|
if generic {
|
|
|
|
|
line := if p.cgen.is_tmp {
|
|
|
|
|
p.cgen.tmp_line
|
|
|
|
|
} else {
|
|
|
|
|
p.cgen.cur_line
|
|
|
|
|
}
|
|
|
|
|
line := if p.cgen.is_tmp {p.cgen.tmp_line}else {p.cgen.cur_line}
|
|
|
|
|
p.cgen.resetln(line.replace('$cgen_name (', '$f.name ('))
|
|
|
|
|
// println('calling inst $f.name: $p.cgen.cur_line')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if !is_interface {
|
|
|
|
|
p.gen(')')
|
|
|
|
|
// }
|
|
|
|
@ -863,12 +854,13 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s
|
|
|
|
|
}
|
|
|
|
|
// println('end of fn call typ=$f.typ')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// for declaration
|
|
|
|
|
// update the Fn object's args[]
|
|
|
|
|
fn (p mut Parser) fn_args(f mut Fn) {
|
|
|
|
|
p.check(.lpar)
|
|
|
|
|
defer { p.check(.rpar) }
|
|
|
|
|
defer {
|
|
|
|
|
p.check(.rpar)
|
|
|
|
|
}
|
|
|
|
|
if f.is_interface {
|
|
|
|
|
interface_arg := Var{
|
|
|
|
|
typ: f.receiver_typ
|
|
|
|
@ -882,7 +874,8 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
|
|
|
|
if types_only {
|
|
|
|
|
for p.tok != .rpar {
|
|
|
|
|
typ := p.get_type()
|
|
|
|
|
if typ == '' { //&& !f.is_c {
|
|
|
|
|
if typ == '' {
|
|
|
|
|
// && !f.is_c {
|
|
|
|
|
if p.prev_tok != .ellipsis {
|
|
|
|
|
p.error('bad fn arg type')
|
|
|
|
|
}
|
|
|
|
@ -892,6 +885,7 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
|
|
|
|
typ: typ
|
|
|
|
|
is_arg: true
|
|
|
|
|
// is_mut: is_mut
|
|
|
|
|
|
|
|
|
|
line_nr: p.scanner.line_nr
|
|
|
|
|
token_idx: p.cur_tok_index()
|
|
|
|
|
}
|
|
|
|
@ -899,6 +893,7 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
|
|
|
|
f.args << v
|
|
|
|
|
if p.tok == .comma {
|
|
|
|
|
p.next()
|
|
|
|
|
p.fspace()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -915,6 +910,7 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
|
|
|
|
is_mut := p.tok == .key_mut
|
|
|
|
|
if is_mut {
|
|
|
|
|
p.check(.key_mut)
|
|
|
|
|
p.fspace()
|
|
|
|
|
}
|
|
|
|
|
// variadic arg
|
|
|
|
|
if p.tok == .ellipsis {
|
|
|
|
@ -935,14 +931,14 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
|
|
|
|
p.register_vargs_stuct(typ, 0)
|
|
|
|
|
}
|
|
|
|
|
typ = 'varg_$typ'
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
typ = '...$typ' // TODO: fix, this is invalid in C
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
p.check_and_register_used_imported_type(typ)
|
|
|
|
|
if is_mut && is_primitive_type(typ) {
|
|
|
|
|
p.error('mutable arguments are only allowed for arrays, maps, and structs.' +
|
|
|
|
|
'\nreturn values instead: `fn foo(n mut int) {` => `fn foo(n int) int {`')
|
|
|
|
|
p.error('mutable arguments are only allowed for arrays, maps, and structs.' + '\nreturn values instead: `fn foo(n mut int) {` => `fn foo(n int) int {`')
|
|
|
|
|
}
|
|
|
|
|
for name in names {
|
|
|
|
|
if is_mut {
|
|
|
|
@ -962,6 +958,7 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
|
|
|
|
}
|
|
|
|
|
if p.tok == .comma {
|
|
|
|
|
p.check(.comma)
|
|
|
|
|
p.fspace()
|
|
|
|
|
}
|
|
|
|
|
// unnamed (C definition)
|
|
|
|
|
if p.tok == .ellipsis {
|
|
|
|
@ -1008,17 +1005,15 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
|
|
|
|
|
mod_name := p.mod.replace('_dot_', '.')
|
|
|
|
|
fn_name := p.cur_fn.name.replace('${p.mod}__', '')
|
|
|
|
|
file_path := cescaped_path(p.file_path)
|
|
|
|
|
p.cgen.resetln(p.cgen.cur_line.replace(
|
|
|
|
|
'v_panic (',
|
|
|
|
|
'panic_debug ($p.scanner.line_nr, tos3("$file_path"), tos3("$mod_name"), tos2((byte *)"$fn_name"), '
|
|
|
|
|
))
|
|
|
|
|
p.cgen.resetln(p.cgen.cur_line.replace('v_panic (', 'panic_debug ($p.scanner.line_nr, tos3("$file_path"), tos3("$mod_name"), tos2((byte *)"$fn_name"), '))
|
|
|
|
|
}
|
|
|
|
|
mut saved_args := []string
|
|
|
|
|
for i,arg in f.args {
|
|
|
|
|
// Receiver is the first arg
|
|
|
|
|
// Skip the receiver, because it was already generated in the expression
|
|
|
|
|
if i == 0 && f.is_method {
|
|
|
|
|
if f.args.len > 1 { // && !p.is_js {
|
|
|
|
|
if f.args.len > 1 {
|
|
|
|
|
// && !p.is_js {
|
|
|
|
|
p.gen(', ')
|
|
|
|
|
}
|
|
|
|
|
// if f.args[0].typ.ends_with('*') {
|
|
|
|
@ -1036,6 +1031,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// Reached the final vararg? Quit
|
|
|
|
@ -1054,15 +1050,13 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
|
|
|
|
|
p.mutable_arg_error(i, arg, f)
|
|
|
|
|
}
|
|
|
|
|
if p.peek() != .name {
|
|
|
|
|
p.error('`$arg.name` is a mutable argument, you need to ' +
|
|
|
|
|
'provide a variable to modify: `${f.name}(... mut a...)`')
|
|
|
|
|
p.error('`$arg.name` is a mutable argument, you need to ' + 'provide a variable to modify: `${f.name}(... mut a...)`')
|
|
|
|
|
}
|
|
|
|
|
p.check(.key_mut)
|
|
|
|
|
p.fspace()
|
|
|
|
|
var_name := p.lit
|
|
|
|
|
v := p.find_var(var_name) or {
|
|
|
|
|
p.error('`$arg.name` is a mutable argument, you need to ' +
|
|
|
|
|
'provide a variable to modify: `${f.name}(... mut a...)`')
|
|
|
|
|
p.error('`$arg.name` is a mutable argument, you need to ' + 'provide a variable to modify: `${f.name}(... mut a...)`')
|
|
|
|
|
exit(1)
|
|
|
|
|
}
|
|
|
|
|
if !v.is_changed {
|
|
|
|
@ -1070,17 +1064,14 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
p.expected_type = arg.typ
|
|
|
|
|
clone := p.pref.autofree && p.mod != 'string' && arg.typ == 'string' &&
|
|
|
|
|
!p.builtin_mod //&& arg.is_moved
|
|
|
|
|
clone := p.pref.autofree && p.mod != 'string' && arg.typ == 'string' && !p.builtin_mod // && arg.is_moved
|
|
|
|
|
if clone {
|
|
|
|
|
p.gen('/*YY f=$f.name arg=$arg.name is_moved=$arg.is_moved*/string_clone(')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// x64 println gen
|
|
|
|
|
if p.pref.x64 && i == 0 && f.name == 'println' && p.tok == .str && p.peek() == .rpar {
|
|
|
|
|
p.x64.gen_print(p.lit)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mut typ := p.bool_expression()
|
|
|
|
|
// Register an interface type usage:
|
|
|
|
|
// fn run(r Animal) { ... }
|
|
|
|
@ -1096,12 +1087,9 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
|
|
|
|
|
p.table.add_gen_type(arg.typ, typ)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if clone {
|
|
|
|
|
p.gen(')')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Optimize `println`: replace it with `printf` to avoid extra allocations and
|
|
|
|
|
// function calls.
|
|
|
|
|
// `println(777)` => `printf("%d\n", 777)`
|
|
|
|
@ -1112,9 +1100,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
|
|
|
|
|
}
|
|
|
|
|
typ = 'string'
|
|
|
|
|
}
|
|
|
|
|
if i == 0 && (f.name == 'println' || f.name == 'print') &&
|
|
|
|
|
!(typ in ['string', 'ustring', 'void' ])
|
|
|
|
|
{
|
|
|
|
|
if i == 0 && (f.name == 'println' || f.name == 'print') && !(typ in ['string', 'ustring', 'void']) {
|
|
|
|
|
//
|
|
|
|
|
T := p.table.find_type(typ)
|
|
|
|
|
$if !windows {
|
|
|
|
@ -1122,8 +1108,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
|
|
|
|
|
fmt := p.typ_to_fmt(typ, 0)
|
|
|
|
|
if fmt != '' && typ != 'bool' {
|
|
|
|
|
nl := if f.name == 'println' {'\\n'}else {''}
|
|
|
|
|
p.cgen.resetln(p.cgen.cur_line.replace(f.name +
|
|
|
|
|
' (', '/*opt*/printf ("' + fmt + '$nl", '))
|
|
|
|
|
p.cgen.resetln(p.cgen.cur_line.replace(f.name + ' (', '/*opt*/printf ("' + fmt + '$nl", '))
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1156,7 +1141,8 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
|
|
|
|
|
p.cgen.set_placeholder(ph, '${typ}_str(')
|
|
|
|
|
p.gen(')')
|
|
|
|
|
continue
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
base := p.base_type(T.name)
|
|
|
|
|
if base != T.name {
|
|
|
|
|
base_type := p.find_type(base)
|
|
|
|
@ -1184,7 +1170,8 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
|
|
|
|
|
if type_mismatch && f.is_generic {
|
|
|
|
|
// println("argument `$arg.name` is generic")
|
|
|
|
|
saved_args << got
|
|
|
|
|
} else if type_mismatch {
|
|
|
|
|
}
|
|
|
|
|
else if type_mismatch {
|
|
|
|
|
mut j := i
|
|
|
|
|
if f.is_method {
|
|
|
|
|
j--
|
|
|
|
@ -1192,14 +1179,16 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
|
|
|
|
|
mut nr := '${j+1}th'
|
|
|
|
|
if j == 0 {
|
|
|
|
|
nr = 'first'
|
|
|
|
|
} else if j == 1 {
|
|
|
|
|
}
|
|
|
|
|
else if j == 1 {
|
|
|
|
|
nr = 'second'
|
|
|
|
|
} else if j == 2 {
|
|
|
|
|
}
|
|
|
|
|
else if j == 2 {
|
|
|
|
|
nr = 'third'
|
|
|
|
|
}
|
|
|
|
|
p.error('cannot use type `$typ` as type `$arg.typ` in $nr ' +
|
|
|
|
|
'argument to `${f.name}()`')
|
|
|
|
|
} else {
|
|
|
|
|
p.error('cannot use type `$typ` as type `$arg.typ` in $nr ' + 'argument to `${f.name}()`')
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
saved_args << ''
|
|
|
|
|
}
|
|
|
|
|
is_interface := p.table.is_interface(arg.typ)
|
|
|
|
@ -1220,7 +1209,8 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
|
|
|
|
|
// Special case for mutable arrays. We can't `&` function
|
|
|
|
|
// results,
|
|
|
|
|
// have to use `(array[]){ expr }` hack.
|
|
|
|
|
if expected.starts_with('array_') && exp_ptr { //&& !arg.is_mut{
|
|
|
|
|
if expected.starts_with('array_') && exp_ptr {
|
|
|
|
|
// && !arg.is_mut{
|
|
|
|
|
p.cgen.set_placeholder(ph, '& /*111*/ (array[]){')
|
|
|
|
|
p.gen('}[0] ')
|
|
|
|
|
}
|
|
|
|
@ -1233,19 +1223,16 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
|
|
|
|
|
// println('fn hack expr=$expr')
|
|
|
|
|
p.cgen.set_placeholder(ph, '& /*113 e="$expected" g="$got"*/ ($got[]){')
|
|
|
|
|
p.gen('}[0] ')
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
p.cgen.set_placeholder(ph, '& /*114*/')
|
|
|
|
|
}
|
|
|
|
|
} $else {
|
|
|
|
|
p.cgen.set_placeholder(ph, '& /*114*/')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
// println('\ne:"$expected" got:"$got"')
|
|
|
|
|
else if ! (expected == 'void*' && got == 'int') &&
|
|
|
|
|
! (expected == 'void*' && got == 'byteptr') &&
|
|
|
|
|
! (expected == 'byte*' && got.contains(']byte')) &&
|
|
|
|
|
! (expected == 'byte*' && got == 'string') &&
|
|
|
|
|
else if !(expected == 'void*' && got == 'int') && !(expected == 'void*' && got == 'byteptr') && !(expected == 'byte*' && got.contains(']byte')) && !(expected == 'byte*' && got == 'string') &&
|
|
|
|
|
// ! (expected == 'void*' && got == 'array_int') {
|
|
|
|
|
!(expected == 'byte*' && got == 'byteptr') && !p.pref.is_bare {
|
|
|
|
|
p.cgen.set_placeholder(ph, '& /*112 e="$expected" g="$got" */')
|
|
|
|
@ -1296,12 +1283,17 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
|
|
|
|
|
// From a given generic function and an argument list matching its signature,
|
|
|
|
|
// create a type instantiation
|
|
|
|
|
fn (p mut Parser) extract_type_inst(f &Fn, args_ []string) TypeInst {
|
|
|
|
|
mut r := TypeInst{}
|
|
|
|
|
mut r := TypeInst{
|
|
|
|
|
}
|
|
|
|
|
mut i := 0
|
|
|
|
|
mut args := args_
|
|
|
|
|
if f.typ != 'void' { args << f.typ }
|
|
|
|
|
if f.typ != 'void' {
|
|
|
|
|
args << f.typ
|
|
|
|
|
}
|
|
|
|
|
for e in args {
|
|
|
|
|
if e == '' { continue }
|
|
|
|
|
if e == '' {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
tp := f.type_pars[i]
|
|
|
|
|
mut ti := e
|
|
|
|
|
if ti.starts_with('fn (') {
|
|
|
|
@ -1309,7 +1301,9 @@ fn (p mut Parser) extract_type_inst(f &Fn, args_ []string) TypeInst {
|
|
|
|
|
mut found := false
|
|
|
|
|
for fa_ in fn_args {
|
|
|
|
|
mut fa := fa_
|
|
|
|
|
for fa.starts_with('array_') { fa = fa[6..] }
|
|
|
|
|
for fa.starts_with('array_') {
|
|
|
|
|
fa = fa[6..]
|
|
|
|
|
}
|
|
|
|
|
if fa == tp {
|
|
|
|
|
r.inst[tp] = fa
|
|
|
|
|
found = true
|
|
|
|
@ -1317,10 +1311,14 @@ fn (p mut Parser) extract_type_inst(f &Fn, args_ []string) TypeInst {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if found { continue }
|
|
|
|
|
if found {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
ti = ti.all_after(') ')
|
|
|
|
|
}
|
|
|
|
|
for ti.starts_with('array_') { ti = ti[6..] }
|
|
|
|
|
for ti.starts_with('array_') {
|
|
|
|
|
ti = ti[6..]
|
|
|
|
|
}
|
|
|
|
|
if r.inst[tp] != '' {
|
|
|
|
|
if r.inst[tp] != ti {
|
|
|
|
|
p.error('type parameter `$tp` has type ${r.inst[tp]}, not `$ti`')
|
|
|
|
@ -1330,7 +1328,9 @@ fn (p mut Parser) extract_type_inst(f &Fn, args_ []string) TypeInst {
|
|
|
|
|
// println("extracted $tp => $ti")
|
|
|
|
|
r.inst[tp] = ti
|
|
|
|
|
i++
|
|
|
|
|
if i >= f.type_pars.len { break }
|
|
|
|
|
if i >= f.type_pars.len {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if r.inst[f.typ] == '' && f.typ in f.type_pars {
|
|
|
|
|
r.inst[f.typ] = '_ANYTYPE_'
|
|
|
|
@ -1346,9 +1346,10 @@ fn (p mut Parser) extract_type_inst(f &Fn, args_ []string) TypeInst {
|
|
|
|
|
|
|
|
|
|
// replace a generic type using TypeInst
|
|
|
|
|
fn replace_generic_type(gen_type string, ti &TypeInst) string {
|
|
|
|
|
mut typ := gen_type.replace('map_', '')
|
|
|
|
|
.replace('varg_', '').trim_right('*')
|
|
|
|
|
for typ.starts_with('array_') { typ = typ[6..] }
|
|
|
|
|
mut typ := gen_type.replace('map_', '').replace('varg_', '').trim_right('*')
|
|
|
|
|
for typ.starts_with('array_') {
|
|
|
|
|
typ = typ[6..]
|
|
|
|
|
}
|
|
|
|
|
if typ in ti.inst {
|
|
|
|
|
typ = gen_type.replace(typ, ti.inst[typ])
|
|
|
|
|
return typ
|
|
|
|
@ -1393,15 +1394,17 @@ fn (p mut Parser) register_vargs_stuct(typ string, len int) string {
|
|
|
|
|
if !p.table.known_type(vargs_struct) {
|
|
|
|
|
p.table.register_type(varg_type)
|
|
|
|
|
p.cgen.typedefs << 'typedef struct $vargs_struct $vargs_struct;\n'
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ex_typ := p.table.find_type(vargs_struct)
|
|
|
|
|
ex_len := ex_typ.fields[1].name[5..ex_typ.fields[1].name.len - 1].int()
|
|
|
|
|
if ex_len > varg_len { varg_len = ex_len }
|
|
|
|
|
if ex_len > varg_len {
|
|
|
|
|
varg_len = ex_len
|
|
|
|
|
}
|
|
|
|
|
p.table.rewrite_type(varg_type)
|
|
|
|
|
}
|
|
|
|
|
p.table.add_field(vargs_struct, 'len', 'int', false, '', .public)
|
|
|
|
|
p.table.add_field(vargs_struct, 'args[$varg_len]', typ, false, '', .public)
|
|
|
|
|
|
|
|
|
|
return vargs_struct
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1418,28 +1421,27 @@ fn (p mut Parser) fn_call_vargs(f Fn) (string, []string) {
|
|
|
|
|
p.check(.comma)
|
|
|
|
|
}
|
|
|
|
|
varg_type,varg_value := p.tmp_expr()
|
|
|
|
|
if varg_type.starts_with('varg_') &&
|
|
|
|
|
(values.len > 0 || p.tok == .comma)
|
|
|
|
|
{
|
|
|
|
|
if varg_type.starts_with('varg_') && (values.len > 0 || p.tok == .comma) {
|
|
|
|
|
p.error('You cannot pass additional vargs when forwarding vargs to another function/method')
|
|
|
|
|
}
|
|
|
|
|
if !f.is_generic {
|
|
|
|
|
p.check_types(last_arg.typ, varg_type)
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if types.len > 0 {
|
|
|
|
|
for t in types {
|
|
|
|
|
p.check_types(varg_type, t)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ref_deref := if last_arg.typ.ends_with('*') && !varg_type.ends_with('*') { '&' }
|
|
|
|
|
else if !last_arg.typ.ends_with('*') && varg_type.ends_with('*') { '*' }
|
|
|
|
|
else { '' }
|
|
|
|
|
ref_deref := if last_arg.typ.ends_with('*') && !varg_type.ends_with('*') {'&'}else if !last_arg.typ.ends_with('*') && varg_type.ends_with('*') {'*'}else {''}
|
|
|
|
|
types << varg_type
|
|
|
|
|
values << '$ref_deref$varg_value'
|
|
|
|
|
}
|
|
|
|
|
for va in p.table.varg_access {
|
|
|
|
|
if va.fn_name != f.name { continue }
|
|
|
|
|
if va.fn_name != f.name {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if va.index >= values.len {
|
|
|
|
|
p.error_with_token_index('variadic arg index out of range: $va.index/${values.len-1}, vargs are 0 indexed', va.tok_idx)
|
|
|
|
|
}
|
|
|
|
@ -1452,9 +1454,11 @@ fn (p mut Parser) fn_call_vargs(f Fn) (string, []string) {
|
|
|
|
|
|
|
|
|
|
fn (p mut Parser) fn_gen_caller_vargs(f &Fn, varg_type string, values []string) {
|
|
|
|
|
is_varg := varg_type.starts_with('varg_')
|
|
|
|
|
if is_varg { // forwarding varg
|
|
|
|
|
if is_varg {
|
|
|
|
|
// forwarding varg
|
|
|
|
|
p.cgen.gen('${values[0]}')
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
vargs_struct := p.register_vargs_stuct(varg_type, values.len)
|
|
|
|
|
p.cgen.gen('&($vargs_struct){.len=$values.len,.args={' + values.join(',') + '}}')
|
|
|
|
|
}
|
|
|
|
@ -1462,10 +1466,12 @@ fn (p mut Parser) fn_gen_caller_vargs(f &Fn, varg_type string, values []string)
|
|
|
|
|
|
|
|
|
|
fn (p mut Parser) register_multi_return_stuct(types []string) string {
|
|
|
|
|
typ := '_V_MulRet_' + types.join('_V_').replace('*', '_PTR_')
|
|
|
|
|
if p.table.known_type(typ) { return typ }
|
|
|
|
|
if p.table.known_type(typ) {
|
|
|
|
|
return typ
|
|
|
|
|
}
|
|
|
|
|
p.table.register_type(Type{
|
|
|
|
|
cat: .struct_,
|
|
|
|
|
name: typ,
|
|
|
|
|
cat: .struct_
|
|
|
|
|
name: typ
|
|
|
|
|
mod: p.mod
|
|
|
|
|
})
|
|
|
|
|
for i,t in typ.replace('_V_MulRet_', '').replace('_PTR_', '*').split('_V_') {
|
|
|
|
@ -1516,7 +1522,8 @@ fn (p mut Parser) dispatch_generic_fn_instance(f mut Fn, ti &TypeInst) {
|
|
|
|
|
if f.is_method {
|
|
|
|
|
// TODO: add_method won't add anything on second pass
|
|
|
|
|
// p.add_method(f.args[0].typ.trim_right('*'), f)
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
p.table.register_fn(f)
|
|
|
|
|
}
|
|
|
|
|
mut gp := p.v.parsers[f.parser_idx]
|
|
|
|
@ -1529,7 +1536,6 @@ fn (p mut Parser) dispatch_generic_fn_instance(f mut Fn, ti &TypeInst) {
|
|
|
|
|
gp.fn_decl()
|
|
|
|
|
p.cgen.lines_extra << p.cgen.lines
|
|
|
|
|
p.restore_state(saved_state, false, true)
|
|
|
|
|
|
|
|
|
|
p.cgen.fns << '${p.fn_signature(f)};'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1598,7 +1604,9 @@ fn (p &Parser) find_misspelled_local_var(name string, min_match f32) string {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
n := name.all_after('.')
|
|
|
|
|
if var.name == '' || (n.len - var.name.len > 2 || var.name.len - n.len > 2) { continue }
|
|
|
|
|
if var.name == '' || (n.len - var.name.len > 2 || var.name.len - n.len > 2) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
c := strings.dice_coefficient(var.name, n)
|
|
|
|
|
if c > closest {
|
|
|
|
|
closest = c
|
|
|
|
@ -1616,6 +1624,7 @@ fn (fns []Fn) contains(f Fn) bool {
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn (p &Parser) fn_signature(f &Fn) string {
|
|
|
|
|
return '$f.typ ${f.name}(${f.str_args(p.table)})'
|
|
|
|
|
}
|
|
|
|
|