Sfoglia il codice sorgente

Add `#no_capture args: ..T` to reuse the backing array stack memory

gingerBill 1 anno fa
parent
commit
edc793d7c1

+ 29 - 29
core/fmt/fmt.odin

@@ -125,7 +125,7 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist
 // 	Returns: A formatted string. 
 //
 @(require_results)
-aprint :: proc(args: ..any, sep := " ", allocator := context.allocator) -> string {
+aprint :: proc(#no_capture args: ..any, sep := " ", allocator := context.allocator) -> string {
 	str: strings.Builder
 	strings.builder_init(&str, allocator)
 	return sbprint(&str, ..args, sep=sep)
@@ -141,7 +141,7 @@ aprint :: proc(args: ..any, sep := " ", allocator := context.allocator) -> strin
 // 	Returns: A formatted string with a newline character at the end.
 //
 @(require_results)
-aprintln :: proc(args: ..any, sep := " ", allocator := context.allocator) -> string {
+aprintln :: proc(#no_capture args: ..any, sep := " ", allocator := context.allocator) -> string {
 	str: strings.Builder
 	strings.builder_init(&str, allocator)
 	return sbprintln(&str, ..args, sep=sep)
@@ -158,7 +158,7 @@ aprintln :: proc(args: ..any, sep := " ", allocator := context.allocator) -> str
 // 	Returns: A formatted string. The returned string must be freed accordingly.
 //
 @(require_results)
-aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator, newline := false) -> string {
+aprintf :: proc(fmt: string, #no_capture args: ..any, allocator := context.allocator, newline := false) -> string {
 	str: strings.Builder
 	strings.builder_init(&str, allocator)
 	return sbprintf(&str, fmt, ..args, newline=newline)
@@ -174,7 +174,7 @@ aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator, newlin
 // 	Returns: A formatted string. The returned string must be freed accordingly.
 //
 @(require_results)
-aprintfln :: proc(fmt: string, args: ..any, allocator := context.allocator) -> string {
+aprintfln :: proc(fmt: string, #no_capture args: ..any, allocator := context.allocator) -> string {
 	return aprintf(fmt, ..args, allocator=allocator, newline=true)
 }
 // 	Creates a formatted string
@@ -188,7 +188,7 @@ aprintfln :: proc(fmt: string, args: ..any, allocator := context.allocator) -> s
 // 	Returns: A formatted string.
 //
 @(require_results)
-tprint :: proc(args: ..any, sep := " ") -> string {
+tprint :: proc(#no_capture args: ..any, sep := " ") -> string {
 	str: strings.Builder
 	strings.builder_init(&str, context.temp_allocator)
 	return sbprint(&str, ..args, sep=sep)
@@ -204,7 +204,7 @@ tprint :: proc(args: ..any, sep := " ") -> string {
 // 	Returns: A formatted string with a newline character at the end.
 //
 @(require_results)
-tprintln :: proc(args: ..any, sep := " ") -> string {
+tprintln :: proc(#no_capture args: ..any, sep := " ") -> string {
 	str: strings.Builder
 	strings.builder_init(&str, context.temp_allocator)
 	return sbprintln(&str, ..args, sep=sep)
@@ -221,7 +221,7 @@ tprintln :: proc(args: ..any, sep := " ") -> string {
 // 	Returns: A formatted string.
 //
 @(require_results)
-tprintf :: proc(fmt: string, args: ..any, newline := false) -> string {
+tprintf :: proc(fmt: string, #no_capture args: ..any, newline := false) -> string {
 	str: strings.Builder
 	strings.builder_init(&str, context.temp_allocator)
 	return sbprintf(&str, fmt, ..args, newline=newline)
@@ -237,7 +237,7 @@ tprintf :: proc(fmt: string, args: ..any, newline := false) -> string {
 // 	Returns: A formatted string.
 //
 @(require_results)
-tprintfln :: proc(fmt: string, args: ..any) -> string {
+tprintfln :: proc(fmt: string, #no_capture args: ..any) -> string {
 	return tprintf(fmt, ..args, newline=true)
 }
 // Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer.
@@ -249,7 +249,7 @@ tprintfln :: proc(fmt: string, args: ..any) -> string {
 //
 // Returns: A formatted string
 //
-bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string {
+bprint :: proc(buf: []byte, #no_capture args: ..any, sep := " ") -> string {
 	sb := strings.builder_from_bytes(buf)
 	return sbprint(&sb, ..args, sep=sep)
 }
@@ -262,7 +262,7 @@ bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string {
 //
 // Returns: A formatted string with a newline character at the end
 //
-bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string {
+bprintln :: proc(buf: []byte, #no_capture args: ..any, sep := " ") -> string {
 	sb := strings.builder_from_bytes(buf)
 	return sbprintln(&sb, ..args, sep=sep)
 }
@@ -276,7 +276,7 @@ bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string {
 //
 // Returns: A formatted string
 //
-bprintf :: proc(buf: []byte, fmt: string, args: ..any, newline := false) -> string {
+bprintf :: proc(buf: []byte, fmt: string, #no_capture args: ..any, newline := false) -> string {
 	sb := strings.builder_from_bytes(buf)
 	return sbprintf(&sb, fmt, ..args, newline=newline)
 }
@@ -289,7 +289,7 @@ bprintf :: proc(buf: []byte, fmt: string, args: ..any, newline := false) -> stri
 //
 // Returns: A formatted string
 //
-bprintfln :: proc(buf: []byte, fmt: string, args: ..any) -> string {
+bprintfln :: proc(buf: []byte, fmt: string, #no_capture args: ..any) -> string {
 	return bprintf(buf, fmt, ..args, newline=true)
 }
 // Runtime assertion with a formatted message
@@ -301,14 +301,14 @@ bprintfln :: proc(buf: []byte, fmt: string, args: ..any) -> string {
 // - loc: The location of the caller
 //
 @(disabled=ODIN_DISABLE_ASSERT)
-assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_location) {
+assertf :: proc(condition: bool, fmt: string, #no_capture args: ..any, loc := #caller_location) {
 	if !condition {
 		// NOTE(dragos): We are using the same trick as in builtin.assert
 		// to improve performance to make the CPU not
 		// execute speculatively, making it about an order of
 		// magnitude faster
 		@(cold)
-		internal :: proc(loc: runtime.Source_Code_Location, fmt: string, args: ..any) {
+		internal :: proc(loc: runtime.Source_Code_Location, fmt: string, #no_capture args: ..any) {
 			p := context.assertion_failure_proc
 			if p == nil {
 				p = runtime.default_assertion_failure_proc
@@ -326,7 +326,7 @@ assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_locati
 // - args: A variadic list of arguments to be formatted
 // - loc: The location of the caller
 //
-panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
+panicf :: proc(fmt: string, #no_capture args: ..any, loc := #caller_location) -> ! {
 	p := context.assertion_failure_proc
 	if p == nil {
 		p = runtime.default_assertion_failure_proc
@@ -346,7 +346,7 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
 // Returns: A formatted C string
 //
 @(require_results)
-caprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
+caprintf :: proc(format: string, #no_capture args: ..any, newline := false) -> cstring {
 	str: strings.Builder
 	strings.builder_init(&str)
 	sbprintf(&str, format, ..args, newline=newline)
@@ -365,7 +365,7 @@ caprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
 // Returns: A formatted C string
 //
 @(require_results)
-caprintfln :: proc(format: string, args: ..any) -> cstring {
+caprintfln :: proc(format: string, #no_capture args: ..any) -> cstring {
 	return caprintf(format, ..args, newline=true)
 }
 // 	Creates a formatted C string
@@ -379,7 +379,7 @@ caprintfln :: proc(format: string, args: ..any) -> cstring {
 // 	Returns: A formatted C string.
 //
 @(require_results)
-ctprint :: proc(args: ..any, sep := " ") -> cstring {
+ctprint :: proc(#no_capture args: ..any, sep := " ") -> cstring {
 	str: strings.Builder
 	strings.builder_init(&str, context.temp_allocator)
 	sbprint(&str, ..args, sep=sep)
@@ -399,7 +399,7 @@ ctprint :: proc(args: ..any, sep := " ") -> cstring {
 // Returns: A formatted C string
 //
 @(require_results)
-ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
+ctprintf :: proc(format: string, #no_capture args: ..any, newline := false) -> cstring {
 	str: strings.Builder
 	strings.builder_init(&str, context.temp_allocator)
 	sbprintf(&str, format, ..args, newline=newline)
@@ -418,7 +418,7 @@ ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
 // Returns: A formatted C string
 //
 @(require_results)
-ctprintfln :: proc(format: string, args: ..any) -> cstring {
+ctprintfln :: proc(format: string, #no_capture args: ..any) -> cstring {
 	return ctprintf(format, ..args, newline=true)
 }
 // Formats using the default print settings and writes to the given strings.Builder
@@ -430,7 +430,7 @@ ctprintfln :: proc(format: string, args: ..any) -> cstring {
 //
 // Returns: A formatted string
 //
-sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
+sbprint :: proc(buf: ^strings.Builder, #no_capture args: ..any, sep := " ") -> string {
 	wprint(strings.to_writer(buf), ..args, sep=sep, flush=true)
 	return strings.to_string(buf^)
 }
@@ -443,7 +443,7 @@ sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
 //
 // Returns: The resulting formatted string
 //
-sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
+sbprintln :: proc(buf: ^strings.Builder, #no_capture args: ..any, sep := " ") -> string {
 	wprintln(strings.to_writer(buf), ..args, sep=sep, flush=true)
 	return strings.to_string(buf^)
 }
@@ -457,7 +457,7 @@ sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
 //
 // Returns: The resulting formatted string
 //
-sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any, newline := false) -> string {
+sbprintf :: proc(buf: ^strings.Builder, fmt: string, #no_capture args: ..any, newline := false) -> string {
 	wprintf(strings.to_writer(buf), fmt, ..args, flush=true, newline=newline)
 	return strings.to_string(buf^)
 }
@@ -469,7 +469,7 @@ sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any, newline := fal
 //
 // Returns: A formatted string
 //
-sbprintfln :: proc(buf: ^strings.Builder, format: string, args: ..any) -> string {
+sbprintfln :: proc(buf: ^strings.Builder, format: string, #no_capture args: ..any) -> string {
 	return sbprintf(buf, format, ..args, newline=true)
 }
 // Formats and writes to an io.Writer using the default print settings
@@ -481,7 +481,7 @@ sbprintfln :: proc(buf: ^strings.Builder, format: string, args: ..any) -> string
 //
 // Returns: The number of bytes written
 //
-wprint :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int {
+wprint :: proc(w: io.Writer, #no_capture args: ..any, sep := " ", flush := true) -> int {
 	fi: Info
 	fi.writer = w
 
@@ -522,7 +522,7 @@ wprint :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int {
 //
 // Returns: The number of bytes written
 //
-wprintln :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int {
+wprintln :: proc(w: io.Writer, #no_capture args: ..any, sep := " ", flush := true) -> int {
 	fi: Info
 	fi.writer = w
 
@@ -549,11 +549,11 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int {
 //
 // Returns: The number of bytes written
 //
-wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline := false) -> int {
+wprintf :: proc(w: io.Writer, fmt: string, #no_capture args: ..any, flush := true, newline := false) -> int {
 	MAX_CHECKED_ARGS :: 64
 	assert(len(args) <= MAX_CHECKED_ARGS, "number of args > 64 is unsupported")
 
-	parse_options :: proc(fi: ^Info, fmt: string, index, end: int, unused_args: ^bit_set[0 ..< MAX_CHECKED_ARGS], args: ..any) -> int {
+	parse_options :: proc(fi: ^Info, fmt: string, index, end: int, unused_args: ^bit_set[0 ..< MAX_CHECKED_ARGS], #no_capture args: ..any) -> int {
 		i := index
 
 		// Prefix
@@ -809,7 +809,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline :
 //
 // Returns: The number of bytes written.
 //
-wprintfln :: proc(w: io.Writer, format: string, args: ..any, flush := true) -> int {
+wprintfln :: proc(w: io.Writer, format: string, #no_capture args: ..any, flush := true) -> int {
 	return wprintf(w, format, ..args, flush=flush, newline=true)
 }
 // Writes a ^runtime.Type_Info value to an io.Writer

+ 12 - 12
core/fmt/fmt_js.odin

@@ -43,7 +43,7 @@ fd_to_writer :: proc(fd: os.Handle, loc := #caller_location) -> io.Writer {
 }
 
 // fprint formats using the default print settings and writes to fd
-fprint :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := #caller_location) -> int {
+fprint :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true, loc := #caller_location) -> int {
 	buf: [1024]byte
 	b: bufio.Writer
 	defer bufio.writer_flush(&b)
@@ -54,7 +54,7 @@ fprint :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := #ca
 }
 
 // fprintln formats using the default print settings and writes to fd
-fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := #caller_location) -> int {
+fprintln :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true, loc := #caller_location) -> int {
 	buf: [1024]byte
 	b: bufio.Writer
 	defer bufio.writer_flush(&b)
@@ -66,7 +66,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := #
 }
 
 // fprintf formats according to the specified format string and writes to fd
-fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline := false, loc := #caller_location) -> int {
+fprintf :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := true, newline := false, loc := #caller_location) -> int {
 	buf: [1024]byte
 	b: bufio.Writer
 	defer bufio.writer_flush(&b)
@@ -78,24 +78,24 @@ fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline
 }
 
 // fprintfln formats according to the specified format string and writes to fd, followed by a newline.
-fprintfln :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, loc := #caller_location) -> int {
+fprintfln :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := true, loc := #caller_location) -> int {
 	return fprintf(fd, fmt, ..args, flush=flush, newline=true, loc=loc)
 }
 
 // print formats using the default print settings and writes to stdout
-print   :: proc(args: ..any, sep := " ", flush := true) -> int { return wprint(w=stdout, args=args, sep=sep, flush=flush) }
+print   :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return wprint(w=stdout, args=args, sep=sep, flush=flush) }
 // println formats using the default print settings and writes to stdout
-println :: proc(args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stdout, args=args, sep=sep, flush=flush) }
+println :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stdout, args=args, sep=sep, flush=flush) }
 // printf formats according to the specififed format string and writes to stdout
-printf  :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush) }
+printf  :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush) }
 // printfln formats according to the specified format string and writes to stdout, followed by a newline.
-printfln :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) }
+printfln :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) }
 
 // eprint formats using the default print settings and writes to stderr
-eprint   :: proc(args: ..any, sep := " ", flush := true) -> int { return wprint(w=stderr, args=args, sep=sep, flush=flush) }
+eprint   :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return wprint(w=stderr, args=args, sep=sep, flush=flush) }
 // eprintln formats using the default print settings and writes to stderr
-eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stderr, args=args, sep=sep, flush=flush) }
+eprintln :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stderr, args=args, sep=sep, flush=flush) }
 // eprintf formats according to the specififed format string and writes to stderr
-eprintf  :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stderr, fmt, ..args, flush=flush) }
+eprintf  :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return wprintf(stderr, fmt, ..args, flush=flush) }
 // eprintfln formats according to the specified format string and writes to stderr, followed by a newline.
-eprintfln :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) }
+eprintfln :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) }

+ 12 - 12
core/fmt/fmt_os.odin

@@ -9,7 +9,7 @@ import "core:io"
 import "core:bufio"
 
 // fprint formats using the default print settings and writes to fd
-fprint :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int {
+fprint :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true) -> int {
 	buf: [1024]byte
 	b: bufio.Writer
 	defer bufio.writer_flush(&b)
@@ -20,7 +20,7 @@ fprint :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int {
 }
 
 // fprintln formats using the default print settings and writes to fd
-fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int {
+fprintln :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true) -> int {
 	buf: [1024]byte
 	b: bufio.Writer
 	defer bufio.writer_flush(&b)
@@ -31,7 +31,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int {
 	return wprintln(w, ..args, sep=sep, flush=flush)
 }
 // fprintf formats according to the specified format string and writes to fd
-fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline := false) -> int {
+fprintf :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := true, newline := false) -> int {
 	buf: [1024]byte
 	b: bufio.Writer
 	defer bufio.writer_flush(&b)
@@ -42,7 +42,7 @@ fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline
 	return wprintf(w, fmt, ..args, flush=flush, newline=newline)
 }
 // fprintfln formats according to the specified format string and writes to fd, followed by a newline.
-fprintfln :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int {
+fprintfln :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := true) -> int {
 	return fprintf(fd, fmt, ..args, flush=flush, newline=true)
 }
 fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info, flush := true) -> (n: int, err: io.Error) {
@@ -67,19 +67,19 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid, flush := true) -> (n: int, err:
 }
 
 // print formats using the default print settings and writes to os.stdout
-print    :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) }
+print    :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) }
 // println formats using the default print settings and writes to os.stdout
-println  :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) }
+println  :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) }
 // printf formats according to the specified format string and writes to os.stdout
-printf   :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) }
+printf   :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) }
 // printfln formats according to the specified format string and writes to os.stdout, followed by a newline.
-printfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush, newline=true) }
+printfln :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush, newline=true) }
 
 // eprint formats using the default print settings and writes to os.stderr
-eprint    :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) }
+eprint    :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) }
 // eprintln formats using the default print settings and writes to os.stderr
-eprintln  :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) }
+eprintln  :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) }
 // eprintf formats according to the specified format string and writes to os.stderr
