aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2023-06-23 12:11:46 +0100
committerGitHub <noreply@github.com>2023-06-23 12:11:46 +0100
commit9841b11a5423e2cba67c19fbd06e28732d36109c (patch)
tree7c067cbc1501c4a044a80944ca282dd7da974074
parent5a6d5374d780e726be82f3576b4f647d9096d012 (diff)
parentc48057081e451c81524c7727ec3ccf434a45726f (diff)
Merge pull request #2597 from odin-lang/ordered-named-arguments
Allowing for Positional and Named Arguments in Procedure Calls
-rw-r--r--core/c/frontend/preprocessor/preprocess.odin4
-rw-r--r--core/compress/gzip/gzip.odin2
-rw-r--r--core/compress/zlib/zlib.odin6
-rw-r--r--core/fmt/fmt.odin18
-rw-r--r--core/fmt/fmt_os.odin12
-rw-r--r--core/image/png/helpers.odin8
-rw-r--r--core/log/log.odin26
-rw-r--r--core/log/log_allocator.odin54
-rw-r--r--core/math/big/radix.odin2
-rw-r--r--core/mem/virtual/arena.odin2
-rw-r--r--core/runtime/core_builtin.odin12
-rw-r--r--core/runtime/error_checks.odin20
-rw-r--r--core/testing/runner_windows.odin2
-rw-r--r--core/testing/testing.odin10
-rw-r--r--core/text/table/table.odin2
-rw-r--r--examples/demo/demo.odin4
-rw-r--r--src/array.cpp4
-rw-r--r--src/check_expr.cpp1765
-rw-r--r--src/check_stmt.cpp8
-rw-r--r--src/check_type.cpp5
-rw-r--r--src/error.cpp31
-rw-r--r--src/gb/gb.h39
-rw-r--r--src/llvm_backend_proc.cpp336
-rw-r--r--src/parser.cpp12
-rw-r--r--src/parser.hpp6
-rw-r--r--src/parser_pos.cpp12
-rw-r--r--src/types.cpp24
27 files changed, 1259 insertions, 1167 deletions
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<T> slice_make(gbAllocator const &allocator, isize count) {
GB_ASSERT(count >= 0);
Slice<T> 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<Operand> 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<Ast *> 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<Operand> *operands = nullptr);
+gb_internal bool check_procedure_type (CheckerContext *c, Type *type, Ast *proc_type_node, Array<Operand> const *operands = nullptr);
gb_internal void check_struct_type (CheckerContext *c, Type *struct_type, Ast *node, Array<Operand> *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<Operand> *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<Ast *> const &nodes,
}
gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_entity, Type *type,
- Array<Operand> *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) {
+ Array<Operand> 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<Operand> *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<Operand> 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;
+ break;
+ case Basic_UntypedInteger:
+ if (is_type_integer(dst) || is_type_rune(dst)) {
+ score = 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 (src->kind == Type_Basic && src->Basic.kind == Basic_UntypedBool) {
- if (is_type_boolean(dst)) {
- if (is_type_typed(type)) {
- return 2;
+ if (score > 0) {
+ if (is_type_typed(dst)) {
+ score += 1;
}
- return 1;
}
- return -1;
+ return score;
}
}
}
@@ -5150,14 +5178,19 @@ enum UnpackFlag : u32 {
};
-gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs, UnpackFlags flags) {
+gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> 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 &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 isize lookup_procedure_parameter(Type *type, String const &parameter_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<Operand> positional_operands, Array<Operand> 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;
-
-
- TypeTuple *param_tuple = nullptr;
- if (pt->params != nullptr) {
- param_tuple = &pt->params->Tuple;
- }
+ bool show_error = show_error_mode == CallArgumentErrorMode::ShowErrors;
-
- CallArgumentError err = CallArgumentError_None;
Type *final_proc_type = proc_type;
Entity *gen_entity = nullptr;
@@ -5347,302 +5398,135 @@ 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) {
- err = CallArgumentError_None;
+ }
- if (variadic) {
- GB_ASSERT(param_tuple != nullptr && param_tuple->variables.count > 0);
- Type *t = param_tuple->variables[0]->type;
- if (is_type_polymorphic(t)) {
- error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input");
- err = CallArgumentError_AmbiguousPolymorphicVariadic;
+ GB_ASSERT(ce->split_args);
+ auto visited = slice_make<bool>(temporary_allocator(), pt->param_count);
+ auto ordered_operands = array_make<Operand>(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;
}
}
- } 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 (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";
- }
-
- 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);
-
- #if 0
- error_line("\t");
- for_array(i, operands) {
- if (i > 0) {
- error_line(", ");
- }
- 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;
- }
- }
-
- 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;
- }
+ 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);
- bool param_is_variadic = pt->variadic && pt->variadic_index == operand_index;
+ for (isize i = 0; i < positional_operand_count; i++) {
+ ordered_operands[i] = positional_operands[i];
+ visited[i] = true;
+ }
- 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;
+ auto variadic_operands = slice(slice_from_array(positional_operands), positional_operand_count, positional_operands.count);
- 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 (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];
- 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);
+ 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;
}
-
- 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;
-
- if (is_type_polymorphic(t)) {
- error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input");
- err = CallArgumentError_AmbiguousPolymorphicVariadic;
+ 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));
}
-
- 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;
- }
- }
- 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);
- }
+ 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;
}
- }
- }
- if (data) {
- data->score = score;
- data->result_type = final_proc_type->Proc.results;
- data->gen_entity = gen_entity;
-
-
- Ast *proc_lit = nullptr;
- if (ce->proc->tav.value.kind == ExactValue_Procedure) {
- Ast *vp = unparen_expr(ce->proc->tav.value.value_procedure);
- if (vp && vp->kind == Ast_ProcLit) {
- proc_lit = vp;
- }
- }
- if (proc_lit == nullptr) {
- add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {});
+ visited[param_index] = true;
+ ordered_operands[param_index] = operand;
}
}
- return err;
-}
-
-gb_internal bool is_call_expr_field_value(AstCallExpr *ce) {
- GB_ASSERT(ce != nullptr);
-
- if (ce->args.count == 0) {
- return false;
- }
- return ce->args[0]->kind == Ast_FieldValue;
-}
+ isize dummy_argument_count = 0;
+ bool actually_variadic = false;
-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;
+ 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;
- TEMPORARY_ALLOCATOR_GUARD();
+ Operand *variadic_operand = &ordered_operands[pt->variadic_index];
- isize param_count = pt->param_count;
- bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count);
- auto ordered_operands = array_make<Operand>(temporary_allocator(), param_count);
- defer ({
- for (Operand const &o : ordered_operands) {
- if (o.expr != nullptr) {
- call->viral_state_flags |= o.expr->viral_state_flags;
- }
- }
- });
+ 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();
- 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));
+ // 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;
}
- 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 (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 < param_count_to_check; i++) {
+
+ 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)) {
@@ -5650,6 +5534,11 @@ gb_internal CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
}
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;
}
@@ -5672,97 +5561,174 @@ gb_internal CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
}
}
- 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;
+ 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;
+ }
+ }
- 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 (!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);
+ }
- 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)) {
+ return s;
+ };
+
+
+ if (ordered_operands.count == 0 && param_count_excluding_defaults == 0) {
+ err = CallArgumentError_None;
+
+ if (variadic) {
+ GB_ASSERT(pt->params != nullptr && pt->params->Tuple.variables.count > 0);
+ Type *t = pt->params->Tuple.variables[0]->type;
+ if (is_type_polymorphic(t)) {
+ if (show_error) {
error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input");
- err = CallArgumentError_AmbiguousPolymorphicVariadic;
- return err;
}
+ err = CallArgumentError_AmbiguousPolymorphicVariadic;
}
- continue;
}
+ } else {
+ 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;
- 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));
- }
+ } 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);
+ }
+
+ for (isize i = 0; i < pt->param_count; i++) {
+ Operand *o = &ordered_operands[i];
+ if (o->mode == Addressing_Invalid) {
+ continue;
}
- } 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 {
+
+ Entity *e = pt->params->Tuple.variables[i];
+ bool param_is_variadic = pt->variadic && pt->variadic_index == i;
+
+ if (e->kind == Entity_TypeName) {
+ GB_ASSERT(pt->is_polymorphic);
+ if (o->mode != Addressing_Type) {
if (show_error) {
- check_assignment(c, o, e->type, str_lit("procedure argument"));
+ error(o->expr, "Expected a type for the argument '%.*s'", LIT(e->token.string));
}
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;
- }
+ if (are_types_identical(e->type, o->type)) {
+ score += assign_score_function(1);
+ } else {
+ score += assign_score_function(MAXIMUM_TYPE_DISTANCE);
}
- } else if (show_error) {
- check_assignment(c, o, e->type, str_lit("procedure argument"));
+ continue;
}
- score += s;
+
+ if (param_is_variadic) {
+ continue;
+ }
+
+ score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error);
}
+ }
- 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 (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;
+
+ 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");
+ }
+ 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);
}
}
if (data) {
data->score = score;
- data->result_type = pt->results;
+ data->result_type = final_proc_type->Proc.results;
data->gen_entity = gen_entity;
- add_type_and_value(c, ce->proc, Addressing_Value, proc_type, {});
+
+
+ Ast *proc_lit = nullptr;
+ if (ce->proc->tav.value.kind == ExactValue_Procedure) {
+ Ast *vp = unparen_expr(ce->proc->tav.value.value_procedure);
+ if (vp && vp->kind == Ast_ProcLit) {
+ proc_lit = vp;
+ }
+ }
+ if (proc_lit == nullptr) {
+ add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {});
+ }
}
return err;
}
+gb_internal bool is_call_expr_field_value(AstCallExpr *ce) {
+ GB_ASSERT(ce != nullptr);
+
+ if (ce->args.count == 0) {
+ return false;
+ }
+ return ce->args[0]->kind == Ast_FieldValue;
+}
+
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<Ast *> const &named_args, Array<Operand> *named_operands, bool show_error) {
+ bool success = true;
-gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call, Slice<Ast *> const &args) {
- ast_node(ce, CallExpr, call);
-
- CallArgumentCheckerType *call_checker = check_call_arguments_internal;
- Array<Operand> 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<Operand>(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 *> 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<Entity *> 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;
+ for_array(i, named_args) {
+ Ast *arg = named_args[i];
+ if (arg->kind != Ast_FieldValue) {
+ if (show_error) {
+ error(arg, "Expected a 'field = value'");
}
- 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);
- }
+ return false;
+ }
+ 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);
}
+ success = false;
+ continue;
}
- } else {
- Array<Entity *> 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);
- }
+ String key = fv->field->Ident.token.string;
+ Ast *value = fv->value;
+
+ Type *type_hint = nullptr;
+ 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;
}
+
}
+ Operand o = {};
+ check_expr_with_type_hint(c, &o, value, type_hint);
+ if (o.mode == Addressing_Invalid) {
+ success = false;
+ }
+ array_add(named_operands, o);
+ }
+ }
+ return success;
+}
+
+gb_internal bool check_call_arguments_single(CheckerContext *c, Ast *call, Operand *operand,
+ Entity *e, Type *proc_type,
+ Array<Operand> const &positional_operands, Array<Operand> 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;
}
+ }
+ GB_ASSERT(proc_type != nullptr);
+ proc_type = base_type(proc_type);
+ GB_ASSERT(proc_type->kind == Type_Proc);
- for_array(i, args) {
- Ast *arg = args[i];
- ast_node(fv, FieldValue, arg);
- Ast *field = fv->field;
+ 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;
+ }
- Type *type_hint = 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);
+ }
- 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 (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, !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;
}
- check_expr_or_type(c, &operands[i], fv->value, type_hint);
- }
- } else {
- operands = array_make<Operand>(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);
}
}
- for (Operand const &o : operands) {
- check_no_copy_assignment(o, str_lit("call expression"));
- }
+ return true;
+}
- if (operand->mode == Addressing_ProcGroup) {
- check_entity_decl(c, operand->proc_group, nullptr, nullptr);
- auto procs = proc_group_entities_cloned(c, *operand);
+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);
- 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);
+ Slice<Ast *> const &positional_args = ce->split_args->positional;
+ Slice<Ast *> 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;
- }
-
- isize param_count = 0;
- isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, &param_count);
-
- if (param_count_excluding_defaults > max_arg_count) {
- array_unordered_remove(&procs, proc_index);
- } else {
- proc_index++;
+ // 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);
+ }
}
}
}
- if (procs.count == 1) {
- Ast *ident = operand->expr;
- while (ident->kind == Ast_SelectorExpr) {
- Ast *s = ident->SelectorExpr.selector;
- ident = s;
- }
+ 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);
+ }
- Entity *e = procs[0];
+ // 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;
+ }
- 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);
+ isize param_count = 0;
+ isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, &param_count);
- 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);
+ if (param_count_excluding_defaults > max_arg_count) {
+ array_unordered_remove(&procs, proc_index);
+ continue;
}
- return data;
+ proc_index++;
}
+ }
+
+ Entity **lhs = nullptr;
+ isize lhs_count = -1;
+ bool is_variadic = false;
+ auto positional_operands = array_make<Operand>(heap_allocator(), 0, 0);
+ auto named_operands = array_make<Operand>(heap_allocator(), 0, 0);
+ defer (array_free(&positional_operands));
+ defer (array_free(&named_operands));
- Entity **lhs = nullptr;
- isize lhs_count = -1;
+ if (procs.count == 1) {
+ Entity *e = procs[0];
- {
- // 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;
- break;
- }
+ 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 {
+ if (proc_arg_count != pt->Proc.param_count) {
+ proc_arg_count_all_equal = false;
+ 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))) {
+ continue;
+ }
-
- 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;
+ 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);
+ 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;
- auto valids = array_make<ValidIndexAndScore>(heap_allocator(), 0, procs.count);
- defer (array_free(&valids));
+ Type *type_hint = nullptr;
- auto proc_entities = array_make<Entity *>(heap_allocator(), 0, procs.count*2 + 1);
- defer (array_free(&proc_entities));
- for (Entity *proc : procs) {
- array_add(&proc_entities, proc);
+ 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);
- gbString expr_name = expr_to_string(operand->expr);
- defer (gb_string_free(expr_name));
+ auto valids = array_make<ValidIndexAndScore>(heap_allocator(), 0, procs.count);
+ defer (array_free(&valids));
- 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;
+ auto proc_entities = array_make<Entity *>(heap_allocator(), 0, procs.count*2 + 1);
+ defer (array_free(&proc_entities));
+ for (Entity *proc : procs) {
+ array_add(&proc_entities, proc);
+ }
- 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;
+ gbString expr_name = expr_to_string(operand->expr);
+ defer (gb_string_free(expr_name));
- 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;
+ 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;
- GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
- if (!evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) {
- continue;
- }
+ ctx.no_polymorphic_errors = true;
+ ctx.allow_polymorphic_types = is_type_polymorphic(pt);
+ ctx.hide_polymorphic_errors = true;
- array_add(&proc_entities, data.gen_entity);
- index = proc_entities.count-1;
- }
+ 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;
- ValidIndexAndScore item = {};
- item.index = index;
- item.score = data.score;
- array_add(&valids, item);
+ 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;
- }
+ 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(", ");
- if (valids.count == 0) {
- begin_error_block();
- defer (end_error_block());
+ gbString type = type_to_string(o.type);
+ defer (gb_string_free(type));
- 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 (i < ce->split_args->named.count) {
+ Ast *named_field = ce->split_args->named[i];
+ ast_node(fv, FieldValue, named_field);
- 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;
+ gbString field = expr_to_string(fv->field);
+ defer (gb_string_free(field));
- 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");
+ error_line("%s = %s", field, type);
+ } else {
+ error_line("%s", type);
}
+ }
+ error_line(")\n");
+ };
- result_type = t_invalid;
- } else if (valids.count > 1) {
- begin_error_block();
- defer (end_error_block());
+ if (valids.count == 0) {
+ 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);
+ 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 (j != pl->where_clauses.count-1) {
- error_line(",");
- }
- }
- error_line("\n\t");
- }
- }
- error_line("at %s\n", token_pos_to_string(pos));
+ 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);
}
- 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;
+ String prefix = {};
+ String prefix_sep = {};
+ if (proc->pkg) {
+ prefix = proc->pkg->name;
+ prefix_sep = str_lit(".");
}
+ String name = proc->token.string;
- 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);
+ 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");
+ }
- 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;
+ 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 (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 (j != pl->where_clauses.count-1) {
+ error_line(",");
+ }
+ }
+ error_line("\n\t");
}
}
- return data;
+ 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<Operand> 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);
+ check_call_arguments_single(c, call, operand,
+ e, e->type,
+ positional_operands, named_operands,
+ CallArgumentErrorMode::ShowErrors,
+ &data);
+ return data;
+ }
+
+ 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<Ast *> positional_args = {};
+ Slice<Ast *> 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;
+ }
}
- 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;
+ named_args = slice(ce->args, positional_args.count, ce->args.count);
- 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;
+ 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 (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 (operand->mode == Addressing_ProcGroup) {
+ return check_call_arguments_proc_group(c, operand, call);
+ }
+
+ auto positional_operands = array_make<Operand>(heap_allocator(), 0, positional_args.count);
+ auto named_operands = array_make<Operand>(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);
}
- return data;
+
}
+ 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;
+ }
- CallArgumentData data = {};
- data.result_type = t_invalid;
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<Ast *> 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<Ast *> 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<Operand> *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<Operand> 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<Operand> *operands) {
+gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, Array<Operand> 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<lbValue> 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<lbValue> *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<lbValue>(permanent_allocator(), pt->param_count);
-
- 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);
- }
- }
- }
+ GB_ASSERT(ce->split_args != nullptr);
- 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);
- }
+ auto args = array_make<lbValue>(permanent_allocator(), 0, pt->param_count);
- isize arg_index = 0;
+ bool vari_expand = (ce->ellipsis.pos.line != 0);
+ bool is_c_vararg = pt->c_vararg;
- 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++;
+ 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;
}
- }
- isize param_count = 0;
- if (pt->params) {
- GB_ASSERT(pt->params->kind == Type_Tuple);
- param_count = pt->params->Tuple.variables.count;
- }
+ GB_ASSERT(e->kind == Entity_Variable);
- auto args = array_make<lbValue>(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 is_c_vararg = pt->c_vararg;
+ 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);
- String proc_name = {};
- if (p->entity != nullptr) {
- proc_name = p->entity->token.string;
- }
- TokenPos pos = ast_token(ce->proc).pos;
+ if (is_c_vararg) {
+ GB_ASSERT(!vari_expand);
- TypeTuple *param_tuple = nullptr;
- if (pt->params) {
- GB_ASSERT(pt->params->kind == Type_Tuple);
- param_tuple = &pt->params->Tuple;
- }
+ Type *elem_type = slice_type->Slice.elem;
- 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];
+ 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 {
- for_array(j, at->Tuple.variables) {
- lbValue v = lb_emit_struct_ev(p, a, cast(i32)j);
- args[arg_index++] = v;
- }
- }
- } 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);
+ Type *elem_type = slice_type->Slice.elem;
- 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);
+ auto var_args = array_make<lbValue>(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);
}
- }
- }
- }
-
- 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);
+ 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);
+ }
- if (slice_len > 0) {
- lbAddr base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true);
+ 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);
- 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]);
+ variadic_args = lb_addr_load(p, slice);
+ }
}
-
- 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);
}
+ array_add(&args, variadic_args);
- arg_count = param_count;
- args[variadic_index] = lb_addr_load(p, slice);
+ break;
+ } else {
+ lbValue value = lb_build_expr(p, ce->split_args->positional[i]);
+ lb_add_values_to_array(p, &args, value);
}
}
- 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);
- }
+ if (!is_c_vararg) {
+ array_resize(&args, pt->param_count);
}
- isize final_count = param_count;
- if (is_c_vararg) {
- final_count = arg_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;
}
- 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) {
+ TokenPos pos = ast_token(ce->proc).pos;
+
+
+ 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);
+ }
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<Ast *> positional;
+ Slice<Ast *> 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<i32> 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<i32>(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 ");