mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-19 13:00:28 +00:00
Bug fix: comparisons with shifts
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
#import "fmt.odin";
|
||||
|
||||
main :: proc() {
|
||||
Fruit :: enum f32 {
|
||||
Apple = 123,
|
||||
Pear = 321,
|
||||
Tomato,
|
||||
}
|
||||
fmt.printf("%s = %f\n", Fruit.Apple, Fruit.Apple);
|
||||
fmt.printf("%f\n", 0.0);
|
||||
fmt.printf("%f\n", 1.0);
|
||||
fmt.printf("%f\n", -0.5);
|
||||
fmt.printf("%+f\n", 1334.67);
|
||||
fmt.printf("%f\n", 789.789);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,14 @@ Type_Info_Enum_Value :: raw_union {
|
||||
i: i64;
|
||||
}
|
||||
|
||||
// NOTE(bill): This much the same as the compiler's
|
||||
Calling_Convention :: enum {
|
||||
ODIN = 0,
|
||||
C = 1,
|
||||
STD = 2,
|
||||
FAST = 3,
|
||||
}
|
||||
|
||||
Type_Info :: union {
|
||||
Named: struct #ordered {
|
||||
name: string;
|
||||
@@ -52,9 +60,10 @@ Type_Info :: union {
|
||||
elem: ^Type_Info;
|
||||
};
|
||||
Procedure: struct #ordered {
|
||||
params: ^Type_Info; // Type_Info.Tuple
|
||||
results: ^Type_Info; // Type_Info.Tuple
|
||||
variadic: bool;
|
||||
params: ^Type_Info; // Type_Info.Tuple
|
||||
results: ^Type_Info; // Type_Info.Tuple
|
||||
variadic: bool;
|
||||
convention: Calling_Convention;
|
||||
};
|
||||
Array: struct #ordered {
|
||||
elem: ^Type_Info;
|
||||
|
||||
400
core/fmt.odin
400
core/fmt.odin
@@ -421,8 +421,7 @@ fmt_write_padding :: proc(fi: ^Fmt_Info, width: int) {
|
||||
}
|
||||
|
||||
fmt_integer :: proc(fi: ^Fmt_Info, u: u64, base: int, signed: bool, digits: string) {
|
||||
u_i64 := u as i64;
|
||||
negative := signed && u_i64 < 0;
|
||||
negative := signed && u as i64 < 0;
|
||||
if negative {
|
||||
u = -u;
|
||||
}
|
||||
@@ -548,49 +547,366 @@ fmt_int :: proc(fi: ^Fmt_Info, u: u64, signed: bool, verb: rune) {
|
||||
fmt_bad_verb(fi, verb);
|
||||
}
|
||||
}
|
||||
fmt_float :: proc(fi: ^Fmt_Info, v: f64, bits: int, verb: rune) {
|
||||
// TODO(bill): Actually print a float correctly
|
||||
// THIS IS FUCKING SHIT!
|
||||
|
||||
__bot := [23]f64{1e+000,1e+001,1e+002,1e+003,1e+004,1e+005,1e+006,1e+007,1e+008,1e+009,1e+010,1e+011,1e+012,1e+013,1e+014,1e+015,1e+016,1e+017,1e+018,1e+019,1e+020,1e+021,1e+022};
|
||||
__negbot := [22]f64{1e-001,1e-002,1e-003,1e-004,1e-005,1e-006,1e-007,1e-008,1e-009,1e-010,1e-011,1e-012,1e-013,1e-014,1e-015,1e-016,1e-017,1e-018,1e-019,1e-020,1e-021,1e-022};
|
||||
__negboterr := [22]f64{-5.551115123125783e-018,-2.0816681711721684e-019,-2.0816681711721686e-020,-4.7921736023859299e-021,-8.1803053914031305e-022,4.5251888174113741e-023,4.5251888174113739e-024,-2.0922560830128471e-025,-6.2281591457779853e-026,-3.6432197315497743e-027,6.0503030718060191e-028,2.0113352370744385e-029,-3.0373745563400371e-030,1.1806906454401013e-032,-7.7705399876661076e-032,2.0902213275965398e-033,-7.1542424054621921e-034,-7.1542424054621926e-035,2.4754073164739869e-036,5.4846728545790429e-037,9.2462547772103625e-038,-4.8596774326570872e-039};
|
||||
__top := [13]f64{1e+023,1e+046,1e+069,1e+092,1e+115,1e+138,1e+161,1e+184,1e+207,1e+230,1e+253,1e+276,1e+299};
|
||||
__negtop := [13]f64{1e-023,1e-046,1e-069,1e-092,1e-115,1e-138,1e-161,1e-184,1e-207,1e-230,1e-253,1e-276,1e-299};
|
||||
__toperr := [13]f64{8388608,6.8601809640529717e+028,-7.253143638152921e+052,-4.3377296974619174e+075,-1.5559416129466825e+098,-3.2841562489204913e+121,-3.7745893248228135e+144,-1.7356668416969134e+167,-3.8893577551088374e+190,-9.9566444326005119e+213,6.3641293062232429e+236,-5.2069140800249813e+259,-5.2504760255204387e+282};
|
||||
__negtoperr := [13]f64{3.9565301985100693e-040,-2.299904345391321e-063,3.6506201437945798e-086,1.1875228833981544e-109,-5.0644902316928607e-132,-6.7156837247865426e-155,-2.812077463003139e-178,-5.7778912386589953e-201,7.4997100559334532e-224,-4.6439668915134491e-247,-6.3691100762962136e-270,-9.436808465446358e-293,8.0970921678014997e-317};
|
||||
|
||||
__digitpair := "00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899";
|
||||
|
||||
|
||||
__powten := [20]u64{1,10,100,1000, 10000,100000,1000000,10000000, 100000000,1000000000,10000000000,100000000000, 1000000000000,10000000000000,100000000000000,1000000000000000, 10000000000000000,100000000000000000,1000000000000000000,10000000000000000000 };
|
||||
|
||||
__TEN_TO_19TH :: 1000000000000000000;
|
||||
|
||||
__ddmulthi :: proc(ol: f64, xh, yh: f64) -> f64 {
|
||||
bt: i64;
|
||||
oh := xh * yh;
|
||||
bt = xh transmute i64;
|
||||
bt &= (~(0 as u64)<<27) as i64;
|
||||
ahi := bt transmute f64;
|
||||
alo := xh-ahi;
|
||||
bt = yh transmute i64;
|
||||
bt &= (~(0 as u64)<<27) as i64;
|
||||
bhi := bt transmute f64;
|
||||
blo := yh-bhi;
|
||||
return ((ahi*bhi-oh)+ahi*blo+alo*bhi)+alo*blo;
|
||||
}
|
||||
|
||||
__ddtoi64 :: proc(xh, xl: f64) -> i64 {
|
||||
ob := xh as i64;
|
||||
vh := ob as f64;
|
||||
ahi := xh-vh;
|
||||
t := ahi-xh;
|
||||
alo := (xh-(ahi-t)) - (vh+t);
|
||||
ob += (ahi+alo+xl) as i64;
|
||||
return ob;
|
||||
}
|
||||
|
||||
__ddrenorm :: proc(oh, ol: f64) -> f64 {
|
||||
s := oh + ol;
|
||||
ol = ol - (s-oh);
|
||||
return s;
|
||||
}
|
||||
|
||||
__ddmultlo :: proc(oh, ol, xh, xl, yh, yl: f64) -> f64 {
|
||||
return ol + (xh*yl + xl*yh);
|
||||
}
|
||||
|
||||
__ddmutlos :: proc(oh, ol, xh, yl: f64) -> f64 {
|
||||
return ol + (xh*yl);
|
||||
}
|
||||
|
||||
__raise_to_power10 :: proc(ohi, olo: ^f64, d: f64, power: i32) { // power can be -323 to +350
|
||||
ph, pl: f64;
|
||||
|
||||
if 0<=power&&power<=22 {
|
||||
ph = __ddmulthi(pl, d, __bot[power]);
|
||||
} else {
|
||||
p2h, p2l: f64;
|
||||
|
||||
e := power; if power<0 { e = -e; }
|
||||
et := (e*0x2c9)>>14;
|
||||
if et>13 {
|
||||
et = 13;
|
||||
}
|
||||
eb := e-(et*23);
|
||||
|
||||
ph = d;
|
||||
pl = 0.0;
|
||||
if power<0 {
|
||||
if eb != 0 {
|
||||
eb -= 1;
|
||||
ph = __ddmulthi(pl, d, __negbot[eb]);
|
||||
ph = __ddmutlos(ph, pl, d, __negboterr[eb]);
|
||||
}
|
||||
if et != 0 {
|
||||
ph = __ddrenorm(ph, pl);
|
||||
et -= 1;
|
||||
p2h = __ddmulthi(p2l, ph, __negtop[et]);
|
||||
p2h = __ddmultlo(p2h, p2l, ph, pl, __negtop[et], __negtoperr[et]);
|
||||
ph = p2h;
|
||||
pl = p2l;
|
||||
}
|
||||
} else {
|
||||
if eb != 0 {
|
||||
e = eb;
|
||||
if eb > 22 {
|
||||
eb = 22;
|
||||
}
|
||||
e -= eb;
|
||||
ph = __ddmulthi(pl, d, __bot[eb]);
|
||||
if e != 0 {
|
||||
ph = __ddrenorm(ph, pl);
|
||||
p2h = __ddmulthi(p2l, ph, __bot[e]);
|
||||
p2h = __ddmutlos(p2h, p2l, __bot[e], pl);
|
||||
ph = p2h;
|
||||
pl = p2l;
|
||||
}
|
||||
}
|
||||
if et != 0 {
|
||||
ph = __ddrenorm(ph, pl);
|
||||
et -= 1;
|
||||
p2h = __ddmulthi(p2l, ph, __top[et]);
|
||||
p2h = __ddmultlo(p2h, p2l, ph, pl, __top[et], __toperr[et]);
|
||||
ph = p2h;
|
||||
pl = p2l;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ph = __ddrenorm(ph, pl);
|
||||
ohi^ = ph;
|
||||
olo^ = pl;
|
||||
}
|
||||
|
||||
__SPECIAL :: 0x7000;
|
||||
|
||||
__real_to_string :: proc(start: ^string, out: []byte, decimal_pos: ^i32, val: f64, frac_digits: i32, verb: rune) -> bool {
|
||||
e, tens: i32;
|
||||
d: f64 = val;
|
||||
|
||||
bits := d transmute i64;
|
||||
expo := (bits>>52 & 2047) as i32;
|
||||
neg := (bits>>63) as i32 != 0;
|
||||
if neg {
|
||||
d = -d;
|
||||
}
|
||||
|
||||
if expo == 2047 {
|
||||
x: i64 = 1<<52-1;
|
||||
if bits&x != 0 {
|
||||
start^ = "NaN";
|
||||
} else {
|
||||
start^ = "Inf";
|
||||
}
|
||||
decimal_pos^ = __SPECIAL;
|
||||
return neg;
|
||||
}
|
||||
|
||||
if expo == 0 { // is zero or denormal
|
||||
if bits<<1 == 0 {
|
||||
decimal_pos^ = 1;
|
||||
out[0] = '0';
|
||||
start^ = out[:1] as string;
|
||||
return neg;
|
||||
}
|
||||
// find the right expo for denormals
|
||||
v: i64 = 1<<51;
|
||||
while bits&v == 0 {
|
||||
expo -=1;
|
||||
v >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// find the decimal exponent as well as the decimal bits of the value
|
||||
{
|
||||
// log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046
|
||||
ph, pl: f64;
|
||||
tens = expo-1023;
|
||||
if tens < 0 {
|
||||
tens = (tens*617)/2048;
|
||||
} else {
|
||||
tens = ((tens*1233)/4096) + 1;
|
||||
}
|
||||
|
||||
// move the significant bits into position and stick them into an int
|
||||
__raise_to_power10(^ph, ^pl, d, 18-tens);
|
||||
|
||||
// get full as much precision from double-double as possible
|
||||
bits = __ddtoi64(ph, pl);
|
||||
|
||||
// check if we undershot
|
||||
if bits as u64 >= __TEN_TO_19TH {
|
||||
tens += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// now do the rounding in integer land
|
||||
match verb {
|
||||
case 'e', 'E', 'f', 'F', 'g', 'G', 'v':
|
||||
break;
|
||||
case 'e', 'E', 'g', 'G':
|
||||
frac_digits += 1;
|
||||
default:
|
||||
frac_digits += tens;
|
||||
}
|
||||
|
||||
if frac_digits < 24 {
|
||||
skip := false;
|
||||
dg: u32 = 1;
|
||||
if bits as u64 >= __powten[9] {
|
||||
dg = 10;
|
||||
}
|
||||
while bits as u64 >= __powten[dg] {
|
||||
dg += 1;
|
||||
if dg == 20 {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!skip) {
|
||||
r: u64;
|
||||
// add 0.5 at the right position and round
|
||||
e = dg as i32 - frac_digits;
|
||||
if e as u32 < 24 {
|
||||
r = __powten[e];
|
||||
bits += (r/2) as i64;
|
||||
if bits as u64 >= __powten[dg] {
|
||||
tens += 1;
|
||||
}
|
||||
bits /= r as i64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// kill long trailing runs of zeros
|
||||
if bits != 0 {
|
||||
skip := false;
|
||||
while true {
|
||||
if bits <= 0xffffffff {
|
||||
break;
|
||||
}
|
||||
if bits%1000 != 0 {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
bits /= 1000;
|
||||
}
|
||||
if !skip {
|
||||
n := bits as u32;
|
||||
while n%1000 == 0 {
|
||||
n /= 1000;
|
||||
}
|
||||
bits = n as i64;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
e = 0;
|
||||
outp := ^out[64];
|
||||
while true {
|
||||
n: u32;
|
||||
o := outp-8;
|
||||
// do the conversion in chunks of u32s (avoid most 64-bit divides, worth it, constant denomiators be damned)
|
||||
if bits >= 100000000 {
|
||||
n = (bits%100000000) as u32;
|
||||
bits /= 100000000;
|
||||
} else {
|
||||
n = bits as u32;
|
||||
bits = 0;
|
||||
}
|
||||
while n != 0 {
|
||||
outp -= 2;
|
||||
(outp as ^u16)^ = (^__digitpair[(n%100)*2] as ^u16)^;
|
||||
n /= 100;
|
||||
e += 2;
|
||||
}
|
||||
if bits == 0 {
|
||||
if e != 0 && outp^ == '0' {
|
||||
outp += 1;
|
||||
e -= 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
while outp != o {
|
||||
outp -= 1;
|
||||
outp^ = '0';
|
||||
e += 1;
|
||||
}
|
||||
}
|
||||
|
||||
decimal_pos^ = tens;
|
||||
start^ = slice_ptr(outp, e) as string;
|
||||
return neg;
|
||||
}
|
||||
|
||||
|
||||
generic_ftoa :: proc(buf: []byte, val: f64, verb: rune, prec, bit_size: int) -> []byte {
|
||||
Float_Info :: struct {
|
||||
mantbits: uint;
|
||||
expbits: uint;
|
||||
bias: int;
|
||||
};
|
||||
f32info := Float_Info{23, 8, -127};
|
||||
f64info := Float_Info{52, 11, -1023};
|
||||
|
||||
|
||||
bits: u64;
|
||||
flt: ^Float_Info;
|
||||
match bit_size {
|
||||
case 32:
|
||||
bits = ((val as f32) transmute u32) as u64;
|
||||
flt = ^f32info;
|
||||
case 64:
|
||||
bits = val transmute u64;
|
||||
flt = ^f64info;
|
||||
default:
|
||||
panic("illegal float bit_size");
|
||||
}
|
||||
|
||||
neg := bits>>(flt.expbits+flt.mantbits) != 0;
|
||||
exp := (bits>>flt.mantbits) as int & (1<<flt.expbits - 1);
|
||||
mant := bits & ((1 as u64)<<flt.mantbits - 1);
|
||||
|
||||
match exp {
|
||||
case 1<<flt.expbits-1:
|
||||
s: string;
|
||||
match {
|
||||
case mant!=0: s = "NaN";
|
||||
case neg: s = "-Inf";
|
||||
default: s = "+Inf";
|
||||
}
|
||||
copy(buf, s as []byte);
|
||||
return buf[:s.count];
|
||||
|
||||
case 0: // denormalized
|
||||
exp+=1;
|
||||
default: // add implicit top bit
|
||||
mant |= (1 as u64)<<flt.mantbits;
|
||||
}
|
||||
|
||||
|
||||
i := 0;
|
||||
match verb {
|
||||
case 'e', 'E':
|
||||
|
||||
case 'v', 'f', 'F':
|
||||
if neg {
|
||||
buf[i] = '-'; i+=1;
|
||||
}
|
||||
buf[i] = '0'; i+=1;
|
||||
if prec > 0 {
|
||||
buf[i] = '.'; i+=1;
|
||||
for j : 0..<prec {
|
||||
ch: byte = '0';
|
||||
}
|
||||
}
|
||||
|
||||
case 'g', 'G':
|
||||
|
||||
}
|
||||
|
||||
return buf[:0];
|
||||
}
|
||||
|
||||
fmt_float :: proc(fi: ^Fmt_Info, v: f64, bit_size: int, verb: rune) {
|
||||
buf: [512]byte;
|
||||
|
||||
match verb {
|
||||
// case 'e', 'E', 'f', 'F', 'g', 'G', 'v':
|
||||
// case 'f', 'F', 'v':
|
||||
|
||||
case 'f', 'F', 'v':
|
||||
b := generic_ftoa(buf[:], v, verb, fi.prec, bit_size);
|
||||
buffer_write(fi.buf, b);
|
||||
default:
|
||||
fmt_bad_verb(fi, verb);
|
||||
return;
|
||||
}
|
||||
|
||||
f := v;
|
||||
|
||||
if f == 0 {
|
||||
buffer_write_byte(fi.buf, '0');
|
||||
return;
|
||||
}
|
||||
|
||||
if f < 0 {
|
||||
buffer_write_byte(fi.buf, '-');
|
||||
f = -f;
|
||||
}
|
||||
i := f as u64;
|
||||
fmt_int(fi, i, false, 'd');
|
||||
f -= i as f64;
|
||||
buffer_write_byte(fi.buf, '.');
|
||||
|
||||
decimal_places := 5;
|
||||
match bits {
|
||||
case 32: decimal_places = 7;
|
||||
case 64: decimal_places = 15;
|
||||
}
|
||||
if fi.prec_set {
|
||||
decimal_places = fi.prec;
|
||||
}
|
||||
|
||||
while mult: f64 = 10.0; decimal_places >= 0 {
|
||||
i = (f * mult) as u64;
|
||||
fmt_int(fi, i, false, 'd');
|
||||
f -= i as f64 / mult;
|
||||
mult *= 10;
|
||||
decimal_places -= 1;
|
||||
}
|
||||
}
|
||||
fmt_string :: proc(fi: ^Fmt_Info, s: string, verb: rune) {
|
||||
match verb {
|
||||
@@ -833,8 +1149,8 @@ fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) {
|
||||
|
||||
if verb == 'T' {
|
||||
ti := arg.type_info;
|
||||
if ti == type_info(^Type_Info) {
|
||||
ti = (arg.data as ^^Type_Info)^;
|
||||
match type a : arg {
|
||||
case ^Type_Info: ti = a;
|
||||
}
|
||||
buffer_write_type(fi.buf, ti);
|
||||
return;
|
||||
|
||||
328
src/check_expr.c
328
src/check_expr.c
@@ -28,143 +28,11 @@ gb_inline Type *check_type(Checker *c, AstNode *expression) {
|
||||
|
||||
|
||||
|
||||
typedef struct DelayedEntity {
|
||||
AstNode * ident;
|
||||
Entity * entity;
|
||||
DeclInfo * decl;
|
||||
} DelayedEntity;
|
||||
|
||||
typedef struct DelayedOtherFields {
|
||||
Entity **other_fields;
|
||||
isize other_field_count;
|
||||
isize other_field_index;
|
||||
|
||||
MapEntity *entity_map;
|
||||
} DelayedOtherFields;
|
||||
|
||||
typedef Array(DelayedEntity) DelayedEntities;
|
||||
|
||||
void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntities *delayed_entities, DelayedOtherFields *dof);
|
||||
|
||||
void check_local_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, DelayedEntities *delayed_entities, DelayedOtherFields *dof) {
|
||||
Operand operand = {Addressing_Invalid};
|
||||
check_expr(c, &operand, ws->cond);
|
||||
if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
|
||||
error_node(ws->cond, "Non-boolean condition in `when` statement");
|
||||
}
|
||||
if (operand.mode != Addressing_Constant) {
|
||||
error_node(ws->cond, "Non-constant condition in `when` statement");
|
||||
}
|
||||
if (ws->body == NULL || ws->body->kind != AstNode_BlockStmt) {
|
||||
error_node(ws->cond, "Invalid body for `when` statement");
|
||||
} else {
|
||||
if (operand.value.kind == ExactValue_Bool &&
|
||||
operand.value.value_bool) {
|
||||
check_local_collect_entities(c, ws->body->BlockStmt.stmts, delayed_entities, dof);
|
||||
} else if (ws->else_stmt) {
|
||||
switch (ws->else_stmt->kind) {
|
||||
case AstNode_BlockStmt:
|
||||
check_local_collect_entities(c, ws->else_stmt->BlockStmt.stmts, delayed_entities, dof);
|
||||
break;
|
||||
case AstNode_WhenStmt:
|
||||
check_local_collect_entities_from_when_stmt(c, &ws->else_stmt->WhenStmt, delayed_entities, dof);
|
||||
break;
|
||||
default:
|
||||
error_node(ws->else_stmt, "Invalid `else` statement in `when` statement");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(bill): The `dof` is for use within records
|
||||
void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntities *delayed_entities, DelayedOtherFields *dof) {
|
||||
for_array(i, nodes) {
|
||||
AstNode *node = nodes.e[i];
|
||||
switch (node->kind) {
|
||||
case_ast_node(ws, WhenStmt, node);
|
||||
// Will be handled later
|
||||
case_end;
|
||||
|
||||
case_ast_node(vd, ValueDecl, node);
|
||||
if (vd->is_var) {
|
||||
// NOTE(bill): Handled later
|
||||
} else {
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names.e[i];
|
||||
if (name->kind != AstNode_Ident) {
|
||||
error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
|
||||
continue;
|
||||
}
|
||||
|
||||
AstNode *init = NULL;
|
||||
if (i < vd->values.count) {
|
||||
init = vd->values.e[i];
|
||||
}
|
||||
|
||||
DeclInfo *d = make_declaration_info(c->allocator, c->context.scope);
|
||||
Entity *e = NULL;
|
||||
|
||||
AstNode *up_init = unparen_expr(init);
|
||||
if (init != NULL && is_ast_node_type(up_init)) {
|
||||
e = make_entity_type_name(c->allocator, d->scope, name->Ident, NULL);
|
||||
d->type_expr = init;
|
||||
d->init_expr = init;
|
||||
} else if (init != NULL && up_init->kind == AstNode_ProcLit) {
|
||||
e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags);
|
||||
d->proc_lit = init;
|
||||
} else {
|
||||
e = make_entity_constant(c->allocator, d->scope, name->Ident, NULL, (ExactValue){0});
|
||||
d->type_expr = vd->type;
|
||||
d->init_expr = init;
|
||||
}
|
||||
GB_ASSERT(e != NULL);
|
||||
e->identifier = name;
|
||||
|
||||
add_entity_and_decl_info(c, name, e, d);
|
||||
|
||||
DelayedEntity delay = {name, e, d};
|
||||
array_add(delayed_entities, delay);
|
||||
}
|
||||
|
||||
check_arity_match(c, vd);
|
||||
}
|
||||
case_end;
|
||||
#if 0
|
||||
case_ast_node(pd, ProcDecl, node);
|
||||
if (!ast_node_expect(pd->name, AstNode_Ident)) {
|
||||
break;
|
||||
}
|
||||
|
||||
Entity *e = make_entity_procedure(c->allocator, c->context.scope, pd->name->Ident, NULL);
|
||||
e->identifier = pd->name;
|
||||
|
||||
DeclInfo *d = make_declaration_info(c->allocator, e->scope);
|
||||
d->proc_lit = node;
|
||||
|
||||
add_entity_and_decl_info(c, pd->name, e, d);
|
||||
check_entity_decl(c, e, d, NULL, NULL);
|
||||
case_end;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(bill): `when` stmts need to be handled after the other as the condition may refer to something
|
||||
// declared after this stmt in source
|
||||
for_array(i, nodes) {
|
||||
AstNode *node = nodes.e[i];
|
||||
switch (node->kind) {
|
||||
case_ast_node(ws, WhenStmt, node);
|
||||
check_local_collect_entities_from_when_stmt(c, ws, delayed_entities, dof);
|
||||
case_end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void check_scope_decls(Checker *c, AstNodeArray nodes, isize reserve_size, DelayedOtherFields *dof) {
|
||||
void check_scope_decls(Checker *c, AstNodeArray nodes, isize reserve_size) {
|
||||
GB_ASSERT(!c->context.scope->is_file);
|
||||
DelayedEntities delayed_entities;
|
||||
array_init_reserve(&delayed_entities, heap_allocator(), reserve_size);
|
||||
check_local_collect_entities(c, nodes, &delayed_entities, dof);
|
||||
check_collect_entities(c, nodes, NULL, &delayed_entities);
|
||||
|
||||
for_array(i, delayed_entities) {
|
||||
DelayedEntity delayed = delayed_entities.e[i];
|
||||
@@ -462,7 +330,7 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
|
||||
|
||||
Type *type = check_type_extra(c, f->type, NULL);
|
||||
|
||||
if (f->is_using) {
|
||||
if (f->flags&FieldFlag_using) {
|
||||
if (f->names.count > 1) {
|
||||
error_node(f->names.e[0], "Cannot apply `using` to more than one of the same type");
|
||||
}
|
||||
@@ -476,7 +344,7 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
|
||||
|
||||
Token name_token = name->Ident;
|
||||
|
||||
Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, f->is_using, cast(i32)field_index);
|
||||
Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, f->flags&FieldFlag_using, cast(i32)field_index);
|
||||
e->identifier = name;
|
||||
if (str_eq(name_token.string, str_lit("_"))) {
|
||||
fields[field_index++] = e;
|
||||
@@ -495,7 +363,7 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
|
||||
}
|
||||
|
||||
|
||||
if (f->is_using) {
|
||||
if (f->flags&FieldFlag_using) {
|
||||
Type *t = base_type(type_deref(type));
|
||||
if (!is_type_struct(t) && !is_type_raw_union(t) &&
|
||||
f->names.count >= 1 &&
|
||||
@@ -843,7 +711,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNodeArray params, bool *is_v
|
||||
for_array(j, p->names) {
|
||||
AstNode *name = p->names.e[j];
|
||||
if (ast_node_expect(name, AstNode_Ident)) {
|
||||
Entity *param = make_entity_param(c->allocator, scope, name->Ident, type, p->is_using);
|
||||
Entity *param = make_entity_param(c->allocator, scope, name->Ident, type, p->flags&FieldFlag_using);
|
||||
add_entity(c, scope, name, param);
|
||||
variables[variable_index++] = param;
|
||||
}
|
||||
@@ -966,25 +834,15 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type) {
|
||||
o->type = t_invalid;
|
||||
return;
|
||||
}
|
||||
#if 0
|
||||
if (e->Variable.param) {
|
||||
o->mode = Addressing_Value;
|
||||
} else {
|
||||
o->mode = Addressing_Variable;
|
||||
}
|
||||
#else
|
||||
o->mode = Addressing_Variable;
|
||||
if (e->Variable.is_immutable) {
|
||||
o->mode = Addressing_Value;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case Entity_TypeName: {
|
||||
o->mode = Addressing_Type;
|
||||
#if 1
|
||||
// TODO(bill): Fix cyclical dependancy checker
|
||||
#endif
|
||||
// TODO(bill): Fix cyclical dependancy checker
|
||||
} break;
|
||||
|
||||
case Entity_Procedure:
|
||||
@@ -1028,7 +886,7 @@ i64 check_array_count(Checker *c, AstNode *e) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Type *type = base_type(base_enum_type(o.type));
|
||||
Type *type = base_type(o.type);
|
||||
if (is_type_untyped(type) || is_type_integer(type)) {
|
||||
if (o.value.kind == ExactValue_Integer) {
|
||||
i64 count = o.value.value_integer;
|
||||
@@ -1252,7 +1110,7 @@ end:
|
||||
|
||||
bool check_unary_op(Checker *c, Operand *o, Token op) {
|
||||
// TODO(bill): Handle errors correctly
|
||||
Type *type = base_type(base_enum_type(base_vector_type(o->type)));
|
||||
Type *type = base_type(base_vector_type(o->type));
|
||||
gbString str = NULL;
|
||||
switch (op.kind) {
|
||||
case Token_Add:
|
||||
@@ -1288,7 +1146,7 @@ bool check_unary_op(Checker *c, Operand *o, Token op) {
|
||||
|
||||
bool check_binary_op(Checker *c, Operand *o, Token op) {
|
||||
// TODO(bill): Handle errors correctly
|
||||
Type *type = base_type(base_enum_type(base_vector_type(o->type)));
|
||||
Type *type = base_type(base_vector_type(o->type));
|
||||
switch (op.kind) {
|
||||
case Token_Sub:
|
||||
case Token_SubEq:
|
||||
@@ -1359,6 +1217,7 @@ bool check_binary_op(Checker *c, Operand *o, Token op) {
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value) {
|
||||
if (in_value.kind == ExactValue_Invalid) {
|
||||
// NOTE(bill): There's already been an error
|
||||
@@ -1389,7 +1248,6 @@ bool check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, Exa
|
||||
}
|
||||
i64 imax = (1ll << (s-1ll));
|
||||
|
||||
|
||||
switch (type->Basic.kind) {
|
||||
case Basic_i8:
|
||||
case Basic_i16:
|
||||
@@ -1581,16 +1439,15 @@ void check_unary_expr(Checker *c, Operand *o, Token op, AstNode *node) {
|
||||
o->mode = Addressing_Value;
|
||||
}
|
||||
|
||||
void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||
|
||||
void check_comparison(Checker *c, Operand *x, Operand *y, TokenKind op) {
|
||||
gbString err_str = NULL;
|
||||
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||
if (check_is_assignable_to(c, x, y->type) ||
|
||||
check_is_assignable_to(c, y, x->type)) {
|
||||
Type *err_type = x->type;
|
||||
bool defined = false;
|
||||
switch (op.kind) {
|
||||
switch (op) {
|
||||
case Token_CmpEq:
|
||||
case Token_NotEq:
|
||||
defined = is_type_comparable(x->type);
|
||||
@@ -1615,7 +1472,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
|
||||
if (!defined) {
|
||||
gbString type_string = type_to_string(err_type);
|
||||
err_str = gb_string_make(c->tmp_allocator,
|
||||
gb_bprintf("operator `%.*s` not defined for type `%s`", LIT(op.string), type_string));
|
||||
gb_bprintf("operator `%.*s` not defined for type `%s`", LIT(token_strings[op]), type_string));
|
||||
gb_string_free(type_string);
|
||||
}
|
||||
} else {
|
||||
@@ -1633,7 +1490,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
|
||||
} else {
|
||||
if (x->mode == Addressing_Constant &&
|
||||
y->mode == Addressing_Constant) {
|
||||
x->value = make_exact_value_bool(compare_exact_values(op.kind, x->value, y->value));
|
||||
x->value = make_exact_value_bool(compare_exact_values(op, x->value, y->value));
|
||||
} else {
|
||||
x->mode = Addressing_Value;
|
||||
|
||||
@@ -1650,8 +1507,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
|
||||
|
||||
if (err_str != NULL) {
|
||||
gb_string_free(err_str);
|
||||
};
|
||||
|
||||
}
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
}
|
||||
|
||||
@@ -1665,7 +1521,7 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
|
||||
}
|
||||
|
||||
bool x_is_untyped = is_type_untyped(x->type);
|
||||
if (!(is_type_integer(base_enum_type(x->type)) || (x_is_untyped && x_val.kind == ExactValue_Integer))) {
|
||||
if (!(is_type_integer(x->type) || (x_is_untyped && x_val.kind == ExactValue_Integer))) {
|
||||
gbString err_str = expr_to_string(x->expr);
|
||||
error_node(node, "Shifted operand `%s` must be an integer", err_str);
|
||||
gb_string_free(err_str);
|
||||
@@ -1673,7 +1529,7 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_type_unsigned(base_enum_type(y->type))) {
|
||||
if (is_type_unsigned(y->type)) {
|
||||
|
||||
} else if (is_type_untyped(y->type)) {
|
||||
convert_to_typed(c, y, t_untyped_integer, 0);
|
||||
@@ -1702,7 +1558,7 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
|
||||
}
|
||||
|
||||
u64 amount = cast(u64)y_val.value_integer;
|
||||
if (amount > 1074) {
|
||||
if (amount > 64) {
|
||||
gbString err_str = expr_to_string(y->expr);
|
||||
error_node(node, "Shift amount too large: `%s`", err_str);
|
||||
gb_string_free(err_str);
|
||||
@@ -1710,7 +1566,7 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_type_integer(base_enum_type(x->type))) {
|
||||
if (!is_type_integer(x->type)) {
|
||||
// NOTE(bill): It could be an untyped float but still representable
|
||||
// as an integer
|
||||
x->type = t_untyped_integer;
|
||||
@@ -1719,17 +1575,19 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
|
||||
x->value = exact_value_shift(be->op.kind, x_val, make_exact_value_integer(amount));
|
||||
|
||||
if (is_type_typed(x->type)) {
|
||||
check_is_expressible(c, x, base_type(base_enum_type(x->type)));
|
||||
check_is_expressible(c, x, base_type(x->type));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
TokenPos pos = ast_node_token(x->expr).pos;
|
||||
if (x_is_untyped) {
|
||||
ExprInfo *info = map_expr_info_get(&c->info.untyped, hash_pointer(x->expr));
|
||||
if (info != NULL) {
|
||||
info->is_lhs = true;
|
||||
}
|
||||
x->mode = Addressing_Value;
|
||||
// x->value = x_val;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1740,6 +1598,14 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
|
||||
gb_string_free(err_str);
|
||||
}
|
||||
|
||||
if (!is_type_integer(x->type)) {
|
||||
gbString err_str = expr_to_string(y->expr);
|
||||
error_node(node, "Shift operand `%s` must be an integer", err_str);
|
||||
gb_string_free(err_str);
|
||||
x->mode = Addressing_Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
x->mode = Addressing_Value;
|
||||
}
|
||||
|
||||
@@ -2129,10 +1995,10 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
|
||||
}
|
||||
|
||||
if (op.kind == Token_Add || op.kind == Token_Sub) {
|
||||
if (is_type_pointer(x->type) && is_type_integer(base_enum_type(y->type))) {
|
||||
if (is_type_pointer(x->type) && is_type_integer(y->type)) {
|
||||
*x = check_ptr_addition(c, op.kind, x, y, node);
|
||||
return;
|
||||
} else if (is_type_integer(base_enum_type(x->type)) && is_type_pointer(y->type)) {
|
||||
} else if (is_type_integer(x->type) && is_type_pointer(y->type)) {
|
||||
if (op.kind == Token_Sub) {
|
||||
gbString lhs = expr_to_string(x->expr);
|
||||
gbString rhs = expr_to_string(y->expr);
|
||||
@@ -2159,7 +2025,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
|
||||
}
|
||||
|
||||
if (token_is_comparison(op)) {
|
||||
check_comparison(c, x, y, op);
|
||||
check_comparison(c, x, y, op.kind);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2259,48 +2125,58 @@ void update_expr_type(Checker *c, AstNode *e, Type *type, bool final) {
|
||||
if (found == NULL) {
|
||||
return;
|
||||
}
|
||||
ExprInfo old = *found;
|
||||
|
||||
switch (e->kind) {
|
||||
case_ast_node(ue, UnaryExpr, e);
|
||||
if (found->value.kind != ExactValue_Invalid) {
|
||||
if (old.value.kind != ExactValue_Invalid) {
|
||||
// NOTE(bill): if `e` is constant, the operands will be constant too.
|
||||
// They don't need to be updated as they will be updated later and
|
||||
// checked at the end of general checking stage.
|
||||
break;
|
||||
}
|
||||
update_expr_type(c, ue->expr, type, final);
|
||||
case_end;
|
||||
|
||||
case_ast_node(be, BinaryExpr, e);
|
||||
if (found->value.kind != ExactValue_Invalid) {
|
||||
if (old.value.kind != ExactValue_Invalid) {
|
||||
// See above note in UnaryExpr case
|
||||
break;
|
||||
}
|
||||
if (!token_is_comparison(be->op)) {
|
||||
if (token_is_shift(be->op)) {
|
||||
update_expr_type(c, be->left, type, final);
|
||||
} else {
|
||||
update_expr_type(c, be->left, type, final);
|
||||
update_expr_type(c, be->right, type, final);
|
||||
}
|
||||
if (token_is_comparison(be->op)) {
|
||||
}
|
||||
else if (token_is_shift(be->op)) {
|
||||
update_expr_type(c, be->left, type, final);
|
||||
} else {
|
||||
update_expr_type(c, be->left, type, final);
|
||||
update_expr_type(c, be->right, type, final);
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(pe, ParenExpr, e);
|
||||
update_expr_type(c, pe->expr, type, final);
|
||||
case_end;
|
||||
}
|
||||
|
||||
if (!final && is_type_untyped(type)) {
|
||||
found->type = base_type(type);
|
||||
map_expr_info_set(&c->info.untyped, key, *found);
|
||||
} else {
|
||||
ExprInfo old = *found;
|
||||
map_expr_info_remove(&c->info.untyped, key);
|
||||
|
||||
if (old.is_lhs && !is_type_integer(type)) {
|
||||
gbString expr_str = expr_to_string(e);
|
||||
gbString type_str = type_to_string(type);
|
||||
error_node(e, "Shifted operand %s must be an integer, got %s", expr_str, type_str);
|
||||
gb_string_free(type_str);
|
||||
gb_string_free(expr_str);
|
||||
return;
|
||||
}
|
||||
|
||||
add_type_and_value(&c->info, e, found->mode, type, found->value);
|
||||
old.type = base_type(type);
|
||||
map_expr_info_set(&c->info.untyped, key, old);
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to remove it and then give it a new one
|
||||
map_expr_info_remove(&c->info.untyped, key);
|
||||
|
||||
if (old.is_lhs && !is_type_integer(type)) {
|
||||
gbString expr_str = expr_to_string(e);
|
||||
gbString type_str = type_to_string(type);
|
||||
error_node(e, "Shifted operand %s must be an integer, got %s", expr_str, type_str);
|
||||
gb_string_free(type_str);
|
||||
gb_string_free(expr_str);
|
||||
return;
|
||||
}
|
||||
|
||||
add_type_and_value(&c->info, e, old.mode, type, old.value);
|
||||
}
|
||||
|
||||
void update_expr_value(Checker *c, AstNode *e, ExactValue value) {
|
||||
@@ -2426,7 +2302,7 @@ bool check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *val
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_type_integer(base_enum_type(operand.type))) {
|
||||
if (!is_type_integer(operand.type)) {
|
||||
gbString expr_str = expr_to_string(operand.expr);
|
||||
error_node(operand.expr, "Index `%s` must be an integer", expr_str);
|
||||
gb_string_free(expr_str);
|
||||
@@ -2665,7 +2541,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
|
||||
if (op.mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
if (!is_type_integer(base_enum_type(op.type))) {
|
||||
if (!is_type_integer(op.type)) {
|
||||
gbString type_str = type_to_string(op.type);
|
||||
error_node(call, "Length for `new_slice` must be an integer, got `%s`", type_str);
|
||||
gb_string_free(type_str);
|
||||
@@ -4605,7 +4481,6 @@ gbString write_expr_to_string(gbString str, AstNode *node);
|
||||
|
||||
gbString write_params_to_string(gbString str, AstNodeArray params, char *sep) {
|
||||
for_array(i, params) {
|
||||
ast_node(p, Field, params.e[i]);
|
||||
if (i > 0) {
|
||||
str = gb_string_appendc(str, sep);
|
||||
}
|
||||
@@ -4615,6 +4490,18 @@ gbString write_params_to_string(gbString str, AstNodeArray params, char *sep) {
|
||||
return str;
|
||||
}
|
||||
|
||||
gbString write_record_fields_to_string(gbString str, AstNodeArray params) {
|
||||
for_array(i, params) {
|
||||
if (i > 0) {
|
||||
str = gb_string_appendc(str, " ");
|
||||
}
|
||||
str = write_expr_to_string(str, params.e[i]);
|
||||
str = gb_string_appendc(str, ";");
|
||||
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
gbString string_append_token(gbString str, Token token) {
|
||||
if (token.string.len > 0) {
|
||||
return gb_string_append_length(str, token.string.text, token.string.len);
|
||||
@@ -4758,12 +4645,12 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
|
||||
str = write_expr_to_string(str, vt->elem);
|
||||
case_end;
|
||||
|
||||
case_ast_node(p, Field, node);
|
||||
if (p->is_using) {
|
||||
case_ast_node(f, Field, node);
|
||||
if (f->flags&FieldFlag_using) {
|
||||
str = gb_string_appendc(str, "using ");
|
||||
}
|
||||
for_array(i, p->names) {
|
||||
AstNode *name = p->names.e[i];
|
||||
for_array(i, f->names) {
|
||||
AstNode *name = f->names.e[i];
|
||||
if (i > 0) {
|
||||
str = gb_string_appendc(str, ", ");
|
||||
}
|
||||
@@ -4771,7 +4658,10 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
|
||||
}
|
||||
|
||||
str = gb_string_appendc(str, ": ");
|
||||
str = write_expr_to_string(str, p->type);
|
||||
if (f->flags&FieldFlag_ellipsis) {
|
||||
str = gb_string_appendc(str, "...");
|
||||
}
|
||||
str = write_expr_to_string(str, f->type);
|
||||
case_end;
|
||||
|
||||
case_ast_node(ce, CallExpr, node);
|
||||
@@ -4798,37 +4688,22 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
|
||||
str = gb_string_appendc(str, "struct ");
|
||||
if (st->is_packed) str = gb_string_appendc(str, "#packed ");
|
||||
if (st->is_ordered) str = gb_string_appendc(str, "#ordered ");
|
||||
for_array(i, st->fields) {
|
||||
if (i > 0) {
|
||||
str = gb_string_appendc(str, "; ");
|
||||
}
|
||||
str = write_expr_to_string(str, st->fields.e[i]);
|
||||
}
|
||||
// str = write_params_to_string(str, st->decl_list, ", ");
|
||||
str = gb_string_appendc(str, "{");
|
||||
str = write_record_fields_to_string(str, st->fields);
|
||||
str = gb_string_appendc(str, "}");
|
||||
case_end;
|
||||
|
||||
case_ast_node(st, RawUnionType, node);
|
||||
str = gb_string_appendc(str, "raw_union {");
|
||||
for_array(i, st->fields) {
|
||||
if (i > 0) {
|
||||
str = gb_string_appendc(str, "; ");
|
||||
}
|
||||
str = write_expr_to_string(str, st->fields.e[i]);
|
||||
}
|
||||
// str = write_params_to_string(str, st->decl_list, ", ");
|
||||
str = gb_string_appendc(str, "raw_union ");
|
||||
str = gb_string_appendc(str, "{");
|
||||
str = write_record_fields_to_string(str, st->fields);
|
||||
str = gb_string_appendc(str, "}");
|
||||
case_end;
|
||||
|
||||
case_ast_node(st, UnionType, node);
|
||||
str = gb_string_appendc(str, "union {");
|
||||
for_array(i, st->fields) {
|
||||
if (i > 0) {
|
||||
str = gb_string_appendc(str, "; ");
|
||||
}
|
||||
str = write_expr_to_string(str, st->fields.e[i]);
|
||||
}
|
||||
// str = write_params_to_string(str, st->decl_list, ", ");
|
||||
str = gb_string_appendc(str, "union ");
|
||||
str = gb_string_appendc(str, "{");
|
||||
str = write_record_fields_to_string(str, st->fields);
|
||||
str = gb_string_appendc(str, "}");
|
||||
case_end;
|
||||
|
||||
@@ -4839,6 +4714,7 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
|
||||
str = gb_string_appendc(str, " ");
|
||||
}
|
||||
str = gb_string_appendc(str, "{");
|
||||
str = write_params_to_string(str, et->fields, ", ");
|
||||
str = gb_string_appendc(str, "}");
|
||||
case_end;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
|
||||
return;
|
||||
}
|
||||
|
||||
check_scope_decls(c, stmts, 1.2*stmts.count, NULL);
|
||||
check_scope_decls(c, stmts, 1.2*stmts.count);
|
||||
|
||||
bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
|
||||
flags &= ~Stmt_FallthroughAllowed;
|
||||
@@ -602,9 +602,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
}
|
||||
|
||||
Type *type = x.type;
|
||||
Type *bt = base_type(base_enum_type(type));
|
||||
|
||||
if (!is_type_integer(bt) && !is_type_float(bt) && !is_type_pointer(bt)) {
|
||||
if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type)) {
|
||||
error(ie->op, "Only numerical and pointer types are allowed within interval expressions");
|
||||
goto skip_expr;
|
||||
}
|
||||
@@ -784,21 +782,21 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
for_array(j, cc->list) {
|
||||
AstNode *expr = cc->list.e[j];
|
||||
Operand y = {0};
|
||||
Operand z = {0};
|
||||
Token eq = {Token_CmpEq};
|
||||
|
||||
check_expr(c, &y, expr);
|
||||
if (x.mode == Addressing_Invalid ||
|
||||
y.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
convert_to_typed(c, &y, x.type, 0);
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
z = y;
|
||||
check_comparison(c, &z, &x, eq);
|
||||
// NOTE(bill): the ordering here matters
|
||||
Operand z = y;
|
||||
check_comparison(c, &z, &x, Token_CmpEq);
|
||||
if (z.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
@@ -806,6 +804,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (y.value.kind != ExactValue_Invalid) {
|
||||
HashKey key = hash_exact_value(y.value);
|
||||
TypeAndToken *found = map_type_and_token_get(&seen, key);
|
||||
|
||||
408
src/checker.c
408
src/checker.c
@@ -206,11 +206,7 @@ gb_global ImplicitValueInfo implicit_value_infos[ImplicitValue_Count] = {0};
|
||||
|
||||
|
||||
|
||||
typedef struct CheckerContext {
|
||||
Scope * scope;
|
||||
DeclInfo * decl;
|
||||
u32 stmt_state_flags;
|
||||
} CheckerContext;
|
||||
|
||||
|
||||
#define MAP_TYPE TypeAndValue
|
||||
#define MAP_PROC map_tav_
|
||||
@@ -242,6 +238,11 @@ typedef struct DelayedDecl {
|
||||
AstNode *decl;
|
||||
} DelayedDecl;
|
||||
|
||||
typedef struct CheckerContext {
|
||||
Scope * scope;
|
||||
DeclInfo * decl;
|
||||
u32 stmt_state_flags;
|
||||
} CheckerContext;
|
||||
|
||||
// NOTE(bill): Symbol tables
|
||||
typedef struct CheckerInfo {
|
||||
@@ -284,6 +285,15 @@ typedef struct Checker {
|
||||
} Checker;
|
||||
|
||||
|
||||
typedef struct DelayedEntity {
|
||||
AstNode * ident;
|
||||
Entity * entity;
|
||||
DeclInfo * decl;
|
||||
} DelayedEntity;
|
||||
|
||||
typedef Array(DelayedEntity) DelayedEntities;
|
||||
|
||||
|
||||
|
||||
|
||||
void init_declaration_info(DeclInfo *d, Scope *scope) {
|
||||
@@ -388,6 +398,35 @@ void check_close_scope(Checker *c) {
|
||||
c->context.scope = c->context.scope->parent;
|
||||
}
|
||||
|
||||
|
||||
Entity *current_scope_lookup_entity(Scope *s, String name) {
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_entity_get(&s->elements, key);
|
||||
if (found) {
|
||||
return *found;
|
||||
}
|
||||
for_array(i, s->shared) {
|
||||
Scope *shared = s->shared.e[i];
|
||||
Entity **found = map_entity_get(&shared->elements, key);
|
||||
if (found) {
|
||||
Entity *e = *found;
|
||||
if (e->kind == Entity_Variable &&
|
||||
!e->scope->is_file &&
|
||||
!e->scope->is_global) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e->scope != shared) {
|
||||
// Do not return imported entities even #include ones
|
||||
continue;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entity **entity_) {
|
||||
bool gone_thru_proc = false;
|
||||
bool gone_thru_file = false;
|
||||
@@ -456,21 +495,6 @@ Entity *scope_lookup_entity(Scope *s, String name) {
|
||||
return entity;
|
||||
}
|
||||
|
||||
Entity *current_scope_lookup_entity(Scope *s, String name) {
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_entity_get(&s->elements, key);
|
||||
if (found) {
|
||||
return *found;
|
||||
}
|
||||
for_array(i, s->shared) {
|
||||
Entity **found = map_entity_get(&s->shared.e[i]->elements, key);
|
||||
if (found) {
|
||||
return *found;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Entity *scope_insert_entity(Scope *s, Entity *entity) {
|
||||
@@ -769,7 +793,8 @@ void add_entity_use(Checker *c, AstNode *identifier, Entity *entity) {
|
||||
if (identifier->kind != AstNode_Ident) {
|
||||
return;
|
||||
}
|
||||
map_entity_set(&c->info.uses, hash_pointer(identifier), entity);
|
||||
HashKey key = hash_pointer(identifier);
|
||||
map_entity_set(&c->info.uses, key, entity);
|
||||
add_declaration_dependency(c, entity); // TODO(bill): Should this be here?
|
||||
}
|
||||
|
||||
@@ -942,7 +967,8 @@ void add_curr_ast_file(Checker *c, AstFile *file) {
|
||||
TokenPos zero_pos = {0};
|
||||
global_error_collector.prev = zero_pos;
|
||||
c->curr_ast_file = file;
|
||||
c->context.decl = file->decl_info;
|
||||
c->context.decl = file->decl_info;
|
||||
c->context.scope = file->scope;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1099,12 +1125,20 @@ void init_preload(Checker *c) {
|
||||
c->done_preload = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool check_arity_match(Checker *c, AstNodeValueDecl *d);
|
||||
void check_collect_entities(Checker *c, AstNodeArray nodes, MapScope *file_scopes, DelayedEntities *delayed_entities);
|
||||
void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, MapScope *file_scopes, DelayedEntities *delayed_entities);
|
||||
|
||||
#include "check_expr.c"
|
||||
#include "check_decl.c"
|
||||
#include "check_stmt.c"
|
||||
|
||||
|
||||
|
||||
|
||||
bool check_arity_match(Checker *c, AstNodeValueDecl *d) {
|
||||
isize lhs = d->names.count;
|
||||
isize rhs = d->values.count;
|
||||
@@ -1135,6 +1169,202 @@ bool check_arity_match(Checker *c, AstNodeValueDecl *d) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, MapScope *file_scopes, DelayedEntities *delayed_entities) {
|
||||
// NOTE(bill): File scope and local scope are different kinds of scopes
|
||||
GB_ASSERT(file_scopes == NULL || delayed_entities == NULL);
|
||||
|
||||
Operand operand = {Addressing_Invalid};
|
||||
check_expr(c, &operand, ws->cond);
|
||||
if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
|
||||
error_node(ws->cond, "Non-boolean condition in `when` statement");
|
||||
}
|
||||
if (operand.mode != Addressing_Constant) {
|
||||
error_node(ws->cond, "Non-constant condition in `when` statement");
|
||||
}
|
||||
if (ws->body == NULL || ws->body->kind != AstNode_BlockStmt) {
|
||||
error_node(ws->cond, "Invalid body for `when` statement");
|
||||
} else {
|
||||
if (operand.value.kind == ExactValue_Bool &&
|
||||
operand.value.value_bool) {
|
||||
check_collect_entities(c, ws->body->BlockStmt.stmts, file_scopes, delayed_entities);
|
||||
} else if (ws->else_stmt) {
|
||||
switch (ws->else_stmt->kind) {
|
||||
case AstNode_BlockStmt:
|
||||
check_collect_entities(c, ws->else_stmt->BlockStmt.stmts, file_scopes, delayed_entities);
|
||||
break;
|
||||
case AstNode_WhenStmt:
|
||||
check_collect_entities_from_when_stmt(c, &ws->else_stmt->WhenStmt, file_scopes, delayed_entities);
|
||||
break;
|
||||
default:
|
||||
error_node(ws->else_stmt, "Invalid `else` statement in `when` statement");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(bill): If file_scopes == NULL, this will act like a local scope
|
||||
void check_collect_entities(Checker *c, AstNodeArray nodes, MapScope *file_scopes, DelayedEntities *delayed_entities) {
|
||||
// NOTE(bill): File scope and local scope are different kinds of scopes
|
||||
GB_ASSERT(file_scopes == NULL || delayed_entities == NULL);
|
||||
if (file_scopes != NULL) {
|
||||
GB_ASSERT(c->context.scope->is_file);
|
||||
}
|
||||
if (delayed_entities != NULL) {
|
||||
GB_ASSERT(!c->context.scope->is_file);
|
||||
}
|
||||
|
||||
for_array(decl_index, nodes) {
|
||||
AstNode *decl = nodes.e[decl_index];
|
||||
if (!is_ast_node_decl(decl) && !is_ast_node_when_stmt(decl)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (decl->kind) {
|
||||
case_ast_node(bd, BadDecl, decl);
|
||||
case_end;
|
||||
|
||||
case_ast_node(ws, WhenStmt, decl);
|
||||
if (c->context.scope->is_file) {
|
||||
error_node(decl, "`when` statements are not allowed at file scope");
|
||||
} else {
|
||||
// Will be handled later
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(vd, ValueDecl, decl);
|
||||
if (vd->is_var) {
|
||||
if (!c->context.scope->is_file) {
|
||||
// NOTE(bill): local scope -> handle later and in order
|
||||
break;
|
||||
}
|
||||
// NOTE(bill): You need to store the entity information here unline a constant declaration
|
||||
isize entity_count = vd->names.count;
|
||||
isize entity_index = 0;
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
DeclInfo *di = NULL;
|
||||
if (vd->values.count > 0) {
|
||||
di = make_declaration_info(heap_allocator(), c->context.scope);
|
||||
di->entities = entities;
|
||||
di->entity_count = entity_count;
|
||||
di->type_expr = vd->type;
|
||||
di->init_expr = vd->values.e[0];
|
||||
}
|
||||
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names.e[i];
|
||||
AstNode *value = NULL;
|
||||
if (i < vd->values.count) {
|
||||
value = vd->values.e[i];
|
||||
}
|
||||
if (name->kind != AstNode_Ident) {
|
||||
error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
|
||||
continue;
|
||||
}
|
||||
Entity *e = make_entity_variable(c->allocator, c->context.scope, name->Ident, NULL);
|
||||
e->identifier = name;
|
||||
entities[entity_index++] = e;
|
||||
|
||||
DeclInfo *d = di;
|
||||
if (d == NULL) {
|
||||
AstNode *init_expr = value;
|
||||
d = make_declaration_info(heap_allocator(), e->scope);
|
||||
d->type_expr = vd->type;
|
||||
d->init_expr = init_expr;
|
||||
d->var_decl_tags = vd->tags;
|
||||
}
|
||||
|
||||
add_entity_and_decl_info(c, name, e, d);
|
||||
}
|
||||
|
||||
check_arity_match(c, vd);
|
||||
} else {
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names.e[i];
|
||||
if (name->kind != AstNode_Ident) {
|
||||
error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
|
||||
continue;
|
||||
}
|
||||
|
||||
AstNode *init = NULL;
|
||||
if (i < vd->values.count) {
|
||||
init = vd->values.e[i];
|
||||
}
|
||||
|
||||
DeclInfo *d = make_declaration_info(c->allocator, c->context.scope);
|
||||
Entity *e = NULL;
|
||||
|
||||
AstNode *up_init = unparen_expr(init);
|
||||
if (init != NULL && is_ast_node_type(up_init)) {
|
||||
e = make_entity_type_name(c->allocator, d->scope, name->Ident, NULL);
|
||||
// TODO(bill): What if vd->type != NULL??? How to handle this case?
|
||||
d->type_expr = init;
|
||||
d->init_expr = init;
|
||||
} else if (init != NULL && up_init->kind == AstNode_ProcLit) {
|
||||
e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags);
|
||||
d->proc_lit = init;
|
||||
d->type_expr = vd->type;
|
||||
} else {
|
||||
e = make_entity_constant(c->allocator, d->scope, name->Ident, NULL, (ExactValue){0});
|
||||
d->type_expr = vd->type;
|
||||
d->init_expr = init;
|
||||
}
|
||||
GB_ASSERT(e != NULL);
|
||||
e->identifier = name;
|
||||
|
||||
add_entity_and_decl_info(c, name, e, d);
|
||||
}
|
||||
|
||||
check_arity_match(c, vd);
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(id, ImportDecl, decl);
|
||||
if (!c->context.scope->is_file) {
|
||||
if (id->is_import) {
|
||||
error_node(decl, "#import declarations are only allowed in the file scope");
|
||||
} else {
|
||||
error_node(decl, "#include declarations are only allowed in the file scope");
|
||||
}
|
||||
// NOTE(bill): _Should_ be caught by the parser
|
||||
// TODO(bill): Better error handling if it isn't
|
||||
continue;
|
||||
}
|
||||
DelayedDecl di = {c->context.scope, decl};
|
||||
array_add(&c->delayed_imports, di);
|
||||
case_end;
|
||||
case_ast_node(fl, ForeignLibrary, decl);
|
||||
if (!c->context.scope->is_file) {
|
||||
error_node(decl, "#foreign_library declarations are only allowed in the file scope");
|
||||
// NOTE(bill): _Should_ be caught by the parser
|
||||
// TODO(bill): Better error handling if it isn't
|
||||
continue;
|
||||
}
|
||||
|
||||
DelayedDecl di = {c->context.scope, decl};
|
||||
array_add(&c->delayed_foreign_libraries, di);
|
||||
case_end;
|
||||
default:
|
||||
if (c->context.scope->is_file) {
|
||||
error_node(decl, "Only declarations are allowed at file scope");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!c->context.scope->is_file) {
|
||||
// NOTE(bill): `when` stmts need to be handled after the other as the condition may refer to something
|
||||
// declared after this stmt in source
|
||||
for_array(i, nodes) {
|
||||
AstNode *node = nodes.e[i];
|
||||
switch (node->kind) {
|
||||
case_ast_node(ws, WhenStmt, node);
|
||||
check_collect_entities_from_when_stmt(c, ws, file_scopes, delayed_entities);
|
||||
case_end;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void check_all_global_entities(Checker *c) {
|
||||
@@ -1171,126 +1401,6 @@ void check_all_global_entities(Checker *c) {
|
||||
}
|
||||
}
|
||||
|
||||
void check_global_collect_entities_from_file(Checker *c, Scope *parent_scope, AstNodeArray nodes, MapScope *file_scopes) {
|
||||
for_array(decl_index, nodes) {
|
||||
AstNode *decl = nodes.e[decl_index];
|
||||
if (!is_ast_node_decl(decl) && !is_ast_node_when_stmt(decl)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (decl->kind) {
|
||||
case_ast_node(bd, BadDecl, decl);
|
||||
case_end;
|
||||
|
||||
case_ast_node(vd, ValueDecl, decl);
|
||||
if (vd->is_var) {
|
||||
// NOTE(bill): You need to store the entity information here unline a constant declaration
|
||||
isize entity_count = vd->names.count;
|
||||
isize entity_index = 0;
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
DeclInfo *di = NULL;
|
||||
if (vd->values.count > 0) {
|
||||
di = make_declaration_info(heap_allocator(), parent_scope);
|
||||
di->entities = entities;
|
||||
di->entity_count = entity_count;
|
||||
di->type_expr = vd->type;
|
||||
di->init_expr = vd->values.e[0];
|
||||
}
|
||||
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names.e[i];
|
||||
AstNode *value = NULL;
|
||||
if (i < vd->values.count) {
|
||||
value = vd->values.e[i];
|
||||
}
|
||||
if (name->kind != AstNode_Ident) {
|
||||
error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
|
||||
continue;
|
||||
}
|
||||
Entity *e = make_entity_variable(c->allocator, parent_scope, name->Ident, NULL);
|
||||
e->identifier = name;
|
||||
entities[entity_index++] = e;
|
||||
|
||||
DeclInfo *d = di;
|
||||
if (d == NULL) {
|
||||
AstNode *init_expr = value;
|
||||
d = make_declaration_info(heap_allocator(), e->scope);
|
||||
d->type_expr = vd->type;
|
||||
d->init_expr = init_expr;
|
||||
d->var_decl_tags = vd->tags;
|
||||
}
|
||||
|
||||
add_entity_and_decl_info(c, name, e, d);
|
||||
}
|
||||
|
||||
check_arity_match(c, vd);
|
||||
} else {
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names.e[i];
|
||||
if (name->kind != AstNode_Ident) {
|
||||
error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
AstNode *init = NULL;
|
||||
if (i < vd->values.count) {
|
||||
init = vd->values.e[i];
|
||||
}
|
||||
|
||||
DeclInfo *d = make_declaration_info(c->allocator, parent_scope);
|
||||
Entity *e = NULL;
|
||||
|
||||
AstNode *up_init = unparen_expr(init);
|
||||
if (init != NULL && is_ast_node_type(up_init)) {
|
||||
e = make_entity_type_name(c->allocator, d->scope, name->Ident, NULL);
|
||||
d->type_expr = init;
|
||||
d->init_expr = init;
|
||||
} else if (init != NULL && up_init->kind == AstNode_ProcLit) {
|
||||
e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags);
|
||||
d->proc_lit = init;
|
||||
} else {
|
||||
e = make_entity_constant(c->allocator, d->scope, name->Ident, NULL, (ExactValue){0});
|
||||
d->type_expr = vd->type;
|
||||
d->init_expr = init;
|
||||
}
|
||||
GB_ASSERT(e != NULL);
|
||||
e->identifier = name;
|
||||
|
||||
add_entity_and_decl_info(c, name, e, d);
|
||||
}
|
||||
|
||||
check_arity_match(c, vd);
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(id, ImportDecl, decl);
|
||||
if (!parent_scope->is_file) {
|
||||
// NOTE(bill): _Should_ be caught by the parser
|
||||
// TODO(bill): Better error handling if it isn't
|
||||
continue;
|
||||
}
|
||||
DelayedDecl di = {parent_scope, decl};
|
||||
array_add(&c->delayed_imports, di);
|
||||
case_end;
|
||||
case_ast_node(fl, ForeignLibrary, decl);
|
||||
if (!parent_scope->is_file) {
|
||||
// NOTE(bill): _Should_ be caught by the parser
|
||||
// TODO(bill): Better error handling if it isn't
|
||||
continue;
|
||||
}
|
||||
|
||||
DelayedDecl di = {parent_scope, decl};
|
||||
array_add(&c->delayed_foreign_libraries, di);
|
||||
case_end;
|
||||
default:
|
||||
if (parent_scope->is_file) {
|
||||
error_node(decl, "Only declarations are allowed at file scope");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void check_import_entities(Checker *c, MapScope *file_scopes) {
|
||||
for_array(i, c->delayed_imports) {
|
||||
@@ -1463,8 +1573,10 @@ void check_parsed_files(Checker *c) {
|
||||
// Collect Entities
|
||||
for_array(i, c->parser->files) {
|
||||
AstFile *f = &c->parser->files.e[i];
|
||||
CheckerContext prev_context = c->context;
|
||||
add_curr_ast_file(c, f);
|
||||
check_global_collect_entities_from_file(c, f->scope, f->decls, &file_scopes);
|
||||
check_collect_entities(c, f->decls, &file_scopes, NULL);
|
||||
c->context = prev_context;
|
||||
}
|
||||
|
||||
check_import_entities(c, &file_scopes);
|
||||
@@ -1472,7 +1584,7 @@ void check_parsed_files(Checker *c) {
|
||||
|
||||
check_all_global_entities(c);
|
||||
init_preload(c); // NOTE(bill): This could be setup previously through the use of `type_info(_of_val)`
|
||||
// NOTE(bill): Nothing is the global scope _should_ depend on this implicit value as implicit
|
||||
// NOTE(bill): Nothing in the global scope _should_ depend on this implicit value as implicit
|
||||
// values are only useful within procedures
|
||||
add_implicit_value(c, ImplicitValue_context, str_lit("context"), str_lit("__context"), t_context);
|
||||
|
||||
@@ -1497,12 +1609,12 @@ void check_parsed_files(Checker *c) {
|
||||
// NOTE(bill): Nested procedures bodies will be added to this "queue"
|
||||
for_array(i, c->procs) {
|
||||
ProcedureInfo *pi = &c->procs.e[i];
|
||||
CheckerContext prev_context = c->context;
|
||||
add_curr_ast_file(c, pi->file);
|
||||
|
||||
bool bounds_check = (pi->tags & ProcTag_bounds_check) != 0;
|
||||
bool no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0;
|
||||
|
||||
CheckerContext prev_context = c->context;
|
||||
|
||||
if (bounds_check) {
|
||||
c->context.stmt_state_flags |= StmtStateFlag_bounds_check;
|
||||
@@ -1554,8 +1666,10 @@ void check_parsed_files(Checker *c) {
|
||||
for_array(i, c->info.definitions.entries) {
|
||||
Entity *e = c->info.definitions.entries.e[i].value;
|
||||
if (e->kind == Entity_TypeName) {
|
||||
// i64 size = type_size_of(c->sizes, c->allocator, e->type);
|
||||
i64 align = type_align_of(c->sizes, c->allocator, e->type);
|
||||
if (e->type != NULL) {
|
||||
// i64 size = type_size_of(c->sizes, c->allocator, e->type);
|
||||
i64 align = type_align_of(c->sizes, c->allocator, e->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
37
src/ir.c
37
src/ir.c
@@ -1516,6 +1516,18 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue *
|
||||
|
||||
|
||||
switch (op) {
|
||||
case Token_Shl:
|
||||
case Token_Shr:
|
||||
left = ir_emit_conv(proc, left, type);
|
||||
if (!is_type_unsigned(ir_type(right))) {
|
||||
Type *t = t_u64;
|
||||
if (proc->module->sizes.word_size == 32) {
|
||||
t = t_u32;
|
||||
}
|
||||
right = ir_emit_conv(proc, right, t);
|
||||
}
|
||||
break;
|
||||
|
||||
case Token_AndNot: {
|
||||
// NOTE(bill): x &~ y == x & (~y) == x & (y ~ -1)
|
||||
// NOTE(bill): "not" `x` == `x` "xor" `-1`
|
||||
@@ -1534,8 +1546,6 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue *
|
||||
case Token_And:
|
||||
case Token_Or:
|
||||
case Token_Xor:
|
||||
case Token_Shl:
|
||||
case Token_Shr:
|
||||
left = ir_emit_conv(proc, left, type);
|
||||
right = ir_emit_conv(proc, right, type);
|
||||
break;
|
||||
@@ -1949,15 +1959,15 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
|
||||
dst->kind == Type_Basic);
|
||||
i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src);
|
||||
i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst);
|
||||
irConvKind kind = irConv_trunc;
|
||||
if (sz == dz) {
|
||||
// NOTE(bill): In LLVM, all integers are signed and rely upon 2's compliment
|
||||
return value;
|
||||
}
|
||||
|
||||
irConvKind kind = irConv_trunc;
|
||||
if (dz >= sz) {
|
||||
// NOTE(bill): Copy the value just for type correctness
|
||||
kind = irConv_bitcast;
|
||||
} else if (dz > sz) {
|
||||
kind = irConv_zext;
|
||||
}
|
||||
|
||||
return ir_emit(proc, ir_make_instr_conv(proc, kind, value, src, dst));
|
||||
}
|
||||
|
||||
@@ -2560,7 +2570,8 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv
|
||||
expr = unparen_expr(expr);
|
||||
switch (expr->kind) {
|
||||
case_ast_node(bl, BasicLit, expr);
|
||||
GB_PANIC("Non-constant basic literal");
|
||||
TokenPos pos = bl->pos;
|
||||
GB_PANIC("Non-constant basic literal %.*s(%td:%td) - %.*s", LIT(pos.file), pos.line, pos.column, LIT(token_strings[bl->kind]));
|
||||
case_end;
|
||||
|
||||
case_ast_node(i, Ident, expr);
|
||||
@@ -2596,7 +2607,7 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv
|
||||
|
||||
case_ast_node(re, RunExpr, expr);
|
||||
// TODO(bill): Run Expression
|
||||
return ir_build_single_expr(proc, re->expr, tv);
|
||||
return ir_build_expr(proc, re->expr);
|
||||
case_end;
|
||||
|
||||
case_ast_node(de, DerefExpr, expr);
|
||||
@@ -5834,9 +5845,10 @@ void ir_gen_tree(irGen *s) {
|
||||
case Type_Proc: {
|
||||
tag = ir_emit_conv(proc, ti_ptr, t_type_info_procedure_ptr);
|
||||
|
||||
irValue *params = ir_emit_struct_ep(proc, tag, 0);
|
||||
irValue *results = ir_emit_struct_ep(proc, tag, 1);
|
||||
irValue *variadic = ir_emit_struct_ep(proc, tag, 2);
|
||||
irValue *params = ir_emit_struct_ep(proc, tag, 0);
|
||||
irValue *results = ir_emit_struct_ep(proc, tag, 1);
|
||||
irValue *variadic = ir_emit_struct_ep(proc, tag, 2);
|
||||
irValue *convention = ir_emit_struct_ep(proc, tag, 3);
|
||||
|
||||
if (t->Proc.params) {
|
||||
ir_emit_store(proc, params, ir_get_type_info_ptr(proc, type_info_data, t->Proc.params));
|
||||
@@ -5845,6 +5857,7 @@ void ir_gen_tree(irGen *s) {
|
||||
ir_emit_store(proc, results, ir_get_type_info_ptr(proc, type_info_data, t->Proc.results));
|
||||
}
|
||||
ir_emit_store(proc, variadic, ir_make_const_bool(a, t->Proc.variadic));
|
||||
ir_emit_store(proc, convention, ir_make_const_int(a, t->Proc.calling_convention));
|
||||
|
||||
// TODO(bill): Type_Info for procedures
|
||||
} break;
|
||||
|
||||
70
src/parser.c
70
src/parser.c
@@ -62,36 +62,41 @@ typedef struct Parser {
|
||||
} Parser;
|
||||
|
||||
typedef enum ProcTag {
|
||||
ProcTag_bounds_check = GB_BIT(0),
|
||||
ProcTag_no_bounds_check = GB_BIT(1),
|
||||
ProcTag_bounds_check = 1<<0,
|
||||
ProcTag_no_bounds_check = 1<<1,
|
||||
|
||||
ProcTag_foreign = GB_BIT(10),
|
||||
ProcTag_export = GB_BIT(11),
|
||||
ProcTag_link_name = GB_BIT(12),
|
||||
ProcTag_inline = GB_BIT(13),
|
||||
ProcTag_no_inline = GB_BIT(14),
|
||||
ProcTag_dll_import = GB_BIT(15),
|
||||
// ProcTag_dll_export = GB_BIT(16),
|
||||
ProcTag_foreign = 1<<10,
|
||||
ProcTag_export = 1<<11,
|
||||
ProcTag_link_name = 1<<12,
|
||||
ProcTag_inline = 1<<13,
|
||||
ProcTag_no_inline = 1<<14,
|
||||
ProcTag_dll_import = 1<<15,
|
||||
// ProcTag_dll_export = 1<<16,
|
||||
} ProcTag;
|
||||
|
||||
typedef enum ProcCallingConvention {
|
||||
ProcCC_Odin = 0,
|
||||
ProcCC_C,
|
||||
ProcCC_Std,
|
||||
ProcCC_Fast,
|
||||
ProcCC_C = 1,
|
||||
ProcCC_Std = 2,
|
||||
ProcCC_Fast = 3,
|
||||
|
||||
ProcCC_Invalid,
|
||||
} ProcCallingConvention;
|
||||
|
||||
typedef enum VarDeclTag {
|
||||
VarDeclTag_thread_local = GB_BIT(0),
|
||||
VarDeclTag_thread_local = 1<<0,
|
||||
} VarDeclTag;
|
||||
|
||||
typedef enum StmtStateFlag {
|
||||
StmtStateFlag_bounds_check = GB_BIT(0),
|
||||
StmtStateFlag_no_bounds_check = GB_BIT(1),
|
||||
StmtStateFlag_bounds_check = 1<<0,
|
||||
StmtStateFlag_no_bounds_check = 1<<1,
|
||||
} StmtStateFlag;
|
||||
|
||||
typedef enum FieldFlag {
|
||||
FieldFlag_using = 1<<0,
|
||||
FieldFlag_ellipsis = 1<<1,
|
||||
} FieldListTag;
|
||||
|
||||
AstNodeArray make_ast_node_array(AstFile *f) {
|
||||
AstNodeArray a;
|
||||
// array_init(&a, gb_arena_allocator(&f->arena));
|
||||
@@ -228,7 +233,10 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
|
||||
}) \
|
||||
AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \
|
||||
AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \
|
||||
AST_NODE_KIND(UsingStmt, "using statement", struct { Token token; AstNode *node; }) \
|
||||
AST_NODE_KIND(UsingStmt, "using statement", struct { \
|
||||
Token token; \
|
||||
AstNode *node; \
|
||||
}) \
|
||||
AST_NODE_KIND(AsmOperand, "assembly operand", struct { \
|
||||
Token string; \
|
||||
AstNode *operand; \
|
||||
@@ -283,7 +291,7 @@ AST_NODE_KIND(_DeclEnd, "", i32) \
|
||||
AST_NODE_KIND(Field, "field", struct { \
|
||||
AstNodeArray names; \
|
||||
AstNode * type; \
|
||||
bool is_using; \
|
||||
u32 flags; \
|
||||
}) \
|
||||
AST_NODE_KIND(_TypeBegin, "", i32) \
|
||||
AST_NODE_KIND(HelperType, "type", struct { \
|
||||
@@ -955,11 +963,11 @@ AstNode *make_bad_decl(AstFile *f, Token begin, Token end) {
|
||||
return result;
|
||||
}
|
||||
|
||||
AstNode *make_field(AstFile *f, AstNodeArray names, AstNode *type, bool is_using) {
|
||||
AstNode *make_field(AstFile *f, AstNodeArray names, AstNode *type, u32 flags) {
|
||||
AstNode *result = make_node(f, AstNode_Field);
|
||||
result->Field.names = names;
|
||||
result->Field.type = type;
|
||||
result->Field.is_using = is_using;
|
||||
result->Field.flags = flags;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2217,7 +2225,7 @@ AstNode *parse_proc_type(AstFile *f, String *foreign_name_, String *link_name_)
|
||||
return make_proc_type(f, proc_token, params, results, tags, cc);
|
||||
}
|
||||
|
||||
AstNodeArray parse_field_list(AstFile *f, isize *name_count_, bool allow_using, bool ellipsis_ok,
|
||||
AstNodeArray parse_field_list(AstFile *f, isize *name_count_, u32 flags,
|
||||
TokenKind separator, TokenKind follow) {
|
||||
AstNodeArray params = make_ast_node_array(f);
|
||||
isize name_count = 0;
|
||||
@@ -2240,7 +2248,7 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, bool allow_using,
|
||||
is_using = false;
|
||||
}
|
||||
|
||||
if (!allow_using && is_using) {
|
||||
if ((flags&FieldFlag_using) == 0 && is_using) {
|
||||
syntax_error(f->curr_token, "`using` is not allowed within this parameter list");
|
||||
is_using = false;
|
||||
}
|
||||
@@ -2250,7 +2258,7 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, bool allow_using,
|
||||
expect_token_after(f, Token_Colon, "parameter list");
|
||||
|
||||
AstNode *type = NULL;
|
||||
if (ellipsis_ok && f->curr_token.kind == Token_Ellipsis) {
|
||||
if ((flags&FieldFlag_ellipsis) != 0 && f->curr_token.kind == Token_Ellipsis) {
|
||||
Token ellipsis = f->curr_token;
|
||||
next_token(f);
|
||||
type = parse_type_attempt(f);
|
||||
@@ -2273,7 +2281,11 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, bool allow_using,
|
||||
syntax_error(f->curr_token, "Expected a type for this parameter declaration");
|
||||
}
|
||||
|
||||
AstNode *param = make_field(f, names, type, is_using);
|
||||
u32 flags = 0;
|
||||
if (is_using) {
|
||||
flags |= FieldFlag_using;
|
||||
}
|
||||
AstNode *param = make_field(f, names, type, flags);
|
||||
array_add(¶ms, param);
|
||||
|
||||
if (separator == Token_Semicolon) {
|
||||
@@ -2291,8 +2303,8 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, bool allow_using,
|
||||
}
|
||||
|
||||
|
||||
AstNodeArray parse_record_fields(AstFile *f, isize *field_count_, bool allow_using, String context) {
|
||||
return parse_field_list(f, field_count_, allow_using, false, Token_Semicolon, Token_CloseBrace);
|
||||
AstNodeArray parse_record_fields(AstFile *f, isize *field_count_, u32 flags, String context) {
|
||||
return parse_field_list(f, field_count_, flags, Token_Semicolon, Token_CloseBrace);
|
||||
}
|
||||
|
||||
AstNode *parse_identifier_or_type(AstFile *f) {
|
||||
@@ -2385,7 +2397,7 @@ AstNode *parse_identifier_or_type(AstFile *f) {
|
||||
|
||||
Token open = expect_token_after(f, Token_OpenBrace, "struct");
|
||||
isize decl_count = 0;
|
||||
AstNodeArray decls = parse_record_fields(f, &decl_count, true, str_lit("struct"));
|
||||
AstNodeArray decls = parse_record_fields(f, &decl_count, FieldFlag_using, str_lit("struct"));
|
||||
Token close = expect_token(f, Token_CloseBrace);
|
||||
|
||||
return make_struct_type(f, token, decls, decl_count, is_packed, is_ordered);
|
||||
@@ -2395,7 +2407,7 @@ AstNode *parse_identifier_or_type(AstFile *f) {
|
||||
Token token = expect_token(f, Token_union);
|
||||
Token open = expect_token_after(f, Token_OpenBrace, "union");
|
||||
isize decl_count = 0;
|
||||
AstNodeArray decls = parse_record_fields(f, &decl_count, false, str_lit("union"));
|
||||
AstNodeArray decls = parse_record_fields(f, &decl_count, 0, str_lit("union"));
|
||||
Token close = expect_token(f, Token_CloseBrace);
|
||||
|
||||
return make_union_type(f, token, decls, decl_count);
|
||||
@@ -2405,7 +2417,7 @@ AstNode *parse_identifier_or_type(AstFile *f) {
|
||||
Token token = expect_token(f, Token_raw_union);
|
||||
Token open = expect_token_after(f, Token_OpenBrace, "raw_union");
|
||||
isize decl_count = 0;
|
||||
AstNodeArray decls = parse_record_fields(f, &decl_count, true, str_lit("raw_union"));
|
||||
AstNodeArray decls = parse_record_fields(f, &decl_count, FieldFlag_using, str_lit("raw_union"));
|
||||
Token close = expect_token(f, Token_CloseBrace);
|
||||
|
||||
return make_raw_union_type(f, token, decls, decl_count);
|
||||
@@ -2479,7 +2491,7 @@ void parse_proc_signature(AstFile *f,
|
||||
AstNodeArray *params,
|
||||
AstNodeArray *results) {
|
||||
expect_token(f, Token_OpenParen);
|
||||
*params = parse_field_list(f, NULL, true, true, Token_Comma, Token_CloseParen);
|
||||
*params = parse_field_list(f, NULL, FieldFlag_using|FieldFlag_ellipsis, Token_Comma, Token_CloseParen);
|
||||
expect_token_after(f, Token_CloseParen, "parameter list");
|
||||
*results = parse_results(f);
|
||||
}
|
||||
|
||||
73
src/types.c
73
src/types.c
@@ -74,8 +74,11 @@ typedef struct TypeRecord {
|
||||
|
||||
// All record types
|
||||
// Theses are arrays
|
||||
Entity **fields; // Entity_Variable (otherwise Entity_TypeName if union)
|
||||
i32 field_count; // == offset_count is struct
|
||||
// Entity_Variable - struct/raw_union
|
||||
// Entity_TypeName - union
|
||||
// Entity_Constant - enum
|
||||
Entity **fields;
|
||||
i32 field_count; // == struct_offsets count
|
||||
AstNode *node;
|
||||
|
||||
i64 * struct_offsets;
|
||||
@@ -84,10 +87,10 @@ typedef struct TypeRecord {
|
||||
bool struct_is_ordered;
|
||||
Entity **fields_in_src_order; // Entity_Variable
|
||||
|
||||
Type * enum_base_type;
|
||||
Entity *enum_count;
|
||||
Entity *enum_min_value;
|
||||
Entity *enum_max_value;
|
||||
Type * enum_base_type;
|
||||
Entity * enum_count;
|
||||
Entity * enum_min_value;
|
||||
Entity * enum_max_value;
|
||||
} TypeRecord;
|
||||
|
||||
#define TYPE_KINDS \
|
||||
@@ -481,28 +484,28 @@ bool is_type_named(Type *t) {
|
||||
return t->kind == Type_Named;
|
||||
}
|
||||
bool is_type_boolean(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(base_enum_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Boolean) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_integer(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(base_enum_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Integer) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_unsigned(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(base_enum_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Unsigned) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_numeric(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(base_enum_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Numeric) != 0;
|
||||
}
|
||||
@@ -536,7 +539,7 @@ bool is_type_untyped(Type *t) {
|
||||
return false;
|
||||
}
|
||||
bool is_type_ordered(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(base_enum_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Ordered) != 0;
|
||||
}
|
||||
@@ -553,21 +556,21 @@ bool is_type_constant_type(Type *t) {
|
||||
return false;
|
||||
}
|
||||
bool is_type_float(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(base_enum_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Float) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_f32(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(base_enum_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return t->Basic.kind == Basic_f32;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_f64(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(base_enum_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return t->Basic.kind == Basic_f64;
|
||||
}
|
||||
@@ -677,8 +680,13 @@ bool is_type_indexable(Type *t) {
|
||||
bool type_has_nil(Type *t) {
|
||||
t = base_type(t);
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
return is_type_rawptr(t);
|
||||
case Type_Basic: {
|
||||
switch (t->Basic.kind) {
|
||||
case Basic_rawptr:
|
||||
case Basic_any:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
case Type_Slice:
|
||||
case Type_Proc:
|
||||
case Type_Pointer:
|
||||
@@ -693,24 +701,22 @@ bool is_type_comparable(Type *t) {
|
||||
t = base_type(t);
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
return t->kind != Basic_UntypedNil;
|
||||
switch (t->Basic.kind) {
|
||||
case Basic_UntypedNil:
|
||||
case Basic_any:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
case Type_Pointer:
|
||||
return true;
|
||||
case Type_Record: {
|
||||
if (false && is_type_struct(t)) {
|
||||
// TODO(bill): Should I even allow this?
|
||||
for (isize i = 0; i < t->Record.field_count; i++) {
|
||||
if (!is_type_comparable(t->Record.fields[i]->type))
|
||||
return false;
|
||||
}
|
||||
} else if (is_type_enum(t)) {
|
||||
if (is_type_enum(t)) {
|
||||
return is_type_comparable(base_enum_type(t));
|
||||
}
|
||||
return false;
|
||||
} break;
|
||||
case Type_Array:
|
||||
return false;
|
||||
// return is_type_comparable(t->Array.elem);
|
||||
case Type_Vector:
|
||||
return is_type_comparable(t->Vector.elem);
|
||||
case Type_Proc:
|
||||
@@ -1189,6 +1195,9 @@ i64 align_formula(i64 size, i64 align) {
|
||||
}
|
||||
|
||||
i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
if (t == NULL) {
|
||||
return 0;
|
||||
}
|
||||
i64 size;
|
||||
TypePath path = {0};
|
||||
type_path_init(&path);
|
||||
@@ -1198,6 +1207,9 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
}
|
||||
|
||||
i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
if (t == NULL) {
|
||||
return 1;
|
||||
}
|
||||
i64 align;
|
||||
TypePath path = {0};
|
||||
type_path_init(&path);
|
||||
@@ -1214,6 +1226,17 @@ i64 type_align_of_internal(BaseTypeSizes s, gbAllocator allocator, Type *t, Type
|
||||
t = base_type(t);
|
||||
|
||||
switch (t->kind) {
|
||||
case Type_Basic: {
|
||||
GB_ASSERT(is_type_typed(t));
|
||||
switch (t->kind) {
|
||||
case Basic_string: return s.word_size;
|
||||
case Basic_any: return s.word_size;
|
||||
|
||||
case Basic_int: case Basic_uint: case Basic_rawptr:
|
||||
return s.word_size;
|
||||
}
|
||||
} break;
|
||||
|
||||
case Type_Array: {
|
||||
Type *elem = t->Array.elem;
|
||||
type_path_push(path, elem);
|
||||
|
||||
Reference in New Issue
Block a user