-eprintf   :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) }
+eprintf   :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) }
 // eprintfln formats according to the specified format string and writes to os.stderr, followed by a newline.
-eprintfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush, newline=true) }
+eprintfln :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush, newline=true) }

+ 17 - 0
src/check_expr.cpp

@@ -6033,6 +6033,23 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
 
 					Entity *vt = pt->params->Tuple.variables[pt->variadic_index];
 					o.type = vt->type;
+
+					// NOTE(bill, 2024-07-14): minimize the stack usage for variadic parameter that use `#no_capture`
+					// on the variadic parameter
+					if (c->decl && (vt->flags & EntityFlag_NoCapture)) {
+						bool found = false;
+						for (NoCaptureData &nc : c->decl->no_captures) {
+							if (are_types_identical(vt->type, nc.slice_type)) {
+								nc.max_count = gb_max(nc.max_count, variadic_operands.count);
+								found = true;
+								break;
+							}
+						}
+						if (!found) {
+							array_add(&c->decl->no_captures, NoCaptureData{vt->type, variadic_operands.count});
+						}
+					}
+
 				} else {
 					dummy_argument_count += 1;
 					o.type = t_untyped_nil;

+ 17 - 0
src/check_type.cpp

@@ -1953,6 +1953,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
 					error(name, "'#by_ptr' can only be applied to variable fields");
 					p->flags &= ~FieldFlag_by_ptr;
 				}
