GOAL P1-1..1-3: 리터럴이 자기 타입에 안 맞으면 거부한다
그리고 그 자리를 파다가 더 나쁜 것이 나왔다. store 의 폭이 목적지가 아니라 값에서 왔다. 정수 리터럴은 더 좁은 것이 요구하기 전까지 i32 이므로 let b: u8 = 200; 은 4바이트가 1바이트 자리로 가는 것으로 도착하고, 4바이트를 쓰면 프레임이 그 옆에 놓은 것을 지운다. let a: i32 = 5; let b: u8 = 300; let d: u8 = 44; a 0 / b 0 / d 44 → a 5 / b 44 / d 44 폭 넓은 지역 하나만 있으면 드러나지 않아서 여태 살아 있었다. exec/narrow.fe 가 폭이 섞인 지역을 나란히 두어 고정한다. 규칙 자체는 SPEC §3 에 넣었다: 리터럴의 타입은 문맥이 요구하는 정수 타입이고, 없으면 i32 다. 범위를 벗어나면 잘리는 것이 아니라 거부된다. 앞의 단항 - 는 리터럴의 일부로 보아 i8 = -128 은 되고 u8 = -1 은 안 된다. 같이 넣은 문장 둘: - §9 미사용 타입 파라미터는 정상이다. typed handle 이 그 모양이고 구현은 이미 그렇게 동작했다. - §7.4 --no-checks 에서 오버플로는 랩어라운드로 정의된다. 타깃이 실제로 하는 일이고 미정의로 두지 않는다. 237/237, 35/35.
This commit is contained in:
@@ -549,6 +549,62 @@ int m7_actual_compatible(FeType *want, FeType *got, FeNode *value)
|
||||
return compatible(want,got,value);
|
||||
}
|
||||
|
||||
/* The magnitude an integer literal spells, ignoring any sign. The same shape
|
||||
lowering uses on the same text, so the two cannot disagree about what was
|
||||
written. */
|
||||
static unsigned long literal_magnitude(const char *s)
|
||||
{
|
||||
unsigned long v = 0;
|
||||
if (!s) return 0;
|
||||
if (s[0]=='0' && (s[1]=='x' || s[1]=='X')) {
|
||||
for (s += 2; *s; ++s) {
|
||||
int d = *s>='0'&&*s<='9' ? *s-'0' :
|
||||
*s>='a'&&*s<='f' ? *s-'a'+10 :
|
||||
*s>='A'&&*s<='F' ? *s-'A'+10 : -1;
|
||||
if (d < 0) { if (*s=='_') continue; break; }
|
||||
v = v*16UL + (unsigned long)d;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
for (; *s; ++s) {
|
||||
if (*s=='_') continue;
|
||||
if (*s<'0' || *s>'9') break;
|
||||
v = v*10UL + (unsigned long)(*s-'0');
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/* SPEC 4.1: an integer literal takes the type its context asks for, and a
|
||||
value that does not fit that type is a mistake where it is written rather
|
||||
than a truncation nobody sees. */
|
||||
static int literal_fits(const FeType *want, const char *text, int negative)
|
||||
{
|
||||
unsigned long v;
|
||||
unsigned long limit;
|
||||
unsigned bits;
|
||||
if (!want || want->kind != FE_TYPE_INT || !text) return 1;
|
||||
bits = want->bits ? want->bits : 32U;
|
||||
if (bits > 32U) bits = 32U;
|
||||
v = literal_magnitude(text);
|
||||
if (want->is_unsigned) {
|
||||
if (negative) return v == 0UL;
|
||||
if (bits >= 32U) return 1;
|
||||
return v <= (1UL << bits) - 1UL;
|
||||
}
|
||||
limit = bits >= 32U ? 2147483647UL : (1UL << (bits - 1U)) - 1UL;
|
||||
return v <= (negative ? limit + 1UL : limit);
|
||||
}
|
||||
|
||||
/* Is this node a plain integer literal, rather than a character, a string, or
|
||||
one of the word-shaped literals? */
|
||||
static int plain_int_literal(const FeNode *n)
|
||||
{
|
||||
return n && n->kind==FE_N_LITERAL && n->text &&
|
||||
n->text[0]!='\'' && n->text[0]!='"' &&
|
||||
strcmp(n->text,"true") && strcmp(n->text,"false") &&
|
||||
strcmp(n->text,"null") && strcmp(n->text,"undefined");
|
||||
}
|
||||
|
||||
FeType *m7_check_expected(FeCheckerState *s, FeNode *value,
|
||||
FeType *expected)
|
||||
{
|
||||
@@ -573,6 +629,21 @@ FeType *m7_check_expected(FeCheckerState *s, FeNode *value,
|
||||
value->sem_context=expected;
|
||||
return expected;
|
||||
}
|
||||
/* An integer literal is `i32` on its own; where an integer type is asked
|
||||
for it is that type instead, and it has to fit in it. */
|
||||
if (expected && expected->kind==FE_TYPE_INT) {
|
||||
FeNode *lit = plain_int_literal(value) ? value :
|
||||
(value->kind==FE_N_UNARY && value->text &&
|
||||
!strcmp(value->text,"-") && plain_int_literal(value->a)
|
||||
? value->a : 0);
|
||||
if (lit) {
|
||||
if (!literal_fits(expected, lit->text, lit!=value))
|
||||
err(s->c,value->loc,"integer literal out of range for its type");
|
||||
lit->sem_type=expected;
|
||||
value->sem_type=expected;
|
||||
return expected;
|
||||
}
|
||||
}
|
||||
actual=check_expr(s,value);
|
||||
if (!expected) return actual;
|
||||
if (expected->kind==FE_TYPE_OPTIONAL && expected->elem &&
|
||||
|
||||
+7
-1
@@ -8,7 +8,13 @@ void store_into(Lower *L, FeIrPlace dst, Slot value, FeNode *n,
|
||||
fe_ir_copy(L->m, L->b, dst, value.place, size);
|
||||
return;
|
||||
}
|
||||
fe_ir_store(L->m, L->b, dst, as_value(L, value, n), value.type);
|
||||
/* How wide the store is belongs to the place, not to the value. An
|
||||
integer literal is `i32` until something narrower asks for it, so
|
||||
`let b: u8 = 200;` arrives here as four bytes going into one -- and
|
||||
writing four wipes out whatever the frame put next to it. */
|
||||
fe_ir_store(L->m, L->b, dst, as_value(L, value, n),
|
||||
size == 1UL ? FE_IR_I8 :
|
||||
size == 2UL ? FE_IR_I16 : value.type);
|
||||
}
|
||||
|
||||
void lower_return(Lower *L, FeNode *n)
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// EXIT:0
|
||||
// OUTPUT:a 5 b 200 c 44 d 9
|
||||
// OUTPUT:e 5 f 1 g 65535
|
||||
// OUTPUT:sum 253
|
||||
unit narrow;
|
||||
|
||||
import std.io;
|
||||
|
||||
// How wide a store is belongs to the place, not to the value. An integer
|
||||
// literal is `i32` until something narrower asks for it, so `let b: u8 = 200;`
|
||||
// arrives at the store as four bytes going into one -- and writing four wipes
|
||||
// out whatever the frame put beside it.
|
||||
//
|
||||
// Several locals of mixed width, next to each other, is what it takes to see
|
||||
// it: each narrow store used to reach back over the one declared before it.
|
||||
|
||||
fn main() -> i32 {
|
||||
let a: i32 = 5;
|
||||
let b: u8 = 200;
|
||||
let c: u8 = 44;
|
||||
let d: i16 = 9;
|
||||
@print("a {} b {} c {} d {}\n", a, b, c, d);
|
||||
|
||||
let e = 5; // no annotation: i32 (SPEC 4.1)
|
||||
let f: i8 = 1;
|
||||
let g: u16 = 65535;
|
||||
@print("e {} f {} g {}\n", e, f as i32, g);
|
||||
|
||||
// And the values are still there after everything else was written.
|
||||
@print("sum {}\n", (b as i32) + (c as i32) + (d as i32) + e - (a as i32));
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// ERROR:8:integer literal out of range
|
||||
unit badlit;
|
||||
|
||||
// SPEC 4.1: an integer literal takes the type its context asks for. 300 is not
|
||||
// a u8, and truncating it silently is the one thing that must not happen.
|
||||
|
||||
fn bad() -> u8 {
|
||||
let n: u8 = 300;
|
||||
return n;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// ERROR:6:integer literal out of range
|
||||
unit badlitng;
|
||||
|
||||
// An unsigned type has no negative values to truncate to.
|
||||
fn bad() -> u8 {
|
||||
let n: u8 = -1;
|
||||
return n;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
unit oklit;
|
||||
|
||||
// The edges of each type are in range, and a literal with no context is i32.
|
||||
|
||||
fn ok() -> i32 {
|
||||
let a: u8 = 255;
|
||||
let b: i8 = -128;
|
||||
let c: i8 = 127;
|
||||
let d: u16 = 65535;
|
||||
let e: i32 = 2147483647;
|
||||
let f: i32 = -2147483648;
|
||||
let g: u8 = 0xFF;
|
||||
let h = 5;
|
||||
let i: usize = 4294967295;
|
||||
return h + (a as i32) + (b as i32) + (c as i32) + (d as i32) +
|
||||
(e - e) + (f - f) + (g as i32) + (i as i32);
|
||||
}
|
||||
Reference in New Issue
Block a user