Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions conformance/native/fail/char-hex-7f.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub fn main(world: World) -> Void raises {
let bad: char = '\x80'
check world.out.write("boundary hex\n")
}
4 changes: 4 additions & 0 deletions conformance/native/fail/char-hex-high.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub fn main(world: World) -> Void raises {
let bad: char = '\xFF'
check world.out.write("high hex\n")
}
4 changes: 4 additions & 0 deletions conformance/native/fail/malformed-float-underscores.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub fn main(world: World) -> Void raises {
let bad: f64 = 1.0_
check world.out.write("malformed underscore float\n")
}
4 changes: 4 additions & 0 deletions conformance/native/fail/string-null-escape.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub fn main(world: World) -> Void raises {
let bad: String = "\0"
check world.out.write("null escape\n")
}
4 changes: 4 additions & 0 deletions conformance/native/fail/string-unknown-escape.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub fn main(world: World) -> Void raises {
let bad: String = "\q"
check world.out.write("unknown escape\n")
}
9 changes: 9 additions & 0 deletions conformance/native/pass/float-literal-underscores.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pub fn main(world: World) -> Void raises {
let grouped: f64 = 1_000.5
let tiny: f64 = 0.0_5
let exponent: f64 = 1.0e1_0
let default_width: f64 = 1_000.0
if grouped == 1000.5 && tiny == 0.05 && exponent == 1.0e10 && default_width == 1000.0 {
check world.out.write("float literal underscores ok\n")
}
}
22 changes: 22 additions & 0 deletions conformance/native/pass/string-escape-canonical.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
pub fn main(world: World) -> Void raises {
let newline: String = "a\nb"
if std.mem.eqlBytes(std.mem.span(newline), std.mem.span("a\nb")) {
check world.out.write("newline ok\n")
}
let tab: String = "x\ty"
if std.mem.eqlBytes(std.mem.span(tab), std.mem.span("x\ty")) {
check world.out.write("tab ok\n")
}
let quote: String = "say \"hi\""
if std.mem.eqlBytes(std.mem.span(quote), std.mem.span("say \"hi\"")) {
check world.out.write("quote ok\n")
}
let backslash: String = "path\\to\\file"
if std.mem.eqlBytes(std.mem.span(backslash), std.mem.span("path\\to\\file")) {
check world.out.write("backslash ok\n")
}
let hex: String = "byte \x41 here"
if std.mem.eqlBytes(std.mem.span(hex), std.mem.span("byte \x41 here")) {
check world.out.write("hex ok\n")
}
}
49 changes: 49 additions & 0 deletions conformance/run.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ for (const fixture of [
"conformance/native/pass/explicit-casts.0",
"conformance/native/pass/float-char-casts.0",
"conformance/native/pass/radix-suffix-literals.0",
"conformance/native/pass/float-literal-underscores.0",
"conformance/native/pass/char-literals.0",
"conformance/native/pass/float-primitives.0",
"conformance/native/pass/wrapping-saturating-arithmetic.0",
Expand Down Expand Up @@ -490,6 +491,7 @@ for (const fixture of [
"conformance/native/pass/byte-view-call-single-eval.0",
"conformance/native/pass/generic-spans.0",
"conformance/native/pass/open-ended-slices.0",
"conformance/native/pass/string-escape-canonical.0",
"conformance/native/pass/string-slices.0",
"conformance/native/pass/string-param-span-slice.0",
"conformance/native/pass/coff-dynamic-byte-slice.0",
Expand Down Expand Up @@ -916,6 +918,7 @@ assert.equal(agentSurfaceOwnedDropReadinessBody.targetReadiness.ok, false);
assert.equal(agentSurfaceOwnedDropReadinessBody.targetReadiness.buildable, false);
assert.equal(agentSurfaceOwnedDropReadinessBody.targetReadiness.languageOk, true);
assert.equal(agentSurfaceOwnedDropReadinessBody.targetReadiness.diagnostics[0].code, "BLD004");
assert.match(agentSurfaceOwnedDropReadinessBody.targetReadiness.diagnostics[0].message, /direct ELF64 local type is unsupported/);
assert.deepEqual(agentSurfaceOwnedDropReadinessBody.targetReadiness.diagnostics[0].backendBlocker, {
target: "linux-musl-x64",
objectFormat: "elf",
Expand All @@ -924,6 +927,32 @@ assert.deepEqual(agentSurfaceOwnedDropReadinessBody.targetReadiness.diagnostics[
unsupportedFeature: "owned<Tracked>",
});

const agentSurfaceOwnedDropCoffReadiness = await execFileAsync(zero, [
"check",
"--json",
"--emit",
"obj",
"--target",
"win32-x64.exe",
"conformance/agent-surface/fixtures/owned-drop-direct-backend-unsupported.0",
]);
const agentSurfaceOwnedDropCoffReadinessBody = JSON.parse(agentSurfaceOwnedDropCoffReadiness.stdout);
assert.equal(agentSurfaceOwnedDropCoffReadinessBody.targetReadiness.diagnostics[0].code, "BLD004");
assert.match(agentSurfaceOwnedDropCoffReadinessBody.targetReadiness.diagnostics[0].message, /direct COFF x64 local type is unsupported/);

const agentSurfaceOwnedDropMachoReadiness = await execFileAsync(zero, [
"check",
"--json",
"--emit",
"obj",
"--target",
"darwin-x64",
"conformance/agent-surface/fixtures/owned-drop-direct-backend-unsupported.0",
]);
const agentSurfaceOwnedDropMachoReadinessBody = JSON.parse(agentSurfaceOwnedDropMachoReadiness.stdout);
assert.equal(agentSurfaceOwnedDropMachoReadinessBody.targetReadiness.diagnostics[0].code, "BLD004");
assert.match(agentSurfaceOwnedDropMachoReadinessBody.targetReadiness.diagnostics[0].message, /direct x86_64 Mach-O local type is unsupported/);

const directCallExeReadiness = await execFileAsync(zero, [
"check",
"--json",
Expand Down Expand Up @@ -5025,6 +5054,22 @@ const charBadEscape = await execFileAsync(zero, ["check", "conformance/native/fa
assert.notEqual(charBadEscape.code, 0);
assert.match(charBadEscape.stderr, /PAR100/);

const charHexHigh = await execFileAsync(zero, ["check", "conformance/native/fail/char-hex-high.0"]).catch((error) => error);
assert.notEqual(charHexHigh.code, 0);
assert.match(charHexHigh.stderr, /PAR100/);

const charHex7f = await execFileAsync(zero, ["check", "conformance/native/fail/char-hex-7f.0"]).catch((error) => error);
assert.notEqual(charHex7f.code, 0);
assert.match(charHex7f.stderr, /PAR100/);

const stringUnknownEscape = await execFileAsync(zero, ["check", "conformance/native/fail/string-unknown-escape.0"]).catch((error) => error);
assert.notEqual(stringUnknownEscape.code, 0);
assert.match(stringUnknownEscape.stderr, /PAR100/);

const stringNullEscape = await execFileAsync(zero, ["check", "conformance/native/fail/string-null-escape.0"]).catch((error) => error);
assert.notEqual(stringNullEscape.code, 0);
assert.match(stringNullEscape.stderr, /PAR100/);

const charToString = await execFileAsync(zero, ["check", "conformance/native/fail/char-to-string.0"]).catch((error) => error);
assert.notEqual(charToString.code, 0);
assert.match(charToString.stderr, /TYP002/);
Expand All @@ -5041,6 +5086,10 @@ const malformedFloatLiteral = await execFileAsync(zero, ["check", "conformance/n
assert.notEqual(malformedFloatLiteral.code, 0);
assert.match(malformedFloatLiteral.stderr, /TYP019/);

const malformedFloatUnderscores = await execFileAsync(zero, ["check", "conformance/native/fail/malformed-float-underscores.0"]).catch((error) => error);
assert.notEqual(malformedFloatUnderscores.code, 0);
assert.match(malformedFloatUnderscores.stderr, /TYP019/);

const floatF32Overflow = await execFileAsync(zero, ["check", "conformance/native/fail/float-f32-overflow.0"]).catch((error) => error);
assert.notEqual(floatF32Overflow.code, 0);
assert.match(floatF32Overflow.stderr, /TYP020/);
Expand Down
1 change: 1 addition & 0 deletions native/zero-c/include/zero.h
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,7 @@ ZDirectRuntimeObjectFacts z_direct_runtime_object_facts(const ZTargetInfo *targe
ZDirectExecutableTargetFacts z_direct_executable_target_facts(const ZTargetInfo *target, const char *requested_backend);
const char *z_direct_backend_expected(const ZTargetInfo *target);
const char *z_direct_backend_help(const ZTargetInfo *target);
const char *z_direct_backend_target_label(const ZTargetInfo *target);
void z_append_http_runtime_json(ZBuf *buf, const ZTargetInfo *target);
void z_append_targets_json(ZBuf *buf);
void z_append_target_names_json(ZBuf *buf);
Expand Down
7 changes: 6 additions & 1 deletion native/zero-c/src/buildability_context.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,12 @@ bool z_build_diag(const ZBuildability *ctx, ZDiag *diag, const char *message, in
diag->line = line > 0 ? line : 1;
diag->column = column > 0 ? column : 1;
diag->length = 1;
snprintf(diag->message, sizeof(diag->message), "%s", message ? message : "direct backend buildability check failed");
const char *label = ctx ? z_direct_backend_target_label(ctx->target) : NULL;
if (label && message && strncmp(message, "direct backend ", 15) == 0) {
snprintf(diag->message, sizeof(diag->message), "direct %s %s", label, message + 15);
} else {
snprintf(diag->message, sizeof(diag->message), "%s", message ? message : "direct backend buildability check failed");
}
snprintf(diag->expected, sizeof(diag->expected), "%s", ctx && ctx->expected ? ctx->expected : "direct backend buildability subset");
snprintf(diag->actual, sizeof(diag->actual), "%s", actual && actual[0] ? actual : "unsupported construct");
snprintf(diag->help, sizeof(diag->help), "%s", ctx && ctx->help ? ctx->help : "choose a supported direct target or simplify the program for this backend");
Expand Down
7 changes: 6 additions & 1 deletion native/zero-c/src/canonical_text.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,14 @@ static bool canon_scan_char(const char *source, size_t *offset, int *column, ZCa
if (escaped == 'x') {
char high_ch = source[*offset + 1];
char low_ch = high_ch ? source[*offset + 2] : 0;
if (canon_hex_digit(high_ch) < 0 || canon_hex_digit(low_ch) < 0) {
int high = canon_hex_digit(high_ch);
int low = canon_hex_digit(low_ch);
if (high < 0 || low < 0) {
return canon_fail(diag, &at_start, "malformed hex character escape", "two hex digits", "invalid escape");
}
if (((high << 4) | low) >= 0x80) {
return canon_fail(diag, &at_start, "character literal must be one byte", "ASCII byte", "non-ASCII");
}
*offset += 3;
*column += 3;
} else {
Expand Down
6 changes: 5 additions & 1 deletion native/zero-c/src/canonical_text_program.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,12 @@ static char *canon_ast_decode_string_literal(const ZCanonicalToken *token, ZDiag
}
zbuf_append_char(&buf, (char)value);
i += 2;
} else {
} else if (escaped == '\\' || escaped == '\'' || escaped == '"') {
zbuf_append_char(&buf, escaped);
} else {
canon_ast_fail(diag, token, "invalid string escape", "escaped byte", text);
free(buf.data);
return z_strdup("");
}
}
if (canon_ast_has_diag(diag)) {
Expand Down
41 changes: 29 additions & 12 deletions native/zero-c/src/checker.c
Original file line number Diff line number Diff line change
Expand Up @@ -5501,25 +5501,42 @@ static bool validate_integer_literal_for_type(const Expr *expr, const char *expe

static bool parse_float_literal(const char *text, double *out, bool *out_of_range) {
if (!text || !text[0]) return false;
if (strchr(text, '_')) return false;
char stripped[64];
size_t write = 0;
bool previous_underscore = false;
bool saw_any_digit = false;
for (size_t read = 0; text[read] != 0 && write + 1 < sizeof(stripped); read++) {
char ch = text[read];
if (ch == '_') {
if (!saw_any_digit || previous_underscore) return false;
previous_underscore = true;
continue;
}
stripped[write++] = ch;
previous_underscore = false;
if (ch >= '0' && ch <= '9') saw_any_digit = true;
}
if (previous_underscore || !saw_any_digit || write == 0 || write + 1 >= sizeof(stripped)) return false;
stripped[write] = 0;

size_t index = 0;
if (!isdigit((unsigned char)text[index])) return false;
while (isdigit((unsigned char)text[index])) index++;
if (text[index] != '.') return false;
if (!isdigit((unsigned char)stripped[index])) return false;
while (isdigit((unsigned char)stripped[index])) index++;
if (stripped[index] != '.') return false;
index++;
if (!isdigit((unsigned char)text[index])) return false;
while (isdigit((unsigned char)text[index])) index++;
if (text[index] == 'e' || text[index] == 'E') {
if (!isdigit((unsigned char)stripped[index])) return false;
while (isdigit((unsigned char)stripped[index])) index++;
if (stripped[index] == 'e' || stripped[index] == 'E') {
index++;
if (text[index] == '+' || text[index] == '-') index++;
if (!isdigit((unsigned char)text[index])) return false;
while (isdigit((unsigned char)text[index])) index++;
if (stripped[index] == '+' || stripped[index] == '-') index++;
if (!isdigit((unsigned char)stripped[index])) return false;
while (isdigit((unsigned char)stripped[index])) index++;
}
if (text[index] != 0) return false;
if (stripped[index] != 0) return false;

errno = 0;
char *end = NULL;
double value = strtod(text, &end);
double value = strtod(stripped, &end);
if (!end || *end != 0) return false;
*out = value;
*out_of_range = errno == ERANGE;
Expand Down
14 changes: 11 additions & 3 deletions native/zero-c/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -9911,9 +9911,17 @@ static void init_lowering_backend_diag(ZDiag *diag, const SourceInput *input, co
diag->line = ir && ir->mir_line > 0 ? ir->mir_line : 1;
diag->column = ir && ir->mir_column > 0 ? ir->mir_column : 1;
diag->length = 1;
snprintf(diag->message, sizeof(diag->message), "%s",
llvm_request ? "LLVM IR backend cannot lower this MIR program yet" :
(ir && ir->mir_message[0] ? ir->mir_message : "direct backend lowering failed"));
const char *ir_message = ir && ir->mir_message[0] ? ir->mir_message : "direct backend lowering failed";
if (llvm_request) {
snprintf(diag->message, sizeof(diag->message), "%s", "LLVM IR backend cannot lower this MIR program yet");
} else {
const char *label = z_direct_backend_target_label(target);
if (label && strncmp(ir_message, "direct backend ", 15) == 0) {
snprintf(diag->message, sizeof(diag->message), "direct %s %s", label, ir_message + 15);
} else {
snprintf(diag->message, sizeof(diag->message), "%s", ir_message);
}
}
snprintf(diag->expected, sizeof(diag->expected), "%s",
llvm_request ? "LLVM IR scalar, fixed-array, and byte-view MIR subset" : z_direct_backend_expected(target));
snprintf(diag->actual, sizeof(diag->actual), "%s", ir && ir->mir_actual[0] ? ir->mir_actual : "unsupported construct");
Expand Down
13 changes: 13 additions & 0 deletions native/zero-c/src/target_backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,16 @@ const char *z_direct_backend_help(const ZTargetInfo *target) {
if (backend == Z_DIRECT_BACKEND_COFF_X64 || strcmp(format, "coff") == 0) return "reduce the program to primitive direct-backend constructs or choose a supported direct target";
return "choose a supported direct target or restrict this program to exported primitive integer arithmetic functions";
}

const char *z_direct_backend_target_label(const ZTargetInfo *target) {
ZDirectBackend backend = target ? z_direct_object_backend(target) : Z_DIRECT_BACKEND_NONE;
switch (backend) {
case Z_DIRECT_BACKEND_ELF64: return "ELF64";
case Z_DIRECT_BACKEND_ELF_AARCH64: return "AArch64 ELF";
case Z_DIRECT_BACKEND_MACHO64: return "AArch64 Mach-O";
case Z_DIRECT_BACKEND_MACHO_X64: return "x86_64 Mach-O";
case Z_DIRECT_BACKEND_COFF_X64: return "COFF x64";
case Z_DIRECT_BACKEND_COFF_AARCH64: return "COFF AArch64";
default: return NULL;
}
}
12 changes: 6 additions & 6 deletions scripts/compiler-metrics.mts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ type CScanState = {
const fileBudgets = {
"native/zero-c/include/zero.h": { maxLines: 990, maxStrcmpCalls: 0 },
"native/zero-c/include/zero_runtime.h": { maxLines: 100, maxStrcmpCalls: 0 },
"native/zero-c/src/checker.c": { maxLines: 11710, maxStrcmpCalls: 287 },
"native/zero-c/src/main.c": { maxLines: 12975, maxStrcmpCalls: 473 },
"native/zero-c/src/checker.c": { maxLines: 11725, maxStrcmpCalls: 287 },
"native/zero-c/src/main.c": { maxLines: 12983, maxStrcmpCalls: 473 },
"native/zero-c/src/ir.c": { maxLines: 4212, maxStrcmpCalls: 229 },
"native/zero-c/src/llvm_backend_metadata.c": { maxLines: 80, maxStrcmpCalls: 0 },
"native/zero-c/src/llvm_toolchain.c": { maxLines: 335, maxStrcmpCalls: 19 },
Expand All @@ -27,16 +27,16 @@ const fileBudgets = {
"native/zero-c/src/buildability.c": { maxLines: 295, maxStrcmpCalls: 2 },
"native/zero-c/src/buildability.h": { maxLines: 20, maxStrcmpCalls: 0 },
"native/zero-c/src/buildability_internal.h": { maxLines: 40, maxStrcmpCalls: 0 },
"native/zero-c/src/buildability_context.c": { maxLines: 185, maxStrcmpCalls: 1 },
"native/zero-c/src/buildability_context.c": { maxLines: 200, maxStrcmpCalls: 1 },
"native/zero-c/src/buildability_targets.c": { maxLines: 190, maxStrcmpCalls: 0 },
"native/zero-c/src/buildability_value_targets.c": { maxLines: 371, maxStrcmpCalls: 0 },
"native/zero-c/src/c_import.c": { maxLines: 750, maxStrcmpCalls: 51 },
"native/zero-c/src/c_import.h": { maxLines: 40, maxStrcmpCalls: 0 },
"native/zero-c/src/call_resolve.c": { maxLines: 200, maxStrcmpCalls: 2 },
"native/zero-c/src/call_resolve.h": { maxLines: 100, maxStrcmpCalls: 0 },
"native/zero-c/src/canonical_text.c": { maxLines: 1508, maxStrcmpCalls: 0 },
"native/zero-c/src/canonical_text.c": { maxLines: 1520, maxStrcmpCalls: 0 },
"native/zero-c/src/canonical_text_format.c": { maxLines: 354, maxStrcmpCalls: 0 },
"native/zero-c/src/canonical_text_program.c": { maxLines: 1493, maxStrcmpCalls: 0 },
"native/zero-c/src/canonical_text_program.c": { maxLines: 1510, maxStrcmpCalls: 0 },
"native/zero-c/src/canonical_text_write.c": { maxLines: 604, maxStrcmpCalls: 2 },
"native/zero-c/src/canonical_text.h": { maxLines: 80, maxStrcmpCalls: 0 },
"native/zero-c/src/coff_format.c": { maxLines: 370, maxStrcmpCalls: 0 },
Expand Down Expand Up @@ -116,7 +116,7 @@ const fileBudgets = {
"native/zero-c/src/std_sig.h": { maxLines: 60, maxStrcmpCalls: 0 },
"native/zero-c/src/std_source.c": { maxLines: 340, maxStrcmpCalls: 2 },
"native/zero-c/src/std_source.h": { maxLines: 30, maxStrcmpCalls: 0 },
"native/zero-c/src/target_backend.c": { maxLines: 369, maxStrcmpCalls: 32 },
"native/zero-c/src/target_backend.c": { maxLines: 390, maxStrcmpCalls: 32 },
"native/zero-c/src/target.c": { maxLines: 465, maxStrcmpCalls: 15 },
"native/zero-c/src/type_core.c": { maxLines: 900, maxStrcmpCalls: 8 },
"native/zero-c/src/type_core.h": { maxLines: 150, maxStrcmpCalls: 0 },
Expand Down