+				if (p->flags&FieldFlag_no_capture) {
+					error(name, "'#no_capture' can only be applied to variable variadic fields");
+					p->flags &= ~FieldFlag_no_capture;
+				}
 
 				param = alloc_entity_type_name(scope, name->Ident.token, type, EntityState_Resolved);
 				param->TypeName.is_type_alias = true;
@@ -2054,6 +2058,15 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
 						p->flags &= ~FieldFlag_by_ptr; // Remove the flag
 					}
 				}
+				if (p->flags&FieldFlag_no_capture) {
+					if (!(is_variadic && variadic_index == variables.count)) {
+						error(name, "'#no_capture' can only be applied to a variadic parameter");
+						p->flags &= ~FieldFlag_no_capture;
+					} else if (p->flags & FieldFlag_c_vararg) {
+						error(name, "'#no_capture' cannot be applied to a #c_vararg parameter");
+						p->flags &= ~FieldFlag_no_capture;
+					}
+				}
 
 				if (is_poly_name) {
 					if (p->flags&FieldFlag_no_alias) {
@@ -2115,6 +2128,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
 			if (p->flags&FieldFlag_by_ptr) {
 				param->flags |= EntityFlag_ByPtr;
 			}
+			if (p->flags&FieldFlag_no_capture) {
+				param->flags |= EntityFlag_NoCapture;
+			}
+
 
 			param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it
 			add_entity(ctx, scope, name, param);

+ 1 - 0
src/checker.cpp

@@ -184,6 +184,7 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
 	ptr_set_init(&d->deps, 0);
 	ptr_set_init(&d->type_info_deps, 0);
 	d->labels.allocator = heap_allocator();
+	d->no_captures.allocator = heap_allocator();
 }
 
 gb_internal DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) {

+ 7 - 0
src/checker.hpp

@@ -181,6 +181,11 @@ char const *ProcCheckedState_strings[ProcCheckedState_COUNT] {
 	"Checked",
 };
 
+struct NoCaptureData {
+	Type *slice_type; // ..elem_type
+	isize max_count;
+};
+
 // DeclInfo is used to store information of certain declarations to allow for "any order" usage
 struct DeclInfo {
 	DeclInfo *    parent; // NOTE(bill): only used for procedure literals at the moment
@@ -219,6 +224,8 @@ struct DeclInfo {
 
 	Array<BlockLabel> labels;
 
+	Array<NoCaptureData> no_captures;
+
 	// NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time
 	struct lbModule *code_gen_module;
 };

+ 1 - 1
src/entity.cpp

@@ -45,7 +45,7 @@ enum EntityFlag : u64 {
 	EntityFlag_Value         = 1ull<<11,
 	EntityFlag_BitFieldField = 1ull<<12,
 
-
+	EntityFlag_NoCapture = 1ull<<13, // #no_capture
 
 	EntityFlag_PolyConst     = 1ull<<15,
 	EntityFlag_NotExported   = 1ull<<16,

+ 7 - 0
src/llvm_backend.hpp

@@ -296,6 +296,11 @@ enum lbProcedureFlag : u32 {
 	lbProcedureFlag_DebugAllocaCopy = 1<<1,
 };
 
+struct lbNoCaptureData {
+	Type *slice_type;
+	lbAddr base_array;
+};
+
 struct lbProcedure {
 	u32 flags;
 	u16 state_flags;
@@ -336,6 +341,8 @@ struct lbProcedure {
 	bool             in_multi_assignment;
 	Array<LLVMValueRef> raw_input_parameters;
 
+	Array<lbNoCaptureData> no_captures;
+
 	LLVMValueRef temp_callee_return_struct_memory;
 
 	Ast *curr_stmt;

+ 26 - 1
src/llvm_backend_proc.cpp

@@ -517,6 +517,7 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
 	lb_start_block(p, p->entry_block);
 
 	map_init(&p->direct_parameters);
+	p->no_captures.allocator = heap_allocator();
 
 	GB_ASSERT(p->type != nullptr);
 
@@ -3450,8 +3451,32 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
 					}
 					isize slice_len = var_args.count;
 					if (slice_len > 0) {
+						lbAddr base_array = {};
+						if (e->flags & EntityFlag_NoCapture) {
+							for (lbNoCaptureData const &nc : p->no_captures) {
+								if (are_types_identical(nc.slice_type, slice_type)) {
+									base_array = nc.base_array;
+									break;
+								}
+							}
+							DeclInfo *d = decl_info_of_entity(p->entity);
+							if (d != nullptr && base_array.addr.value == nullptr) {
+								for (NoCaptureData const &nc : d->no_captures) {
+									if (are_types_identical(nc.slice_type, slice_type)) {
+										base_array = lb_add_local_generated(p, alloc_type_array(elem_type, nc.max_count), true);
+										array_add(&p->no_captures, lbNoCaptureData{slice_type, base_array});
+										break;
+									}
+								}
+							}
+						}
+
+						if (base_array.addr.value == nullptr) {
+							base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true);
+						}
+						GB_ASSERT(base_array.addr.value != nullptr);
+
 						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);

+ 1 - 0
src/parser.cpp

@@ -4014,6 +4014,7 @@ struct ParseFieldPrefixMapping {
 gb_global ParseFieldPrefixMapping const parse_field_prefix_mappings[] = {
 	{str_lit("using"),        Token_using,     FieldFlag_using},
 	{str_lit("no_alias"),     Token_Hash,      FieldFlag_no_alias},
+	{str_lit("no_capture"),   Token_Hash,      FieldFlag_no_capture},
 	{str_lit("c_vararg"),     Token_Hash,      FieldFlag_c_vararg},
 	{str_lit("const"),        Token_Hash,      FieldFlag_const},
 	{str_lit("any_int"),      Token_Hash,      FieldFlag_any_int},

+ 6 - 2
src/parser.hpp

@@ -330,9 +330,10 @@ enum FieldFlag : u32 {
 	FieldFlag_subtype   = 1<<7,
 	FieldFlag_by_ptr    = 1<<8,
 	FieldFlag_no_broadcast = 1<<9, // disallow array programming
+	FieldFlag_no_capture  = 1<<10,
 
 	// Internal use by the parser only
-	FieldFlag_Tags      = 1<<10,
+	FieldFlag_Tags      = 1<<11,
 	FieldFlag_Results   = 1<<16,
 
 
@@ -340,7 +341,10 @@ enum FieldFlag : u32 {
 	FieldFlag_Invalid   = 1u<<31,
 
 	// Parameter List Restrictions
-	FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr|FieldFlag_no_broadcast,
+	FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|
+	                      FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr|FieldFlag_no_broadcast|
+	                      FieldFlag_no_capture,
+
 	FieldFlag_Struct    = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags,
 };