lang: introduce cstring — the C-boundary string (Odin model)
cstring is ONE pointer to a null-terminated u8 buffer, C's char*: thin (8 bytes, no length; cstring_len walks to the terminator), crossing #foreign boundaries verbatim in both directions, with ?cstring as the nullable case lowering to the same bare pointer (null = absent). Conversion discipline mirrors Odin: a string LITERAL coerces implicitly (its bytes are terminated constants); any other string is rejected with a diagnostic naming to_cstring (it may be an unterminated view); and cstring never coerces to string implicitly — from_cstring(c) is the explicit zero-copy view, pricing the strlen. Plumbing: TypeId/TypeInfo builtin slot 18 (first_user 19), name classifiers, size/align/name tables, LLVM ptr lowering, the ?T pointer niche, the xx pointer ladder, the literal-gated coercion plan (isConstString + data_ptr), and the reserved-spelling set. std gains cstring_len/from_cstring/to_cstring (fmt.sx, re-exported); the old cstring(size) allocator helper is renamed alloc_string everywhere; getenv migrates to (name: cstring) -> ?cstring as the canonical user and env() drops its manual strlen/memcpy. Pinned: examples/1222 (FFI both directions, literal coercion, ?cstring null paths, round trip) and examples/1173 (both coercion diagnostics); FAIL pre-feature. The alloc_string rename + getenv signature shift the .ir snapshots — regenerated. zig build test 426/426; run_examples 604/604. Spec: reserved spelling + cstring section + C-interop rows.
This commit is contained in:
13
examples/1173-diagnostics-cstring-coercions.sx
Normal file
13
examples/1173-diagnostics-cstring-coercions.sx
Normal file
@@ -0,0 +1,13 @@
|
||||
// cstring's coercion discipline (Odin-style): only a string LITERAL
|
||||
// converts implicitly — an arbitrary string may be an unterminated view
|
||||
// (use to_cstring); and cstring never converts to string implicitly —
|
||||
// the length is an O(n) strlen the code must ask for (from_cstring).
|
||||
#import "modules/std.sx";
|
||||
|
||||
main :: () -> i32 {
|
||||
s := concat("a", "b");
|
||||
c : cstring = s; // error: non-literal string -> cstring
|
||||
t : string = c; // error: cstring -> string
|
||||
print("{}{}\n", t, cstring_len(c));
|
||||
return 0;
|
||||
}
|
||||
38
examples/1222-ffi-cstring-type.sx
Normal file
38
examples/1222-ffi-cstring-type.sx
Normal file
@@ -0,0 +1,38 @@
|
||||
// The `cstring` type: ONE pointer to a null-terminated u8 buffer — C's
|
||||
// `char *`. Crosses #foreign boundaries verbatim in both directions;
|
||||
// `?cstring` is the nullable case (null pointer = absent); string
|
||||
// LITERALS coerce implicitly (terminated constants); arbitrary strings
|
||||
// materialize via to_cstring; from_cstring is the zero-copy view back.
|
||||
#import "modules/std.sx";
|
||||
|
||||
libc :: #library "c";
|
||||
strerror_c :: (code: i32) -> cstring #foreign libc "strerror";
|
||||
getenv_c :: (name: cstring) -> ?cstring #foreign libc "getenv";
|
||||
dlerror_c :: () -> ?cstring #foreign libc "dlerror";
|
||||
|
||||
main :: () -> i32 {
|
||||
// literal -> cstring param; cstring return -> view
|
||||
e := strerror_c(2);
|
||||
v := from_cstring(e);
|
||||
if v.len < 5 { print("BUG: strerror short\n"); return 1; }
|
||||
print("strerror ok\n");
|
||||
|
||||
// ?cstring: present, absent, and null-returning C call
|
||||
p := getenv_c("PATH");
|
||||
if p == null { print("BUG: PATH null\n"); return 2; }
|
||||
if cstring_len(p!) < 1 { print("BUG: PATH empty\n"); return 3; }
|
||||
q := getenv_c("NO_SUCH_VAR_ZZZ");
|
||||
if q != null { print("BUG: missing not null\n"); return 4; }
|
||||
d := dlerror_c();
|
||||
if d != null { print("BUG: dlerror non-null\n"); return 5; }
|
||||
print("?cstring ok\n");
|
||||
|
||||
// round trip: built string -> owned cstring -> view -> equality
|
||||
s := concat("hi-", "there");
|
||||
c := to_cstring(s);
|
||||
r := from_cstring(c);
|
||||
if r != "hi-there" { print("BUG: round trip\n"); return 6; }
|
||||
if cstring_len(c) != 8 { print("BUG: len\n"); return 7; }
|
||||
print("round trip ok\n");
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
1
|
||||
11
examples/expected/1173-diagnostics-cstring-coercions.stderr
Normal file
11
examples/expected/1173-diagnostics-cstring-coercions.stderr
Normal file
@@ -0,0 +1,11 @@
|
||||
error: only a string LITERAL coerces to 'cstring' implicitly; an arbitrary string may be an unterminated view — materialize it with to_cstring(s)
|
||||
--> examples/1173-diagnostics-cstring-coercions.sx:9:5
|
||||
|
|
||||
9 | c : cstring = s; // error: non-literal string -> cstring
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: 'cstring' does not coerce to 'string' implicitly (the length is implicit); convert with from_cstring(c)
|
||||
--> examples/1173-diagnostics-cstring-coercions.sx:10:5
|
||||
|
|
||||
10 | t : string = c; // error: cstring -> string
|
||||
| ^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -16,7 +16,7 @@ declare ptr @memset(ptr, i32, i64)
|
||||
declare void @out.1(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @cstring(ptr, i64) #0
|
||||
declare ptr @alloc_string(ptr, i64) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @int_to_string(ptr, i64) #0
|
||||
@@ -45,6 +45,15 @@ declare ptr @concat(ptr, ptr, ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @substr(ptr, ptr, i64, i64) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @cstring_len(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @from_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @to_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @path_join(ptr, ptr) #0
|
||||
|
||||
@@ -266,9 +275,6 @@ declare i32 @feof(ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @getenv(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @strlen(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i32 @system(ptr) #0
|
||||
|
||||
|
||||
1
examples/expected/1222-ffi-cstring-type.exit
Normal file
1
examples/expected/1222-ffi-cstring-type.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
1
examples/expected/1222-ffi-cstring-type.stderr
Normal file
1
examples/expected/1222-ffi-cstring-type.stderr
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
3
examples/expected/1222-ffi-cstring-type.stdout
Normal file
3
examples/expected/1222-ffi-cstring-type.stdout
Normal file
@@ -0,0 +1,3 @@
|
||||
strerror ok
|
||||
?cstring ok
|
||||
round trip ok
|
||||
@@ -48,7 +48,7 @@ declare ptr @memset(ptr, i32, i64)
|
||||
declare void @out.1(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal { ptr, i64 } @cstring(ptr %0, i64 %1) #0 {
|
||||
define internal { ptr, i64 } @alloc_string(ptr %0, i64 %1) #0 {
|
||||
entry:
|
||||
%alloca = alloca i64, align 8
|
||||
store i64 %1, ptr %alloca, align 8
|
||||
@@ -99,7 +99,7 @@ if.merge.15: ; preds = %entry
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
%icmpN = icmp slt i64 %loadN, 0
|
||||
store i1 %icmpN, ptr %allocaN, align 1
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 20)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 20)
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
store i64 19, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
@@ -203,7 +203,7 @@ entry:
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%add = add i64 %loadN, %loadN
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %add)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %add)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -234,7 +234,7 @@ entry:
|
||||
%allocaN = alloca i64, align 8
|
||||
store i64 %3, ptr %allocaN, align 8
|
||||
%load = load i64, ptr %allocaN, align 8
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %load)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %load)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -249,6 +249,15 @@ entry:
|
||||
ret { ptr, i64 } %loadN
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @cstring_len(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @from_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @to_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @path_join(ptr, ptr) #0
|
||||
|
||||
@@ -758,9 +767,6 @@ declare i32 @feof(ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @getenv(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @strlen(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i32 @system(ptr) #0
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ declare ptr @memset(ptr, i32, i64)
|
||||
declare void @out.1(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal { ptr, i64 } @cstring(ptr %0, i64 %1) #0 {
|
||||
define internal { ptr, i64 } @alloc_string(ptr %0, i64 %1) #0 {
|
||||
entry:
|
||||
%alloca = alloca i64, align 8
|
||||
store i64 %1, ptr %alloca, align 8
|
||||
@@ -115,7 +115,7 @@ if.merge.17: ; preds = %entry
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
%icmpN = icmp slt i64 %loadN, 0
|
||||
store i1 %icmpN, ptr %allocaN, align 1
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 20)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 20)
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
store i64 19, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
@@ -219,7 +219,7 @@ entry:
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%add = add i64 %loadN, %loadN
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %add)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %add)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -250,7 +250,7 @@ entry:
|
||||
%allocaN = alloca i64, align 8
|
||||
store i64 %3, ptr %allocaN, align 8
|
||||
%load = load i64, ptr %allocaN, align 8
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %load)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %load)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -265,6 +265,15 @@ entry:
|
||||
ret { ptr, i64 } %loadN
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @cstring_len(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @from_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @to_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @path_join(ptr, ptr) #0
|
||||
|
||||
@@ -774,9 +783,6 @@ declare i32 @feof(ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @getenv(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @strlen(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i32 @system(ptr) #0
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -40,7 +40,7 @@ declare ptr @memset(ptr, i32, i64)
|
||||
declare void @out.1(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal { ptr, i64 } @cstring(ptr %0, i64 %1) #0 {
|
||||
define internal { ptr, i64 } @alloc_string(ptr %0, i64 %1) #0 {
|
||||
entry:
|
||||
%alloca = alloca i64, align 8
|
||||
store i64 %1, ptr %alloca, align 8
|
||||
@@ -91,7 +91,7 @@ if.merge.15: ; preds = %entry
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
%icmpN = icmp slt i64 %loadN, 0
|
||||
store i1 %icmpN, ptr %allocaN, align 1
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 20)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 20)
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
store i64 19, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
@@ -195,7 +195,7 @@ entry:
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%add = add i64 %loadN, %loadN
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %add)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %add)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -226,7 +226,7 @@ entry:
|
||||
%allocaN = alloca i64, align 8
|
||||
store i64 %3, ptr %allocaN, align 8
|
||||
%load = load i64, ptr %allocaN, align 8
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %load)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %load)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -241,6 +241,15 @@ entry:
|
||||
ret { ptr, i64 } %loadN
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @cstring_len(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @from_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @to_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @path_join(ptr, ptr) #0
|
||||
|
||||
@@ -750,9 +759,6 @@ declare i32 @feof(ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @getenv(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @strlen(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i32 @system(ptr) #0
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -66,7 +66,7 @@ declare ptr @memset(ptr, i32, i64)
|
||||
declare void @out.1(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal { ptr, i64 } @cstring(ptr %0, i64 %1) #0 {
|
||||
define internal { ptr, i64 } @alloc_string(ptr %0, i64 %1) #0 {
|
||||
entry:
|
||||
%alloca = alloca i64, align 8
|
||||
store i64 %1, ptr %alloca, align 8
|
||||
@@ -117,7 +117,7 @@ if.merge.15: ; preds = %entry
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
%icmpN = icmp slt i64 %loadN, 0
|
||||
store i1 %icmpN, ptr %allocaN, align 1
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 20)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 20)
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
store i64 19, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
@@ -221,7 +221,7 @@ entry:
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%add = add i64 %loadN, %loadN
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %add)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %add)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -252,7 +252,7 @@ entry:
|
||||
%allocaN = alloca i64, align 8
|
||||
store i64 %3, ptr %allocaN, align 8
|
||||
%load = load i64, ptr %allocaN, align 8
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %load)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %load)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -267,6 +267,15 @@ entry:
|
||||
ret { ptr, i64 } %loadN
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @cstring_len(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @from_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @to_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @path_join(ptr, ptr) #0
|
||||
|
||||
@@ -776,9 +785,6 @@ declare i32 @feof(ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @getenv(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @strlen(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i32 @system(ptr) #0
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ declare ptr @memset(ptr, i32, i64)
|
||||
declare void @out.1(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal { ptr, i64 } @cstring(ptr %0, i64 %1) #0 {
|
||||
define internal { ptr, i64 } @alloc_string(ptr %0, i64 %1) #0 {
|
||||
entry:
|
||||
%alloca = alloca i64, align 8
|
||||
store i64 %1, ptr %alloca, align 8
|
||||
@@ -93,7 +93,7 @@ if.merge.17: ; preds = %entry
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
%icmpN = icmp slt i64 %loadN, 0
|
||||
store i1 %icmpN, ptr %allocaN, align 1
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 20)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 20)
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
store i64 19, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
@@ -197,7 +197,7 @@ entry:
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%add = add i64 %loadN, %loadN
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %add)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %add)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -228,7 +228,7 @@ entry:
|
||||
%allocaN = alloca i64, align 8
|
||||
store i64 %3, ptr %allocaN, align 8
|
||||
%load = load i64, ptr %allocaN, align 8
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %load)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %load)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -243,6 +243,15 @@ entry:
|
||||
ret { ptr, i64 } %loadN
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @cstring_len(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @from_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @to_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @path_join(ptr, ptr) #0
|
||||
|
||||
@@ -752,9 +761,6 @@ declare i32 @feof(ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @getenv(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @strlen(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i32 @system(ptr) #0
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ declare ptr @memset(ptr, i32, i64)
|
||||
declare void @out.1(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal { ptr, i64 } @cstring(ptr %0, i64 %1) #0 {
|
||||
define internal { ptr, i64 } @alloc_string(ptr %0, i64 %1) #0 {
|
||||
entry:
|
||||
%alloca = alloca i64, align 8
|
||||
store i64 %1, ptr %alloca, align 8
|
||||
@@ -91,7 +91,7 @@ if.merge.17: ; preds = %entry
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
%icmpN = icmp slt i64 %loadN, 0
|
||||
store i1 %icmpN, ptr %allocaN, align 1
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 20)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 20)
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
store i64 19, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
@@ -195,7 +195,7 @@ entry:
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%add = add i64 %loadN, %loadN
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %add)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %add)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -226,7 +226,7 @@ entry:
|
||||
%allocaN = alloca i64, align 8
|
||||
store i64 %3, ptr %allocaN, align 8
|
||||
%load = load i64, ptr %allocaN, align 8
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %load)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %load)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -241,6 +241,15 @@ entry:
|
||||
ret { ptr, i64 } %loadN
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @cstring_len(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @from_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @to_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @path_join(ptr, ptr) #0
|
||||
|
||||
@@ -750,9 +759,6 @@ declare i32 @feof(ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @getenv(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @strlen(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i32 @system(ptr) #0
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ declare ptr @memset(ptr, i32, i64)
|
||||
declare void @out.1(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal { ptr, i64 } @cstring(ptr %0, i64 %1) #0 {
|
||||
define internal { ptr, i64 } @alloc_string(ptr %0, i64 %1) #0 {
|
||||
entry:
|
||||
%alloca = alloca i64, align 8
|
||||
store i64 %1, ptr %alloca, align 8
|
||||
@@ -91,7 +91,7 @@ if.merge.17: ; preds = %entry
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
%icmpN = icmp slt i64 %loadN, 0
|
||||
store i1 %icmpN, ptr %allocaN, align 1
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 20)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 20)
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
store i64 19, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
@@ -195,7 +195,7 @@ entry:
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%add = add i64 %loadN, %loadN
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %add)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %add)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -226,7 +226,7 @@ entry:
|
||||
%allocaN = alloca i64, align 8
|
||||
store i64 %3, ptr %allocaN, align 8
|
||||
%load = load i64, ptr %allocaN, align 8
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %load)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %load)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -241,6 +241,15 @@ entry:
|
||||
ret { ptr, i64 } %loadN
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @cstring_len(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @from_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @to_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @path_join(ptr, ptr) #0
|
||||
|
||||
@@ -750,9 +759,6 @@ declare i32 @feof(ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @getenv(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @strlen(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i32 @system(ptr) #0
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ declare ptr @memset(ptr, i32, i64)
|
||||
declare void @out.1(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal { ptr, i64 } @cstring(ptr %0, i64 %1) #0 {
|
||||
define internal { ptr, i64 } @alloc_string(ptr %0, i64 %1) #0 {
|
||||
entry:
|
||||
%alloca = alloca i64, align 8
|
||||
store i64 %1, ptr %alloca, align 8
|
||||
@@ -91,7 +91,7 @@ if.merge.17: ; preds = %entry
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
%icmpN = icmp slt i64 %loadN, 0
|
||||
store i1 %icmpN, ptr %allocaN, align 1
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 20)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 20)
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
store i64 19, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
@@ -195,7 +195,7 @@ entry:
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%add = add i64 %loadN, %loadN
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %add)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %add)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -226,7 +226,7 @@ entry:
|
||||
%allocaN = alloca i64, align 8
|
||||
store i64 %3, ptr %allocaN, align 8
|
||||
%load = load i64, ptr %allocaN, align 8
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %load)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %load)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -241,6 +241,15 @@ entry:
|
||||
ret { ptr, i64 } %loadN
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @cstring_len(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @from_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @to_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @path_join(ptr, ptr) #0
|
||||
|
||||
@@ -750,9 +759,6 @@ declare i32 @feof(ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @getenv(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @strlen(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i32 @system(ptr) #0
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ declare ptr @memset(ptr, i32, i64)
|
||||
declare void @out.1(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal { ptr, i64 } @cstring(ptr %0, i64 %1) #0 {
|
||||
define internal { ptr, i64 } @alloc_string(ptr %0, i64 %1) #0 {
|
||||
entry:
|
||||
%alloca = alloca i64, align 8
|
||||
store i64 %1, ptr %alloca, align 8
|
||||
@@ -91,7 +91,7 @@ if.merge.17: ; preds = %entry
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
%icmpN = icmp slt i64 %loadN, 0
|
||||
store i1 %icmpN, ptr %allocaN, align 1
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 20)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 20)
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
store i64 19, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
@@ -195,7 +195,7 @@ entry:
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%add = add i64 %loadN, %loadN
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %add)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %add)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -226,7 +226,7 @@ entry:
|
||||
%allocaN = alloca i64, align 8
|
||||
store i64 %3, ptr %allocaN, align 8
|
||||
%load = load i64, ptr %allocaN, align 8
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %load)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %load)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -241,6 +241,15 @@ entry:
|
||||
ret { ptr, i64 } %loadN
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @cstring_len(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @from_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @to_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @path_join(ptr, ptr) #0
|
||||
|
||||
@@ -750,9 +759,6 @@ declare i32 @feof(ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @getenv(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @strlen(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i32 @system(ptr) #0
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ declare ptr @memset(ptr, i32, i64)
|
||||
declare void @out.1(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal { ptr, i64 } @cstring(ptr %0, i64 %1) #0 {
|
||||
define internal { ptr, i64 } @alloc_string(ptr %0, i64 %1) #0 {
|
||||
entry:
|
||||
%alloca = alloca i64, align 8
|
||||
store i64 %1, ptr %alloca, align 8
|
||||
@@ -91,7 +91,7 @@ if.merge.17: ; preds = %entry
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
%icmpN = icmp slt i64 %loadN, 0
|
||||
store i1 %icmpN, ptr %allocaN, align 1
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 20)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 20)
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
store i64 19, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
@@ -195,7 +195,7 @@ entry:
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%add = add i64 %loadN, %loadN
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %add)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %add)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -226,7 +226,7 @@ entry:
|
||||
%allocaN = alloca i64, align 8
|
||||
store i64 %3, ptr %allocaN, align 8
|
||||
%load = load i64, ptr %allocaN, align 8
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %load)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %load)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -241,6 +241,15 @@ entry:
|
||||
ret { ptr, i64 } %loadN
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @cstring_len(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @from_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @to_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @path_join(ptr, ptr) #0
|
||||
|
||||
@@ -750,9 +759,6 @@ declare i32 @feof(ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @getenv(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @strlen(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i32 @system(ptr) #0
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ declare ptr @memset(ptr, i32, i64)
|
||||
declare void @out.1(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal { ptr, i64 } @cstring(ptr %0, i64 %1) #0 {
|
||||
define internal { ptr, i64 } @alloc_string(ptr %0, i64 %1) #0 {
|
||||
entry:
|
||||
%alloca = alloca i64, align 8
|
||||
store i64 %1, ptr %alloca, align 8
|
||||
@@ -91,7 +91,7 @@ if.merge.17: ; preds = %entry
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
%icmpN = icmp slt i64 %loadN, 0
|
||||
store i1 %icmpN, ptr %allocaN, align 1
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 20)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 20)
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
store i64 19, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
@@ -195,7 +195,7 @@ entry:
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%add = add i64 %loadN, %loadN
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %add)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %add)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -226,7 +226,7 @@ entry:
|
||||
%allocaN = alloca i64, align 8
|
||||
store i64 %3, ptr %allocaN, align 8
|
||||
%load = load i64, ptr %allocaN, align 8
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %load)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %load)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -241,6 +241,15 @@ entry:
|
||||
ret { ptr, i64 } %loadN
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @cstring_len(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @from_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @to_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @path_join(ptr, ptr) #0
|
||||
|
||||
@@ -750,9 +759,6 @@ declare i32 @feof(ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @getenv(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @strlen(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i32 @system(ptr) #0
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ declare ptr @memset(ptr, i32, i64)
|
||||
declare void @out.1(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal { ptr, i64 } @cstring(ptr %0, i64 %1) #0 {
|
||||
define internal { ptr, i64 } @alloc_string(ptr %0, i64 %1) #0 {
|
||||
entry:
|
||||
%alloca = alloca i64, align 8
|
||||
store i64 %1, ptr %alloca, align 8
|
||||
@@ -91,7 +91,7 @@ if.merge.17: ; preds = %entry
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
%icmpN = icmp slt i64 %loadN, 0
|
||||
store i1 %icmpN, ptr %allocaN, align 1
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 20)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 20)
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
store i64 19, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
@@ -195,7 +195,7 @@ entry:
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%add = add i64 %loadN, %loadN
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %add)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %add)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -226,7 +226,7 @@ entry:
|
||||
%allocaN = alloca i64, align 8
|
||||
store i64 %3, ptr %allocaN, align 8
|
||||
%load = load i64, ptr %allocaN, align 8
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %load)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %load)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -241,6 +241,15 @@ entry:
|
||||
ret { ptr, i64 } %loadN
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @cstring_len(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @from_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @to_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @path_join(ptr, ptr) #0
|
||||
|
||||
@@ -750,9 +759,6 @@ declare i32 @feof(ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @getenv(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @strlen(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i32 @system(ptr) #0
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ declare ptr @memset(ptr, i32, i64)
|
||||
declare void @out.1(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define internal { ptr, i64 } @cstring(ptr %0, i64 %1) #0 {
|
||||
define internal { ptr, i64 } @alloc_string(ptr %0, i64 %1) #0 {
|
||||
entry:
|
||||
%alloca = alloca i64, align 8
|
||||
store i64 %1, ptr %alloca, align 8
|
||||
@@ -91,7 +91,7 @@ if.merge.17: ; preds = %entry
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
%icmpN = icmp slt i64 %loadN, 0
|
||||
store i1 %icmpN, ptr %allocaN, align 1
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 20)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 20)
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
store i64 19, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %alloca, align 8
|
||||
@@ -195,7 +195,7 @@ entry:
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%loadN = load i64, ptr %allocaN, align 8
|
||||
%add = add i64 %loadN, %loadN
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %add)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %add)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -226,7 +226,7 @@ entry:
|
||||
%allocaN = alloca i64, align 8
|
||||
store i64 %3, ptr %allocaN, align 8
|
||||
%load = load i64, ptr %allocaN, align 8
|
||||
%call = call { ptr, i64 } @cstring(ptr %0, i64 %load)
|
||||
%call = call { ptr, i64 } @alloc_string(ptr %0, i64 %load)
|
||||
%allocaN = alloca { ptr, i64 }, align 8
|
||||
store { ptr, i64 } %call, ptr %allocaN, align 8
|
||||
%loadN = load { ptr, i64 }, ptr %allocaN, align 8
|
||||
@@ -241,6 +241,15 @@ entry:
|
||||
ret { ptr, i64 } %loadN
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @cstring_len(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @from_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @to_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @path_join(ptr, ptr) #0
|
||||
|
||||
@@ -750,9 +759,6 @@ declare i32 @feof(ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @getenv(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @strlen(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i32 @system(ptr) #0
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ declare ptr @memset(ptr, i32, i64)
|
||||
declare void @out.1(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @cstring(ptr, i64) #0
|
||||
declare ptr @alloc_string(ptr, i64) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @int_to_string(ptr, i64) #0
|
||||
@@ -52,6 +52,15 @@ declare ptr @concat(ptr, ptr, ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @substr(ptr, ptr, i64, i64) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @cstring_len(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @from_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @to_cstring(ptr, ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @path_join(ptr, ptr) #0
|
||||
|
||||
@@ -273,9 +282,6 @@ declare i32 @feof(ptr) #0
|
||||
; Function Attrs: nounwind
|
||||
declare ptr @getenv(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i64 @strlen(ptr) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i32 @system(ptr) #0
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ bundle_main :: () -> bool {
|
||||
// buffer for libc / `[:0]u8` callees. Allocated from
|
||||
// `context.allocator` like the rest of the bundling stage.
|
||||
str_to_cstr :: (s: string) -> [:0]u8 {
|
||||
buf := cstring(s.len);
|
||||
buf := alloc_string(s.len);
|
||||
memcpy(buf.ptr, s.ptr, s.len);
|
||||
buf
|
||||
}
|
||||
@@ -927,7 +927,7 @@ MANIFEST, pkg_esc, lib_esc, lib_esc, lib_esc)
|
||||
|
||||
// `co/swipelab/sxchess/SxApp` → `co.swipelab.sxchess.SxApp`.
|
||||
slash_to_dot :: (path: string) -> string {
|
||||
buf := cstring(path.len);
|
||||
buf := alloc_string(path.len);
|
||||
i := 0;
|
||||
while i < path.len {
|
||||
c := path[i];
|
||||
|
||||
@@ -69,7 +69,10 @@ optional_to_string :: fmt.optional_to_string;
|
||||
concat :: fmt.concat;
|
||||
substr :: fmt.substr;
|
||||
path_join :: fmt.path_join;
|
||||
cstring :: fmt.cstring;
|
||||
alloc_string :: fmt.alloc_string;
|
||||
cstring_len :: fmt.cstring_len;
|
||||
from_cstring :: fmt.from_cstring;
|
||||
to_cstring :: fmt.to_cstring;
|
||||
alloc_slice :: fmt.alloc_slice;
|
||||
|
||||
// fmt internals, re-exported only because they were always part of the
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
// --- Slice & string allocation ---
|
||||
|
||||
cstring :: (size: i64) -> string {
|
||||
alloc_string :: (size: i64) -> string {
|
||||
raw := context.allocator.alloc_bytes(size + 1);
|
||||
memset(raw, 0, size + 1);
|
||||
s : string = ---;
|
||||
@@ -31,7 +31,7 @@ int_to_string :: (n: i64) -> string {
|
||||
// overflows for i64::MIN (its magnitude is unrepresentable as a
|
||||
// positive i64). sx `%` truncates toward zero, so `n % 10` keeps n's
|
||||
// sign; take each remainder's absolute value for the digit.
|
||||
tmp := cstring(20);
|
||||
tmp := alloc_string(20);
|
||||
i := 19;
|
||||
v := n;
|
||||
while v != 0 {
|
||||
@@ -56,7 +56,7 @@ uint_to_string :: (n: i64) -> string {
|
||||
// next limb; the per-step accumulator stays well within i64
|
||||
// (max 9*65536 + 65535), so signed `/` and `%` are exact.
|
||||
g := decompose_u16x4(n);
|
||||
tmp := cstring(20);
|
||||
tmp := alloc_string(20);
|
||||
i := 19;
|
||||
while g[0] != 0 or g[1] != 0 or g[2] != 0 or g[3] != 0 {
|
||||
rem := 0;
|
||||
@@ -89,7 +89,7 @@ float_to_string :: (f: f64) -> string {
|
||||
fl := fstr.len;
|
||||
prefix := if neg then 1 else 0;
|
||||
total := prefix + il + 1 + 6;
|
||||
buf := cstring(total);
|
||||
buf := alloc_string(total);
|
||||
pos := 0;
|
||||
if neg { buf[0] = 45; pos = 1; }
|
||||
memcpy(@buf[pos], istr.ptr, il);
|
||||
@@ -144,7 +144,7 @@ int_to_hex_string :: (n: i64) -> string {
|
||||
if n == 0 { return "0"; }
|
||||
|
||||
g := decompose_u16x4(n);
|
||||
buf := cstring(16);
|
||||
buf := alloc_string(16);
|
||||
hex_group(buf, 0, g[0]);
|
||||
hex_group(buf, 4, g[1]);
|
||||
hex_group(buf, 8, g[2]);
|
||||
@@ -162,18 +162,48 @@ int_to_hex_string :: (n: i64) -> string {
|
||||
concat :: (a: string, b: string) -> string {
|
||||
al := a.len;
|
||||
bl := b.len;
|
||||
buf := cstring(al + bl);
|
||||
buf := alloc_string(al + bl);
|
||||
memcpy(buf.ptr, a.ptr, al);
|
||||
memcpy(@buf[al], b.ptr, bl);
|
||||
buf
|
||||
}
|
||||
|
||||
substr :: (s: string, start: i64, len: i64) -> string {
|
||||
buf := cstring(len);
|
||||
buf := alloc_string(len);
|
||||
memcpy(buf.ptr, @s[start], len);
|
||||
buf
|
||||
}
|
||||
|
||||
// ── cstring: the C-boundary string ────────────────────────────────────
|
||||
// `cstring` is ONE pointer to a null-terminated u8 buffer — C's `char *`.
|
||||
// It carries no length (`cstring_len` walks to the terminator) and
|
||||
// crosses `#foreign` boundaries verbatim in both directions; `?cstring`
|
||||
// is the nullable case (null pointer = absent). String LITERALS coerce
|
||||
// to `cstring` implicitly — their bytes are terminated constants; every
|
||||
// other `string` must materialize through `to_cstring`.
|
||||
|
||||
// Byte length of `c` (strlen — O(n), walks to the terminator).
|
||||
cstring_len :: (c: cstring) -> i64 {
|
||||
p : [*]u8 = xx c;
|
||||
n := 0;
|
||||
while p[n] != 0 { n += 1; }
|
||||
n
|
||||
}
|
||||
|
||||
// A zero-copy string VIEW over `c`'s bytes ({ptr, strlen}). The view
|
||||
// shares C's buffer — `substr` it if it must outlive the source.
|
||||
from_cstring :: (c: cstring) -> string {
|
||||
p : [*]u8 = xx c;
|
||||
string.{ ptr = p, len = cstring_len(c) }
|
||||
}
|
||||
|
||||
// An owned, terminated copy of `s` as a `cstring`.
|
||||
to_cstring :: (s: string) -> cstring {
|
||||
z := alloc_string(s.len);
|
||||
memcpy(z.ptr, s.ptr, s.len);
|
||||
xx z.ptr
|
||||
}
|
||||
|
||||
// Join path components with the POSIX separator ('/'). Skips empty
|
||||
// components and collapses duplicate separators at component
|
||||
// boundaries. Used for bundle paths where Apple .app and Android APK
|
||||
|
||||
@@ -130,7 +130,7 @@ read_file :: (path: [:0]u8) -> ?string {
|
||||
size := lseek(fd, 0, SEEK_END);
|
||||
if size < 0 { close(fd); return null; }
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
buf := cstring(size);
|
||||
buf := alloc_string(size);
|
||||
n := read(fd, buf.ptr, xx size);
|
||||
close(fd);
|
||||
if cast(i64) n != size { return null; }
|
||||
@@ -191,7 +191,7 @@ create_dir_all :: (path: [:0]u8) -> bool {
|
||||
last -= 1;
|
||||
}
|
||||
if last > 0 {
|
||||
parent := cstring(last);
|
||||
parent := alloc_string(last);
|
||||
memcpy(parent.ptr, path.ptr, last);
|
||||
if !create_dir_all(parent) { return false; }
|
||||
}
|
||||
@@ -211,7 +211,7 @@ copy_file :: (src: [:0]u8, dst: [:0]u8) -> bool {
|
||||
return false;
|
||||
}
|
||||
ok := true;
|
||||
buf := cstring(65536);
|
||||
buf := alloc_string(65536);
|
||||
loop := true;
|
||||
while loop {
|
||||
n := read(src_fd, buf.ptr, 65536);
|
||||
|
||||
@@ -24,8 +24,7 @@ popen :: (cmd: [:0]u8, mode: [:0]u8) -> *void #foreign libc;
|
||||
pclose :: (stream: *void) -> i32 #foreign libc;
|
||||
fread :: (ptr: [*]u8, size: usize, nmemb: usize, stream: *void) -> usize #foreign libc;
|
||||
feof :: (stream: *void) -> i32 #foreign libc;
|
||||
getenv :: (name: [:0]u8) -> *u8 #foreign libc;
|
||||
strlen :: (s: *u8) -> usize #foreign libc;
|
||||
getenv :: (name: cstring) -> ?cstring #foreign libc;
|
||||
system :: (cmd: [:0]u8) -> i32 #foreign libc;
|
||||
|
||||
// ── Public types ─────────────────────────────────────────────────────
|
||||
@@ -53,7 +52,7 @@ run :: (cmd: [:0]u8) -> ?ProcessResult {
|
||||
if cast(i64) f == 0 { return null; }
|
||||
|
||||
out := "";
|
||||
buf := cstring(4096);
|
||||
buf := alloc_string(4096);
|
||||
loop := true;
|
||||
while loop {
|
||||
n := fread(buf.ptr, 1, 4096, f);
|
||||
@@ -84,14 +83,11 @@ run :: (cmd: [:0]u8) -> ?ProcessResult {
|
||||
// Read an environment variable. Returns null if unset; an empty
|
||||
// string if set to "".
|
||||
env :: (name: [:0]u8) -> ?string {
|
||||
p := getenv(name);
|
||||
addr : i64 = xx p;
|
||||
if addr == 0 { return null; }
|
||||
n := strlen(p);
|
||||
if n == 0 { return ""; }
|
||||
buf := cstring(cast(i64) n);
|
||||
memcpy(buf.ptr, xx p, cast(i64) n);
|
||||
buf
|
||||
p := getenv(to_cstring(name));
|
||||
if p == null { return null; }
|
||||
v := from_cstring(p!);
|
||||
if v.len == 0 { return ""; }
|
||||
substr(v, 0, v.len)
|
||||
}
|
||||
|
||||
// Locate an executable by walking `$PATH`. Returns the absolute path
|
||||
@@ -103,7 +99,7 @@ find_executable :: (name: [:0]u8) -> ?string {
|
||||
// (executable names like `codesign`, `javac`, `aapt2`).
|
||||
cmd := concat("command -v ", name);
|
||||
// Need null-terminated for popen.
|
||||
cmd_z := cstring(cmd.len);
|
||||
cmd_z := alloc_string(cmd.len);
|
||||
memcpy(cmd_z.ptr, cmd.ptr, cmd.len);
|
||||
if r := run(cmd_z) {
|
||||
if r.exit_code != 0 { return null; }
|
||||
|
||||
34
specs.md
34
specs.md
@@ -16,7 +16,7 @@ Line comments start with `//` and extend to end of line.
|
||||
#### Reserved type names
|
||||
|
||||
A spelling that names a builtin type — the arbitrary-width integers `i1`..`i64` /
|
||||
`u1`..`u64`, plus `bool`, `string`, `void`, `f32`, `f64`, `usize`, `isize`, `Any` —
|
||||
`u1`..`u64`, plus `bool`, `string`, `cstring`, `void`, `f32`, `f64`, `usize`, `isize`, `Any` —
|
||||
is reserved. A bare reserved spelling is rejected at **value-binding and
|
||||
declaration-name sites**: a value binding (`:=` / typed local / parameter), a
|
||||
`::` **constant** or **function** declaration, an `impl` method **definition**,
|
||||
@@ -39,8 +39,8 @@ slot), so a reserved-spelled impl method still needs the backtick
|
||||
(`` `i2 :: (self) ``), exactly like a free function. See `examples/0158`.
|
||||
|
||||
The bare member-name exemption applies only to the **identifier-classified**
|
||||
reserved spellings — `i1`..`i64`, `u1`..`u64`, `bool`, `string`, `void`, `usize`,
|
||||
`isize`, `Any` — which all lex as ordinary identifiers. The two
|
||||
reserved spellings — `i1`..`i64`, `u1`..`u64`, `bool`, `string`, `cstring`, `void`,
|
||||
`usize`, `isize`, `Any` — which all lex as ordinary identifiers. The two
|
||||
**keyword-classified** reserved spellings, `f32` and `f64`, are lexer keywords, and
|
||||
member-name slots require an identifier token; a bare `f32` / `f64` is therefore
|
||||
rejected at parse (`expected field name in struct`) even in a member position. Use
|
||||
@@ -1043,6 +1043,29 @@ the chain leaves the checked zone.
|
||||
|
||||
**Fat pointer layout**: `[:0]u8`, `string`, and `[]T` are `{ptr, i64}` structs. The raw pointer is always the first field at offset 0. This means `*[:0]u8` works as C's `char**` — a C function dereferences through the outer pointer and reads the raw `char*` from offset 0.
|
||||
|
||||
### cstring
|
||||
|
||||
`cstring` is the C-boundary string: ONE pointer to a null-terminated u8
|
||||
buffer — exactly C's `char *`. It is thin (8 bytes, no length field;
|
||||
`cstring_len` walks to the terminator, O(n)) and crosses `#foreign`
|
||||
boundaries verbatim in BOTH directions. `?cstring` is the nullable case
|
||||
and lowers to the same bare pointer (null = absent) — the natural type
|
||||
for `getenv`-style returns and optional `char *` parameters.
|
||||
|
||||
Conversion discipline (Odin's model):
|
||||
|
||||
- A string **literal** coerces to `cstring` implicitly — literal bytes
|
||||
are terminated constants in the binary, so the conversion is free.
|
||||
- Any **other** `string` does NOT coerce: it may be an unterminated view
|
||||
(`string.{ptr, len}` windows, writer output). Materialize an owned,
|
||||
terminated copy with `to_cstring(s)`.
|
||||
- `cstring` does not coerce to `string` implicitly — the length is an
|
||||
O(n) strlen the code must ask for. `from_cstring(c)` is the zero-copy
|
||||
view (shares C's buffer); `substr(from_cstring(c), 0, n)` the owned
|
||||
copy.
|
||||
- `xx` bit-casts `cstring` ↔ `*u8` / `[*]u8` / integer-pointer values
|
||||
for low-level interop.
|
||||
|
||||
### Optional Types
|
||||
|
||||
Optional types represent values that may or may not be present.
|
||||
@@ -1182,7 +1205,10 @@ write_fd :: (fd: i32, buf: [*]u8, count: u64) -> i64 #foreign libc "write";
|
||||
|
||||
| C type | sx type | Notes |
|
||||
|--------|---------|-------|
|
||||
| `const char*` (input) | `[:0]u8` | compiler extracts `.ptr` at call site |
|
||||
| `const char*` (input) | `cstring` | the pointer, verbatim; literals coerce |
|
||||
| `const char*` (input, legacy) | `[:0]u8` | compiler extracts `.ptr` at call site |
|
||||
| `const char*` (return) | `cstring` | the pointer, verbatim; `from_cstring` to view |
|
||||
| nullable `const char*` (both directions) | `?cstring` | null pointer = `null` |
|
||||
| `char*` (output buffer) | `[*]u8` | raw buffer, no length |
|
||||
| `const char**` | `*[:0]u8` | address of `[:0]u8` — `.ptr` at offset 0 |
|
||||
| `int*` (single out) | `*i32` | |
|
||||
|
||||
@@ -64,13 +64,14 @@ pub const TypeLowering = struct {
|
||||
.bool => self.e.cached_i1,
|
||||
.error_set => self.e.cached_i32, // u32 tag id on the error channel
|
||||
.string => self.e.getStringStructType(),
|
||||
.cstring => self.e.cached_ptr,
|
||||
.pointer, .many_pointer, .function => self.e.cached_ptr,
|
||||
.closure => self.e.getClosureStructType(),
|
||||
.slice => self.e.getStringStructType(), // same {ptr, i64} layout
|
||||
.optional => |opt| {
|
||||
// ?*T / ?fn → bare pointer (null = none)
|
||||
const child_info = self.e.ir_mod.types.get(opt.child);
|
||||
if (child_info == .pointer or child_info == .many_pointer or child_info == .function) {
|
||||
if (child_info == .pointer or child_info == .many_pointer or child_info == .function or child_info == .cstring) {
|
||||
return self.e.cached_ptr;
|
||||
}
|
||||
if (child_info == .closure) {
|
||||
|
||||
@@ -43,11 +43,15 @@ pub const CoercionResolver = struct {
|
||||
widen, // same kind, dst wider
|
||||
narrow, // same kind, dst narrower
|
||||
array_to_slice, // [N]T → []T (materialize backing storage + header)
|
||||
string_to_cstring, // literal-only implicit; other strings need to_cstring
|
||||
cstring_to_string_reject, // explicit from_cstring required (diagnostic)
|
||||
none, // nothing applies — pass the value through
|
||||
};
|
||||
|
||||
pub fn classify(self: CoercionResolver, src_ty: TypeId, dst_ty: TypeId) CoercionPlan {
|
||||
if (src_ty == dst_ty) return .no_op;
|
||||
if (src_ty == .string and dst_ty == .cstring) return .string_to_cstring;
|
||||
if (src_ty == .cstring and dst_ty == .string) return .cstring_to_string_reject;
|
||||
if (src_ty == .any and dst_ty != .any) return .unbox_any;
|
||||
if (dst_ty == .any and src_ty != .any) return .box_any;
|
||||
|
||||
@@ -113,8 +117,8 @@ pub const CoercionResolver = struct {
|
||||
const dst_float = Lowering.isFloat(dst_ty);
|
||||
const src_int = self.l.isIntEx(src_ty);
|
||||
const dst_int = self.l.isIntEx(dst_ty);
|
||||
const src_ptr = !src_ty.isBuiltin() and self.l.module.types.get(src_ty) == .pointer;
|
||||
const dst_ptr = !dst_ty.isBuiltin() and self.l.module.types.get(dst_ty) == .pointer;
|
||||
const src_ptr = (!src_ty.isBuiltin() and self.l.module.types.get(src_ty) == .pointer) or src_ty == .cstring;
|
||||
const dst_ptr = (!dst_ty.isBuiltin() and self.l.module.types.get(dst_ty) == .pointer) or dst_ty == .cstring;
|
||||
|
||||
if (src_int and dst_float) return .int_to_float;
|
||||
if (src_float and dst_int) return .float_to_int;
|
||||
|
||||
@@ -630,6 +630,30 @@ pub fn coerceMode(self: *Lowering, val: Ref, src_ty: TypeId, dst_ty: TypeId, mod
|
||||
const unwrapped = self.builder.emit(.{ .optional_unwrap = .{ .operand = val } }, child_ty);
|
||||
return self.coerceMode(unwrapped, child_ty, dst_ty, mode);
|
||||
},
|
||||
// string → cstring: ONLY a string LITERAL coerces implicitly — its
|
||||
// bytes are a terminated constant (Odin's literal blessing). Any
|
||||
// other string may be an unterminated view, so it must materialize
|
||||
// through `to_cstring`.
|
||||
.string_to_cstring => {
|
||||
if (self.builder.isConstString(val)) {
|
||||
return self.builder.emit(.{ .data_ptr = .{ .operand = val } }, .cstring);
|
||||
}
|
||||
if (self.diagnostics) |d| {
|
||||
const cs = self.builder.current_span;
|
||||
d.addFmt(.err, ast.Span{ .start = cs.start, .end = cs.end }, "only a string LITERAL coerces to 'cstring' implicitly; an arbitrary string may be an unterminated view — materialize it with to_cstring(s)", .{});
|
||||
}
|
||||
return val;
|
||||
},
|
||||
// cstring → string: the length is implicit (strlen), so the
|
||||
// conversion is never silent — `from_cstring(c)` is the zero-copy
|
||||
// view, `substr(from_cstring(c), 0, ...)` the owned copy.
|
||||
.cstring_to_string_reject => {
|
||||
if (self.diagnostics) |d| {
|
||||
const cs = self.builder.current_span;
|
||||
d.addFmt(.err, ast.Span{ .start = cs.start, .end = cs.end }, "'cstring' does not coerce to 'string' implicitly (the length is implicit); convert with from_cstring(c)", .{});
|
||||
}
|
||||
return val;
|
||||
},
|
||||
// void → Optional: produce null (void is the type of null_literal)
|
||||
.void_to_optional => return self.builder.constNull(dst_ty),
|
||||
// Concrete → Optional wrapping (coerce to the inner type first)
|
||||
|
||||
@@ -358,6 +358,24 @@ pub const Builder = struct {
|
||||
/// value and the span it was emitted with; else null. The implicit
|
||||
/// float→int coercion rule reads this to fold an integral literal to its
|
||||
/// int (and to locate a non-integral one for its diagnostic).
|
||||
/// True iff `ref` is a `const_string` instruction — a string LITERAL
|
||||
/// value (terminated constant data), the only string shape that may
|
||||
/// implicitly coerce to `cstring`.
|
||||
pub fn isConstString(self: *Builder, ref: Ref) bool {
|
||||
if (self.func == null) return false;
|
||||
const func = self.currentFunc();
|
||||
const ref_idx = @intFromEnum(ref);
|
||||
if (ref_idx < func.params.len) return false;
|
||||
for (func.blocks.items) |*block| {
|
||||
const first = block.first_ref;
|
||||
if (ref_idx >= first and ref_idx < first + @as(u32, @intCast(block.insts.items.len))) {
|
||||
const i = block.insts.items[ref_idx - first];
|
||||
return i.op == .const_string;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn constFloatInfo(self: *Builder, ref: Ref) ?ConstFloatInfo {
|
||||
if (self.func == null) return null;
|
||||
const func = self.currentFunc();
|
||||
|
||||
@@ -55,6 +55,7 @@ pub const TypeResolver = struct {
|
||||
if (std.mem.eql(u8, name, "f64")) return .f64;
|
||||
if (std.mem.eql(u8, name, "bool")) return .bool;
|
||||
if (std.mem.eql(u8, name, "string")) return .string;
|
||||
if (std.mem.eql(u8, name, "cstring")) return .cstring;
|
||||
if (std.mem.eql(u8, name, "void")) return .void;
|
||||
if (std.mem.eql(u8, name, "Any")) return .any;
|
||||
// `Type` values are runtime-representable as Any-shaped pairs
|
||||
|
||||
@@ -33,9 +33,10 @@ pub const TypeId = enum(u32) {
|
||||
isize = 15,
|
||||
usize = 16,
|
||||
void = 17,
|
||||
_, // user-defined types start at 18
|
||||
cstring = 18, // thin null-terminated char* (see TypeInfo.cstring)
|
||||
_, // user-defined types start at 19
|
||||
|
||||
pub const first_user: u32 = 18;
|
||||
pub const first_user: u32 = 19;
|
||||
|
||||
pub fn index(self: TypeId) u32 {
|
||||
return @intFromEnum(self);
|
||||
@@ -63,6 +64,7 @@ pub const TypeInfo = union(enum) {
|
||||
void,
|
||||
bool,
|
||||
string, // [:0]u8 — fat pointer {ptr, len}
|
||||
cstring, // thin null-terminated char* — ONE pointer, length implicit (strlen)
|
||||
|
||||
@"struct": StructInfo,
|
||||
@"enum": EnumInfo,
|
||||
@@ -382,6 +384,7 @@ pub const TypeTable = struct {
|
||||
.isize, // 15: isize (pointer-sized signed)
|
||||
.usize, // 16: usize (pointer-sized unsigned)
|
||||
.void, // 17
|
||||
.cstring, // 18: thin null-terminated char*
|
||||
};
|
||||
for (&builtins) |info| {
|
||||
table.infos.append(alloc, info) catch unreachable;
|
||||
@@ -587,13 +590,14 @@ pub const TypeTable = struct {
|
||||
.f32 => 4,
|
||||
.f64 => 8,
|
||||
.string => 16, // {ptr, len}
|
||||
.cstring => 8, // one pointer
|
||||
.pointer, .many_pointer, .function => 8,
|
||||
.closure => 16, // {fn_ptr, env}
|
||||
.optional => |opt| blk: {
|
||||
// Sentinel-shaped optionals (pointer/closure/protocol) cost
|
||||
// no extra storage — null reuses the payload's null state.
|
||||
const child_info = self.get(opt.child);
|
||||
if (child_info == .pointer or child_info == .many_pointer or child_info == .function) break :blk 8;
|
||||
if (child_info == .pointer or child_info == .many_pointer or child_info == .function or child_info == .cstring) break :blk 8;
|
||||
if (child_info == .closure) break :blk 16;
|
||||
if (child_info == .@"struct" and child_info.@"struct".is_protocol) break :blk self.sizeOf(opt.child);
|
||||
// Discriminated form: payload + has_value flag (8-aligned).
|
||||
@@ -682,6 +686,7 @@ pub const TypeTable = struct {
|
||||
if (ty == .i32 or ty == .u32 or ty == .f32) return 4;
|
||||
if (ty == .i64 or ty == .u64 or ty == .f64) return 8;
|
||||
if (ty == .usize or ty == .isize) return ptr_size;
|
||||
if (ty == .cstring) return ptr_size;
|
||||
if (ty == .string) return 16; // {ptr, i64} — always 16 (i64 alignment pads on wasm32)
|
||||
if (ty == .any) return 16; // {i64 tag, i64 value} — Any boxed layout
|
||||
if (ty.isBuiltin()) return ptr_size; // default for unknown builtins
|
||||
@@ -783,6 +788,7 @@ pub const TypeTable = struct {
|
||||
if (ty == .i64 or ty == .u64 or ty == .f64) return 8;
|
||||
if (ty == .usize or ty == .isize) return ptr_align;
|
||||
if (ty == .string) return 8; // i64 drives alignment
|
||||
if (ty == .cstring) return ptr_align;
|
||||
if (ty == .any) return 8; // {i64, i64} aligns to 8
|
||||
if (ty.isBuiltin()) return ptr_align;
|
||||
const info = self.get(ty);
|
||||
@@ -853,6 +859,7 @@ pub const TypeTable = struct {
|
||||
.f32 => "f32",
|
||||
.f64 => "f64",
|
||||
.string => "string",
|
||||
.cstring => "cstring",
|
||||
.any => "Any",
|
||||
.noreturn => "noreturn",
|
||||
.isize => "isize",
|
||||
@@ -999,7 +1006,7 @@ fn hashTypeInfo(h: *std.hash.Wyhash, info: TypeInfo) void {
|
||||
switch (info) {
|
||||
.signed => |w| h.update(&.{w}),
|
||||
.unsigned => |w| h.update(&.{w}),
|
||||
.f32, .f64, .void, .bool, .string, .any, .noreturn, .usize, .isize, .unresolved => {},
|
||||
.f32, .f64, .void, .bool, .string, .cstring, .any, .noreturn, .usize, .isize, .unresolved => {},
|
||||
.pointer => |p| h.update(std.mem.asBytes(&p.pointee)),
|
||||
.many_pointer => |p| h.update(std.mem.asBytes(&p.element)),
|
||||
.slice => |s| h.update(std.mem.asBytes(&s.element)),
|
||||
@@ -1070,7 +1077,7 @@ fn typeInfoEql(a: TypeInfo, b: TypeInfo) bool {
|
||||
return switch (a) {
|
||||
.signed => |w| w == b.signed,
|
||||
.unsigned => |w| w == b.unsigned,
|
||||
.f32, .f64, .void, .bool, .string, .any, .noreturn, .usize, .isize, .unresolved => true,
|
||||
.f32, .f64, .void, .bool, .string, .cstring, .any, .noreturn, .usize, .isize, .unresolved => true,
|
||||
.pointer => |p| p.pointee == b.pointer.pointee,
|
||||
.many_pointer => |p| p.element == b.many_pointer.element,
|
||||
.slice => |s| s.element == b.slice.element,
|
||||
|
||||
@@ -20,6 +20,7 @@ pub const Type = union(enum) {
|
||||
void_type,
|
||||
boolean,
|
||||
string_type,
|
||||
cstring_type,
|
||||
enum_type: []const u8,
|
||||
struct_type: []const u8,
|
||||
union_type: []const u8,
|
||||
@@ -116,6 +117,7 @@ pub const Type = union(enum) {
|
||||
if (name.len == 0) return null;
|
||||
return switch (name[0]) {
|
||||
's' => if (std.mem.eql(u8, name, "string")) .string_type else null,
|
||||
'c' => if (std.mem.eql(u8, name, "cstring")) .cstring_type else null,
|
||||
'u' => {
|
||||
if (std.mem.eql(u8, name, "usize")) return .usize_type;
|
||||
if (name.len >= 2) {
|
||||
@@ -199,6 +201,7 @@ pub const Type = union(enum) {
|
||||
.f64 => "f64",
|
||||
.boolean => "bool",
|
||||
.string_type => "string",
|
||||
.cstring_type => "cstring",
|
||||
.void_type => "void",
|
||||
.usize_type => "usize",
|
||||
.isize_type => "isize",
|
||||
@@ -286,6 +289,7 @@ pub const Type = union(enum) {
|
||||
.f64 => "f64",
|
||||
.boolean => "bool",
|
||||
.string_type => "string",
|
||||
.cstring_type => "cstring",
|
||||
.void_type => "void",
|
||||
.any_type => "Any",
|
||||
.usize_type => "usize",
|
||||
|
||||
Reference in New Issue
Block a user