diff --git a/core/c/frontend/preprocessor/preprocess.odin b/core/c/frontend/preprocessor/preprocess.odin index 4cfad2c1c..b5eab0bb3 100644 --- a/core/c/frontend/preprocessor/preprocess.odin +++ b/core/c/frontend/preprocessor/preprocess.odin @@ -1118,7 +1118,7 @@ expand_macro :: proc(cpp: ^Preprocessor, rest: ^^Token, tok: ^Token) -> bool { search_include_next :: proc(cpp: ^Preprocessor, filename: string) -> (path: string, ok: bool) { for ; cpp.include_next_index < len(cpp.include_paths); cpp.include_next_index += 1 { - tpath := filepath.join(elems={cpp.include_paths[cpp.include_next_index], filename}, allocator=context.temp_allocator) + tpath := filepath.join({cpp.include_paths[cpp.include_next_index], filename}, allocator=context.temp_allocator) if os.exists(tpath) { return strings.clone(tpath), true } @@ -1136,7 +1136,7 @@ search_include_paths :: proc(cpp: ^Preprocessor, filename: string) -> (path: str } for include_path in cpp.include_paths { - tpath := filepath.join(elems={include_path, filename}, allocator=context.temp_allocator) + tpath := filepath.join({include_path, filename}, allocator=context.temp_allocator) if os.exists(tpath) { path, ok = strings.clone(tpath), true cpp.filepath_cache[filename] = path diff --git a/core/compress/gzip/gzip.odin b/core/compress/gzip/gzip.odin index 4de4d1b63..b0ca4491b 100644 --- a/core/compress/gzip/gzip.odin +++ b/core/compress/gzip/gzip.odin @@ -335,7 +335,7 @@ load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, exp // fmt.printf("GZIP: Expected Payload Size: %v\n", expected_output_size); - zlib_error := zlib.inflate_raw(z=z, expected_output_size=expected_output_size) + zlib_error := zlib.inflate_raw(z, expected_output_size=expected_output_size) if zlib_error != nil { return zlib_error } diff --git a/core/compress/zlib/zlib.odin b/core/compress/zlib/zlib.odin index 855eef7a8..21172e8e8 100644 --- a/core/compress/zlib/zlib.odin +++ b/core/compress/zlib/zlib.odin @@ -471,7 +471,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f } // Parse ZLIB stream without header. - inflate_raw(z=ctx, expected_output_size=expected_output_size) or_return + inflate_raw(ctx, expected_output_size=expected_output_size) or_return if !raw { compress.discard_to_next_byte_lsb(ctx) @@ -665,7 +665,7 @@ inflate_from_byte_array :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, e ctx.input_data = input ctx.output = buf - return inflate_from_context(ctx=&ctx, raw=raw, expected_output_size=expected_output_size) + return inflate_from_context(&ctx, raw=raw, expected_output_size=expected_output_size) } inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, expected_output_size := -1) -> (err: Error) { @@ -674,7 +674,7 @@ inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := fals ctx.input_data = input ctx.output = buf - return inflate_raw(z=&ctx, expected_output_size=expected_output_size) + return inflate_raw(&ctx, expected_output_size=expected_output_size) } inflate :: proc{inflate_from_context, inflate_from_byte_array} diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 61f7b5d99..f1f94b1b3 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -123,7 +123,7 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist aprint :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str) - sbprint(buf=&str, args=args, sep=sep) + sbprint(&str, ..args, sep=sep) return strings.to_string(str) } // Creates a formatted string with a newline character at the end @@ -139,7 +139,7 @@ aprint :: proc(args: ..any, sep := " ") -> string { aprintln :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str) - sbprintln(buf=&str, args=args, sep=sep) + sbprintln(&str, ..args, sep=sep) return strings.to_string(str) } // Creates a formatted string using a format string and arguments @@ -171,7 +171,7 @@ aprintf :: proc(fmt: string, args: ..any) -> string { tprint :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) - sbprint(buf=&str, args=args, sep=sep) + sbprint(&str, ..args, sep=sep) return strings.to_string(str) } // Creates a formatted string with a newline character at the end @@ -187,7 +187,7 @@ tprint :: proc(args: ..any, sep := " ") -> string { tprintln :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) - sbprintln(buf=&str, args=args, sep=sep) + sbprintln(&str, ..args, sep=sep) return strings.to_string(str) } // Creates a formatted string using a format string and arguments @@ -217,7 +217,7 @@ tprintf :: proc(fmt: string, args: ..any) -> string { // bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string { sb := strings.builder_from_bytes(buf[0:len(buf)]) - return sbprint(buf=&sb, args=args, sep=sep) + return sbprint(&sb, ..args, sep=sep) } // Creates a formatted string using a supplied buffer as the backing array, appends newline. Writes into the buffer. // @@ -230,7 +230,7 @@ bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string { // bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string { sb := strings.builder_from_bytes(buf[0:len(buf)]) - return sbprintln(buf=&sb, args=args, sep=sep) + return sbprintln(&sb, ..args, sep=sep) } // Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer. // @@ -327,7 +327,7 @@ ctprintf :: proc(format: string, args: ..any) -> cstring { // Returns: A formatted string // sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { - wprint(w=strings.to_writer(buf), args=args, sep=sep) + wprint(strings.to_writer(buf), ..args, sep=sep) return strings.to_string(buf^) } // Formats and writes to a strings.Builder buffer using the default print settings @@ -340,7 +340,7 @@ sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { // Returns: The resulting formatted string // sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { - wprintln(w=strings.to_writer(buf), args=args, sep=sep) + wprintln(strings.to_writer(buf), ..args, sep=sep) return strings.to_string(buf^) } // Formats and writes to a strings.Builder buffer according to the specified format string @@ -353,7 +353,7 @@ sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { // Returns: The resulting formatted string // sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string { - wprintf(w=strings.to_writer(buf), fmt=fmt, args=args) + wprintf(strings.to_writer(buf), fmt, ..args) return strings.to_string(buf^) } // Formats and writes to an io.Writer using the default print settings diff --git a/core/fmt/fmt_os.odin b/core/fmt/fmt_os.odin index 861b0c3b9..5b3a68502 100644 --- a/core/fmt/fmt_os.odin +++ b/core/fmt/fmt_os.odin @@ -14,7 +14,7 @@ fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int { bufio.writer_init_with_buf(&b, {os.stream_from_handle(fd)}, buf[:]) w := bufio.writer_to_writer(&b) - return wprint(w=w, args=args, sep=sep) + return wprint(w, ..args, sep=sep) } // fprintln formats using the default print settings and writes to fd @@ -26,7 +26,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int { bufio.writer_init_with_buf(&b, {os.stream_from_handle(fd)}, buf[:]) w := bufio.writer_to_writer(&b) - return wprintln(w=w, args=args, sep=sep) + return wprintln(w, ..args, sep=sep) } // fprintf formats according to the specified format string and writes to fd fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int { @@ -61,15 +61,15 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) { } // print formats using the default print settings and writes to os.stdout -print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) } +print :: proc(args: ..any, sep := " ") -> int { return fprint(os.stdout, ..args, sep=sep) } // println formats using the default print settings and writes to os.stdout -println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) } +println :: proc(args: ..any, sep := " ") -> int { return fprintln(os.stdout, ..args, sep=sep) } // printf formats according to the specified format string and writes to os.stdout printf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) } // eprint formats using the default print settings and writes to os.stderr -eprint :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) } +eprint :: proc(args: ..any, sep := " ") -> int { return fprint(os.stderr, ..args, sep=sep) } // eprintln formats using the default print settings and writes to os.stderr -eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) } +eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(os.stderr, ..args, sep=sep) } // eprintf formats according to the specified format string and writes to os.stderr eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) } diff --git a/core/image/png/helpers.odin b/core/image/png/helpers.odin index bfe56d62e..025c2b866 100644 --- a/core/image/png/helpers.odin +++ b/core/image/png/helpers.odin @@ -99,7 +99,7 @@ text :: proc(c: image.PNG_Chunk) -> (res: Text, ok: bool) { case .tEXt: ok = true - fields := bytes.split(s=c.data, sep=[]u8{0}, allocator=context.temp_allocator) + fields := bytes.split(c.data, sep=[]u8{0}, allocator=context.temp_allocator) if len(fields) == 2 { res.keyword = strings.clone(string(fields[0])) res.text = strings.clone(string(fields[1])) @@ -110,7 +110,7 @@ text :: proc(c: image.PNG_Chunk) -> (res: Text, ok: bool) { case .zTXt: ok = true - fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator) + fields := bytes.split_n(c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator) if len(fields) != 3 || len(fields[1]) != 0 { // Compression method must be 0=Deflate, which thanks to the split above turns // into an empty slice @@ -199,7 +199,7 @@ text_destroy :: proc(text: Text) { iccp :: proc(c: image.PNG_Chunk) -> (res: iCCP, ok: bool) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) - fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator) + fields := bytes.split_n(c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator) if len(fields[0]) < 1 || len(fields[0]) > 79 { // Invalid profile name @@ -263,7 +263,7 @@ splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) { } runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) - fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=2, allocator=context.temp_allocator) + fields := bytes.split_n(c.data, sep=[]u8{0}, n=2, allocator=context.temp_allocator) if len(fields) != 2 { return } diff --git a/core/log/log.odin b/core/log/log.odin index a699247b8..f3554791b 100644 --- a/core/log/log.odin +++ b/core/log/log.odin @@ -76,43 +76,43 @@ nil_logger :: proc() -> Logger { } debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) { - logf(level=.Debug, fmt_str=fmt_str, args=args, location=location) + logf(.Debug, fmt_str, ..args, location=location) } infof :: proc(fmt_str: string, args: ..any, location := #caller_location) { - logf(level=.Info, fmt_str=fmt_str, args=args, location=location) + logf(.Info, fmt_str, ..args, location=location) } warnf :: proc(fmt_str: string, args: ..any, location := #caller_location) { - logf(level=.Warning, fmt_str=fmt_str, args=args, location=location) + logf(.Warning, fmt_str, ..args, location=location) } errorf :: proc(fmt_str: string, args: ..any, location := #caller_location) { - logf(level=.Error, fmt_str=fmt_str, args=args, location=location) + logf(.Error, fmt_str, ..args, location=location) } fatalf :: proc(fmt_str: string, args: ..any, location := #caller_location) { - logf(level=.Fatal, fmt_str=fmt_str, args=args, location=location) + logf(.Fatal, fmt_str, ..args, location=location) } debug :: proc(args: ..any, sep := " ", location := #caller_location) { - log(level=.Debug, args=args, sep=sep, location=location) + log(.Debug, ..args, sep=sep, location=location) } info :: proc(args: ..any, sep := " ", location := #caller_location) { - log(level=.Info, args=args, sep=sep, location=location) + log(.Info, ..args, sep=sep, location=location) } warn :: proc(args: ..any, sep := " ", location := #caller_location) { - log(level=.Warning, args=args, sep=sep, location=location) + log(.Warning, ..args, sep=sep, location=location) } error :: proc(args: ..any, sep := " ", location := #caller_location) { - log(level=.Error, args=args, sep=sep, location=location) + log(.Error, ..args, sep=sep, location=location) } fatal :: proc(args: ..any, sep := " ", location := #caller_location) { - log(level=.Fatal, args=args, sep=sep, location=location) + log(.Fatal, ..args, sep=sep, location=location) } panic :: proc(args: ..any, location := #caller_location) -> ! { - log(level=.Fatal, args=args, location=location) + log(.Fatal, ..args, location=location) runtime.panic("log.panic", location) } panicf :: proc(fmt_str: string, args: ..any, location := #caller_location) -> ! { - logf(level=.Fatal, fmt_str=fmt_str, args=args, location=location) + logf(.Fatal, fmt_str, ..args, location=location) runtime.panic("log.panicf", location) } @@ -127,7 +127,7 @@ log :: proc(level: Level, args: ..any, sep := " ", location := #caller_location) if level < logger.lowest_level { return } - str := fmt.tprint(args=args, sep=sep) //NOTE(Hoej): While tprint isn't thread-safe, no logging is. + str := fmt.tprint(..args, sep=sep) //NOTE(Hoej): While tprint isn't thread-safe, no logging is. logger.procedure(logger.data, level, str, logger.options, location) } diff --git a/core/log/log_allocator.odin b/core/log/log_allocator.odin index f4d1841db..934f0d643 100644 --- a/core/log/log_allocator.odin +++ b/core/log/log_allocator.odin @@ -38,60 +38,60 @@ log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode, switch mode { case .Alloc: logf( - level=la.level, - fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc, size=%d, alignment=%d)", - args = {la.prefix, padding, size, alignment}, + la.level, + "%s%s>>> ALLOCATOR(mode=.Alloc, size=%d, alignment=%d)", + la.prefix, padding, size, alignment, location = location, ) case .Alloc_Non_Zeroed: logf( - level=la.level, - fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc_Non_Zeroed, size=%d, alignment=%d)", - args = {la.prefix, padding, size, alignment}, + la.level, + "%s%s>>> ALLOCATOR(mode=.Alloc_Non_Zeroed, size=%d, alignment=%d)", + la.prefix, padding, size, alignment, location = location, ) case .Free: if old_size != 0 { logf( - level=la.level, - fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p, size=%d)", - args = {la.prefix, padding, old_memory, old_size}, + la.level, + "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p, size=%d)", + la.prefix, padding, old_memory, old_size, location = location, ) } else { logf( - level=la.level, - fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p)", - args = {la.prefix, padding, old_memory}, + la.level, + "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p)", + la.prefix, padding, old_memory, location = location, ) } case .Free_All: logf( - level=la.level, - fmt_str = "%s%s<<< ALLOCATOR(mode=.Free_All)", - args = {la.prefix, padding}, + la.level, + "%s%s<<< ALLOCATOR(mode=.Free_All)", + la.prefix, padding, location = location, ) case .Resize: logf( - level=la.level, - fmt_str = "%s%s>>> ALLOCATOR(mode=.Resize, ptr=%p, old_size=%d, size=%d, alignment=%d)", - args = {la.prefix, padding, old_memory, old_size, size, alignment}, + la.level, + "%s%s>>> ALLOCATOR(mode=.Resize, ptr=%p, old_size=%d, size=%d, alignment=%d)", + la.prefix, padding, old_memory, old_size, size, alignment, location = location, ) case .Query_Features: logf( - level=la.level, - fmt_str = "%s%ALLOCATOR(mode=.Query_Features)", - args = {la.prefix, padding}, + la.level, + "%s%ALLOCATOR(mode=.Query_Features)", + la.prefix, padding, location = location, ) case .Query_Info: logf( - level=la.level, - fmt_str = "%s%ALLOCATOR(mode=.Query_Info)", - args = {la.prefix, padding}, + la.level, + "%s%ALLOCATOR(mode=.Query_Info)", + la.prefix, padding, location = location, ) } @@ -103,9 +103,9 @@ log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode, defer la.locked = false if err != nil { logf( - level=la.level, - fmt_str = "%s%ALLOCATOR ERROR=%v", - args = {la.prefix, padding, error}, + la.level, + "%s%ALLOCATOR ERROR=%v", + la.prefix, padding, error, location = location, ) } diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index 2b758dc35..d15ce0e98 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -429,7 +429,7 @@ internal_int_write_to_ascii_file :: proc(a: ^Int, filename: string, radix := i8( len = l, } - ok := os.write_entire_file(name=filename, data=data, truncate=true) + ok := os.write_entire_file(filename, data, truncate=true) return nil if ok else .Cannot_Write_File } diff --git a/core/mem/virtual/arena.odin b/core/mem/virtual/arena.odin index 027a6ce6e..e73a39660 100644 --- a/core/mem/virtual/arena.odin +++ b/core/mem/virtual/arena.odin @@ -120,7 +120,7 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l if arena.minimum_block_size == 0 { arena.minimum_block_size = DEFAULT_ARENA_STATIC_RESERVE_SIZE } - arena_init_static(arena=arena, reserved=arena.minimum_block_size, commit_size=DEFAULT_ARENA_STATIC_COMMIT_SIZE) or_return + arena_init_static(arena, reserved=arena.minimum_block_size, commit_size=DEFAULT_ARENA_STATIC_COMMIT_SIZE) or_return } fallthrough case .Buffer: diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index c5cb8cc07..9f2899bcc 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -112,7 +112,7 @@ remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_locatio // Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic. @builtin pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { - assert(len(array) > 0, "", loc) + assert(len(array) > 0, loc=loc) res = array[len(array)-1] (^Raw_Dynamic_Array)(array).len -= 1 return res @@ -136,7 +136,7 @@ pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check // Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic. @builtin pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check { - assert(len(array) > 0, "", loc) + assert(len(array) > 0, loc=loc) res = array[0] if len(array) > 1 { copy(array[0:], array[1:]) @@ -424,7 +424,7 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> a := (^Raw_Dynamic_Array)(array) when size_of(E) != 0 { data := ([^]E)(a.data) - assert(condition=data != nil, loc=loc) + assert(data != nil, loc=loc) data[a.len] = arg } a.len += 1 @@ -459,7 +459,7 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) a := (^Raw_Dynamic_Array)(array) when size_of(E) != 0 { data := ([^]E)(a.data) - assert(condition=data != nil, loc=loc) + assert(data != nil, loc=loc) intrinsics.mem_copy(&data[a.len], raw_data(args), size_of(E) * arg_len) } a.len += arg_len @@ -472,7 +472,7 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) @builtin append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { args := transmute([]E)arg - return append_elems(array=array, args=args, loc=loc) + return append_elems(array, ..args, loc=loc) } @@ -481,7 +481,7 @@ append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #ca append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { n_arg: int for arg in args { - n_arg, err = append(array = array, args = transmute([]E)(arg), loc = loc) + n_arg, err = append(array, ..transmute([]E)(arg), loc=loc) n += n_arg if err != nil { return diff --git a/core/runtime/error_checks.odin b/core/runtime/error_checks.odin index a667b9065..c189642af 100644 --- a/core/runtime/error_checks.odin +++ b/core/runtime/error_checks.odin @@ -22,7 +22,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index return } @(cold) - handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) { + handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Index ") print_i64(i64(index)) @@ -83,7 +83,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32, return } @(cold) - handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) { + handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid dynamic array indices ") print_i64(i64(low)) @@ -104,7 +104,7 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32 return } @(cold) - handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) { + handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Matrix indices [") print_i64(i64(row_index)) @@ -128,7 +128,7 @@ when ODIN_NO_RTTI { return } @(cold) - handle_error :: proc "contextless" (file: string, line, column: i32) { + handle_error :: proc "contextless" (file: string, line, column: i32) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid type assertion\n") type_assertion_trap() @@ -141,7 +141,7 @@ when ODIN_NO_RTTI { return } @(cold) - handle_error :: proc "contextless" (file: string, line, column: i32) { + handle_error :: proc "contextless" (file: string, line, column: i32) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid type assertion\n") type_assertion_trap() @@ -154,7 +154,7 @@ when ODIN_NO_RTTI { return } @(cold) - handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) { + handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid type assertion from ") print_typeid(from) @@ -199,7 +199,7 @@ when ODIN_NO_RTTI { } @(cold) - handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) { + handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! { actual := variant_type(from, from_data) @@ -225,7 +225,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio return } @(cold) - handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) { + handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) -> ! { print_caller_location(loc) print_string(" Invalid slice length for make: ") print_i64(i64(len)) @@ -240,7 +240,7 @@ make_dynamic_array_error_loc :: #force_inline proc "contextless" (using loc := # return } @(cold) - handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) { + handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) -> ! { print_caller_location(loc) print_string(" Invalid dynamic array parameters for make: ") print_i64(i64(len)) @@ -257,7 +257,7 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca return } @(cold) - handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) { + handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) -> ! { print_caller_location(loc) print_string(" Invalid map capacity for make: ") print_i64(i64(cap)) diff --git a/core/testing/runner_windows.odin b/core/testing/runner_windows.odin index 525eae685..17bcfce26 100644 --- a/core/testing/runner_windows.odin +++ b/core/testing/runner_windows.odin @@ -191,7 +191,7 @@ run_internal_test :: proc(t: ^T, it: Internal_Test) { global_exception_handler = win32.AddVectoredExceptionHandler(0, exception_handler_proc) context.assertion_failure_proc = proc(prefix, message: string, loc: runtime.Source_Code_Location) -> ! { - errorf(t=global_current_t, format="%s %s", args={prefix, message}, loc=loc) + errorf(global_current_t, "%s %s", prefix, message, loc=loc) intrinsics.trap() } diff --git a/core/testing/testing.odin b/core/testing/testing.odin index 37f9fe4d9..0ceb58e33 100644 --- a/core/testing/testing.odin +++ b/core/testing/testing.odin @@ -46,15 +46,15 @@ errorf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) { } fail :: proc(t: ^T, loc := #caller_location) { - error(t=t, args={"FAIL"}, loc=loc) + error(t, "FAIL", loc=loc) t.error_count += 1 } fail_now :: proc(t: ^T, msg := "", loc := #caller_location) { if msg != "" { - error(t=t, args={"FAIL:", msg}, loc=loc) + error(t, "FAIL:", msg, loc=loc) } else { - error(t=t, args={"FAIL"}, loc=loc) + error(t, "FAIL", loc=loc) } t.error_count += 1 if t._fail_now != nil { @@ -84,14 +84,14 @@ cleanup :: proc(t: ^T, procedure: proc(rawptr), user_data: rawptr) { expect :: proc(t: ^T, ok: bool, msg: string = "", loc := #caller_location) -> bool { if !ok { - error(t=t, args={msg}, loc=loc) + error(t, msg, loc=loc) } return ok } expect_value :: proc(t: ^T, value, expected: $T, loc := #caller_location) -> bool where intrinsics.type_is_comparable(T) { ok := value == expected if !ok { - errorf(t=t, format="expected %v, got %v", args={expected, value}, loc=loc) + errorf(t, "expected %v, got %v", expected, value, loc=loc) } return ok } diff --git a/core/text/table/table.odin b/core/text/table/table.odin index 2b60df98f..8d96cb26f 100644 --- a/core/text/table/table.odin +++ b/core/text/table/table.odin @@ -114,7 +114,7 @@ set_cell_alignment :: proc(tbl: ^Table, row, col: int, alignment: Cell_Alignment format :: proc(tbl: ^Table, _fmt: string, args: ..any, loc := #caller_location) -> string { context.allocator = tbl.format_allocator - return fmt.aprintf(fmt = _fmt, args = args) + return fmt.aprintf(_fmt, ..args) } header :: proc(tbl: ^Table, values: ..any, loc := #caller_location) { diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 4b02bcd53..7c98ca728 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -1175,13 +1175,13 @@ threading_example :: proc() { N :: 3 pool: thread.Pool - thread.pool_init(pool=&pool, thread_count=N, allocator=context.allocator) + thread.pool_init(&pool, allocator=context.allocator, thread_count=N) defer thread.pool_destroy(&pool) for i in 0..<30 { // be mindful of the allocator used for tasks. The allocator needs to be thread safe, or be owned by the task for exclusive use - thread.pool_add_task(pool=&pool, procedure=task_proc, data=nil, user_index=i, allocator=context.allocator) + thread.pool_add_task(&pool, allocator=context.allocator, procedure=task_proc, data=nil, user_index=i) } thread.pool_start(&pool) diff --git a/src/array.cpp b/src/array.cpp index f1a1f93e2..d8e25d25d 100644 --- a/src/array.cpp +++ b/src/array.cpp @@ -80,7 +80,9 @@ gb_internal Slice slice_make(gbAllocator const &allocator, isize count) { GB_ASSERT(count >= 0); Slice s = {}; s.data = gb_alloc_array(allocator, T, count); - GB_ASSERT(s.data != nullptr); + if (count > 0) { + GB_ASSERT(s.data != nullptr); + } s.count = count; return s; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 830b5315d..83e44b39f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -14,6 +14,7 @@ enum CallArgumentError { CallArgumentError_ParameterMissing, CallArgumentError_DuplicateParameter, CallArgumentError_NoneConstantParameter, + CallArgumentError_OutOfOrderParameters, CallArgumentError_MAX, }; @@ -33,12 +34,13 @@ gb_global char const *CallArgumentError_strings[CallArgumentError_MAX] = { "ParameterMissing", "DuplicateParameter", "NoneConstantParameter", + "OutOfOrderParameters", }; -enum CallArgumentErrorMode { - CallArgumentMode_NoErrors, - CallArgumentMode_ShowErrors, +enum struct CallArgumentErrorMode { + NoErrors, + ShowErrors, }; struct CallArgumentData { @@ -65,11 +67,6 @@ gb_internal int valid_index_and_score_cmp(void const *a, void const *b) { -#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(CheckerContext *c, Ast *call, Type *proc_type, Entity *entity, Array operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data) -typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType); - - - gb_internal void check_expr (CheckerContext *c, Operand *operand, Ast *expression); gb_internal void check_multi_expr (CheckerContext *c, Operand *operand, Ast *expression); gb_internal void check_multi_expr_or_type (CheckerContext *c, Operand *operand, Ast *expression); @@ -94,14 +91,13 @@ gb_internal void check_stmt (CheckerContext *c, Ast *nod gb_internal void check_stmt_list (CheckerContext *c, Slice const &stmts, u32 flags); gb_internal void check_init_constant (CheckerContext *c, Entity *e, Operand *operand); gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Type *type, ExactValue *out_value); -gb_internal bool check_procedure_type (CheckerContext *c, Type *type, Ast *proc_type_node, Array *operands = nullptr); +gb_internal bool check_procedure_type (CheckerContext *c, Type *type, Ast *proc_type_node, Array const *operands = nullptr); gb_internal void check_struct_type (CheckerContext *c, Type *struct_type, Ast *node, Array *poly_operands, Type *named_type = nullptr, Type *original_type_for_poly = nullptr); gb_internal void check_union_type (CheckerContext *c, Type *union_type, Ast *node, Array *poly_operands, Type *named_type = nullptr, Type *original_type_for_poly = nullptr); -gb_internal CallArgumentData check_call_arguments (CheckerContext *c, Operand *operand, Type *proc_type, Ast *call); -gb_internal Type * check_init_variable (CheckerContext *c, Entity *e, Operand *operand, String context_name); +gb_internal Type * check_init_variable (CheckerContext *c, Entity *e, Operand *operand, String context_name); gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type); @@ -121,6 +117,7 @@ gb_internal void check_or_return_split_types(CheckerContext *c, Operand *x, Stri gb_internal bool is_diverging_expr(Ast *expr); +gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_); enum LoadDirectiveResult { LoadDirective_Success = 0, @@ -335,7 +332,7 @@ gb_internal void check_scope_decls(CheckerContext *c, Slice const &nodes, } gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_entity, Type *type, - Array *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) { + Array const *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) { /////////////////////////////////////////////////////////////////////////////// // // // TODO CLEANUP(bill): This procedure is very messy and hacky. Clean this!!! // @@ -602,7 +599,7 @@ gb_internal bool check_polymorphic_procedure_assignment(CheckerContext *c, Opera return find_or_generate_polymorphic_procedure(c, base_entity, type, nullptr, poly_def_node, poly_proc_data); } -gb_internal bool find_or_generate_polymorphic_procedure_from_parameters(CheckerContext *c, Entity *base_entity, Array *operands, Ast *poly_def_node, PolyProcData *poly_proc_data) { +gb_internal bool find_or_generate_polymorphic_procedure_from_parameters(CheckerContext *c, Entity *base_entity, Array const *operands, Ast *poly_def_node, PolyProcData *poly_proc_data) { return find_or_generate_polymorphic_procedure(c, base_entity, nullptr, operands, poly_def_node, poly_proc_data); } @@ -677,6 +674,11 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand return 1; } break; + case Basic_UntypedString: + if (is_type_string(dst)) { + return 1; + } + break; case Basic_UntypedFloat: if (is_type_float(dst)) { return 1; @@ -701,23 +703,49 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand } return -1; } - if (src->kind == Type_Basic && src->Basic.kind == Basic_UntypedRune) { - if (is_type_integer(dst) || is_type_rune(dst)) { - if (is_type_typed(type)) { - return 2; + if (src->kind == Type_Basic) { + i64 score = -1; + switch (src->Basic.kind) { + case Basic_UntypedRune: + if (is_type_integer(dst) || is_type_rune(dst)) { + score = 1; } - return 1; - } - return -1; - } - if (src->kind == Type_Basic && src->Basic.kind == Basic_UntypedBool) { - if (is_type_boolean(dst)) { - if (is_type_typed(type)) { - return 2; + break; + case Basic_UntypedInteger: + if (is_type_integer(dst) || is_type_rune(dst)) { + score = 1; } - return 1; + break; + case Basic_UntypedString: + if (is_type_string(dst)) { + score = 1; + } + break; + case Basic_UntypedFloat: + if (is_type_float(dst)) { + score = 1; + } + break; + case Basic_UntypedComplex: + if (is_type_complex(dst)) { + score = 1; + } + if (is_type_quaternion(dst)) { + score = 2; + } + break; + case Basic_UntypedQuaternion: + if (is_type_quaternion(dst)) { + score = 1; + } + break; } - return -1; + if (score > 0) { + if (is_type_typed(dst)) { + score += 1; + } + } + return score; } } } @@ -5150,14 +5178,19 @@ enum UnpackFlag : u32 { }; -gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array *operands, Slice const &rhs, UnpackFlags flags) { +gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array *operands, Slice const &rhs_arguments, UnpackFlags flags) { bool allow_ok = (flags & UnpackFlag_AllowOk) != 0; bool is_variadic = (flags & UnpackFlag_IsVariadic) != 0; bool allow_undef = (flags & UnpackFlag_AllowUndef) != 0; bool optional_ok = false; isize tuple_index = 0; - for_array(i, rhs) { + for (Ast *rhs : rhs_arguments) { + if (rhs->kind == Ast_FieldValue) { + error(rhs, "Invalid use of 'field = value'"); + rhs = rhs->FieldValue.value; + } + CheckerContext c_ = *ctx; CheckerContext *c = &c_; @@ -5165,12 +5198,11 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize Type *type_hint = nullptr; + if (lhs != nullptr && tuple_index < lhs_count) { // NOTE(bill): override DeclInfo for dependency Entity *e = lhs[tuple_index]; if (e != nullptr) { - // DeclInfo *decl = decl_info_of_entity(e); - // if (decl) c->decl = decl; type_hint = e->type; if (e->flags & EntityFlag_Ellipsis) { GB_ASSERT(is_type_slice(e->type)); @@ -5182,8 +5214,6 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize // NOTE(bill): override DeclInfo for dependency Entity *e = lhs[lhs_count-1]; if (e != nullptr) { - // DeclInfo *decl = decl_info_of_entity(e); - // if (decl) c->decl = decl; type_hint = e->type; if (e->flags & EntityFlag_Ellipsis) { GB_ASSERT(is_type_slice(e->type)); @@ -5193,15 +5223,15 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize } } - Ast *rhs_expr = unparen_expr(rhs[i]); + Ast *rhs_expr = unparen_expr(rhs); if (allow_undef && rhs_expr != nullptr && rhs_expr->kind == Ast_Uninit) { // NOTE(bill): Just handle this very specific logic here o.type = t_untyped_uninit; o.mode = Addressing_Value; - o.expr = rhs[i]; - add_type_and_value(c, rhs[i], o.mode, o.type, o.value); + o.expr = rhs; + add_type_and_value(c, rhs, o.mode, o.type, o.value); } else { - check_expr_base(c, &o, rhs[i], type_hint); + check_expr_base(c, &o, rhs, type_hint); } if (o.mode == Addressing_NoValue) { error_operand_no_value(&o); @@ -5209,7 +5239,7 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize } if (o.type == nullptr || o.type->kind != Type_Tuple) { - if (allow_ok && lhs_count == 2 && rhs.count == 1 && + if (allow_ok && lhs_count == 2 && rhs_arguments.count == 1 && (o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk || o.mode == Addressing_OptionalOkPtr)) { Ast *expr = unparen_expr(o.expr); @@ -5309,7 +5339,36 @@ gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize * } -gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { +gb_internal isize lookup_procedure_parameter(TypeProc *pt, String const ¶meter_name) { + isize param_count = pt->param_count; + for (isize i = 0; i < param_count; i++) { + Entity *e = pt->params->Tuple.variables[i]; + String name = e->token.string; + if (is_blank_ident(name)) { + continue; + } + if (name == parameter_name) { + return i; + } + } + return -1; +} + +gb_internal isize lookup_procedure_parameter(Type *type, String const ¶meter_name) { + type = base_type(type); + GB_ASSERT(type->kind == Type_Proc); + return lookup_procedure_parameter(&type->Proc, parameter_name); +} + +gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, Ast *call, + Entity *entity, Type *proc_type, + Array positional_operands, Array const &named_operands, + CallArgumentErrorMode show_error_mode, + CallArgumentData *data) { + TEMPORARY_ALLOCATOR_GUARD(); + + CallArgumentError err = CallArgumentError_None; + ast_node(ce, CallExpr, call); GB_ASSERT(is_type_proc(proc_type)); proc_type = base_type(proc_type); @@ -5320,16 +5379,8 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { bool variadic = pt->variadic; bool vari_expand = (ce->ellipsis.pos.line != 0); i64 score = 0; - bool show_error = show_error_mode == CallArgumentMode_ShowErrors; + bool show_error = show_error_mode == CallArgumentErrorMode::ShowErrors; - - TypeTuple *param_tuple = nullptr; - if (pt->params != nullptr) { - param_tuple = &pt->params->Tuple; - } - - - CallArgumentError err = CallArgumentError_None; Type *final_proc_type = proc_type; Entity *gen_entity = nullptr; @@ -5347,192 +5398,304 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { LIT(ce->proc->Ident.token.string)); } err = CallArgumentError_NonVariadicExpand; - } else if (operands.count == 0 && param_count_excluding_defaults == 0) { + } + + GB_ASSERT(ce->split_args); + auto visited = slice_make(temporary_allocator(), pt->param_count); + auto ordered_operands = array_make(temporary_allocator(), pt->param_count); + defer ({ + for (Operand const &o : ordered_operands) { + if (o.expr != nullptr) { + call->viral_state_flags |= o.expr->viral_state_flags; + } + } + }); + + isize positional_operand_count = positional_operands.count; + if (variadic) { + positional_operand_count = gb_min(positional_operands.count, pt->variadic_index); + } else if (positional_operand_count > pt->param_count) { + err = CallArgumentError_TooManyArguments; + char const *err_fmt = "Too many arguments for '%s', expected %td arguments, got %td"; + if (show_error) { + gbString proc_str = expr_to_string(ce->proc); + defer (gb_string_free(proc_str)); + error(call, err_fmt, proc_str, param_count_excluding_defaults, positional_operands.count); + } + return err; + } + positional_operand_count = gb_min(positional_operand_count, pt->param_count); + + for (isize i = 0; i < positional_operand_count; i++) { + ordered_operands[i] = positional_operands[i]; + visited[i] = true; + } + + auto variadic_operands = slice(slice_from_array(positional_operands), positional_operand_count, positional_operands.count); + + if (named_operands.count != 0) { + GB_ASSERT(ce->split_args->named.count == named_operands.count); + for_array(i, ce->split_args->named) { + Ast *arg = ce->split_args->named[i]; + Operand operand = named_operands[i]; + + ast_node(fv, FieldValue, arg); + if (fv->field->kind != Ast_Ident) { + if (show_error) { + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name '%s' in procedure call", expr_str); + gb_string_free(expr_str); + } + err = CallArgumentError_InvalidFieldValue; + continue; + } + String name = fv->field->Ident.token.string; + isize param_index = lookup_procedure_parameter(pt, name); + if (param_index < 0) { + if (show_error) { + error(arg, "No parameter named '%.*s' for this procedure type", LIT(name)); + } + err = CallArgumentError_ParameterNotFound; + continue; + } + if (visited[param_index]) { + if (show_error) { + error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name)); + } + err = CallArgumentError_DuplicateParameter; + continue; + } + + visited[param_index] = true; + ordered_operands[param_index] = operand; + } + } + + isize dummy_argument_count = 0; + bool actually_variadic = false; + + if (variadic) { + if (visited[pt->variadic_index] && + positional_operand_count < positional_operands.count) { + if (show_error) { + String name = pt->params->Tuple.variables[pt->variadic_index]->token.string; + error(call, "Variadic parameters already handled with a named argument '%.*s' in procedure call", LIT(name)); + } + err = CallArgumentError_DuplicateParameter; + } else if (!visited[pt->variadic_index]) { + visited[pt->variadic_index] = true; + + Operand *variadic_operand = &ordered_operands[pt->variadic_index]; + + if (vari_expand) { + GB_ASSERT(variadic_operands.count != 0); + *variadic_operand = variadic_operands[0]; + variadic_operand->type = default_type(variadic_operand->type); + actually_variadic = true; + } else { + AstFile *f = call->file(); + + // HACK(bill): this is an awful hack + Operand o = {}; + o.mode = Addressing_Value; + o.expr = ast_ident(f, make_token_ident("nil")); + o.expr->Ident.token.pos = ast_token(call).pos; + if (variadic_operands.count != 0) { + actually_variadic = true; + o.expr->Ident.token.pos = ast_token(variadic_operands[0].expr).pos; + + Entity *vt = pt->params->Tuple.variables[pt->variadic_index]; + if (is_type_polymorphic(vt->type)) { + o.type = alloc_type_slice(default_type(variadic_operands[0].type)); + } else { + o.type = vt->type; + } + } else { + dummy_argument_count += 1; + o.type = t_untyped_nil; + } + *variadic_operand = o; + } + } + + } + + for (Operand const &o : ordered_operands) { + if (o.mode != Addressing_Invalid) { + check_no_copy_assignment(o, str_lit("procedure call expression")); + } + } + + for (isize i = 0; i < pt->param_count; i++) { + if (!visited[i]) { + Entity *e = pt->params->Tuple.variables[i]; + if (is_blank_ident(e->token)) { + continue; + } + if (e->kind == Entity_Variable) { + if (e->Variable.param_value.kind != ParameterValue_Invalid) { + ordered_operands[i].mode = Addressing_Value; + ordered_operands[i].type = e->type; + ordered_operands[i].expr = e->Variable.param_value.original_ast_expr; + + dummy_argument_count += 1; + score += assign_score_function(1); + continue; + } + } + + if (show_error) { + if (e->kind == Entity_TypeName) { + error(call, "Type parameter '%.*s' is missing in procedure call", + LIT(e->token.string)); + } else if (e->kind == Entity_Constant && e->Constant.value.kind != ExactValue_Invalid) { + // Ignore + } else { + gbString str = type_to_string(e->type); + error(call, "Parameter '%.*s' of type '%s' is missing in procedure call", + LIT(e->token.string), str); + gb_string_free(str); + } + } + err = CallArgumentError_ParameterMissing; + } + } + + auto eval_param_and_score = [](CheckerContext *c, Operand *o, Type *param_type, CallArgumentError &err, bool param_is_variadic, Entity *e, bool show_error) -> i64 { + i64 s = 0; + if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic)) { + bool ok = false; + if (e && e->flags & EntityFlag_AnyInt) { + if (is_type_integer(param_type)) { + ok = check_is_castable_to(c, o, param_type); + } + } + if (ok) { + s = assign_score_function(MAXIMUM_TYPE_DISTANCE); + } else { + if (show_error) { + check_assignment(c, o, param_type, str_lit("procedure argument")); + } + err = CallArgumentError_WrongTypes; + } + + } else if (show_error) { + check_assignment(c, o, param_type, str_lit("procedure argument")); + } + + if (e && e->flags & EntityFlag_ConstInput) { + if (o->mode != Addressing_Constant) { + if (show_error) { + error(o->expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string)); + } + err = CallArgumentError_NoneConstantParameter; + } + } + + + if (!err && is_type_any(param_type)) { + add_type_info_type(c, o->type); + } + if (o->mode == Addressing_Type && is_type_typeid(param_type)) { + add_type_info_type(c, o->type); + add_type_and_value(c, o->expr, Addressing_Value, param_type, exact_value_typeid(o->type)); + } else if (show_error && is_type_untyped(o->type)) { + update_untyped_expr_type(c, o->expr, param_type, true); + } + + return s; + }; + + + if (ordered_operands.count == 0 && param_count_excluding_defaults == 0) { err = CallArgumentError_None; if (variadic) { - GB_ASSERT(param_tuple != nullptr && param_tuple->variables.count > 0); - Type *t = param_tuple->variables[0]->type; + GB_ASSERT(pt->params != nullptr && pt->params->Tuple.variables.count > 0); + Type *t = pt->params->Tuple.variables[0]->type; if (is_type_polymorphic(t)) { - error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input"); + if (show_error) { + error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input"); + } err = CallArgumentError_AmbiguousPolymorphicVariadic; } } } else { - i32 error_code = 0; - if (operands.count < param_count_excluding_defaults) { - error_code = -1; - } else if (!variadic && operands.count > param_count) { - error_code = +1; + if (pt->is_polymorphic && !pt->is_poly_specialized && err == CallArgumentError_None) { + PolyProcData poly_proc_data = {}; + if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &ordered_operands, call, &poly_proc_data)) { + gen_entity = poly_proc_data.gen_entity; + Type *gept = base_type(gen_entity->type); + GB_ASSERT(is_type_proc(gept)); + final_proc_type = gen_entity->type; + pt = &gept->Proc; + + } else { + err = CallArgumentError_WrongTypes; + } } - if (error_code != 0) { - err = CallArgumentError_TooManyArguments; - char const *err_fmt = "Too many arguments for '%s', expected %td arguments, got %td"; - if (error_code < 0) { - err = CallArgumentError_TooFewArguments; - err_fmt = "Too few arguments for '%s', expected %td arguments, got %td"; + + for (isize i = 0; i < pt->param_count; i++) { + Operand *o = &ordered_operands[i]; + if (o->mode == Addressing_Invalid) { + continue; } - if (show_error) { - gbString proc_str = expr_to_string(ce->proc); - defer (gb_string_free(proc_str)); - error(call, err_fmt, proc_str, param_count_excluding_defaults, operands.count); + Entity *e = pt->params->Tuple.variables[i]; + bool param_is_variadic = pt->variadic && pt->variadic_index == i; - #if 0 - error_line("\t"); - for_array(i, operands) { - if (i > 0) { - error_line(", "); + if (e->kind == Entity_TypeName) { + GB_ASSERT(pt->is_polymorphic); + if (o->mode != Addressing_Type) { + if (show_error) { + error(o->expr, "Expected a type for the argument '%.*s'", LIT(e->token.string)); } - gbString s = expr_to_string(operands[i].expr); - error_line("%s", s); - gb_string_free(s); - } - error_line("\n"); - #endif - } - } else { - // NOTE(bill): Generate the procedure type for this generic instance - if (pt->is_polymorphic && !pt->is_poly_specialized) { - PolyProcData poly_proc_data = {}; - if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &operands, call, &poly_proc_data)) { - gen_entity = poly_proc_data.gen_entity; - GB_ASSERT(is_type_proc(gen_entity->type)); - final_proc_type = gen_entity->type; - } else { err = CallArgumentError_WrongTypes; } + if (are_types_identical(e->type, o->type)) { + score += assign_score_function(1); + } else { + score += assign_score_function(MAXIMUM_TYPE_DISTANCE); + } + continue; } - GB_ASSERT(is_type_proc(final_proc_type)); - TypeProc *pt = &final_proc_type->Proc; - - GB_ASSERT(pt->params != nullptr); - auto sig_params = pt->params->Tuple.variables; - isize operand_index = 0; - isize max_operand_count = gb_min(param_count, operands.count); - for (; operand_index < max_operand_count; operand_index++) { - Entity *e = sig_params[operand_index]; - Type *t = e->type; - Operand o = operands[operand_index]; - if (o.expr != nullptr) { - call->viral_state_flags |= o.expr->viral_state_flags; - } - - if (e->kind == Entity_TypeName) { - // GB_ASSERT(!variadic); - if (o.mode == Addressing_Invalid) { - continue; - } else if (o.mode != Addressing_Type) { - if (show_error) { - error(o.expr, "Expected a type for the argument '%.*s'", LIT(e->token.string)); - } - err = CallArgumentError_WrongTypes; - } - - if (are_types_identical(e->type, o.type)) { - score += assign_score_function(1); - } else { - score += assign_score_function(MAXIMUM_TYPE_DISTANCE); - } - - continue; - } - - bool param_is_variadic = pt->variadic && pt->variadic_index == operand_index; - - i64 s = 0; - if (!check_is_assignable_to_with_score(c, &o, t, &s, param_is_variadic)) { - bool ok = false; - if (e->flags & EntityFlag_AnyInt) { - if (is_type_integer(t)) { - ok = check_is_castable_to(c, &o, t); - } - } - if (ok) { - s = assign_score_function(MAXIMUM_TYPE_DISTANCE); - } else { - if (show_error) { - check_assignment(c, &o, t, str_lit("argument")); - } - // TODO(bill, 2021-05-05): Is this incorrect logic to only fail if there is ambiguity for definite? - if (o.mode == Addressing_Invalid) { - err = CallArgumentError_WrongTypes; - } - } - } else if (show_error) { - check_assignment(c, &o, t, str_lit("argument")); - } - score += s; - - if (e->flags & EntityFlag_ConstInput) { - if (o.mode != Addressing_Constant) { - if (show_error) { - error(o.expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string)); - } - err = CallArgumentError_NoneConstantParameter; - } - } - - if (o.mode == Addressing_Type && is_type_typeid(e->type)) { - add_type_info_type(c, o.type); - add_type_and_value(c, o.expr, Addressing_Value, e->type, exact_value_typeid(o.type)); - } else if (show_error && is_type_untyped(o.type)) { - update_untyped_expr_type(c, o.expr, t, true); - } - + if (param_is_variadic) { + continue; } - if (variadic) { - bool variadic_expand = false; - Type *slice = sig_params[param_count]->type; - GB_ASSERT(is_type_slice(slice)); - Type *elem = base_type(slice)->Slice.elem; - Type *t = elem; + score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error); + } + } - if (is_type_polymorphic(t)) { - error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input"); - err = CallArgumentError_AmbiguousPolymorphicVariadic; - } + if (variadic) { + Type *slice = pt->params->Tuple.variables[pt->variadic_index]->type; + GB_ASSERT(is_type_slice(slice)); + Type *elem = base_type(slice)->Slice.elem; + Type *t = elem; - for (; operand_index < operands.count; operand_index++) { - Operand o = operands[operand_index]; - if (vari_expand) { - variadic_expand = true; - t = slice; - if (operand_index != param_count) { - if (show_error) { - error(o.expr, "'..' in a variadic procedure can only have one variadic argument at the end"); - } - if (data) { - data->score = score; - data->result_type = final_proc_type->Proc.results; - data->gen_entity = gen_entity; - } - return CallArgumentError_MultipleVariadicExpand; - } + if (is_type_polymorphic(t)) { + error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input %s", type_to_string(final_proc_type)); + err = CallArgumentError_AmbiguousPolymorphicVariadic; + } + + for_array(operand_index, variadic_operands) { + Operand *o = &variadic_operands[operand_index]; + if (vari_expand) { + t = slice; + if (operand_index > 0) { + if (show_error) { + error(o->expr, "'..' in a variadic procedure can only have one variadic argument at the end"); } - i64 s = 0; - if (!check_is_assignable_to_with_score(c, &o, t, &s, true)) { - if (show_error) { - check_assignment(c, &o, t, str_lit("argument")); - } - err = CallArgumentError_WrongTypes; - } else if (show_error) { - check_assignment(c, &o, t, str_lit("argument")); - } - score += s; - if (is_type_any(elem)) { - add_type_info_type(c, o.type); - } - if (o.mode == Addressing_Type && is_type_typeid(t)) { - add_type_info_type(c, o.type); - add_type_and_value(c, o.expr, Addressing_Value, t, exact_value_typeid(o.type)); - } else if (show_error && is_type_untyped(o.type)) { - update_untyped_expr_type(c, o.expr, t, true); + if (data) { + data->score = score; + data->result_type = final_proc_type->Proc.results; + data->gen_entity = gen_entity; } + return CallArgumentError_MultipleVariadicExpand; } } + score += eval_param_and_score(c, o, t, err, true, nullptr, show_error); } } @@ -5566,203 +5729,6 @@ gb_internal bool is_call_expr_field_value(AstCallExpr *ce) { return ce->args[0]->kind == Ast_FieldValue; } -gb_internal isize lookup_procedure_parameter(TypeProc *pt, String parameter_name) { - isize param_count = pt->param_count; - for (isize i = 0; i < param_count; i++) { - Entity *e = pt->params->Tuple.variables[i]; - String name = e->token.string; - if (is_blank_ident(name)) { - continue; - } - if (name == parameter_name) { - return i; - } - } - return -1; -} - -gb_internal CALL_ARGUMENT_CHECKER(check_named_call_arguments) { - ast_node(ce, CallExpr, call); - GB_ASSERT(is_type_proc(proc_type)); - proc_type = base_type(proc_type); - TypeProc *pt = &proc_type->Proc; - - i64 score = 0; - bool show_error = show_error_mode == CallArgumentMode_ShowErrors; - CallArgumentError err = CallArgumentError_None; - - TEMPORARY_ALLOCATOR_GUARD(); - - isize param_count = pt->param_count; - bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count); - auto ordered_operands = array_make(temporary_allocator(), param_count); - defer ({ - for (Operand const &o : ordered_operands) { - if (o.expr != nullptr) { - call->viral_state_flags |= o.expr->viral_state_flags; - } - } - }); - - for_array(i, ce->args) { - Ast *arg = ce->args[i]; - ast_node(fv, FieldValue, arg); - if (fv->field->kind != Ast_Ident) { - if (show_error) { - gbString expr_str = expr_to_string(fv->field); - error(arg, "Invalid parameter name '%s' in procedure call", expr_str); - gb_string_free(expr_str); - } - err = CallArgumentError_InvalidFieldValue; - continue; - } - String name = fv->field->Ident.token.string; - isize index = lookup_procedure_parameter(pt, name); - if (index < 0) { - if (show_error) { - error(arg, "No parameter named '%.*s' for this procedure type", LIT(name)); - } - err = CallArgumentError_ParameterNotFound; - continue; - } - if (visited[index]) { - if (show_error) { - error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name)); - } - err = CallArgumentError_DuplicateParameter; - continue; - } - - visited[index] = true; - ordered_operands[index] = operands[i]; - } - - // NOTE(bill): Check for default values and missing parameters - isize param_count_to_check = param_count; - if (pt->variadic) { - param_count_to_check--; - } - for (isize i = 0; i < param_count_to_check; i++) { - if (!visited[i]) { - Entity *e = pt->params->Tuple.variables[i]; - if (is_blank_ident(e->token)) { - continue; - } - if (e->kind == Entity_Variable) { - if (e->Variable.param_value.kind != ParameterValue_Invalid) { - score += assign_score_function(1); - continue; - } - } - - if (show_error) { - if (e->kind == Entity_TypeName) { - error(call, "Type parameter '%.*s' is missing in procedure call", - LIT(e->token.string)); - } else if (e->kind == Entity_Constant && e->Constant.value.kind != ExactValue_Invalid) { - // Ignore - } else { - gbString str = type_to_string(e->type); - error(call, "Parameter '%.*s' of type '%s' is missing in procedure call", - LIT(e->token.string), str); - gb_string_free(str); - } - } - err = CallArgumentError_ParameterMissing; - } - } - - Entity *gen_entity = nullptr; - if (pt->is_polymorphic && !pt->is_poly_specialized && err == CallArgumentError_None) { - PolyProcData poly_proc_data = {}; - if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &ordered_operands, call, &poly_proc_data)) { - gen_entity = poly_proc_data.gen_entity; - Type *gept = base_type(gen_entity->type); - GB_ASSERT(is_type_proc(gept)); - proc_type = gept; - pt = &gept->Proc; - } else { - err = CallArgumentError_WrongTypes; - } - } - - - for (isize i = 0; i < param_count; i++) { - Entity *e = pt->params->Tuple.variables[i]; - Operand *o = &ordered_operands[i]; - bool param_is_variadic = pt->variadic && pt->variadic_index == i; - - - if (o->mode == Addressing_Invalid) { - if (param_is_variadic) { - Type *slice = e->type; - GB_ASSERT(is_type_slice(slice)); - Type *elem = base_type(slice)->Slice.elem; - if (is_type_polymorphic(elem)) { - error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input"); - err = CallArgumentError_AmbiguousPolymorphicVariadic; - return err; - } - } - continue; - } - - if (e->kind == Entity_TypeName) { - GB_ASSERT(pt->is_polymorphic); - if (o->mode != Addressing_Type) { - if (show_error) { - error(o->expr, "Expected a type for the argument '%.*s'", LIT(e->token.string)); - } - err = CallArgumentError_WrongTypes; - } - if (are_types_identical(e->type, o->type)) { - score += assign_score_function(1); - } else { - score += assign_score_function(MAXIMUM_TYPE_DISTANCE); - } - } else { - i64 s = 0; - if (!check_is_assignable_to_with_score(c, o, e->type, &s, param_is_variadic)) { - bool ok = false; - if (ok) { - s = assign_score_function(MAXIMUM_TYPE_DISTANCE); - } else { - if (show_error) { - check_assignment(c, o, e->type, str_lit("procedure argument")); - } - err = CallArgumentError_WrongTypes; - } - - if (e->flags & EntityFlag_ConstInput) { - if (o->mode != Addressing_Constant) { - if (show_error) { - error(o->expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string)); - } - err = CallArgumentError_NoneConstantParameter; - } - } - } else if (show_error) { - check_assignment(c, o, e->type, str_lit("procedure argument")); - } - score += s; - } - - if (o->mode == Addressing_Type && is_type_typeid(e->type)) { - add_type_info_type(c, o->type); - add_type_and_value(c, o->expr, Addressing_Value, e->type, exact_value_typeid(o->type)); - } - } - - if (data) { - data->score = score; - data->result_type = pt->results; - data->gen_entity = gen_entity; - add_type_and_value(c, ce->proc, Addressing_Value, proc_type, {}); - } - - return err; -} - gb_internal Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize *lhs_count_, bool *is_variadic) { Entity **lhs = nullptr; isize lhs_count = -1; @@ -5867,535 +5833,658 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco return true; } +gb_internal bool check_named_arguments(CheckerContext *c, Type *type, Slice const &named_args, Array *named_operands, bool show_error) { + bool success = true; -gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call, Slice const &args) { - ast_node(ce, CallExpr, call); - - CallArgumentCheckerType *call_checker = check_call_arguments_internal; - Array operands = {}; - defer (array_free(&operands)); - - Type *result_type = t_invalid; - - if (is_call_expr_field_value(ce)) { - call_checker = check_named_call_arguments; - - operands = array_make(heap_allocator(), args.count); - - // NOTE(bill): This is give type hints for the named parameters - // in order to improve the type inference system - - StringMap type_hint_map = {}; // Key: String - string_map_init(&type_hint_map, 2*args.count); - defer (string_map_destroy(&type_hint_map)); - - Type *ptype = nullptr; - bool single_case = true; - - if (operand->mode == Addressing_ProcGroup) { - single_case = false; - Array procs = proc_group_entities(c, *operand); - if (procs.count == 1) { - ptype = procs[0]->type; - single_case = true; - } - } else { - ptype = proc_type; + type = base_type(type); + if (named_args.count > 0) { + TypeProc *pt = nullptr; + if (is_type_proc(type)) { + pt = &type->Proc; } - if (single_case) { - Type *bptype = base_type(ptype); - if (is_type_proc(bptype)) { - TypeProc *pt = &bptype->Proc; - TypeTuple *param_tuple = nullptr; - if (pt->params != nullptr) { - param_tuple = &pt->params->Tuple; - } - if (param_tuple != nullptr) { - for (Entity *e : param_tuple->variables) { - if (is_blank_ident(e->token)) { - continue; - } - string_map_set(&type_hint_map, e->token.string, e->type); - } + for_array(i, named_args) { + Ast *arg = named_args[i]; + if (arg->kind != Ast_FieldValue) { + if (show_error) { + error(arg, "Expected a 'field = value'"); } + return false; } - } else { - Array procs = proc_group_entities(c, *operand); - for (Entity *proc : procs) { - Type *proc_type = base_type(proc->type); - if (is_type_proc(proc_type)) { - TypeProc *pt = &proc_type->Proc; - TypeTuple *param_tuple = nullptr; - if (pt->params != nullptr) { - param_tuple = &pt->params->Tuple; - } - if (param_tuple == nullptr) { - continue; - } - for (Entity *e : param_tuple->variables) { - if (is_blank_ident(e->token)) { - continue; - } - StringHashKey key = string_hash_string(e->token.string); - Type **found = string_map_get(&type_hint_map, key); - if (found) { - Type *t = *found; - if (t == nullptr) { - // NOTE(bill): Ambiguous named parameter across all types - continue; - } - if (are_types_identical(t, e->type)) { - // NOTE(bill): No need to set again - } else { - // NOTE(bill): Ambiguous named parameter across all types so set it to a nullptr - string_map_set(&type_hint_map, key, cast(Type *)nullptr); - } - } else { - string_map_set(&type_hint_map, key, e->type); - } - } - } - } - - } - - - for_array(i, args) { - Ast *arg = args[i]; ast_node(fv, FieldValue, arg); - Ast *field = fv->field; + if (fv->field->kind != Ast_Ident) { + if (show_error) { + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name '%s' in procedure call", expr_str); + gb_string_free(expr_str); + } + success = false; + continue; + } + String key = fv->field->Ident.token.string; + Ast *value = fv->value; Type *type_hint = nullptr; - - if (field != nullptr && field->kind == Ast_Ident) { - String key = field->Ident.token.string; - Type **found = string_map_get(&type_hint_map, key); - if (found) { - type_hint = *found; + if (pt) { + isize param_index = lookup_procedure_parameter(pt, key); + if (param_index < 0) { + if (show_error) { + error(value, "No parameter named '%.*s' for this procedure type", LIT(key)); + } + success = false; + continue; } + + Entity *e = pt->params->Tuple.variables[param_index]; + if (!is_type_polymorphic(e->type)) { + type_hint = e->type; + } + } - check_expr_or_type(c, &operands[i], fv->value, type_hint); + Operand o = {}; + check_expr_with_type_hint(c, &o, value, type_hint); + if (o.mode == Addressing_Invalid) { + success = false; + } + array_add(named_operands, o); } - } else { - operands = array_make(heap_allocator(), 0, 2*args.count); - Entity **lhs = nullptr; - isize lhs_count = -1; - bool is_variadic = false; - if (proc_type != nullptr && is_type_proc(proc_type)) { - lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic); - } - if (operand->mode != Addressing_ProcGroup) { - check_unpack_arguments(c, lhs, lhs_count, &operands, args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); + + } + return success; +} + +gb_internal bool check_call_arguments_single(CheckerContext *c, Ast *call, Operand *operand, + Entity *e, Type *proc_type, + Array const &positional_operands, Array const &named_operands, + CallArgumentErrorMode show_error_mode, + CallArgumentData *data) { + + bool return_on_failure = show_error_mode == CallArgumentErrorMode::NoErrors; + + Ast *ident = operand->expr; + while (ident->kind == Ast_SelectorExpr) { + Ast *s = ident->SelectorExpr.selector; + ident = s; + } + + if (e == nullptr) { + e = entity_of_node(ident); + if (e != nullptr) { + proc_type = e->type; } } - for (Operand const &o : operands) { - check_no_copy_assignment(o, str_lit("call expression")); + GB_ASSERT(proc_type != nullptr); + proc_type = base_type(proc_type); + GB_ASSERT(proc_type->kind == Type_Proc); + + CallArgumentError err = check_call_arguments_internal(c, call, e, proc_type, positional_operands, named_operands, show_error_mode, data); + if (return_on_failure && err != CallArgumentError_None) { + return false; } - if (operand->mode == Addressing_ProcGroup) { - check_entity_decl(c, operand->proc_group, nullptr, nullptr); + Entity *entity_to_use = data->gen_entity != nullptr ? data->gen_entity : e; + if (!return_on_failure && entity_to_use != nullptr) { + add_entity_use(c, ident, entity_to_use); + update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); + add_type_and_value(c, operand->expr, operand->mode, entity_to_use->type, operand->value); + } - auto procs = proc_group_entities_cloned(c, *operand); + if (data->gen_entity != nullptr) { + Entity *e = data->gen_entity; + DeclInfo *decl = data->gen_entity->decl_info; + CheckerContext ctx = *c; + ctx.scope = decl->scope; + ctx.decl = decl; + ctx.proc_name = e->token.string; + ctx.curr_proc_decl = decl; + ctx.curr_proc_sig = e->type; - if (procs.count > 1) { - isize max_arg_count = args.count; - for (Ast *arg : args) { - // NOTE(bill): The only thing that may have multiple values - // will be a call expression (assuming `or_return` and `()` will be stripped) - arg = strip_or_return_expr(arg); + GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); + bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, !return_on_failure); + if (return_on_failure) { + if (!ok) { + return false; + } + + } else { + decl->where_clauses_evaluated = true; + if (ok && (data->gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { + check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); + } + if (is_type_proc(data->gen_entity->type)) { + Type *t = base_type(entity_to_use->type); + data->result_type = t->Proc.results; + } + } + } + + return true; +} + + +gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, Operand *operand, Ast *call) { + ast_node(ce, CallExpr, call); + GB_ASSERT(ce->split_args != nullptr); + + Slice const &positional_args = ce->split_args->positional; + Slice const &named_args = ce->split_args->named; + + CallArgumentData data = {}; + data.result_type = t_invalid; + + GB_ASSERT(operand->mode == Addressing_ProcGroup); + auto procs = proc_group_entities_cloned(c, *operand); + + if (procs.count > 1) { + isize max_arg_count = positional_args.count + named_args.count; + for (Ast *arg : positional_args) { + // NOTE(bill): The only thing that may have multiple values + // will be a call expression (assuming `or_return` and `()` will be stripped) + arg = strip_or_return_expr(arg); + if (arg && arg->kind == Ast_CallExpr) { + max_arg_count = ISIZE_MAX; + break; + } + } + if (max_arg_count != ISIZE_MAX) for (Ast *arg : named_args) { + // NOTE(bill): The only thing that may have multiple values + // will be a call expression (assuming `or_return` and `()` will be stripped) + if (arg->kind == Ast_FieldValue) { + arg = strip_or_return_expr(arg->FieldValue.value); if (arg && arg->kind == Ast_CallExpr) { max_arg_count = ISIZE_MAX; break; } } + } - for (isize proc_index = 0; proc_index < procs.count; /**/) { - Entity *proc = procs[proc_index]; - Type *pt = base_type(proc->type); - if (!(pt != nullptr && is_type_proc(pt))) { - proc_index++; - continue; + // ignore named arguments first + for (Ast *arg : named_args) { + if (arg->kind != Ast_FieldValue) { + continue; + } + ast_node(fv, FieldValue, arg); + if (fv->field->kind != Ast_Ident) { + continue; + } + String key = fv->field->Ident.token.string; + for (isize proc_index = procs.count-1; proc_index >= 0; proc_index--) { + Type *t = procs[proc_index]->type; + if (is_type_proc(t)) { + isize param_index = lookup_procedure_parameter(t, key); + if (param_index < 0) { + array_unordered_remove(&procs, proc_index); + } } + } + } - isize param_count = 0; - isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, ¶m_count); + if (procs.count == 0) { + // if any of the named arguments are wrong, the `procs` will be empty + // just start from scratch + array_free(&procs); + procs = proc_group_entities_cloned(c, *operand); + } - if (param_count_excluding_defaults > max_arg_count) { - array_unordered_remove(&procs, proc_index); + // filter by positional argument length + for (isize proc_index = 0; proc_index < procs.count; /**/) { + Entity *proc = procs[proc_index]; + Type *pt = base_type(proc->type); + if (!(pt != nullptr && is_type_proc(pt))) { + proc_index++; + continue; + } + + isize param_count = 0; + isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, ¶m_count); + + if (param_count_excluding_defaults > max_arg_count) { + array_unordered_remove(&procs, proc_index); + continue; + } + proc_index++; + } + } + + Entity **lhs = nullptr; + isize lhs_count = -1; + bool is_variadic = false; + + auto positional_operands = array_make(heap_allocator(), 0, 0); + auto named_operands = array_make(heap_allocator(), 0, 0); + defer (array_free(&positional_operands)); + defer (array_free(&named_operands)); + + if (procs.count == 1) { + Entity *e = procs[0]; + + lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic); + check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); + + if (check_named_arguments(c, e->type, named_args, &named_operands, true)) { + check_call_arguments_single(c, call, operand, + e, e->type, + positional_operands, named_operands, + CallArgumentErrorMode::ShowErrors, + &data); + } + return data; + } + + { + // NOTE(bill, 2019-07-13): This code is used to improve the type inference for procedure groups + // where the same positional parameter has the same type value (and ellipsis) + bool proc_arg_count_all_equal = true; + isize proc_arg_count = -1; + for (Entity *p : procs) { + Type *pt = base_type(p->type); + if (pt != nullptr && is_type_proc(pt)) { + if (proc_arg_count < 0) { + proc_arg_count = pt->Proc.param_count; } else { - proc_index++; + if (proc_arg_count != pt->Proc.param_count) { + proc_arg_count_all_equal = false; + break; + } } } } - if (procs.count == 1) { - Ast *ident = operand->expr; - while (ident->kind == Ast_SelectorExpr) { - Ast *s = ident->SelectorExpr.selector; - ident = s; - } + if (proc_arg_count >= 0 && proc_arg_count_all_equal) { + lhs_count = proc_arg_count; + if (lhs_count > 0) { + lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count); + for (isize param_index = 0; param_index < lhs_count; param_index++) { + Entity *e = nullptr; + for (Entity *p : procs) { + Type *pt = base_type(p->type); + if (!(pt != nullptr && is_type_proc(pt))) { + continue; + } - Entity *e = procs[0]; - - Entity **lhs = nullptr; - isize lhs_count = -1; - bool is_variadic = false; - lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic); - check_unpack_arguments(c, lhs, lhs_count, &operands, args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); - - CallArgumentData data = {}; - CallArgumentError err = call_checker(c, call, e->type, e, operands, CallArgumentMode_ShowErrors, &data); - if (err != CallArgumentError_None) { - // handle error - } - Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; - add_entity_use(c, ident, entity_to_use); - if (entity_to_use != nullptr) { - update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); - } - return data; - } - - - Entity **lhs = nullptr; - isize lhs_count = -1; - - { - // NOTE(bill, 2019-07-13): This code is used to improve the type inference for procedure groups - // where the same positional parameter has the same type value (and ellipsis) - bool proc_arg_count_all_equal = true; - isize proc_arg_count = -1; - for (Entity *p : procs) { - Type *pt = base_type(p->type); - if (pt != nullptr && is_type_proc(pt)) { - if (proc_arg_count < 0) { - proc_arg_count = pt->Proc.param_count; - } else { - if (proc_arg_count != pt->Proc.param_count) { - proc_arg_count_all_equal = false; + if (e == nullptr) { + e = pt->Proc.params->Tuple.variables[param_index]; + } else { + Entity *f = pt->Proc.params->Tuple.variables[param_index]; + if (e == f) { + continue; + } + if (are_types_identical(e->type, f->type)) { + bool ee = (e->flags & EntityFlag_Ellipsis) != 0; + bool fe = (f->flags & EntityFlag_Ellipsis) != 0; + if (ee == fe) { + continue; + } + } + // NOTE(bill): Entities are not close enough to be used + e = nullptr; break; } } - } - } - - - - if (proc_arg_count >= 0 && proc_arg_count_all_equal) { - lhs_count = proc_arg_count; - if (lhs_count > 0) { - lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count); - for (isize param_index = 0; param_index < lhs_count; param_index++) { - Entity *e = nullptr; - for (Entity *p : procs) { - Type *pt = base_type(p->type); - if (pt != nullptr && is_type_proc(pt)) { - if (e == nullptr) { - e = pt->Proc.params->Tuple.variables[param_index]; - } else { - Entity *f = pt->Proc.params->Tuple.variables[param_index]; - if (e == f) { - continue; - } - if (are_types_identical(e->type, f->type)) { - bool ee = (e->flags & EntityFlag_Ellipsis) != 0; - bool fe = (f->flags & EntityFlag_Ellipsis) != 0; - if (ee == fe) { - continue; - } - } - // NOTE(bill): Entities are not close enough to be used - e = nullptr; - break; - } - } - } - lhs[param_index] = e; - } + lhs[param_index] = e; } } } + } + check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); - check_unpack_arguments(c, lhs, lhs_count, &operands, args, UnpackFlag_None); - - if (lhs != nullptr) { - gb_free(heap_allocator(), lhs); - } - - auto valids = array_make(heap_allocator(), 0, procs.count); - defer (array_free(&valids)); - - auto proc_entities = array_make(heap_allocator(), 0, procs.count*2 + 1); - defer (array_free(&proc_entities)); - for (Entity *proc : procs) { - array_add(&proc_entities, proc); - } - - - gbString expr_name = expr_to_string(operand->expr); - defer (gb_string_free(expr_name)); - - for_array(i, procs) { - Entity *p = procs[i]; - Type *pt = base_type(p->type); - if (pt != nullptr && is_type_proc(pt)) { - CallArgumentError err = CallArgumentError_None; - CallArgumentData data = {}; - CheckerContext ctx = *c; - - ctx.no_polymorphic_errors = true; - ctx.allow_polymorphic_types = is_type_polymorphic(pt); - ctx.hide_polymorphic_errors = true; - - err = call_checker(&ctx, call, pt, p, operands, CallArgumentMode_NoErrors, &data); - if (err != CallArgumentError_None) { - continue; - } - isize index = i; - - if (data.gen_entity != nullptr) { - Entity *e = data.gen_entity; - DeclInfo *decl = data.gen_entity->decl_info; - ctx.scope = decl->scope; - ctx.decl = decl; - ctx.proc_name = e->token.string; - ctx.curr_proc_decl = decl; - ctx.curr_proc_sig = e->type; - - GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); - if (!evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) { - continue; - } - - array_add(&proc_entities, data.gen_entity); - index = proc_entities.count-1; - } - - ValidIndexAndScore item = {}; - item.index = index; - item.score = data.score; - array_add(&valids, item); - } - } - - if (valids.count > 1) { - gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp); - i64 best_score = valids[0].score; - Entity *best_entity = proc_entities[valids[0].index]; - GB_ASSERT(best_entity != nullptr); - for (isize i = 1; i < valids.count; i++) { - if (best_score > valids[i].score) { - valids.count = i; - break; - } - if (best_entity == proc_entities[valids[i].index]) { - valids.count = i; - break; - } - } - } - - - if (valids.count == 0) { - begin_error_block(); - defer (end_error_block()); - - error(operand->expr, "No procedures or ambiguous call for procedure group '%s' that match with the given arguments", expr_name); - if (operands.count == 0) { - error_line("\tNo given arguments\n"); - } else { - error_line("\tGiven argument types: ("); - for_array(i, operands) { - Operand o = operands[i]; - if (i > 0) error_line(", "); - gbString type = type_to_string(o.type); - defer (gb_string_free(type)); - error_line("%s", type); - } - error_line(")\n"); - } - - if (procs.count > 0) { - error_line("Did you mean to use one of the following:\n"); - } - for (Entity *proc : procs) { - TokenPos pos = proc->token.pos; - Type *t = base_type(proc->type); - if (t == t_invalid) continue; - GB_ASSERT(t->kind == Type_Proc); - gbString pt; - defer (gb_string_free(pt)); - if (t->Proc.node != nullptr) { - pt = expr_to_string(t->Proc.node); - } else { - pt = type_to_string(t); - } - String prefix = {}; - String prefix_sep = {}; - if (proc->pkg) { - prefix = proc->pkg->name; - prefix_sep = str_lit("."); - } - String name = proc->token.string; - - char const *sep = "::"; - if (proc->kind == Entity_Variable) { - sep = ":="; - } - error_line("\t%.*s%.*s%.*s %s %s at %s\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, token_pos_to_string(pos)); - } - if (procs.count > 0) { - error_line("\n"); - } - - result_type = t_invalid; - } else if (valids.count > 1) { - begin_error_block(); - defer (end_error_block()); - - error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name); - error_line("\tGiven argument types: ("); - for_array(i, operands) { - Operand o = operands[i]; - if (i > 0) error_line(", "); - gbString type = type_to_string(o.type); - defer (gb_string_free(type)); - error_line("%s", type); - } - error_line(")\n"); - - for (isize i = 0; i < valids.count; i++) { - Entity *proc = proc_entities[valids[i].index]; - GB_ASSERT(proc != nullptr); - TokenPos pos = proc->token.pos; - Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc); - gbString pt = nullptr; - defer (gb_string_free(pt)); - if (t->Proc.node != nullptr) { - pt = expr_to_string(t->Proc.node); - } else { - pt = type_to_string(t); - } - String name = proc->token.string; - char const *sep = "::"; - if (proc->kind == Entity_Variable) { - sep = ":="; - } - error_line("\t%.*s %s %s ", LIT(name), sep, pt); - if (proc->decl_info->proc_lit != nullptr) { - GB_ASSERT(proc->decl_info->proc_lit->kind == Ast_ProcLit); - auto *pl = &proc->decl_info->proc_lit->ProcLit; - if (pl->where_token.kind != Token_Invalid) { - error_line("\n\t\twhere "); - for_array(j, pl->where_clauses) { - Ast *clause = pl->where_clauses[j]; - if (j != 0) { - error_line("\t\t "); - } - gbString str = expr_to_string(clause); - error_line("%s", str); - gb_string_free(str); - - if (j != pl->where_clauses.count-1) { - error_line(","); - } - } - error_line("\n\t"); - } - } - error_line("at %s\n", token_pos_to_string(pos)); - } - result_type = t_invalid; - } else { - GB_ASSERT(valids.count == 1); - Ast *ident = operand->expr; - while (ident->kind == Ast_SelectorExpr) { - Ast *s = ident->SelectorExpr.selector; - ident = s; - } - - Entity *e = proc_entities[valids[0].index]; - GB_ASSERT(e != nullptr); - - proc_type = e->type; - CallArgumentData data = {}; - CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data); - gb_unused(err); - Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; - add_entity_use(c, ident, entity_to_use); - if (entity_to_use != nullptr) { - update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); - } - - if (data.gen_entity != nullptr) { - Entity *e = data.gen_entity; - DeclInfo *decl = data.gen_entity->decl_info; - CheckerContext ctx = *c; - ctx.scope = decl->scope; - ctx.decl = decl; - ctx.proc_name = e->token.string; - ctx.curr_proc_decl = decl; - ctx.curr_proc_sig = e->type; - - GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); - bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); - decl->where_clauses_evaluated = true; - - if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { - check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); - } - } + for_array(i, named_args) { + Ast *arg = named_args[i]; + if (arg->kind != Ast_FieldValue) { + error(arg, "Expected a 'field = value'"); return data; } + ast_node(fv, FieldValue, arg); + if (fv->field->kind != Ast_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name '%s' in procedure call", expr_str); + gb_string_free(expr_str); + return data; + } + String key = fv->field->Ident.token.string; + Ast *value = fv->value; + + Type *type_hint = nullptr; + + for (isize lhs_idx = 0; lhs_idx < lhs_count; lhs_idx++) { + Entity *e = lhs[lhs_idx]; + if (e != nullptr && e->token.string == key && + !is_type_polymorphic(e->type)) { + type_hint = e->type; + break; + } + } + Operand o = {}; + check_expr_with_type_hint(c, &o, value, type_hint); + array_add(&named_operands, o); + } + + gb_free(heap_allocator(), lhs); + + auto valids = array_make(heap_allocator(), 0, procs.count); + defer (array_free(&valids)); + + auto proc_entities = array_make(heap_allocator(), 0, procs.count*2 + 1); + defer (array_free(&proc_entities)); + for (Entity *proc : procs) { + array_add(&proc_entities, proc); + } + + + gbString expr_name = expr_to_string(operand->expr); + defer (gb_string_free(expr_name)); + + for_array(i, procs) { + Entity *p = procs[i]; + Type *pt = base_type(p->type); + if (pt != nullptr && is_type_proc(pt)) { + CallArgumentData data = {}; + CheckerContext ctx = *c; + + ctx.no_polymorphic_errors = true; + ctx.allow_polymorphic_types = is_type_polymorphic(pt); + ctx.hide_polymorphic_errors = true; + + bool is_a_candidate = check_call_arguments_single(&ctx, call, operand, + p, pt, + positional_operands, named_operands, + CallArgumentErrorMode::NoErrors, + &data); + if (!is_a_candidate) { + continue; + } + isize index = i; + + if (data.gen_entity != nullptr) { + array_add(&proc_entities, data.gen_entity); + index = proc_entities.count-1; + } + + ValidIndexAndScore item = {}; + item.index = index; + item.score = data.score; + array_add(&valids, item); + } + } + + if (valids.count > 1) { + gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp); + i64 best_score = valids[0].score; + Entity *best_entity = proc_entities[valids[0].index]; + GB_ASSERT(best_entity != nullptr); + for (isize i = 1; i < valids.count; i++) { + if (best_score > valids[i].score) { + valids.count = i; + break; + } + if (best_entity == proc_entities[valids[i].index]) { + valids.count = i; + break; + } + } + } + + auto print_argument_types = [&]() { + error_line("\tGiven argument types: ("); + isize i = 0; + for (Operand const &o : positional_operands) { + if (i++ > 0) error_line(", "); + gbString type = type_to_string(o.type); + defer (gb_string_free(type)); + error_line("%s", type); + } + for (Operand const &o : named_operands) { + if (i++ > 0) error_line(", "); + + gbString type = type_to_string(o.type); + defer (gb_string_free(type)); + + if (i < ce->split_args->named.count) { + Ast *named_field = ce->split_args->named[i]; + ast_node(fv, FieldValue, named_field); + + gbString field = expr_to_string(fv->field); + defer (gb_string_free(field)); + + error_line("%s = %s", field, type); + } else { + error_line("%s", type); + } + } + error_line(")\n"); + }; + + if (valids.count == 0) { + begin_error_block(); + defer (end_error_block()); + + error(operand->expr, "No procedures or ambiguous call for procedure group '%s' that match with the given arguments", expr_name); + if (positional_operands.count == 0 && named_operands.count == 0) { + error_line("\tNo given arguments\n"); + } else { + print_argument_types(); + } + + if (procs.count > 0) { + error_line("Did you mean to use one of the following:\n"); + } + for (Entity *proc : procs) { + TokenPos pos = proc->token.pos; + Type *t = base_type(proc->type); + if (t == t_invalid) continue; + GB_ASSERT(t->kind == Type_Proc); + gbString pt; + defer (gb_string_free(pt)); + if (t->Proc.node != nullptr) { + pt = expr_to_string(t->Proc.node); + } else { + pt = type_to_string(t); + } + String prefix = {}; + String prefix_sep = {}; + if (proc->pkg) { + prefix = proc->pkg->name; + prefix_sep = str_lit("."); + } + String name = proc->token.string; + + char const *sep = "::"; + if (proc->kind == Entity_Variable) { + sep = ":="; + } + error_line("\t%.*s%.*s%.*s %s %s at %s\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, token_pos_to_string(pos)); + } + if (procs.count > 0) { + error_line("\n"); + } + + data.result_type = t_invalid; + } else if (valids.count > 1) { + begin_error_block(); + defer (end_error_block()); + + error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name); + print_argument_types(); + + for (isize i = 0; i < valids.count; i++) { + Entity *proc = proc_entities[valids[i].index]; + GB_ASSERT(proc != nullptr); + TokenPos pos = proc->token.pos; + Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc); + gbString pt = nullptr; + defer (gb_string_free(pt)); + if (t->Proc.node != nullptr) { + pt = expr_to_string(t->Proc.node); + } else { + pt = type_to_string(t); + } + String name = proc->token.string; + char const *sep = "::"; + if (proc->kind == Entity_Variable) { + sep = ":="; + } + error_line("\t%.*s %s %s ", LIT(name), sep, pt); + if (proc->decl_info->proc_lit != nullptr) { + GB_ASSERT(proc->decl_info->proc_lit->kind == Ast_ProcLit); + auto *pl = &proc->decl_info->proc_lit->ProcLit; + if (pl->where_token.kind != Token_Invalid) { + error_line("\n\t\twhere "); + for_array(j, pl->where_clauses) { + Ast *clause = pl->where_clauses[j]; + if (j != 0) { + error_line("\t\t "); + } + gbString str = expr_to_string(clause); + error_line("%s", str); + gb_string_free(str); + + if (j != pl->where_clauses.count-1) { + error_line(","); + } + } + error_line("\n\t"); + } + } + error_line("at %s\n", token_pos_to_string(pos)); + } + data.result_type = t_invalid; } else { + GB_ASSERT(valids.count == 1); Ast *ident = operand->expr; while (ident->kind == Ast_SelectorExpr) { Ast *s = ident->SelectorExpr.selector; ident = s; } - Entity *e = entity_of_node(ident); + Entity *e = proc_entities[valids[0].index]; + GB_ASSERT(e != nullptr); + Array named_operands = {}; - CallArgumentData data = {}; - CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data); - gb_unused(err); - Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; - add_entity_use(c, ident, entity_to_use); - if (entity_to_use != nullptr) { - update_untyped_expr_type(c, operand->expr, entity_to_use->type, true); - } - if (data.gen_entity != nullptr) { - Entity *e = data.gen_entity; - DeclInfo *decl = data.gen_entity->decl_info; - CheckerContext ctx = *c; - ctx.scope = decl->scope; - ctx.decl = decl; - ctx.proc_name = e->token.string; - ctx.curr_proc_decl = decl; - ctx.curr_proc_sig = e->type; - - GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); - bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); - decl->where_clauses_evaluated = true; - - if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) { - check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags); - } - } + check_call_arguments_single(c, call, operand, + e, e->type, + positional_operands, named_operands, + CallArgumentErrorMode::ShowErrors, + &data); return data; } - - CallArgumentData data = {}; - data.result_type = t_invalid; return data; } +gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Ast *call) { + Type *proc_type = nullptr; + + CallArgumentData data = {}; + data.result_type = t_invalid; + + proc_type = base_type(operand->type); + + TypeProc *pt = nullptr; + if (proc_type) { + pt = &proc_type->Proc; + } + + TEMPORARY_ALLOCATOR_GUARD(); + ast_node(ce, CallExpr, call); + + bool any_failure = false; + + // Split positional and named args into separate arrays/slices + Slice positional_args = {}; + Slice named_args = {}; + + if (ce->split_args == nullptr) { + positional_args = ce->args; + for (isize i = 0; i < ce->args.count; i++) { + Ast *arg = ce->args.data[i]; + if (arg->kind == Ast_FieldValue) { + positional_args.count = i; + break; + } + } + named_args = slice(ce->args, positional_args.count, ce->args.count); + + auto split_args = gb_alloc_item(permanent_allocator(), AstSplitArgs); + split_args->positional = positional_args; + split_args->named = named_args; + ce->split_args = split_args; + } else { + positional_args = ce->split_args->positional; + named_args = ce->split_args->named; + } + + if (operand->mode == Addressing_ProcGroup) { + return check_call_arguments_proc_group(c, operand, call); + } + + auto positional_operands = array_make(heap_allocator(), 0, positional_args.count); + auto named_operands = array_make(heap_allocator(), 0, 0); + + defer (array_free(&positional_operands)); + defer (array_free(&named_operands)); + + if (positional_args.count > 0) { + isize lhs_count = -1; + bool is_variadic = false; + Entity **lhs = nullptr; + if (pt != nullptr) { + lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic); + } + check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None); + } + + if (named_args.count > 0) { + for_array(i, named_args) { + Ast *arg = named_args[i]; + if (arg->kind != Ast_FieldValue) { + error(arg, "Expected a 'field = value'"); + return data; + } + ast_node(fv, FieldValue, arg); + if (fv->field->kind != Ast_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name '%s' in procedure call", expr_str); + any_failure = true; + gb_string_free(expr_str); + continue; + } + String key = fv->field->Ident.token.string; + Ast *value = fv->value; + + isize param_index = lookup_procedure_parameter(pt, key); + Type *type_hint = nullptr; + if (param_index >= 0) { + Entity *e = pt->params->Tuple.variables[param_index]; + type_hint = e->type; + } + + Operand o = {}; + check_expr_with_type_hint(c, &o, value, type_hint); + if (o.mode == Addressing_Invalid) { + any_failure = true; + } + array_add(&named_operands, o); + } + + } + + if (!any_failure) { + check_call_arguments_single(c, call, operand, + nullptr, proc_type, + positional_operands, named_operands, + CallArgumentErrorMode::ShowErrors, + &data); + } else if (pt) { + data.result_type = pt->results; + } + + return data; +} + gb_internal isize lookup_polymorphic_record_parameter(Type *t, String parameter_name) { if (!is_type_polymorphic_record(t)) { return -1; @@ -6740,6 +6829,46 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O +// returns true on success +gb_internal bool check_call_parameter_mixture(Slice const &args, char const *context, bool allow_mixed=false) { + bool success = true; + if (args.count > 0) { + if (allow_mixed) { + bool was_named = false; + for (Ast *arg : args) { + if (was_named && arg->kind != Ast_FieldValue) { + error(arg, "Non-named parameter is not allowed to follow named parameter i.e. 'field = value' in a %s", context); + success = false; + break; + } + was_named = was_named || arg->kind == Ast_FieldValue; + } + } else { + bool first_is_field_value = (args[0]->kind == Ast_FieldValue); + for (Ast *arg : args) { + bool mix = false; + if (first_is_field_value) { + mix = arg->kind != Ast_FieldValue; + } else { + mix = arg->kind == Ast_FieldValue; + } + if (mix) { + error(arg, "Mixture of 'field = value' and value elements in a %s is not allowed", context); + success = false; + } + } + } + + } + return success; +} + +#define CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN(context_, ...) if (!check_call_parameter_mixture(args, context_, ##__VA_ARGS__)) { \ + operand->mode = Addressing_Invalid; \ + operand->expr = call; \ + return Expr_Stmt; \ +} + gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice const &args, ProcInlining inlining, Type *type_hint) { if (proc != nullptr && @@ -6779,30 +6908,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } - if (args.count > 0) { - bool fail = false; - bool first_is_field_value = (args[0]->kind == Ast_FieldValue); - for (Ast *arg : args) { - bool mix = false; - if (first_is_field_value) { - mix = arg->kind != Ast_FieldValue; - } else { - mix = arg->kind == Ast_FieldValue; - } - if (mix) { - error(arg, "Mixture of 'field = value' and value elements in a procedure call is not allowed"); - fail = true; - } - } - - if (fail) { - operand->mode = Addressing_Invalid; - operand->expr = call; - return Expr_Stmt; - } - } - if (operand->mode == Addressing_Invalid) { + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure call"); for (Ast *arg : args) { if (arg->kind == Ast_FieldValue) { arg = arg->FieldValue.value; @@ -6817,6 +6924,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c if (operand->mode == Addressing_Type) { Type *t = operand->type; if (is_type_polymorphic_record(t)) { + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("polymorphic type construction"); + if (!is_type_named(t)) { gbString s = expr_to_string(operand->expr); error(call, "Illegal use of an unnamed polymorphic record, %s", s); @@ -6842,6 +6951,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c operand->type = t_invalid; } } else { + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("type conversion"); + operand->mode = Addressing_Invalid; isize arg_count = args.count; switch (arg_count) { @@ -6887,6 +6998,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } if (operand->mode == Addressing_Builtin) { + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("builtin call"); + i32 id = operand->builtin_id; Entity *e = entity_of_node(operand->expr); if (e != nullptr && e->token.string == "expand_to_tuple") { @@ -6900,6 +7013,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c return builtin_procs[id].kind; } + CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN(operand->mode == Addressing_ProcGroup ? "procedure group call": "procedure call", true); + Entity *initial_entity = entity_of_node(operand->expr); if (initial_entity != nullptr && initial_entity->kind == Entity_Procedure) { @@ -6911,8 +7026,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } - Type *proc_type = base_type(operand->type); if (operand->mode != Addressing_ProcGroup) { + Type *proc_type = base_type(operand->type); bool valid_type = (proc_type != nullptr) && is_type_proc(proc_type); bool valid_mode = is_operand_value(*operand); if (!valid_type || !valid_mode) { @@ -6930,7 +7045,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } - CallArgumentData data = check_call_arguments(c, operand, proc_type, call, args); + CallArgumentData data = check_call_arguments(c, operand, call); Type *result_type = data.result_type; gb_zero_item(operand); operand->expr = call; @@ -6941,7 +7056,10 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c return Expr_Stmt; } - Type *pt = base_type(proc_type); + Type *pt = base_type(operand->type); + if (pt == nullptr) { + pt = t_invalid; + } if (pt == t_invalid) { if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) { pt = type_of_expr(operand->expr->CallExpr.proc); @@ -6986,7 +7104,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c if (decl->proc_lit) { ast_node(pl, ProcLit, decl->proc_lit); if (pl->inlining == ProcInlining_no_inline) { - error(call, "'inline' cannot be applied to a procedure that has be marked as 'no_inline'"); + error(call, "'#force_inline' cannot be applied to a procedure that has be marked as '#force_no_inline'"); } } } @@ -6999,9 +7117,6 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c operand->expr = call; { - if (proc_type == t_invalid) { - // gb_printf_err("%s\n", expr_to_string(operand->expr)); - } Type *type = nullptr; if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) { type = type_of_expr(operand->expr->CallExpr.proc); @@ -7019,8 +7134,6 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } - // add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value); - return Expr_Expr; } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 09af496ab..cf6f998e5 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2207,7 +2207,13 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { } else if (operands.count != result_count) { // Ignore error message as it has most likely already been reported if (all_operands_valid(operands)) { - error(node, "Expected %td return values, got %td", result_count, operands.count); + if (operands.count == 1) { + gbString t = type_to_string(operands[0].type); + error(node, "Expected %td return values, got %td (%s)", result_count, operands.count, t); + gb_string_free(t); + } else { + error(node, "Expected %td return values, got %td", result_count, operands.count); + } } } else { for (isize i = 0; i < result_count; i++) { diff --git a/src/check_type.cpp b/src/check_type.cpp index a69dcdadc..a68f83ba9 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1416,7 +1416,7 @@ gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_ } -gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is_variadic_, isize *variadic_index_, bool *success_, isize *specialization_count_, Array *operands) { +gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is_variadic_, isize *variadic_index_, bool *success_, isize *specialization_count_, Array const *operands) { if (_params == nullptr) { return nullptr; } @@ -1664,7 +1664,6 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para ExactValue poly_const = {}; if (operands != nullptr && variables.count < operands->count) { - Operand op = (*operands)[variables.count]; if (op.expr == nullptr) { // NOTE(bill): 2019-03-30 @@ -1967,7 +1966,7 @@ gb_internal Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_res // NOTE(bill): 'operands' is for generating non generic procedure type -gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, Array *operands) { +gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, Array const *operands) { ast_node(pt, ProcType, proc_type_node); if (ctx->polymorphic_scope == nullptr && ctx->allow_polymorphic_types) { diff --git a/src/error.cpp b/src/error.cpp index defc2593f..eb010eb36 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -265,7 +265,8 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { defer (gb_string_free(the_line)); if (the_line != nullptr) { - String line = make_string(cast(u8 const *)the_line, gb_string_length(the_line)); + char const *line_text = the_line; + isize line_len = gb_string_length(the_line); // TODO(bill): This assumes ASCII @@ -285,21 +286,27 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { isize squiggle_extra = 0; - if (line.len > MAX_LINE_LENGTH_PADDED) { + if (line_len > MAX_LINE_LENGTH_PADDED) { i32 left = MAX_TAB_WIDTH; - line.text += offset-left; - line.len -= offset-left; - offset = left+MAX_TAB_WIDTH/2; - if (line.len > MAX_LINE_LENGTH_PADDED) { - line.len = MAX_LINE_LENGTH_PADDED; - if (error_length > line.len-left) { - error_length = cast(i32)line.len - left; + if (offset > 0) { + line_text += offset-left; + line_len -= offset-left; + offset = left+MAX_TAB_WIDTH/2; + } + if (line_len > MAX_LINE_LENGTH_PADDED) { + line_len = MAX_LINE_LENGTH_PADDED; + if (error_length > line_len-left) { + error_length = cast(i32)line_len - left; squiggle_extra = 1; } } - error_out("... %.*s ...", LIT(line)); + if (offset > 0) { + error_out("... %.*s ...", cast(i32)line_len, line_text); + } else { + error_out("%.*s ...", cast(i32)line_len, line_text); + } } else { - error_out("%.*s", LIT(line)); + error_out("%.*s", cast(i32)line_len, line_text); } error_out("\n\t"); @@ -312,7 +319,7 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { error_out("^"); if (end.file_id == pos.file_id) { if (end.line > pos.line) { - for (i32 i = offset; i < line.len; i++) { + for (i32 i = offset; i < line_len; i++) { error_out("~"); } } else if (end.line == pos.line && end.column > pos.column) { diff --git a/src/gb/gb.h b/src/gb/gb.h index bc4c1f27d..3d4bff9b4 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -3299,12 +3299,39 @@ void const *gb_memrchr(void const *data, u8 c, isize n) { -gb_inline void *gb_alloc_align (gbAllocator a, isize size, isize alignment) { return a.proc(a.data, gbAllocation_Alloc, size, alignment, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); } -gb_inline void *gb_alloc (gbAllocator a, isize size) { return gb_alloc_align(a, size, GB_DEFAULT_MEMORY_ALIGNMENT); } -gb_inline void gb_free (gbAllocator a, void *ptr) { if (ptr != NULL) a.proc(a.data, gbAllocation_Free, 0, 0, ptr, 0, GB_DEFAULT_ALLOCATOR_FLAGS); } -gb_inline void gb_free_all (gbAllocator a) { a.proc(a.data, gbAllocation_FreeAll, 0, 0, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); } -gb_inline void *gb_resize (gbAllocator a, void *ptr, isize old_size, isize new_size) { return gb_resize_align(a, ptr, old_size, new_size, GB_DEFAULT_MEMORY_ALIGNMENT); } -gb_inline void *gb_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment) { return a.proc(a.data, gbAllocation_Resize, new_size, alignment, ptr, old_size, GB_DEFAULT_ALLOCATOR_FLAGS); } +gb_inline void *gb_alloc_align (gbAllocator a, isize size, isize alignment) { + if (size == 0) { + return NULL; + } + return a.proc(a.data, gbAllocation_Alloc, size, alignment, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); +} +gb_inline void *gb_alloc(gbAllocator a, isize size) { + return gb_alloc_align(a, size, GB_DEFAULT_MEMORY_ALIGNMENT); +} +gb_inline void gb_free(gbAllocator a, void *ptr) { + if (ptr != NULL) { + a.proc(a.data, gbAllocation_Free, 0, 0, ptr, 0, GB_DEFAULT_ALLOCATOR_FLAGS); + } +} +gb_inline void gb_free_all(gbAllocator a) { + a.proc(a.data, gbAllocation_FreeAll, 0, 0, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); +} +gb_inline void *gb_resize(gbAllocator a, void *ptr, isize old_size, isize new_size) { + return gb_resize_align(a, ptr, old_size, new_size, GB_DEFAULT_MEMORY_ALIGNMENT); +} +gb_inline void *gb_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment) { + if (new_size == 0) { + if (ptr != NULL) { + return a.proc(a.data, gbAllocation_Free, 0, 0, ptr, old_size, GB_DEFAULT_ALLOCATOR_FLAGS); + } + return NULL; + } else if (ptr == NULL) { + return a.proc(a.data, gbAllocation_Alloc, new_size, alignment, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); + } else if (old_size == new_size && ((uintptr)ptr % (uintptr)alignment) == 0) { + return ptr; + } + return a.proc(a.data, gbAllocation_Resize, new_size, alignment, ptr, old_size, GB_DEFAULT_ALLOCATOR_FLAGS); +} gb_inline void *gb_alloc_copy (gbAllocator a, void const *src, isize size) { return gb_memcopy(gb_alloc(a, size), src, size); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 8b9f8b249..47137c540 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1160,7 +1160,13 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array c } - Entity **found = map_get(&p->module->procedure_values, value.value); + LLVMValueRef the_proc_value = value.value; + + if (LLVMIsAConstantExpr(the_proc_value)) { + // NOTE(bill): it's a bit cast + the_proc_value = LLVMGetOperand(the_proc_value, 0); + } + Entity **found = map_get(&p->module->procedure_values, the_proc_value); if (found != nullptr) { Entity *e = *found; if (e != nullptr && entity_has_deferred_procedure(e)) { @@ -3145,6 +3151,18 @@ gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) { } return res; } + +gb_internal void lb_add_values_to_array(lbProcedure *p, Array *args, lbValue value) { + if (is_type_tuple(value.type)) { + for_array(i, value.type->Tuple.variables) { + lbValue sub_value = lb_emit_struct_ev(p, value, cast(i32)i); + array_add(args, sub_value); + } + } else { + array_add(args, value); + } +} + gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { lbModule *m = p->module; @@ -3219,245 +3237,143 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { GB_ASSERT(proc_type_->kind == Type_Proc); TypeProc *pt = &proc_type_->Proc; - if (is_call_expr_field_value(ce)) { - auto args = array_make(permanent_allocator(), pt->param_count); + GB_ASSERT(ce->split_args != nullptr); - for_array(arg_index, ce->args) { - Ast *arg = ce->args[arg_index]; - ast_node(fv, FieldValue, arg); - GB_ASSERT(fv->field->kind == Ast_Ident); - String name = fv->field->Ident.token.string; - isize index = lookup_procedure_parameter(pt, name); - GB_ASSERT(index >= 0); - TypeAndValue tav = type_and_value_of_expr(fv->value); - if (tav.mode == Addressing_Type) { - args[index] = lb_const_nil(m, tav.type); - } else { - args[index] = lb_build_expr(p, fv->value); - } - } - TypeTuple *params = &pt->params->Tuple; - for (isize i = 0; i < args.count; i++) { - Entity *e = params->variables[i]; - if (e->kind == Entity_TypeName) { - args[i] = lb_const_nil(m, e->type); - } else if (e->kind == Entity_Constant) { - continue; - } else { - GB_ASSERT(e->kind == Entity_Variable); - if (args[i].value == nullptr) { - args[i] = lb_handle_param_value(p, e->type, e->Variable.param_value, ast_token(expr).pos); - } else if (is_type_typeid(e->type) && !is_type_typeid(args[i].type)) { - args[i] = lb_typeid(p->module, args[i].type); - } else { - args[i] = lb_emit_conv(p, args[i], e->type); - } - } - } + auto args = array_make(permanent_allocator(), 0, pt->param_count); - for (isize i = 0; i < args.count; i++) { - Entity *e = params->variables[i]; - if (args[i].type == nullptr) { - continue; - } else if (is_type_untyped_uninit(args[i].type)) { - args[i] = lb_const_undef(m, e->type); - } else if (is_type_untyped_nil(args[i].type)) { - args[i] = lb_const_nil(m, e->type); - } - } - - return lb_emit_call(p, value, args, ce->inlining); - } - - isize arg_index = 0; - - isize arg_count = 0; - for_array(i, ce->args) { - Ast *arg = ce->args[i]; - TypeAndValue tav = type_and_value_of_expr(arg); - GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s %d", expr_to_string(arg), expr_to_string(expr), tav.mode); - GB_ASSERT_MSG(tav.mode != Addressing_ProcGroup, "%s", expr_to_string(arg)); - Type *at = tav.type; - if (is_type_tuple(at)) { - arg_count += at->Tuple.variables.count; - } else { - arg_count++; - } - } - - isize param_count = 0; - if (pt->params) { - GB_ASSERT(pt->params->kind == Type_Tuple); - param_count = pt->params->Tuple.variables.count; - } - - auto args = array_make(permanent_allocator(), cast(isize)gb_max(param_count, arg_count)); - isize variadic_index = pt->variadic_index; - bool variadic = pt->variadic && variadic_index >= 0; - bool vari_expand = ce->ellipsis.pos.line != 0; + bool vari_expand = (ce->ellipsis.pos.line != 0); bool is_c_vararg = pt->c_vararg; - String proc_name = {}; - if (p->entity != nullptr) { - proc_name = p->entity->token.string; + for_array(i, ce->split_args->positional) { + Entity *e = pt->params->Tuple.variables[i]; + if (e->kind == Entity_TypeName) { + array_add(&args, lb_const_nil(p->module, e->type)); + continue; + } else if (e->kind == Entity_Constant) { + array_add(&args, lb_const_value(p->module, e->type, e->Constant.value)); + continue; + } + + GB_ASSERT(e->kind == Entity_Variable); + + if (pt->variadic && pt->variadic_index == i) { + lbValue variadic_args = lb_const_nil(p->module, e->type); + auto variadic = slice(ce->split_args->positional, pt->variadic_index, ce->split_args->positional.count); + if (variadic.count != 0) { + // variadic call argument generation + Type *slice_type = e->type; + GB_ASSERT(slice_type->kind == Type_Slice); + + if (is_c_vararg) { + GB_ASSERT(!vari_expand); + + Type *elem_type = slice_type->Slice.elem; + + for (Ast *var_arg : variadic) { + lbValue arg = lb_build_expr(p, var_arg); + if (is_type_any(elem_type)) { + array_add(&args, lb_emit_conv(p, arg, default_type(arg.type))); + } else { + array_add(&args, lb_emit_conv(p, arg, elem_type)); + } + } + break; + } else if (vari_expand) { + GB_ASSERT(variadic.count == 1); + variadic_args = lb_build_expr(p, variadic[0]); + variadic_args = lb_emit_conv(p, variadic_args, slice_type); + } else { + Type *elem_type = slice_type->Slice.elem; + + auto var_args = array_make(heap_allocator(), 0, variadic.count); + defer (array_free(&var_args)); + for (Ast *var_arg : variadic) { + lbValue v = lb_build_expr(p, var_arg); + lb_add_values_to_array(p, &var_args, v); + } + isize slice_len = var_args.count; + if (slice_len > 0) { + lbAddr slice = lb_add_local_generated(p, slice_type, true); + lbAddr base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true); + + for (isize i = 0; i < var_args.count; i++) { + lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)i); + lbValue var_arg = var_args[i]; + var_arg = lb_emit_conv(p, var_arg, elem_type); + lb_emit_store(p, addr, var_arg); + } + + lbValue base_elem = lb_emit_array_epi(p, base_array.addr, 0); + lbValue len = lb_const_int(p->module, t_int, slice_len); + lb_fill_slice(p, slice, base_elem, len); + + variadic_args = lb_addr_load(p, slice); + } + } + } + array_add(&args, variadic_args); + + break; + } else { + lbValue value = lb_build_expr(p, ce->split_args->positional[i]); + lb_add_values_to_array(p, &args, value); + } } + + if (!is_c_vararg) { + array_resize(&args, pt->param_count); + } + + for (Ast *arg : ce->split_args->named) { + ast_node(fv, FieldValue, arg); + GB_ASSERT(fv->field->kind == Ast_Ident); + String name = fv->field->Ident.token.string; + gb_unused(name); + isize param_index = lookup_procedure_parameter(pt, name); + GB_ASSERT(param_index >= 0); + + lbValue value = lb_build_expr(p, fv->value); + GB_ASSERT(!is_type_tuple(value.type)); + args[param_index] = value; + } + TokenPos pos = ast_token(ce->proc).pos; - TypeTuple *param_tuple = nullptr; - if (pt->params) { - GB_ASSERT(pt->params->kind == Type_Tuple); - param_tuple = &pt->params->Tuple; - } - for_array(i, ce->args) { - Ast *arg = ce->args[i]; - TypeAndValue arg_tv = type_and_value_of_expr(arg); - if (arg_tv.mode == Addressing_Type) { - args[arg_index++] = lb_const_nil(m, arg_tv.type); - } else { - lbValue a = lb_build_expr(p, arg); - Type *at = a.type; - if (at->kind == Type_Tuple) { - lbTupleFix *tf = map_get(&p->tuple_fix_map, a.value); - if (tf) { - for_array(j, tf->values) { - args[arg_index++] = tf->values[j]; - } - } else { - for_array(j, at->Tuple.variables) { - lbValue v = lb_emit_struct_ev(p, a, cast(i32)j); - args[arg_index++] = v; - } + if (pt->params != nullptr) { + GB_ASSERT(args.count >= pt->params->Tuple.variables.count); + for_array(arg_index, pt->params->Tuple.variables) { + Entity *e = pt->params->Tuple.variables[arg_index]; + if (pt->variadic && arg_index == pt->variadic_index) { + if (!is_c_vararg && args[arg_index].value == 0) { + args[arg_index] = lb_const_nil(p->module, e->type); } - } else { - args[arg_index++] = a; - } - } - } - - - if (param_count > 0) { - GB_ASSERT_MSG(pt->params != nullptr, "%s %td", expr_to_string(expr), pt->param_count); - GB_ASSERT(param_count < 1000000); - - if (arg_count < param_count) { - isize end = cast(isize)param_count; - if (variadic) { - end = variadic_index; - } - while (arg_index < end) { - Entity *e = param_tuple->variables[arg_index]; - GB_ASSERT(e->kind == Entity_Variable); - args[arg_index++] = lb_handle_param_value(p, e->type, e->Variable.param_value, ast_token(expr).pos); - } - } - - if (is_c_vararg) { - GB_ASSERT(variadic); - GB_ASSERT(!vari_expand); - isize i = 0; - for (; i < variadic_index; i++) { - Entity *e = param_tuple->variables[i]; - if (e->kind == Entity_Variable) { - args[i] = lb_emit_conv(p, args[i], e->type); - } - } - Type *variadic_type = param_tuple->variables[i]->type; - GB_ASSERT(is_type_slice(variadic_type)); - variadic_type = base_type(variadic_type)->Slice.elem; - if (!is_type_any(variadic_type)) { - for (; i < arg_count; i++) { - args[i] = lb_emit_conv(p, args[i], variadic_type); - } - } else { - for (; i < arg_count; i++) { - args[i] = lb_emit_conv(p, args[i], default_type(args[i].type)); - } - } - } else if (variadic) { - isize i = 0; - for (; i < variadic_index; i++) { - Entity *e = param_tuple->variables[i]; - if (e->kind == Entity_Variable) { - args[i] = lb_emit_conv(p, args[i], e->type); - } - } - if (!vari_expand) { - Type *variadic_type = param_tuple->variables[i]->type; - GB_ASSERT(is_type_slice(variadic_type)); - variadic_type = base_type(variadic_type)->Slice.elem; - for (; i < arg_count; i++) { - args[i] = lb_emit_conv(p, args[i], variadic_type); - } - } - } else { - for (isize i = 0; i < param_count; i++) { - Entity *e = param_tuple->variables[i]; - if (e->kind == Entity_Variable) { - if (args[i].value == nullptr) { - continue; - } - GB_ASSERT_MSG(args[i].value != nullptr, "%.*s", LIT(e->token.string)); - if (is_type_typeid(e->type) && !is_type_typeid(args[i].type)) { - GB_ASSERT(LLVMIsNull(args[i].value)); - args[i] = lb_typeid(p->module, args[i].type); - } else { - args[i] = lb_emit_conv(p, args[i], e->type); - } - } - } - } - - if (variadic && !vari_expand && !is_c_vararg) { - // variadic call argument generation - Type *slice_type = param_tuple->variables[variadic_index]->type; - Type *elem_type = base_type(slice_type)->Slice.elem; - lbAddr slice = lb_add_local_generated(p, slice_type, true); - isize slice_len = arg_count+1 - (variadic_index+1); - - if (slice_len > 0) { - lbAddr base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true); - - for (isize i = variadic_index, j = 0; i < arg_count; i++, j++) { - lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)j); - lb_emit_store(p, addr, args[i]); - } - - lbValue base_elem = lb_emit_array_epi(p, base_array.addr, 0); - lbValue len = lb_const_int(m, t_int, slice_len); - lb_fill_slice(p, slice, base_elem, len); - } - - arg_count = param_count; - args[variadic_index] = lb_addr_load(p, slice); - } - } - - if (variadic && variadic_index+1 < param_count) { - for (isize i = variadic_index+1; i < param_count; i++) { - Entity *e = param_tuple->variables[i]; - args[i] = lb_handle_param_value(p, e->type, e->Variable.param_value, ast_token(expr).pos); - } - } - - isize final_count = param_count; - if (is_c_vararg) { - final_count = arg_count; - } - - if (param_tuple != nullptr) { - for (isize i = 0; i < gb_min(args.count, param_tuple->variables.count); i++) { - Entity *e = param_tuple->variables[i]; - if (args[i].type == nullptr) { continue; - } else if (is_type_untyped_uninit(args[i].type)) { - args[i] = lb_const_undef(m, e->type); - } else if (is_type_untyped_nil(args[i].type)) { - args[i] = lb_const_nil(m, e->type); + } + + lbValue arg = args[arg_index]; + if (arg.value == nullptr) { + switch (e->kind) { + case Entity_TypeName: + args[arg_index] = lb_const_nil(p->module, e->type); + break; + case Entity_Variable: + args[arg_index] = lb_handle_param_value(p, e->type, e->Variable.param_value, pos); + break; + + case Entity_Constant: + args[arg_index] = lb_const_value(p->module, e->type, e->Constant.value); + break; + default: + GB_PANIC("Unknown entity kind %.*s\n", LIT(entity_strings[e->kind])); + } + } else { + args[arg_index] = lb_emit_conv(p, arg, e->type); } } } + isize final_count = is_c_vararg ? args.count : pt->param_count; auto call_args = array_slice(args, 0, final_count); return lb_emit_call(p, value, call_args, ce->inlining); } diff --git a/src/parser.cpp b/src/parser.cpp index 883342b21..b756412ff 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2752,9 +2752,9 @@ gb_internal Ast *parse_call_expr(AstFile *f, Ast *operand) { open_paren = expect_token(f, Token_OpenParen); + bool seen_ellipsis = false; while (f->curr_token.kind != Token_CloseParen && - f->curr_token.kind != Token_EOF && - ellipsis.pos.line == 0) { + f->curr_token.kind != Token_EOF) { if (f->curr_token.kind == Token_Comma) { syntax_error(f->curr_token, "Expected an expression not ,"); } else if (f->curr_token.kind == Token_Eq) { @@ -2777,11 +2777,15 @@ gb_internal Ast *parse_call_expr(AstFile *f, Ast *operand) { Ast *value = parse_value(f); arg = ast_field_value(f, arg, value, eq); - - + } else if (seen_ellipsis) { + syntax_error(arg, "Positional arguments are not allowed after '..'"); } array_add(&args, arg); + if (ellipsis.pos.line != 0) { + seen_ellipsis = true; + } + if (!allow_field_separator(f)) { break; } diff --git a/src/parser.hpp b/src/parser.hpp index 6ba4ef6d6..900fddbab 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -367,6 +367,11 @@ gb_global char const *union_type_kind_strings[UnionType_COUNT] = { "#shared_nil", }; +struct AstSplitArgs { + Slice positional; + Slice named; +}; + #define AST_KINDS \ AST_KIND(Ident, "identifier", struct { \ Token token; \ @@ -442,6 +447,7 @@ AST_KIND(_ExprBegin, "", bool) \ ProcInlining inlining; \ bool optional_ok_one; \ bool was_selector; \ + AstSplitArgs *split_args; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(EnumFieldValue, "enum field value", struct { \ diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp index 52d49e897..3d2e8f27d 100644 --- a/src/parser_pos.cpp +++ b/src/parser_pos.cpp @@ -37,11 +37,15 @@ gb_internal Token ast_token(Ast *node) { return ast_token(node->ImplicitSelectorExpr.selector); } return node->ImplicitSelectorExpr.token; - case Ast_IndexExpr: return node->IndexExpr.open; - case Ast_MatrixIndexExpr: return node->MatrixIndexExpr.open; - case Ast_SliceExpr: return node->SliceExpr.open; + case Ast_IndexExpr: return ast_token(node->IndexExpr.expr); + case Ast_MatrixIndexExpr: return ast_token(node->MatrixIndexExpr.expr); + case Ast_SliceExpr: return ast_token(node->SliceExpr.expr); case Ast_Ellipsis: return node->Ellipsis.token; - case Ast_FieldValue: return node->FieldValue.eq; + case Ast_FieldValue: + if (node->FieldValue.field) { + return ast_token(node->FieldValue.field); + } + return node->FieldValue.eq; case Ast_EnumFieldValue: return ast_token(node->EnumFieldValue.name); case Ast_DerefExpr: return node->DerefExpr.op; case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x); diff --git a/src/types.cpp b/src/types.cpp index 3cc077f84..385ca926d 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2108,8 +2108,12 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) { return is_type_polymorphic(t->Matrix.elem, or_specialized); case Type_Tuple: - for_array(i, t->Tuple.variables) { - if (is_type_polymorphic(t->Tuple.variables[i]->type, or_specialized)) { + for (Entity *e : t->Tuple.variables) { + if (e->kind == Entity_Constant) { + if (e->Constant.value.kind != ExactValue_Invalid) { + return or_specialized; + } + } else if (is_type_polymorphic(e->type, or_specialized)) { return true; } } @@ -2119,7 +2123,6 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) { if (t->Proc.is_polymorphic) { return true; } - #if 1 if (t->Proc.param_count > 0 && is_type_polymorphic(t->Proc.params, or_specialized)) { return true; @@ -2128,7 +2131,6 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) { is_type_polymorphic(t->Proc.results, or_specialized)) { return true; } - #endif break; case Type_Enum: @@ -3326,6 +3328,9 @@ gb_internal bool are_struct_fields_reordered(Type *type) { type = base_type(type); GB_ASSERT(type->kind == Type_Struct); type_set_offsets(type); + if (type->Struct.fields.count == 0) { + return false; + } GB_ASSERT(type->Struct.offsets != nullptr); i64 prev_offset = 0; @@ -3344,6 +3349,9 @@ gb_internal Slice struct_fields_index_by_increasing_offset(gbAllocator allo type = base_type(type); GB_ASSERT(type->kind == Type_Struct); type_set_offsets(type); + if (type->Struct.fields.count == 0) { + return {}; + } GB_ASSERT(type->Struct.offsets != nullptr); auto indices = slice_make(allocator, type->Struct.fields.count); @@ -4273,6 +4281,10 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha if (var == nullptr) { continue; } + if (comma_index++ > 0) { + str = gb_string_appendc(str, ", "); + } + String name = var->token.string; if (var->kind == Entity_Constant) { str = gb_string_appendc(str, "$"); @@ -4289,10 +4301,6 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha continue; } - if (comma_index++ > 0) { - str = gb_string_appendc(str, ", "); - } - if (var->kind == Entity_Variable) { if (var->flags&EntityFlag_CVarArg) { str = gb_string_appendc(str, "#c_vararg ");