parser, checker, cgen: minor cleanup of go_expr() (#13875)
							parent
							
								
									db3bbb58cf
								
							
						
					
					
						commit
						bc98c11d9d
					
				|  | @ -2111,32 +2111,6 @@ fn (mut c Checker) global_decl(mut node ast.GlobalDecl) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn (mut c Checker) go_expr(mut node ast.GoExpr) ast.Type { |  | ||||||
| 	ret_type := c.call_expr(mut node.call_expr) |  | ||||||
| 	if node.call_expr.or_block.kind != .absent { |  | ||||||
| 		c.error('optional handling cannot be done in `go` call. Do it when calling `.wait()`', |  | ||||||
| 			node.call_expr.or_block.pos) |  | ||||||
| 	} |  | ||||||
| 	// Make sure there are no mutable arguments
 |  | ||||||
| 	for arg in node.call_expr.args { |  | ||||||
| 		if arg.is_mut && !arg.typ.is_ptr() { |  | ||||||
| 			c.error('function in `go` statement cannot contain mutable non-reference arguments', |  | ||||||
| 				arg.expr.pos()) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if node.call_expr.is_method && node.call_expr.receiver_type.is_ptr() |  | ||||||
| 		&& !node.call_expr.left_type.is_ptr() { |  | ||||||
| 		c.error('method in `go` statement cannot have non-reference mutable receiver', |  | ||||||
| 			node.call_expr.left.pos()) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if c.pref.backend.is_js() { |  | ||||||
| 		return c.table.find_or_register_promise(ret_type) |  | ||||||
| 	} else { |  | ||||||
| 		return c.table.find_or_register_thread(ret_type) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn (mut c Checker) asm_stmt(mut stmt ast.AsmStmt) { | fn (mut c Checker) asm_stmt(mut stmt ast.AsmStmt) { | ||||||
| 	if stmt.is_goto { | 	if stmt.is_goto { | ||||||
| 		c.warn('inline assembly goto is not supported, it will most likely not work', | 		c.warn('inline assembly goto is not supported, it will most likely not work', | ||||||
|  |  | ||||||
|  | @ -1484,6 +1484,32 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { | ||||||
| 	return ast.void_type | 	return ast.void_type | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | fn (mut c Checker) go_expr(mut node ast.GoExpr) ast.Type { | ||||||
|  | 	ret_type := c.call_expr(mut node.call_expr) | ||||||
|  | 	if node.call_expr.or_block.kind != .absent { | ||||||
|  | 		c.error('optional handling cannot be done in `go` call. Do it when calling `.wait()`', | ||||||
|  | 			node.call_expr.or_block.pos) | ||||||
|  | 	} | ||||||
|  | 	// Make sure there are no mutable arguments
 | ||||||
|  | 	for arg in node.call_expr.args { | ||||||
|  | 		if arg.is_mut && !arg.typ.is_ptr() { | ||||||
|  | 			c.error('function in `go` statement cannot contain mutable non-reference arguments', | ||||||
|  | 				arg.expr.pos()) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if node.call_expr.is_method && node.call_expr.receiver_type.is_ptr() | ||||||
|  | 		&& !node.call_expr.left_type.is_ptr() { | ||||||
|  | 		c.error('method in `go` statement cannot have non-reference mutable receiver', | ||||||
|  | 			node.call_expr.left.pos()) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if c.pref.backend.is_js() { | ||||||
|  | 		return c.table.find_or_register_promise(ret_type) | ||||||
|  | 	} else { | ||||||
|  | 		return c.table.find_or_register_thread(ret_type) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| fn (mut c Checker) deprecate_fnmethod(kind string, name string, the_fn ast.Fn, node ast.CallExpr) { | fn (mut c Checker) deprecate_fnmethod(kind string, name string, the_fn ast.Fn, node ast.CallExpr) { | ||||||
| 	mut deprecation_message := '' | 	mut deprecation_message := '' | ||||||
| 	now := time.now() | 	now := time.now() | ||||||
|  |  | ||||||
|  | @ -5146,258 +5146,6 @@ fn (mut g Gen) enum_val(node ast.EnumVal) { | ||||||
| 	g.write('${styp}__$node.val') | 	g.write('${styp}__$node.val') | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn (mut g Gen) go_expr(node ast.GoExpr) { |  | ||||||
| 	line := g.go_before_stmt(0) |  | ||||||
| 	mut handle := '' |  | ||||||
| 	tmp := g.new_tmp_var() |  | ||||||
| 	mut expr := node.call_expr |  | ||||||
| 	mut name := expr.name // util.no_dots(expr.name)
 |  | ||||||
| 	// TODO: fn call is duplicated. merge with fn_call().
 |  | ||||||
| 	for i, concrete_type in expr.concrete_types { |  | ||||||
| 		if concrete_type != ast.void_type && concrete_type != 0 { |  | ||||||
| 			// Using _T_ to differentiate between get<string> and get_string
 |  | ||||||
| 			// `foo<int>()` => `foo_T_int()`
 |  | ||||||
| 			if i == 0 { |  | ||||||
| 				name += '_T' |  | ||||||
| 			} |  | ||||||
| 			name += '_' + g.typ(concrete_type) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if expr.is_method { |  | ||||||
| 		receiver_sym := g.table.sym(expr.receiver_type) |  | ||||||
| 		name = receiver_sym.name + '_' + name |  | ||||||
| 	} else if mut expr.left is ast.AnonFn { |  | ||||||
| 		g.gen_anon_fn_decl(mut expr.left) |  | ||||||
| 		name = expr.left.decl.name |  | ||||||
| 	} else if expr.is_fn_var { |  | ||||||
| 		name = g.table.sym(expr.fn_var_type).name |  | ||||||
| 	} |  | ||||||
| 	name = util.no_dots(name) |  | ||||||
| 	if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') { |  | ||||||
| 		mut key := expr.name |  | ||||||
| 		if expr.is_method { |  | ||||||
| 			sym := g.table.sym(expr.receiver_type) |  | ||||||
| 			key = sym.name + '.' + expr.name |  | ||||||
| 		} |  | ||||||
| 		g.write('/* obf go: $key */') |  | ||||||
| 		name = g.obf_table[key] or { |  | ||||||
| 			panic('cgen: obf name "$key" not found, this should never happen') |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	g.empty_line = true |  | ||||||
| 	g.writeln('// start go') |  | ||||||
| 	wrapper_struct_name := 'thread_arg_' + name |  | ||||||
| 	wrapper_fn_name := name + '_thread_wrapper' |  | ||||||
| 	arg_tmp_var := 'arg_' + tmp |  | ||||||
| 	g.writeln('$wrapper_struct_name *$arg_tmp_var = malloc(sizeof(thread_arg_$name));') |  | ||||||
| 	if expr.is_method { |  | ||||||
| 		g.write('$arg_tmp_var->arg0 = ') |  | ||||||
| 		// TODO is this needed?
 |  | ||||||
| 		/* |  | ||||||
| 		if false && !expr.return_type.is_ptr() { |  | ||||||
| 			g.write('&') |  | ||||||
| 		} |  | ||||||
| 		*/ |  | ||||||
| 		g.expr(expr.left) |  | ||||||
| 		g.writeln(';') |  | ||||||
| 	} |  | ||||||
| 	for i, arg in expr.args { |  | ||||||
| 		g.write('$arg_tmp_var->arg${i + 1} = ') |  | ||||||
| 		g.expr(arg.expr) |  | ||||||
| 		g.writeln(';') |  | ||||||
| 	} |  | ||||||
| 	s_ret_typ := g.typ(node.call_expr.return_type) |  | ||||||
| 	if g.pref.os == .windows && node.call_expr.return_type != ast.void_type { |  | ||||||
| 		g.writeln('$arg_tmp_var->ret_ptr = malloc(sizeof($s_ret_typ));') |  | ||||||
| 	} |  | ||||||
| 	is_opt := node.call_expr.return_type.has_flag(.optional) |  | ||||||
| 	mut gohandle_name := '' |  | ||||||
| 	if node.call_expr.return_type == ast.void_type { |  | ||||||
| 		gohandle_name = if is_opt { '__v_thread_Option_void' } else { '__v_thread' } |  | ||||||
| 	} else { |  | ||||||
| 		opt := if is_opt { 'Option_' } else { '' } |  | ||||||
| 		gohandle_name = '__v_thread_$opt${g.table.sym(g.unwrap_generic(node.call_expr.return_type)).cname}' |  | ||||||
| 	} |  | ||||||
| 	if g.pref.os == .windows { |  | ||||||
| 		simple_handle := if node.is_expr && node.call_expr.return_type != ast.void_type { |  | ||||||
| 			'thread_handle_$tmp' |  | ||||||
| 		} else { |  | ||||||
| 			'thread_$tmp' |  | ||||||
| 		} |  | ||||||
| 		g.writeln('HANDLE $simple_handle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)$wrapper_fn_name, $arg_tmp_var, 0, 0);') |  | ||||||
| 		g.writeln('if (!$simple_handle) panic_lasterr(tos3("`go ${name}()`: "));') |  | ||||||
| 		if node.is_expr && node.call_expr.return_type != ast.void_type { |  | ||||||
| 			g.writeln('$gohandle_name thread_$tmp = {') |  | ||||||
| 			g.writeln('\t.ret_ptr = $arg_tmp_var->ret_ptr,') |  | ||||||
| 			g.writeln('\t.handle = thread_handle_$tmp') |  | ||||||
| 			g.writeln('};') |  | ||||||
| 		} |  | ||||||
| 		if !node.is_expr { |  | ||||||
| 			g.writeln('CloseHandle(thread_$tmp);') |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		g.writeln('pthread_t thread_$tmp;') |  | ||||||
| 		g.writeln('int ${tmp}_thr_res = pthread_create(&thread_$tmp, NULL, (void*)$wrapper_fn_name, $arg_tmp_var);') |  | ||||||
| 		g.writeln('if (${tmp}_thr_res) panic_error_number(tos3("`go ${name}()`: "), ${tmp}_thr_res);') |  | ||||||
| 		if !node.is_expr { |  | ||||||
| 			g.writeln('pthread_detach(thread_$tmp);') |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	g.writeln('// end go') |  | ||||||
| 	if node.is_expr { |  | ||||||
| 		handle = 'thread_$tmp' |  | ||||||
| 		// create wait handler for this return type if none exists
 |  | ||||||
| 		waiter_fn_name := gohandle_name + '_wait' |  | ||||||
| 		mut should_register := false |  | ||||||
| 		lock g.waiter_fns { |  | ||||||
| 			if waiter_fn_name !in g.waiter_fns { |  | ||||||
| 				g.waiter_fns << waiter_fn_name |  | ||||||
| 				should_register = true |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if should_register { |  | ||||||
| 			g.gowrappers.writeln('\n$s_ret_typ ${waiter_fn_name}($gohandle_name thread) {') |  | ||||||
| 			mut c_ret_ptr_ptr := 'NULL' |  | ||||||
| 			if node.call_expr.return_type != ast.void_type { |  | ||||||
| 				g.gowrappers.writeln('\t$s_ret_typ* ret_ptr;') |  | ||||||
| 				c_ret_ptr_ptr = '&ret_ptr' |  | ||||||
| 			} |  | ||||||
| 			if g.pref.os == .windows { |  | ||||||
| 				if node.call_expr.return_type == ast.void_type { |  | ||||||
| 					g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread, INFINITE);') |  | ||||||
| 				} else { |  | ||||||
| 					g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread.handle, INFINITE);') |  | ||||||
| 					g.gowrappers.writeln('\tret_ptr = thread.ret_ptr;') |  | ||||||
| 				} |  | ||||||
| 			} else { |  | ||||||
| 				g.gowrappers.writeln('\tint stat = pthread_join(thread, (void **)$c_ret_ptr_ptr);') |  | ||||||
| 			} |  | ||||||
| 			g.gowrappers.writeln('\tif (stat != 0) { _v_panic(_SLIT("unable to join thread")); }') |  | ||||||
| 			if g.pref.os == .windows { |  | ||||||
| 				if node.call_expr.return_type == ast.void_type { |  | ||||||
| 					g.gowrappers.writeln('\tCloseHandle(thread);') |  | ||||||
| 				} else { |  | ||||||
| 					g.gowrappers.writeln('\tCloseHandle(thread.handle);') |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			if node.call_expr.return_type != ast.void_type { |  | ||||||
| 				g.gowrappers.writeln('\t$s_ret_typ ret = *ret_ptr;') |  | ||||||
| 				g.gowrappers.writeln('\tfree(ret_ptr);') |  | ||||||
| 				g.gowrappers.writeln('\treturn ret;') |  | ||||||
| 			} |  | ||||||
| 			g.gowrappers.writeln('}') |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	// Register the wrapper type and function
 |  | ||||||
| 	mut should_register := false |  | ||||||
| 	lock g.threaded_fns { |  | ||||||
| 		if name !in g.threaded_fns { |  | ||||||
| 			g.threaded_fns << name |  | ||||||
| 			should_register = true |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if should_register { |  | ||||||
| 		g.type_definitions.writeln('\ntypedef struct $wrapper_struct_name {') |  | ||||||
| 		if expr.is_method { |  | ||||||
| 			styp := g.typ(expr.receiver_type) |  | ||||||
| 			g.type_definitions.writeln('\t$styp arg0;') |  | ||||||
| 		} |  | ||||||
| 		need_return_ptr := g.pref.os == .windows && node.call_expr.return_type != ast.void_type |  | ||||||
| 		if expr.args.len == 0 && !need_return_ptr { |  | ||||||
| 			g.type_definitions.writeln('EMPTY_STRUCT_DECLARATION;') |  | ||||||
| 		} else { |  | ||||||
| 			for i, arg in expr.args { |  | ||||||
| 				styp := g.typ(arg.typ) |  | ||||||
| 				g.type_definitions.writeln('\t$styp arg${i + 1};') |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if need_return_ptr { |  | ||||||
| 			g.type_definitions.writeln('\tvoid* ret_ptr;') |  | ||||||
| 		} |  | ||||||
| 		g.type_definitions.writeln('} $wrapper_struct_name;') |  | ||||||
| 		thread_ret_type := if g.pref.os == .windows { 'u32' } else { 'void*' } |  | ||||||
| 		g.type_definitions.writeln('$thread_ret_type ${wrapper_fn_name}($wrapper_struct_name *arg);') |  | ||||||
| 		g.gowrappers.writeln('$thread_ret_type ${wrapper_fn_name}($wrapper_struct_name *arg) {') |  | ||||||
| 		if node.call_expr.return_type != ast.void_type { |  | ||||||
| 			if g.pref.os == .windows { |  | ||||||
| 				g.gowrappers.write_string('\t*(($s_ret_typ*)(arg->ret_ptr)) = ') |  | ||||||
| 			} else { |  | ||||||
| 				g.gowrappers.writeln('\t$s_ret_typ* ret_ptr = malloc(sizeof($s_ret_typ));') |  | ||||||
| 				g.gowrappers.write_string('\t*ret_ptr = ') |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			g.gowrappers.write_string('\t') |  | ||||||
| 		} |  | ||||||
| 		if expr.is_method { |  | ||||||
| 			unwrapped_rec_type := g.unwrap_generic(expr.receiver_type) |  | ||||||
| 			typ_sym := g.table.sym(unwrapped_rec_type) |  | ||||||
| 			if typ_sym.kind == .interface_ |  | ||||||
| 				&& (typ_sym.info as ast.Interface).defines_method(expr.name) { |  | ||||||
| 				rec_cc_type := g.cc_type(unwrapped_rec_type, false) |  | ||||||
| 				receiver_type_name := util.no_dots(rec_cc_type) |  | ||||||
| 				g.gowrappers.write_string('${c_name(receiver_type_name)}_name_table[') |  | ||||||
| 				g.gowrappers.write_string('arg->arg0') |  | ||||||
| 				dot := if expr.left_type.is_ptr() { '->' } else { '.' } |  | ||||||
| 				mname := c_name(expr.name) |  | ||||||
| 				g.gowrappers.write_string('${dot}_typ]._method_${mname}(') |  | ||||||
| 				g.gowrappers.write_string('arg->arg0') |  | ||||||
| 				g.gowrappers.write_string('${dot}_object') |  | ||||||
| 			} else { |  | ||||||
| 				g.gowrappers.write_string('${name}(') |  | ||||||
| 				g.gowrappers.write_string('arg->arg0') |  | ||||||
| 			} |  | ||||||
| 			if expr.args.len > 0 { |  | ||||||
| 				g.gowrappers.write_string(', ') |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			g.gowrappers.write_string('${name}(') |  | ||||||
| 		} |  | ||||||
| 		if expr.args.len > 0 { |  | ||||||
| 			mut has_cast := false |  | ||||||
| 			for i in 0 .. expr.args.len { |  | ||||||
| 				if g.table.sym(expr.expected_arg_types[i]).kind == .interface_ |  | ||||||
| 					&& g.table.sym(expr.args[i].typ).kind != .interface_ { |  | ||||||
| 					has_cast = true |  | ||||||
| 					break |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			if has_cast { |  | ||||||
| 				pos := g.out.len |  | ||||||
| 				g.call_args(expr) |  | ||||||
| 				mut call_args_str := g.out.after(pos) |  | ||||||
| 				g.out.go_back(call_args_str.len) |  | ||||||
| 				mut rep_group := []string{cap: 2 * expr.args.len} |  | ||||||
| 				for i in 0 .. expr.args.len { |  | ||||||
| 					rep_group << g.expr_string(expr.args[i].expr) |  | ||||||
| 					rep_group << 'arg->arg${i + 1}' |  | ||||||
| 				} |  | ||||||
| 				call_args_str = call_args_str.replace_each(rep_group) |  | ||||||
| 				g.gowrappers.write_string(call_args_str) |  | ||||||
| 			} else { |  | ||||||
| 				for i in 0 .. expr.args.len { |  | ||||||
| 					g.gowrappers.write_string('arg->arg${i + 1}') |  | ||||||
| 					if i != expr.args.len - 1 { |  | ||||||
| 						g.gowrappers.write_string(', ') |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		g.gowrappers.writeln(');') |  | ||||||
| 		g.gowrappers.writeln('\tfree(arg);') |  | ||||||
| 		if g.pref.os != .windows && node.call_expr.return_type != ast.void_type { |  | ||||||
| 			g.gowrappers.writeln('\treturn ret_ptr;') |  | ||||||
| 		} else { |  | ||||||
| 			g.gowrappers.writeln('\treturn 0;') |  | ||||||
| 		} |  | ||||||
| 		g.gowrappers.writeln('}') |  | ||||||
| 	} |  | ||||||
| 	if node.is_expr { |  | ||||||
| 		g.empty_line = false |  | ||||||
| 		g.write(line) |  | ||||||
| 		g.write(handle) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn (mut g Gen) as_cast(node ast.AsCast) { | fn (mut g Gen) as_cast(node ast.AsCast) { | ||||||
| 	// Make sure the sum type can be cast to this type (the types
 | 	// Make sure the sum type can be cast to this type (the types
 | ||||||
| 	// are the same), otherwise panic.
 | 	// are the same), otherwise panic.
 | ||||||
|  |  | ||||||
|  | @ -1667,6 +1667,258 @@ fn (mut g Gen) call_args(node ast.CallExpr) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | fn (mut g Gen) go_expr(node ast.GoExpr) { | ||||||
|  | 	line := g.go_before_stmt(0) | ||||||
|  | 	mut handle := '' | ||||||
|  | 	tmp := g.new_tmp_var() | ||||||
|  | 	mut expr := node.call_expr | ||||||
|  | 	mut name := expr.name // util.no_dots(expr.name)
 | ||||||
|  | 	// TODO: fn call is duplicated. merge with fn_call().
 | ||||||
|  | 	for i, concrete_type in expr.concrete_types { | ||||||
|  | 		if concrete_type != ast.void_type && concrete_type != 0 { | ||||||
|  | 			// Using _T_ to differentiate between get<string> and get_string
 | ||||||
|  | 			// `foo<int>()` => `foo_T_int()`
 | ||||||
|  | 			if i == 0 { | ||||||
|  | 				name += '_T' | ||||||
|  | 			} | ||||||
|  | 			name += '_' + g.typ(concrete_type) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if expr.is_method { | ||||||
|  | 		receiver_sym := g.table.sym(expr.receiver_type) | ||||||
|  | 		name = receiver_sym.name + '_' + name | ||||||
|  | 	} else if mut expr.left is ast.AnonFn { | ||||||
|  | 		g.gen_anon_fn_decl(mut expr.left) | ||||||
|  | 		name = expr.left.decl.name | ||||||
|  | 	} else if expr.is_fn_var { | ||||||
|  | 		name = g.table.sym(expr.fn_var_type).name | ||||||
|  | 	} | ||||||
|  | 	name = util.no_dots(name) | ||||||
|  | 	if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') { | ||||||
|  | 		mut key := expr.name | ||||||
|  | 		if expr.is_method { | ||||||
|  | 			sym := g.table.sym(expr.receiver_type) | ||||||
|  | 			key = sym.name + '.' + expr.name | ||||||
|  | 		} | ||||||
|  | 		g.write('/* obf go: $key */') | ||||||
|  | 		name = g.obf_table[key] or { | ||||||
|  | 			panic('cgen: obf name "$key" not found, this should never happen') | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	g.empty_line = true | ||||||
|  | 	g.writeln('// start go') | ||||||
|  | 	wrapper_struct_name := 'thread_arg_' + name | ||||||
|  | 	wrapper_fn_name := name + '_thread_wrapper' | ||||||
|  | 	arg_tmp_var := 'arg_' + tmp | ||||||
|  | 	g.writeln('$wrapper_struct_name *$arg_tmp_var = malloc(sizeof(thread_arg_$name));') | ||||||
|  | 	if expr.is_method { | ||||||
|  | 		g.write('$arg_tmp_var->arg0 = ') | ||||||
|  | 		// TODO is this needed?
 | ||||||
|  | 		/* | ||||||
|  | 		if false && !expr.return_type.is_ptr() { | ||||||
|  | 			g.write('&') | ||||||
|  | 		} | ||||||
|  | 		*/ | ||||||
|  | 		g.expr(expr.left) | ||||||
|  | 		g.writeln(';') | ||||||
|  | 	} | ||||||
|  | 	for i, arg in expr.args { | ||||||
|  | 		g.write('$arg_tmp_var->arg${i + 1} = ') | ||||||
|  | 		g.expr(arg.expr) | ||||||
|  | 		g.writeln(';') | ||||||
|  | 	} | ||||||
|  | 	s_ret_typ := g.typ(node.call_expr.return_type) | ||||||
|  | 	if g.pref.os == .windows && node.call_expr.return_type != ast.void_type { | ||||||
|  | 		g.writeln('$arg_tmp_var->ret_ptr = malloc(sizeof($s_ret_typ));') | ||||||
|  | 	} | ||||||
|  | 	is_opt := node.call_expr.return_type.has_flag(.optional) | ||||||
|  | 	mut gohandle_name := '' | ||||||
|  | 	if node.call_expr.return_type == ast.void_type { | ||||||
|  | 		gohandle_name = if is_opt { '__v_thread_Option_void' } else { '__v_thread' } | ||||||
|  | 	} else { | ||||||
|  | 		opt := if is_opt { 'Option_' } else { '' } | ||||||
|  | 		gohandle_name = '__v_thread_$opt${g.table.sym(g.unwrap_generic(node.call_expr.return_type)).cname}' | ||||||
|  | 	} | ||||||
|  | 	if g.pref.os == .windows { | ||||||
|  | 		simple_handle := if node.is_expr && node.call_expr.return_type != ast.void_type { | ||||||
|  | 			'thread_handle_$tmp' | ||||||
|  | 		} else { | ||||||
|  | 			'thread_$tmp' | ||||||
|  | 		} | ||||||
|  | 		g.writeln('HANDLE $simple_handle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)$wrapper_fn_name, $arg_tmp_var, 0, 0);') | ||||||
|  | 		g.writeln('if (!$simple_handle) panic_lasterr(tos3("`go ${name}()`: "));') | ||||||
|  | 		if node.is_expr && node.call_expr.return_type != ast.void_type { | ||||||
|  | 			g.writeln('$gohandle_name thread_$tmp = {') | ||||||
|  | 			g.writeln('\t.ret_ptr = $arg_tmp_var->ret_ptr,') | ||||||
|  | 			g.writeln('\t.handle = thread_handle_$tmp') | ||||||
|  | 			g.writeln('};') | ||||||
|  | 		} | ||||||
|  | 		if !node.is_expr { | ||||||
|  | 			g.writeln('CloseHandle(thread_$tmp);') | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		g.writeln('pthread_t thread_$tmp;') | ||||||
|  | 		g.writeln('int ${tmp}_thr_res = pthread_create(&thread_$tmp, NULL, (void*)$wrapper_fn_name, $arg_tmp_var);') | ||||||
|  | 		g.writeln('if (${tmp}_thr_res) panic_error_number(tos3("`go ${name}()`: "), ${tmp}_thr_res);') | ||||||
|  | 		if !node.is_expr { | ||||||
|  | 			g.writeln('pthread_detach(thread_$tmp);') | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	g.writeln('// end go') | ||||||
|  | 	if node.is_expr { | ||||||
|  | 		handle = 'thread_$tmp' | ||||||
|  | 		// create wait handler for this return type if none exists
 | ||||||
|  | 		waiter_fn_name := gohandle_name + '_wait' | ||||||
|  | 		mut should_register := false | ||||||
|  | 		lock g.waiter_fns { | ||||||
|  | 			if waiter_fn_name !in g.waiter_fns { | ||||||
|  | 				g.waiter_fns << waiter_fn_name | ||||||
|  | 				should_register = true | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if should_register { | ||||||
|  | 			g.gowrappers.writeln('\n$s_ret_typ ${waiter_fn_name}($gohandle_name thread) {') | ||||||
|  | 			mut c_ret_ptr_ptr := 'NULL' | ||||||
|  | 			if node.call_expr.return_type != ast.void_type { | ||||||
|  | 				g.gowrappers.writeln('\t$s_ret_typ* ret_ptr;') | ||||||
|  | 				c_ret_ptr_ptr = '&ret_ptr' | ||||||
|  | 			} | ||||||
|  | 			if g.pref.os == .windows { | ||||||
|  | 				if node.call_expr.return_type == ast.void_type { | ||||||
|  | 					g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread, INFINITE);') | ||||||
|  | 				} else { | ||||||
|  | 					g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread.handle, INFINITE);') | ||||||
|  | 					g.gowrappers.writeln('\tret_ptr = thread.ret_ptr;') | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				g.gowrappers.writeln('\tint stat = pthread_join(thread, (void **)$c_ret_ptr_ptr);') | ||||||
|  | 			} | ||||||
|  | 			g.gowrappers.writeln('\tif (stat != 0) { _v_panic(_SLIT("unable to join thread")); }') | ||||||
|  | 			if g.pref.os == .windows { | ||||||
|  | 				if node.call_expr.return_type == ast.void_type { | ||||||
|  | 					g.gowrappers.writeln('\tCloseHandle(thread);') | ||||||
|  | 				} else { | ||||||
|  | 					g.gowrappers.writeln('\tCloseHandle(thread.handle);') | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if node.call_expr.return_type != ast.void_type { | ||||||
|  | 				g.gowrappers.writeln('\t$s_ret_typ ret = *ret_ptr;') | ||||||
|  | 				g.gowrappers.writeln('\tfree(ret_ptr);') | ||||||
|  | 				g.gowrappers.writeln('\treturn ret;') | ||||||
|  | 			} | ||||||
|  | 			g.gowrappers.writeln('}') | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// Register the wrapper type and function
 | ||||||
|  | 	mut should_register := false | ||||||
|  | 	lock g.threaded_fns { | ||||||
|  | 		if name !in g.threaded_fns { | ||||||
|  | 			g.threaded_fns << name | ||||||
|  | 			should_register = true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if should_register { | ||||||
|  | 		g.type_definitions.writeln('\ntypedef struct $wrapper_struct_name {') | ||||||
|  | 		if expr.is_method { | ||||||
|  | 			styp := g.typ(expr.receiver_type) | ||||||
|  | 			g.type_definitions.writeln('\t$styp arg0;') | ||||||
|  | 		} | ||||||
|  | 		need_return_ptr := g.pref.os == .windows && node.call_expr.return_type != ast.void_type | ||||||
|  | 		if expr.args.len == 0 && !need_return_ptr { | ||||||
|  | 			g.type_definitions.writeln('EMPTY_STRUCT_DECLARATION;') | ||||||
|  | 		} else { | ||||||
|  | 			for i, arg in expr.args { | ||||||
|  | 				styp := g.typ(arg.typ) | ||||||
|  | 				g.type_definitions.writeln('\t$styp arg${i + 1};') | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if need_return_ptr { | ||||||
|  | 			g.type_definitions.writeln('\tvoid* ret_ptr;') | ||||||
|  | 		} | ||||||
|  | 		g.type_definitions.writeln('} $wrapper_struct_name;') | ||||||
|  | 		thread_ret_type := if g.pref.os == .windows { 'u32' } else { 'void*' } | ||||||
|  | 		g.type_definitions.writeln('$thread_ret_type ${wrapper_fn_name}($wrapper_struct_name *arg);') | ||||||
|  | 		g.gowrappers.writeln('$thread_ret_type ${wrapper_fn_name}($wrapper_struct_name *arg) {') | ||||||
|  | 		if node.call_expr.return_type != ast.void_type { | ||||||
|  | 			if g.pref.os == .windows { | ||||||
|  | 				g.gowrappers.write_string('\t*(($s_ret_typ*)(arg->ret_ptr)) = ') | ||||||
|  | 			} else { | ||||||
|  | 				g.gowrappers.writeln('\t$s_ret_typ* ret_ptr = malloc(sizeof($s_ret_typ));') | ||||||
|  | 				g.gowrappers.write_string('\t*ret_ptr = ') | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			g.gowrappers.write_string('\t') | ||||||
|  | 		} | ||||||
|  | 		if expr.is_method { | ||||||
|  | 			unwrapped_rec_type := g.unwrap_generic(expr.receiver_type) | ||||||
|  | 			typ_sym := g.table.sym(unwrapped_rec_type) | ||||||
|  | 			if typ_sym.kind == .interface_ | ||||||
|  | 				&& (typ_sym.info as ast.Interface).defines_method(expr.name) { | ||||||
|  | 				rec_cc_type := g.cc_type(unwrapped_rec_type, false) | ||||||
|  | 				receiver_type_name := util.no_dots(rec_cc_type) | ||||||
|  | 				g.gowrappers.write_string('${c_name(receiver_type_name)}_name_table[') | ||||||
|  | 				g.gowrappers.write_string('arg->arg0') | ||||||
|  | 				dot := if expr.left_type.is_ptr() { '->' } else { '.' } | ||||||
|  | 				mname := c_name(expr.name) | ||||||
|  | 				g.gowrappers.write_string('${dot}_typ]._method_${mname}(') | ||||||
|  | 				g.gowrappers.write_string('arg->arg0') | ||||||
|  | 				g.gowrappers.write_string('${dot}_object') | ||||||
|  | 			} else { | ||||||
|  | 				g.gowrappers.write_string('${name}(') | ||||||
|  | 				g.gowrappers.write_string('arg->arg0') | ||||||
|  | 			} | ||||||
|  | 			if expr.args.len > 0 { | ||||||
|  | 				g.gowrappers.write_string(', ') | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			g.gowrappers.write_string('${name}(') | ||||||
|  | 		} | ||||||
|  | 		if expr.args.len > 0 { | ||||||
|  | 			mut has_cast := false | ||||||
|  | 			for i in 0 .. expr.args.len { | ||||||
|  | 				if g.table.sym(expr.expected_arg_types[i]).kind == .interface_ | ||||||
|  | 					&& g.table.sym(expr.args[i].typ).kind != .interface_ { | ||||||
|  | 					has_cast = true | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if has_cast { | ||||||
|  | 				pos := g.out.len | ||||||
|  | 				g.call_args(expr) | ||||||
|  | 				mut call_args_str := g.out.after(pos) | ||||||
|  | 				g.out.go_back(call_args_str.len) | ||||||
|  | 				mut rep_group := []string{cap: 2 * expr.args.len} | ||||||
|  | 				for i in 0 .. expr.args.len { | ||||||
|  | 					rep_group << g.expr_string(expr.args[i].expr) | ||||||
|  | 					rep_group << 'arg->arg${i + 1}' | ||||||
|  | 				} | ||||||
|  | 				call_args_str = call_args_str.replace_each(rep_group) | ||||||
|  | 				g.gowrappers.write_string(call_args_str) | ||||||
|  | 			} else { | ||||||
|  | 				for i in 0 .. expr.args.len { | ||||||
|  | 					g.gowrappers.write_string('arg->arg${i + 1}') | ||||||
|  | 					if i != expr.args.len - 1 { | ||||||
|  | 						g.gowrappers.write_string(', ') | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		g.gowrappers.writeln(');') | ||||||
|  | 		g.gowrappers.writeln('\tfree(arg);') | ||||||
|  | 		if g.pref.os != .windows && node.call_expr.return_type != ast.void_type { | ||||||
|  | 			g.gowrappers.writeln('\treturn ret_ptr;') | ||||||
|  | 		} else { | ||||||
|  | 			g.gowrappers.writeln('\treturn 0;') | ||||||
|  | 		} | ||||||
|  | 		g.gowrappers.writeln('}') | ||||||
|  | 	} | ||||||
|  | 	if node.is_expr { | ||||||
|  | 		g.empty_line = false | ||||||
|  | 		g.write(line) | ||||||
|  | 		g.write(handle) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // similar to `autofree_call_pregen()` but only to to handle [keep_args_alive] for C functions
 | // similar to `autofree_call_pregen()` but only to to handle [keep_args_alive] for C functions
 | ||||||
| fn (mut g Gen) keep_alive_call_pregen(node ast.CallExpr) int { | fn (mut g Gen) keep_alive_call_pregen(node ast.CallExpr) int { | ||||||
| 	g.empty_line = true | 	g.empty_line = true | ||||||
|  |  | ||||||
|  | @ -545,27 +545,6 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn (mut p Parser) go_expr() ast.GoExpr { |  | ||||||
| 	p.next() |  | ||||||
| 	spos := p.tok.pos() |  | ||||||
| 	expr := p.expr(0) |  | ||||||
| 	call_expr := if expr is ast.CallExpr { |  | ||||||
| 		expr |  | ||||||
| 	} else { |  | ||||||
| 		p.error_with_pos('expression in `go` must be a function call', expr.pos()) |  | ||||||
| 		ast.CallExpr{ |  | ||||||
| 			scope: p.scope |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	pos := spos.extend(p.prev_tok.pos()) |  | ||||||
| 	p.register_auto_import('sync.threads') |  | ||||||
| 	p.table.gostmts++ |  | ||||||
| 	return ast.GoExpr{ |  | ||||||
| 		call_expr: call_expr |  | ||||||
| 		pos: pos |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn (p &Parser) fileis(s string) bool { | fn (p &Parser) fileis(s string) bool { | ||||||
| 	return p.file_name.contains(s) | 	return p.file_name.contains(s) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -928,6 +928,27 @@ fn (mut p Parser) fn_args() ([]ast.Param, bool, bool) { | ||||||
| 	return args, types_only, is_variadic | 	return args, types_only, is_variadic | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | fn (mut p Parser) go_expr() ast.GoExpr { | ||||||
|  | 	p.next() | ||||||
|  | 	spos := p.tok.pos() | ||||||
|  | 	expr := p.expr(0) | ||||||
|  | 	call_expr := if expr is ast.CallExpr { | ||||||
|  | 		expr | ||||||
|  | 	} else { | ||||||
|  | 		p.error_with_pos('expression in `go` must be a function call', expr.pos()) | ||||||
|  | 		ast.CallExpr{ | ||||||
|  | 			scope: p.scope | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	pos := spos.extend(p.prev_tok.pos()) | ||||||
|  | 	p.register_auto_import('sync.threads') | ||||||
|  | 	p.table.gostmts++ | ||||||
|  | 	return ast.GoExpr{ | ||||||
|  | 		call_expr: call_expr | ||||||
|  | 		pos: pos | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| fn (mut p Parser) closure_vars() []ast.Param { | fn (mut p Parser) closure_vars() []ast.Param { | ||||||
| 	p.check(.lsbr) | 	p.check(.lsbr) | ||||||
| 	mut vars := []ast.Param{cap: 5} | 	mut vars := []ast.Param{cap: 5} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue