wcc: reject final-band integer overflow; restore rewind columns

This commit is contained in:
2026-08-09 03:25:17 +09:00
parent cd11d6e573
commit 53a9e94736
5 changed files with 76 additions and 9 deletions

View File

@@ -181,6 +181,9 @@ static u64
parseint(const char *s, u64 n, int base, int *ok)
{
u64 v = 0;
u64 b = (u64)base;
u64 cutoff = (u64)~0ULL / b;
u64 cutlim = (u64)~0ULL % b;
int got = 0;
for (u64 i = 0; i < n; i++) {
int c = (unsigned char)s[i];
@@ -192,9 +195,11 @@ parseint(const char *s, u64 n, int base, int *ok)
else if (c >= 'A' && c <= 'F') d = c - 'A' + 10;
else { *ok = 0; return 0; }
if (d >= base) { *ok = 0; return 0; }
/* overflow? cheap check */
if (v > (u64)~0ULL / (u64)base) { *ok = 0; return 0; }
v = v * (u64)base + (u64)d;
if (v > cutoff || (v == cutoff && (u64)d > cutlim)) {
*ok = 0;
return 0;
}
v = v * b + (u64)d;
got = 1;
}
*ok = got;
@@ -369,6 +374,7 @@ lexnum(Lex *l, Pos start)
/* A typed suffix must be glued (no whitespace) to the digits. */
if (isidstart(lpeek(l, 0))) {
u64 sb = l->pos;
i32 sc = l->col;
while (isidcont(lpeek(l, 0))) lget(l);
u64 sl = l->pos - sb;
const char *names[] = {
@@ -387,9 +393,8 @@ lexnum(Lex *l, Pos start)
if (match) {
t.tsuffix = astrndup(l->a, l->src + sb, sl);
} else {
/* not a known suffix — rewind so the run becomes a
* separate token. */
l->pos = sb;
l->col = sc;
}
}
return t;

View File

@@ -269,6 +269,9 @@ fn skipws(l: *lex) bool = {
fn parseint(p: *u8, n: u64, base: i32, ok: *bool) u64 = {
let v: u64 = 0u64;
let b: u64 = base: u64;
let cutoff: u64 = ~0u64 / b;
let cutlim: u64 = ~0u64 % b;
let got: bool = false;
let i: u64 = 0u64;
for (i < n) {
@@ -294,8 +297,11 @@ fn parseint(p: *u8, n: u64, base: i32, ok: *bool) u64 = {
};
if (d < 0) { *ok = false; return 0u64; };
if (d >= base) { *ok = false; return 0u64; };
if (v > ~0u64 / (base: u64)) { *ok = false; return 0u64; };
v = v * (base: u64) + (d: u64);
if (v > cutoff || (v == cutoff && (d: u64) > cutlim)) {
*ok = false;
return 0u64;
};
v = v * b + (d: u64);
got = true;
i += 1u64;
};
@@ -551,6 +557,7 @@ fn lexnum(l: *lex, start: *pos, out: *tok) void = {
if (pc >= 0) {
if (isidstart(pc: rune)) {
let sb: u64 = l.lpos;
let sc: i32 = l.col;
for (true) {
let cc: i32 = lpeek(l, 0u64);
if (cc < 0) { break; };
@@ -591,6 +598,7 @@ fn lexnum(l: *lex, start: *pos, out: *tok) void = {
out.tsuffix = strings.dup(view);
} else {
l.lpos = sb;
l.col = sc;
};
};
};

View File

@@ -309,3 +309,32 @@ fn checkfloat(src: str, want: u64) void = {
checkfloat("1.0e308", 0x7FE1CCF385EBC8A0u64);
checkfloat("2.225073858507202e-308", 0x0010000000000001u64);
};
@test fn intfinalband() void = {
let src: str = "18446744073709551616";
let l: lex;
lexinit(&l, "t", src.ptr, src.len: u64);
let t: tok;
lexnext(&l, &t);
assert(!(t.kind != tkind.TK_ERR));
assert(!(l.errs != 1));
};
@test fn suffixrewindcols() void = {
let src: str = "1foo bar";
let l: lex;
lexinit(&l, "t", src.ptr, src.len: u64);
let t: tok;
lexnext(&l, &t);
assert(!(t.kind != tkind.TK_INT));
assert(!(t.col != 1));
assert(!(t.uval != 1u64));
lexnext(&l, &t);
assert(!(t.kind != tkind.TK_IDENT));
assert(!(t.col != 2));
assert(!(t.text != "foo"));
lexnext(&l, &t);
assert(!(t.kind != tkind.TK_IDENT));
assert(!(t.col != 6));
assert(!(t.text != "bar"));
};

View File

@@ -80,6 +80,7 @@ static const struct row rows[] = {
{ "0xDE_AD_BE_EF", "INT(3735928559)" },
{ "0b1010", "INT(10)" },
{ "0o777", "INT(511)" },
{ "18446744073709551616", "ERR(18446744073709551616)" },
{ "3.14", "FLOAT(3.14)" },
{ "1.5e3", "FLOAT(1500)" },
@@ -149,6 +150,28 @@ runerr(const char *src)
return ok;
}
static int
runrewind(void)
{
const char *src = "1foo bar";
Arena *a = newarena();
Lex l;
lexinit(&l, a, "<test>", src, strlen(src));
Tok n = lexnext(&l);
Tok f = lexnext(&l);
Tok b = lexnext(&l);
int ok = n.kind == TK_INT && n.pos.col == 1 && n.v.uval == 1
&& f.kind == TK_IDENT && f.pos.col == 2
&& strcmp(f.text, "foo") == 0
&& b.kind == TK_IDENT && b.pos.col == 6
&& strcmp(b.text, "bar") == 0;
if (!ok)
fprintf(stderr, "suffix rewind columns: want 1,2,6 got %d,%d,%d\n",
n.pos.col, f.pos.col, b.pos.col);
freearena(a);
return ok;
}
static const char *const errrows[] = {
"'\\uZ'", /* unexpected rune scanning for escape */
"\"\\u00g0\"", /* non-hex digit inside a string escape */
@@ -169,6 +192,8 @@ main(void)
fail++;
}
}
if (!runrewind())
fail++;
for (size_t i = 0; i < sizeof errrows / sizeof errrows[0]; i++) {
if (!runerr(errrows[i])) {
fprintf(stderr, "errrow %zu failed\n", i);

View File

@@ -1,7 +1,7 @@
//ww:error "bad integer literal"
// 989_intoverflow_reject dec_overflow: a 21-digit decimal exceeds u64.
// Exact final-digit band: MAX/10 with 6 greater than MAX%10.
package main;
export fn main() i32 = {
let x: u64 = 99999999999999999999u64;
let x: u64 = 18446744073709551616u64;
return x: i32;
};