Merge branch 'master' into bill/rexcode

This commit is contained in:
gingerBill
2026-06-22 12:58:36 +01:00
6 changed files with 160 additions and 31 deletions

View File

@@ -378,7 +378,14 @@ advance_token :: proc(p: ^Parser) -> tokenizer.Token {
prev := p.prev_tok
if next_token0(p) {
consume_comment_groups(p, prev)
#partial switch p.curr_tok.kind {
case .Comment:
consume_comment_groups(p, prev)
case .Semicolon:
if p.expr_level > 0 && p.curr_tok.text == "\n" {
advance_token(p)
}
}
}
return prev
}
@@ -2366,10 +2373,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
case .Open_Paren:
open := expect_token(p, .Open_Paren)
p.expr_level += 1
prev_expr_level := p.expr_level
p.expr_level = max(p.expr_level, 0) + 1
expr := parse_expr(p, false)
skip_possible_newline(p)
p.expr_level -= 1
p.expr_level = prev_expr_level
close := expect_token(p, .Close_Paren)
pe := ast.new(ast.Paren_Expr, open.pos, end_pos(close))
@@ -3203,11 +3210,12 @@ parse_elem_list :: proc(p: ^Parser) -> []^ast.Expr {
parse_literal_value :: proc(p: ^Parser, type: ^ast.Expr) -> ^ast.Comp_Lit {
elems: []^ast.Expr
open := expect_token(p, .Open_Brace)
p.expr_level += 1
prev_expr_level := p.expr_level
p.expr_level = 0
if p.curr_tok.kind != .Close_Brace {
elems = parse_elem_list(p)
}
p.expr_level -= 1
p.expr_level = prev_expr_level
skip_possible_newline(p)
close := expect_closing_brace_of_field_list(p)
@@ -3226,7 +3234,8 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr {
ellipsis: tokenizer.Token
p.expr_level += 1
prev_expr_level := p.expr_level
p.expr_level = 0
open := expect_token(p, .Open_Paren)
seen_ellipsis := false
@@ -3273,8 +3282,8 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr {
allow_token(p, .Comma) or_break
}
p.expr_level = prev_expr_level
close := expect_closing_token_of_field_list(p, .Close_Paren, "argument list")
p.expr_level -= 1
ce := ast.new(ast.Call_Expr, operand.pos, end_pos(close))
ce.expr = operand
@@ -3360,8 +3369,8 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
}
}
close := expect_token(p, .Close_Bracket)
p.expr_level -= 1
close := expect_token(p, .Close_Bracket)
if is_slice_op {
if interval.kind == .Comma {

View File

@@ -2147,19 +2147,31 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
}
}
} else {
isize multiple_return_offset = 0;
for_array(i, cl->elems) {
Entity *f = type->Struct.fields[i];
Entity *f = type->Struct.fields[i+multiple_return_offset];
TypeAndValue tav = cl->elems[i]->tav;
ExactValue val = {};
if (tav.mode != Addressing_Invalid) {
val = tav.value;
// INFO: dead code:
// ExactValue val = {};
// if (tav.mode != Addressing_Invalid) {
// val = tav.value;
// }
if (is_type_tuple(tav.type)){
multiple_return_offset += tav.type->Tuple.variables.count-1;
}
i32 index = field_remapping[f->Variable.field_index];
if (elem_type_can_be_constant(f->type)) {
lbValue value = lb_const_value(m, f->type, tav.value, tav.type, cc);
LLVMTypeRef value_type = LLVMTypeOf(value.value);
GB_ASSERT_MSG(lb_sizeof(value_type) == type_size_of(f->type), "%s vs %s", LLVMPrintTypeToString(value_type), type_to_string(f->type));
isize lb_sizeof_value_type = lb_sizeof(value_type);
isize type_size_of_f_type = type_size_of(f->type);
GB_ASSERT_MSG(lb_sizeof_value_type ==type_size_of_f_type, "%s vs %s", LLVMPrintTypeToString(value_type), type_to_string(f->type));
values[index] = value.value;
visited[index] = true;
}

View File

@@ -178,11 +178,44 @@ gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt,
return exit_code;
}
gb_internal void system_must_exec_command_line_app(char const *name, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
system_exec_command_line_app_internal(/* exit_on_err= */ true, name, fmt, va);
va_end(va);
#if defined(GB_SYSTEM_WINDOWS)
#include <process.h>
#else
#include <spawn.h>
extern char **environ;
#endif
int run_subprocess(const char *name, const char **args) {
#if defined(GB_SYSTEM_WINDOWS)
return (int)_spawnv(_P_WAIT, name, args);
#else
pid_t pid;
int status;
status = posix_spawn(&pid, name, NULL, NULL, (char *const *)args, environ);
if (status != 0) {
gb_printf_err("Could not spawn subprocess: %s\n", strerror(errno));
return -1;
}
for (;;) {
if (waitpid(pid, &status, WUNTRACED) < 0) {
gb_printf_err("Could not wait on subprocess: (pid: %d): %s\n", pid, strerror(errno));
return -1;
}
if (WIFEXITED(status)) {
return WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
struct rlimit limit = { 0, 0, };
setrlimit(RLIMIT_CORE, &limit);
raise(WTERMSIG(status));
return -1;
} else if (WIFSTOPPED(status)) {
return -1;
}
}
GB_PANIC("Subprocess failure");
#endif
}
#if defined(GB_SYSTEM_WINDOWS)
@@ -3653,10 +3686,11 @@ int main(int arg_count, char const **arg_ptr) {
init_build_context_error_pos_style();
Array<String> args = setup_args(arg_count, arg_ptr);
Array<String> run_args = array_make<String>(heap_allocator(), 0, arg_count);
defer (array_free(&run_args));
String command = args[1];
String init_filename = {};
String run_args_string = {};
isize last_non_run_arg = args.count;
for_array(i, args) {
@@ -3680,9 +3714,6 @@ int main(int arg_count, char const **arg_ptr) {
build_context.command_kind = Command_test;
}
Array<String> run_args = array_make<String>(heap_allocator(), 0, arg_count);
defer (array_free(&run_args));
isize run_args_start_idx = -1;
for_array(i, args) {
if (args[i] == "--") {
@@ -3704,7 +3735,6 @@ int main(int arg_count, char const **arg_ptr) {
}
}
args = array_slice(args, 0, last_non_run_arg);
run_args_string = string_join_and_quote(heap_allocator(), run_args);
init_filename = args[2];
run_output = true;
@@ -4293,8 +4323,23 @@ end_of_code_gen:;
String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
defer (gb_free(heap_allocator(), exe_name.text));
system_must_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(exe_name), LIT(run_args_string));
const char* exe_name_cstring = alloc_cstring(heap_allocator(), exe_name);
Array<const char *> run_args_cstring = array_make<const char *>(heap_allocator(), 0, run_args.count);
defer({
for_array(i, run_args_cstring) { gb_free(heap_allocator(), (void*)run_args_cstring[i]); }
array_free(&run_args_cstring);
});
array_add(&run_args_cstring, exe_name_cstring);
for_array(i, run_args) {
array_add(&run_args_cstring, alloc_cstring(heap_allocator(), run_args[i]));
}
array_add(&run_args_cstring, NULL);
int subprocess_res = run_subprocess(exe_name_cstring, run_args_cstring.data);
if (subprocess_res) {
gb_exit(subprocess_res);
}
if (!build_context.keep_executable) {
char const *filename = cast(char const *)exe_name.text;
gb_file_remove(filename);

View File

@@ -3094,7 +3094,7 @@ gb_internal bool lookup_subtype_polymorphic_selection(Type *dst, Type *src, Sele
return true;
}
}
if ((f->flags & EntityFlag_Using) != 0 && is_type_struct(f->type)) {
if ((f->flags & EntityFlags_IsSubtype) != 0 && is_type_struct(f->type)) {
String name = lookup_subtype_polymorphic_field(dst, f->type);
if (name.len > 0) {
array_add(&sel->index, cast(i32)i);
@@ -5043,11 +5043,7 @@ gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isi
return level+1;
}
}
// Only follow the chain transitively when the field also has `using`, which is
// what the backend's lookup_subtype_polymorphic_selection requires (it gates
// recursion on EntityFlag_Using). A plain `#subtype`-only field enables a
// single-hop conversion but not a two-or-more hop transitive one.
if (f->flags & EntityFlag_Using) {
if (f->flags & EntityFlags_IsSubtype) {
isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr, allow_polymorphic);
if (nested_level > 0) {
return nested_level;

View File

@@ -94,3 +94,40 @@ test_parse_stb_image :: proc(t: ^testing.T) {
testing.expectf(t, value.syntax_error_count == 0, "%v should contain zero errors", key)
}
}
@test
test_parse_multiline_ternary :: proc(t: ^testing.T) {
context.allocator = context.temp_allocator
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
file := ast.File{
fullpath = "test.odin",
src = `
package main
my_func :: proc (cond: bool, a: string, b: string) -> string {
out := (
cond
? a
: b
)
return out
}
`,
}
p := parser.default_parser()
p.err = proc(pos: tokenizer.Pos, format: string, args: ..any) {
message := fmt.tprintf(format, ..args)
log.errorf("%s(%d:%d): %s", pos.file, pos.line, pos.column, message)
}
p.warn = proc(pos: tokenizer.Pos, format: string, args: ..any) {
message := fmt.tprintf(format, ..args)
log.warnf("%s(%d:%d): %s", pos.file, pos.line, pos.column, message)
}
ok := parser.parse_file(&p, &file)
testing.expect(t, ok, "bad parse")
testing.expect(t, file.syntax_error_count == 0, "should contain zero errors")
}

View File

@@ -0,0 +1,30 @@
package test_issues
import "core:testing"
@(test)
test_issue_6853 :: proc(t: ^testing.T) {
test_s :: struct {
a, b, c, d, e: u64,
}
expected := test_s{1, 2, 3, 4, 5}
case0 :: proc() -> (u64, u64) {return 1, 2}
test0 := test_s{case0(), 3, 4, 5}
testing.expect_value(t, test0, expected)
case1 :: proc() -> (u64, u64, u64) {return 1, 2, 3}
test1 := test_s{case1(), 4, 5}
testing.expect_value(t, test1, expected)
case2 :: proc() -> (u64, u64) {return 2, 3}
test2 := test_s{1, case2(), 4, 5}
testing.expect_value(t, test2, expected)
case3_1 :: proc() -> (u64, u64) {return 1, 2}
case3_2 :: proc() -> (u64, u64) {return 3, 4}
test3 := test_s{case3_1(), case3_2(), 5}
testing.expect_value(t, test3, expected)
}