mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 07:38:48 +00:00
Bug fix: comparisons with shifts
This commit is contained in:
+5
-6
@@ -1,10 +1,9 @@
|
|||||||
#import "fmt.odin";
|
#import "fmt.odin";
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
Fruit :: enum f32 {
|
fmt.printf("%f\n", 0.0);
|
||||||
Apple = 123,
|
fmt.printf("%f\n", 1.0);
|
||||||
Pear = 321,
|
fmt.printf("%f\n", -0.5);
|
||||||
Tomato,
|
fmt.printf("%+f\n", 1334.67);
|
||||||
}
|
fmt.printf("%f\n", 789.789);
|
||||||
fmt.printf("%s = %f\n", Fruit.Apple, Fruit.Apple);
|
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-3
@@ -30,6 +30,14 @@ Type_Info_Enum_Value :: raw_union {
|
|||||||
i: i64;
|
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 {
|
Type_Info :: union {
|
||||||
Named: struct #ordered {
|
Named: struct #ordered {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -52,9 +60,10 @@ Type_Info :: union {
|
|||||||
elem: ^Type_Info;
|
elem: ^Type_Info;
|
||||||
};
|
};
|
||||||
Procedure: struct #ordered {
|
Procedure: struct #ordered {
|
||||||
params: ^Type_Info; // Type_Info.Tuple
|
params: ^Type_Info; // Type_Info.Tuple
|
||||||
results: ^Type_Info; // Type_Info.Tuple
|
results: ^Type_Info; // Type_Info.Tuple
|
||||||
variadic: bool;
|
variadic: bool;
|
||||||
|
convention: Calling_Convention;
|
||||||
};
|
};
|
||||||
Array: struct #ordered {
|
Array: struct #ordered {
|
||||||
elem: ^Type_Info;
|
elem: ^Type_Info;
|
||||||
|
|||||||
+358
-42
@@ -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) {
|
fmt_integer :: proc(fi: ^Fmt_Info, u: u64, base: int, signed: bool, digits: string) {
|
||||||
u_i64 := u as i64;
|
negative := signed && u as i64 < 0;
|
||||||
negative := signed && u_i64 < 0;
|
|
||||||
if negative {
|
if negative {
|
||||||
u = -u;
|
u = -u;
|
||||||
}
|
}
|
||||||
@@ -548,49 +547,366 @@ fmt_int :: proc(fi: ^Fmt_Info, u: u64, signed: bool, verb: rune) {
|
|||||||
fmt_bad_verb(fi, verb);
|
fmt_bad_verb(fi, verb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt_float :: proc(fi: ^Fmt_Info, v: f64, bits: int, verb: rune) {
|
|
||||||
// TODO(bill): Actually print a float correctly
|
__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};
|
||||||
// THIS IS FUCKING SHIT!
|
__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 {
|
match verb {
|
||||||
case 'e', 'E', 'f', 'F', 'g', 'G', 'v':
|
case 'e', 'E', 'g', 'G':
|
||||||
break;
|
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:
|
default:
|
||||||
fmt_bad_verb(fi, verb);
|
fmt_bad_verb(fi, verb);
|
||||||
return;
|
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) {
|
fmt_string :: proc(fi: ^Fmt_Info, s: string, verb: rune) {
|
||||||
match verb {
|
match verb {
|
||||||
@@ -833,8 +1149,8 @@ fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) {
|
|||||||
|
|
||||||
if verb == 'T' {
|
if verb == 'T' {
|
||||||
ti := arg.type_info;
|
ti := arg.type_info;
|
||||||
if ti == type_info(^Type_Info) {
|
match type a : arg {
|
||||||
ti = (arg.data as ^^Type_Info)^;
|
case ^Type_Info: ti = a;
|
||||||
}
|
}
|
||||||
buffer_write_type(fi.buf, ti);
|
buffer_write_type(fi.buf, ti);
|
||||||
return;
|
return;
|
||||||
|
|||||||
+102
-226
@@ -28,143 +28,11 @@ gb_inline Type *check_type(Checker *c, AstNode *expression) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct DelayedEntity {
|
void check_scope_decls(Checker *c, AstNodeArray nodes, isize reserve_size) {
|
||||||
AstNode * ident;
|
GB_ASSERT(!c->context.scope->is_file);
|
||||||
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) {
|
|
||||||
DelayedEntities delayed_entities;
|
DelayedEntities delayed_entities;
|
||||||
array_init_reserve(&delayed_entities, heap_allocator(), reserve_size);
|
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) {
|
for_array(i, delayed_entities) {
|
||||||
DelayedEntity delayed = delayed_entities.e[i];
|
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);
|
Type *type = check_type_extra(c, f->type, NULL);
|
||||||
|
|
||||||
if (f->is_using) {
|
if (f->flags&FieldFlag_using) {
|
||||||
if (f->names.count > 1) {
|
if (f->names.count > 1) {
|
||||||
error_node(f->names.e[0], "Cannot apply `using` to more than one of the same type");
|
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;
|
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;
|
e->identifier = name;
|
||||||
if (str_eq(name_token.string, str_lit("_"))) {
|
if (str_eq(name_token.string, str_lit("_"))) {
|
||||||
fields[field_index++] = e;
|
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));
|
Type *t = base_type(type_deref(type));
|
||||||
if (!is_type_struct(t) && !is_type_raw_union(t) &&
|
if (!is_type_struct(t) && !is_type_raw_union(t) &&
|
||||||
f->names.count >= 1 &&
|
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) {
|
for_array(j, p->names) {
|
||||||
AstNode *name = p->names.e[j];
|
AstNode *name = p->names.e[j];
|
||||||
if (ast_node_expect(name, AstNode_Ident)) {
|
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);
|
add_entity(c, scope, name, param);
|
||||||
variables[variable_index++] = 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;
|
o->type = t_invalid;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#if 0
|
|
||||||
if (e->Variable.param) {
|
|
||||||
o->mode = Addressing_Value;
|
|
||||||
} else {
|
|
||||||
o->mode = Addressing_Variable;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
o->mode = Addressing_Variable;
|
o->mode = Addressing_Variable;
|
||||||
if (e->Variable.is_immutable) {
|
if (e->Variable.is_immutable) {
|
||||||
o->mode = Addressing_Value;
|
o->mode = Addressing_Value;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Entity_TypeName: {
|
case Entity_TypeName: {
|
||||||
o->mode = Addressing_Type;
|
o->mode = Addressing_Type;
|
||||||
#if 1
|
// TODO(bill): Fix cyclical dependancy checker
|
||||||
// TODO(bill): Fix cyclical dependancy checker
|
|
||||||
#endif
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Entity_Procedure:
|
case Entity_Procedure:
|
||||||
@@ -1028,7 +886,7 @@ i64 check_array_count(Checker *c, AstNode *e) {
|
|||||||
}
|
}
|
||||||
return 0;
|
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 (is_type_untyped(type) || is_type_integer(type)) {
|
||||||
if (o.value.kind == ExactValue_Integer) {
|
if (o.value.kind == ExactValue_Integer) {
|
||||||
i64 count = o.value.value_integer;
|
i64 count = o.value.value_integer;
|
||||||
@@ -1252,7 +1110,7 @@ end:
|
|||||||
|
|
||||||
bool check_unary_op(Checker *c, Operand *o, Token op) {
|
bool check_unary_op(Checker *c, Operand *o, Token op) {
|
||||||
// TODO(bill): Handle errors correctly
|
// 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;
|
gbString str = NULL;
|
||||||
switch (op.kind) {
|
switch (op.kind) {
|
||||||
case Token_Add:
|
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) {
|
bool check_binary_op(Checker *c, Operand *o, Token op) {
|
||||||
// TODO(bill): Handle errors correctly
|
// 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) {
|
switch (op.kind) {
|
||||||
case Token_Sub:
|
case Token_Sub:
|
||||||
case Token_SubEq:
|
case Token_SubEq:
|
||||||
@@ -1359,6 +1217,7 @@ bool check_binary_op(Checker *c, Operand *o, Token op) {
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value) {
|
bool check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value) {
|
||||||
if (in_value.kind == ExactValue_Invalid) {
|
if (in_value.kind == ExactValue_Invalid) {
|
||||||
// NOTE(bill): There's already been an error
|
// 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));
|
i64 imax = (1ll << (s-1ll));
|
||||||
|
|
||||||
|
|
||||||
switch (type->Basic.kind) {
|
switch (type->Basic.kind) {
|
||||||
case Basic_i8:
|
case Basic_i8:
|
||||||
case Basic_i16:
|
case Basic_i16:
|
||||||
@@ -1581,16 +1439,15 @@ void check_unary_expr(Checker *c, Operand *o, Token op, AstNode *node) {
|
|||||||
o->mode = Addressing_Value;
|
o->mode = Addressing_Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
|
void check_comparison(Checker *c, Operand *x, Operand *y, TokenKind op) {
|
||||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
|
||||||
|
|
||||||
gbString err_str = NULL;
|
gbString err_str = NULL;
|
||||||
|
|
||||||
|
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||||
if (check_is_assignable_to(c, x, y->type) ||
|
if (check_is_assignable_to(c, x, y->type) ||
|
||||||
check_is_assignable_to(c, y, x->type)) {
|
check_is_assignable_to(c, y, x->type)) {
|
||||||
Type *err_type = x->type;
|
Type *err_type = x->type;
|
||||||
bool defined = false;
|
bool defined = false;
|
||||||
switch (op.kind) {
|
switch (op) {
|
||||||
case Token_CmpEq:
|
case Token_CmpEq:
|
||||||
case Token_NotEq:
|
case Token_NotEq:
|
||||||
defined = is_type_comparable(x->type);
|
defined = is_type_comparable(x->type);
|
||||||
@@ -1615,7 +1472,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
|
|||||||
if (!defined) {
|
if (!defined) {
|
||||||
gbString type_string = type_to_string(err_type);
|
gbString type_string = type_to_string(err_type);
|
||||||
err_str = gb_string_make(c->tmp_allocator,
|
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);
|
gb_string_free(type_string);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1633,7 +1490,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
|
|||||||
} else {
|
} else {
|
||||||
if (x->mode == Addressing_Constant &&
|
if (x->mode == Addressing_Constant &&
|
||||||
y->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 {
|
} else {
|
||||||
x->mode = Addressing_Value;
|
x->mode = Addressing_Value;
|
||||||
|
|
||||||
@@ -1650,8 +1507,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
|
|||||||
|
|
||||||
if (err_str != NULL) {
|
if (err_str != NULL) {
|
||||||
gb_string_free(err_str);
|
gb_string_free(err_str);
|
||||||
};
|
}
|
||||||
|
|
||||||
gb_temp_arena_memory_end(tmp);
|
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);
|
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);
|
gbString err_str = expr_to_string(x->expr);
|
||||||
error_node(node, "Shifted operand `%s` must be an integer", err_str);
|
error_node(node, "Shifted operand `%s` must be an integer", err_str);
|
||||||
gb_string_free(err_str);
|
gb_string_free(err_str);
|
||||||
@@ -1673,7 +1529,7 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_type_unsigned(base_enum_type(y->type))) {
|
if (is_type_unsigned(y->type)) {
|
||||||
|
|
||||||
} else if (is_type_untyped(y->type)) {
|
} else if (is_type_untyped(y->type)) {
|
||||||
convert_to_typed(c, y, t_untyped_integer, 0);
|
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;
|
u64 amount = cast(u64)y_val.value_integer;
|
||||||
if (amount > 1074) {
|
if (amount > 64) {
|
||||||
gbString err_str = expr_to_string(y->expr);
|
gbString err_str = expr_to_string(y->expr);
|
||||||
error_node(node, "Shift amount too large: `%s`", err_str);
|
error_node(node, "Shift amount too large: `%s`", err_str);
|
||||||
gb_string_free(err_str);
|
gb_string_free(err_str);
|
||||||
@@ -1710,7 +1566,7 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
|
|||||||
return;
|
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
|
// NOTE(bill): It could be an untyped float but still representable
|
||||||
// as an integer
|
// as an integer
|
||||||
x->type = t_untyped_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));
|
x->value = exact_value_shift(be->op.kind, x_val, make_exact_value_integer(amount));
|
||||||
|
|
||||||
if (is_type_typed(x->type)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TokenPos pos = ast_node_token(x->expr).pos;
|
||||||
if (x_is_untyped) {
|
if (x_is_untyped) {
|
||||||
ExprInfo *info = map_expr_info_get(&c->info.untyped, hash_pointer(x->expr));
|
ExprInfo *info = map_expr_info_get(&c->info.untyped, hash_pointer(x->expr));
|
||||||
if (info != NULL) {
|
if (info != NULL) {
|
||||||
info->is_lhs = true;
|
info->is_lhs = true;
|
||||||
}
|
}
|
||||||
x->mode = Addressing_Value;
|
x->mode = Addressing_Value;
|
||||||
|
// x->value = x_val;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1740,6 +1598,14 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
|
|||||||
gb_string_free(err_str);
|
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;
|
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 (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);
|
*x = check_ptr_addition(c, op.kind, x, y, node);
|
||||||
return;
|
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) {
|
if (op.kind == Token_Sub) {
|
||||||
gbString lhs = expr_to_string(x->expr);
|
gbString lhs = expr_to_string(x->expr);
|
||||||
gbString rhs = expr_to_string(y->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)) {
|
if (token_is_comparison(op)) {
|
||||||
check_comparison(c, x, y, op);
|
check_comparison(c, x, y, op.kind);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2259,48 +2125,58 @@ void update_expr_type(Checker *c, AstNode *e, Type *type, bool final) {
|
|||||||
if (found == NULL) {
|
if (found == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
ExprInfo old = *found;
|
||||||
|
|
||||||
switch (e->kind) {
|
switch (e->kind) {
|
||||||
case_ast_node(ue, UnaryExpr, e);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
update_expr_type(c, ue->expr, type, final);
|
update_expr_type(c, ue->expr, type, final);
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(be, BinaryExpr, e);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
if (!token_is_comparison(be->op)) {
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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;
|
case_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!final && is_type_untyped(type)) {
|
if (!final && is_type_untyped(type)) {
|
||||||
found->type = base_type(type);
|
old.type = base_type(type);
|
||||||
map_expr_info_set(&c->info.untyped, key, *found);
|
map_expr_info_set(&c->info.untyped, key, old);
|
||||||
} else {
|
return;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
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;
|
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);
|
gbString expr_str = expr_to_string(operand.expr);
|
||||||
error_node(operand.expr, "Index `%s` must be an integer", expr_str);
|
error_node(operand.expr, "Index `%s` must be an integer", expr_str);
|
||||||
gb_string_free(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) {
|
if (op.mode == Addressing_Invalid) {
|
||||||
return false;
|
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);
|
gbString type_str = type_to_string(op.type);
|
||||||
error_node(call, "Length for `new_slice` must be an integer, got `%s`", type_str);
|
error_node(call, "Length for `new_slice` must be an integer, got `%s`", type_str);
|
||||||
gb_string_free(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) {
|
gbString write_params_to_string(gbString str, AstNodeArray params, char *sep) {
|
||||||
for_array(i, params) {
|
for_array(i, params) {
|
||||||
ast_node(p, Field, params.e[i]);
|
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
str = gb_string_appendc(str, sep);
|
str = gb_string_appendc(str, sep);
|
||||||
}
|
}
|
||||||
@@ -4615,6 +4490,18 @@ gbString write_params_to_string(gbString str, AstNodeArray params, char *sep) {
|
|||||||
return str;
|
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) {
|
gbString string_append_token(gbString str, Token token) {
|
||||||
if (token.string.len > 0) {
|
if (token.string.len > 0) {
|
||||||
return gb_string_append_length(str, token.string.text, token.string.len);
|
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);
|
str = write_expr_to_string(str, vt->elem);
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(p, Field, node);
|
case_ast_node(f, Field, node);
|
||||||
if (p->is_using) {
|
if (f->flags&FieldFlag_using) {
|
||||||
str = gb_string_appendc(str, "using ");
|
str = gb_string_appendc(str, "using ");
|
||||||
}
|
}
|
||||||
for_array(i, p->names) {
|
for_array(i, f->names) {
|
||||||
AstNode *name = p->names.e[i];
|
AstNode *name = f->names.e[i];
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
str = gb_string_appendc(str, ", ");
|
str = gb_string_appendc(str, ", ");
|
||||||
}
|
}
|
||||||
@@ -4771,7 +4658,10 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
str = gb_string_appendc(str, ": ");
|
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_end;
|
||||||
|
|
||||||
case_ast_node(ce, CallExpr, node);
|
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 ");
|
str = gb_string_appendc(str, "struct ");
|
||||||
if (st->is_packed) str = gb_string_appendc(str, "#packed ");
|
if (st->is_packed) str = gb_string_appendc(str, "#packed ");
|
||||||
if (st->is_ordered) str = gb_string_appendc(str, "#ordered ");
|
if (st->is_ordered) str = gb_string_appendc(str, "#ordered ");
|
||||||
for_array(i, st->fields) {
|
str = gb_string_appendc(str, "{");
|
||||||
if (i > 0) {
|
str = write_record_fields_to_string(str, st->fields);
|
||||||
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 = gb_string_appendc(str, "}");
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(st, RawUnionType, node);
|
case_ast_node(st, RawUnionType, node);
|
||||||
str = gb_string_appendc(str, "raw_union {");
|
str = gb_string_appendc(str, "raw_union ");
|
||||||
for_array(i, st->fields) {
|
str = gb_string_appendc(str, "{");
|
||||||
if (i > 0) {
|
str = write_record_fields_to_string(str, st->fields);
|
||||||
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 = gb_string_appendc(str, "}");
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(st, UnionType, node);
|
case_ast_node(st, UnionType, node);
|
||||||
str = gb_string_appendc(str, "union {");
|
str = gb_string_appendc(str, "union ");
|
||||||
for_array(i, st->fields) {
|
str = gb_string_appendc(str, "{");
|
||||||
if (i > 0) {
|
str = write_record_fields_to_string(str, st->fields);
|
||||||
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 = gb_string_appendc(str, "}");
|
||||||
case_end;
|
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 = gb_string_appendc(str, "{");
|
str = gb_string_appendc(str, "{");
|
||||||
|
str = write_params_to_string(str, et->fields, ", ");
|
||||||
str = gb_string_appendc(str, "}");
|
str = gb_string_appendc(str, "}");
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
|
|||||||
+7
-8
@@ -3,7 +3,7 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
|
|||||||
return;
|
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;
|
bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
|
||||||
flags &= ~Stmt_FallthroughAllowed;
|
flags &= ~Stmt_FallthroughAllowed;
|
||||||
@@ -602,9 +602,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Type *type = x.type;
|
Type *type = x.type;
|
||||||
Type *bt = base_type(base_enum_type(type));
|
if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type)) {
|
||||||
|
|
||||||
if (!is_type_integer(bt) && !is_type_float(bt) && !is_type_pointer(bt)) {
|
|
||||||
error(ie->op, "Only numerical and pointer types are allowed within interval expressions");
|
error(ie->op, "Only numerical and pointer types are allowed within interval expressions");
|
||||||
goto skip_expr;
|
goto skip_expr;
|
||||||
}
|
}
|
||||||
@@ -784,21 +782,21 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
|||||||
for_array(j, cc->list) {
|
for_array(j, cc->list) {
|
||||||
AstNode *expr = cc->list.e[j];
|
AstNode *expr = cc->list.e[j];
|
||||||
Operand y = {0};
|
Operand y = {0};
|
||||||
Operand z = {0};
|
|
||||||
Token eq = {Token_CmpEq};
|
|
||||||
|
|
||||||
check_expr(c, &y, expr);
|
check_expr(c, &y, expr);
|
||||||
if (x.mode == Addressing_Invalid ||
|
if (x.mode == Addressing_Invalid ||
|
||||||
y.mode == Addressing_Invalid) {
|
y.mode == Addressing_Invalid) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
convert_to_typed(c, &y, x.type, 0);
|
convert_to_typed(c, &y, x.type, 0);
|
||||||
if (y.mode == Addressing_Invalid) {
|
if (y.mode == Addressing_Invalid) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
z = y;
|
// NOTE(bill): the ordering here matters
|
||||||
check_comparison(c, &z, &x, eq);
|
Operand z = y;
|
||||||
|
check_comparison(c, &z, &x, Token_CmpEq);
|
||||||
if (z.mode == Addressing_Invalid) {
|
if (z.mode == Addressing_Invalid) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -806,6 +804,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (y.value.kind != ExactValue_Invalid) {
|
if (y.value.kind != ExactValue_Invalid) {
|
||||||
HashKey key = hash_exact_value(y.value);
|
HashKey key = hash_exact_value(y.value);
|
||||||
TypeAndToken *found = map_type_and_token_get(&seen, key);
|
TypeAndToken *found = map_type_and_token_get(&seen, key);
|
||||||
|
|||||||
+261
-147
@@ -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_TYPE TypeAndValue
|
||||||
#define MAP_PROC map_tav_
|
#define MAP_PROC map_tav_
|
||||||
@@ -242,6 +238,11 @@ typedef struct DelayedDecl {
|
|||||||
AstNode *decl;
|
AstNode *decl;
|
||||||
} DelayedDecl;
|
} DelayedDecl;
|
||||||
|
|
||||||
|
typedef struct CheckerContext {
|
||||||
|
Scope * scope;
|
||||||
|
DeclInfo * decl;
|
||||||
|
u32 stmt_state_flags;
|
||||||
|
} CheckerContext;
|
||||||
|
|
||||||
// NOTE(bill): Symbol tables
|
// NOTE(bill): Symbol tables
|
||||||
typedef struct CheckerInfo {
|
typedef struct CheckerInfo {
|
||||||
@@ -284,6 +285,15 @@ typedef struct Checker {
|
|||||||
} Checker;
|
} Checker;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct DelayedEntity {
|
||||||
|
AstNode * ident;
|
||||||
|
Entity * entity;
|
||||||
|
DeclInfo * decl;
|
||||||
|
} DelayedEntity;
|
||||||
|
|
||||||
|
typedef Array(DelayedEntity) DelayedEntities;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void init_declaration_info(DeclInfo *d, Scope *scope) {
|
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;
|
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_) {
|
void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entity **entity_) {
|
||||||
bool gone_thru_proc = false;
|
bool gone_thru_proc = false;
|
||||||
bool gone_thru_file = false;
|
bool gone_thru_file = false;
|
||||||
@@ -456,21 +495,6 @@ Entity *scope_lookup_entity(Scope *s, String name) {
|
|||||||
return entity;
|
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) {
|
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) {
|
if (identifier->kind != AstNode_Ident) {
|
||||||
return;
|
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?
|
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};
|
TokenPos zero_pos = {0};
|
||||||
global_error_collector.prev = zero_pos;
|
global_error_collector.prev = zero_pos;
|
||||||
c->curr_ast_file = file;
|
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;
|
c->done_preload = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool check_arity_match(Checker *c, AstNodeValueDecl *d);
|
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_expr.c"
|
||||||
#include "check_decl.c"
|
#include "check_decl.c"
|
||||||
#include "check_stmt.c"
|
#include "check_stmt.c"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool check_arity_match(Checker *c, AstNodeValueDecl *d) {
|
bool check_arity_match(Checker *c, AstNodeValueDecl *d) {
|
||||||
isize lhs = d->names.count;
|
isize lhs = d->names.count;
|
||||||
isize rhs = d->values.count;
|
isize rhs = d->values.count;
|
||||||
@@ -1135,6 +1169,202 @@ bool check_arity_match(Checker *c, AstNodeValueDecl *d) {
|
|||||||
return true;
|
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) {
|
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) {
|
void check_import_entities(Checker *c, MapScope *file_scopes) {
|
||||||
for_array(i, c->delayed_imports) {
|
for_array(i, c->delayed_imports) {
|
||||||
@@ -1463,8 +1573,10 @@ void check_parsed_files(Checker *c) {
|
|||||||
// Collect Entities
|
// Collect Entities
|
||||||
for_array(i, c->parser->files) {
|
for_array(i, c->parser->files) {
|
||||||
AstFile *f = &c->parser->files.e[i];
|
AstFile *f = &c->parser->files.e[i];
|
||||||
|
CheckerContext prev_context = c->context;
|
||||||
add_curr_ast_file(c, f);
|
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);
|
check_import_entities(c, &file_scopes);
|
||||||
@@ -1472,7 +1584,7 @@ void check_parsed_files(Checker *c) {
|
|||||||
|
|
||||||
check_all_global_entities(c);
|
check_all_global_entities(c);
|
||||||
init_preload(c); // NOTE(bill): This could be setup previously through the use of `type_info(_of_val)`
|
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
|
// values are only useful within procedures
|
||||||
add_implicit_value(c, ImplicitValue_context, str_lit("context"), str_lit("__context"), t_context);
|
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"
|
// NOTE(bill): Nested procedures bodies will be added to this "queue"
|
||||||
for_array(i, c->procs) {
|
for_array(i, c->procs) {
|
||||||
ProcedureInfo *pi = &c->procs.e[i];
|
ProcedureInfo *pi = &c->procs.e[i];
|
||||||
|
CheckerContext prev_context = c->context;
|
||||||
add_curr_ast_file(c, pi->file);
|
add_curr_ast_file(c, pi->file);
|
||||||
|
|
||||||
bool bounds_check = (pi->tags & ProcTag_bounds_check) != 0;
|
bool bounds_check = (pi->tags & ProcTag_bounds_check) != 0;
|
||||||
bool no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0;
|
bool no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0;
|
||||||
|
|
||||||
CheckerContext prev_context = c->context;
|
|
||||||
|
|
||||||
if (bounds_check) {
|
if (bounds_check) {
|
||||||
c->context.stmt_state_flags |= StmtStateFlag_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) {
|
for_array(i, c->info.definitions.entries) {
|
||||||
Entity *e = c->info.definitions.entries.e[i].value;
|
Entity *e = c->info.definitions.entries.e[i].value;
|
||||||
if (e->kind == Entity_TypeName) {
|
if (e->kind == Entity_TypeName) {
|
||||||
// i64 size = type_size_of(c->sizes, c->allocator, e->type);
|
if (e->type != NULL) {
|
||||||
i64 align = type_align_of(c->sizes, c->allocator, e->type);
|
// i64 size = type_size_of(c->sizes, c->allocator, e->type);
|
||||||
|
i64 align = type_align_of(c->sizes, c->allocator, e->type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1516,6 +1516,18 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue *
|
|||||||
|
|
||||||
|
|
||||||
switch (op) {
|
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: {
|
case Token_AndNot: {
|
||||||
// NOTE(bill): x &~ y == x & (~y) == x & (y ~ -1)
|
// NOTE(bill): x &~ y == x & (~y) == x & (y ~ -1)
|
||||||
// NOTE(bill): "not" `x` == `x` "xor" `-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_And:
|
||||||
case Token_Or:
|
case Token_Or:
|
||||||
case Token_Xor:
|
case Token_Xor:
|
||||||
case Token_Shl:
|
|
||||||
case Token_Shr:
|
|
||||||
left = ir_emit_conv(proc, left, type);
|
left = ir_emit_conv(proc, left, type);
|
||||||
right = ir_emit_conv(proc, right, type);
|
right = ir_emit_conv(proc, right, type);
|
||||||
break;
|
break;
|
||||||
@@ -1949,15 +1959,15 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
|
|||||||
dst->kind == Type_Basic);
|
dst->kind == Type_Basic);
|
||||||
i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src);
|
i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src);
|
||||||
i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst);
|
i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst);
|
||||||
|
irConvKind kind = irConv_trunc;
|
||||||
if (sz == dz) {
|
if (sz == dz) {
|
||||||
// NOTE(bill): In LLVM, all integers are signed and rely upon 2's compliment
|
// NOTE(bill): In LLVM, all integers are signed and rely upon 2's compliment
|
||||||
return value;
|
// NOTE(bill): Copy the value just for type correctness
|
||||||
}
|
kind = irConv_bitcast;
|
||||||
|
} else if (dz > sz) {
|
||||||
irConvKind kind = irConv_trunc;
|
|
||||||
if (dz >= sz) {
|
|
||||||
kind = irConv_zext;
|
kind = irConv_zext;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ir_emit(proc, ir_make_instr_conv(proc, kind, value, src, dst));
|
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);
|
expr = unparen_expr(expr);
|
||||||
switch (expr->kind) {
|
switch (expr->kind) {
|
||||||
case_ast_node(bl, BasicLit, expr);
|
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_end;
|
||||||
|
|
||||||
case_ast_node(i, Ident, expr);
|
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);
|
case_ast_node(re, RunExpr, expr);
|
||||||
// TODO(bill): Run Expression
|
// TODO(bill): Run Expression
|
||||||
return ir_build_single_expr(proc, re->expr, tv);
|
return ir_build_expr(proc, re->expr);
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(de, DerefExpr, expr);
|
case_ast_node(de, DerefExpr, expr);
|
||||||
@@ -5834,9 +5845,10 @@ void ir_gen_tree(irGen *s) {
|
|||||||
case Type_Proc: {
|
case Type_Proc: {
|
||||||
tag = ir_emit_conv(proc, ti_ptr, t_type_info_procedure_ptr);
|
tag = ir_emit_conv(proc, ti_ptr, t_type_info_procedure_ptr);
|
||||||
|
|
||||||
irValue *params = ir_emit_struct_ep(proc, tag, 0);
|
irValue *params = ir_emit_struct_ep(proc, tag, 0);
|
||||||
irValue *results = ir_emit_struct_ep(proc, tag, 1);
|
irValue *results = ir_emit_struct_ep(proc, tag, 1);
|
||||||
irValue *variadic = ir_emit_struct_ep(proc, tag, 2);
|
irValue *variadic = ir_emit_struct_ep(proc, tag, 2);
|
||||||
|
irValue *convention = ir_emit_struct_ep(proc, tag, 3);
|
||||||
|
|
||||||
if (t->Proc.params) {
|
if (t->Proc.params) {
|
||||||
ir_emit_store(proc, params, ir_get_type_info_ptr(proc, type_info_data, 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, 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, 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
|
// TODO(bill): Type_Info for procedures
|
||||||
} break;
|
} break;
|
||||||
|
|||||||
+41
-29
@@ -62,36 +62,41 @@ typedef struct Parser {
|
|||||||
} Parser;
|
} Parser;
|
||||||
|
|
||||||
typedef enum ProcTag {
|
typedef enum ProcTag {
|
||||||
ProcTag_bounds_check = GB_BIT(0),
|
ProcTag_bounds_check = 1<<0,
|
||||||
ProcTag_no_bounds_check = GB_BIT(1),
|
ProcTag_no_bounds_check = 1<<1,
|
||||||
|
|
||||||
ProcTag_foreign = GB_BIT(10),
|
ProcTag_foreign = 1<<10,
|
||||||
ProcTag_export = GB_BIT(11),
|
ProcTag_export = 1<<11,
|
||||||
ProcTag_link_name = GB_BIT(12),
|
ProcTag_link_name = 1<<12,
|
||||||
ProcTag_inline = GB_BIT(13),
|
ProcTag_inline = 1<<13,
|
||||||
ProcTag_no_inline = GB_BIT(14),
|
ProcTag_no_inline = 1<<14,
|
||||||
ProcTag_dll_import = GB_BIT(15),
|
ProcTag_dll_import = 1<<15,
|
||||||
// ProcTag_dll_export = GB_BIT(16),
|
// ProcTag_dll_export = 1<<16,
|
||||||
} ProcTag;
|
} ProcTag;
|
||||||
|
|
||||||
typedef enum ProcCallingConvention {
|
typedef enum ProcCallingConvention {
|
||||||
ProcCC_Odin = 0,
|
ProcCC_Odin = 0,
|
||||||
ProcCC_C,
|
ProcCC_C = 1,
|
||||||
ProcCC_Std,
|
ProcCC_Std = 2,
|
||||||
ProcCC_Fast,
|
ProcCC_Fast = 3,
|
||||||
|
|
||||||
ProcCC_Invalid,
|
ProcCC_Invalid,
|
||||||
} ProcCallingConvention;
|
} ProcCallingConvention;
|
||||||
|
|
||||||
typedef enum VarDeclTag {
|
typedef enum VarDeclTag {
|
||||||
VarDeclTag_thread_local = GB_BIT(0),
|
VarDeclTag_thread_local = 1<<0,
|
||||||
} VarDeclTag;
|
} VarDeclTag;
|
||||||
|
|
||||||
typedef enum StmtStateFlag {
|
typedef enum StmtStateFlag {
|
||||||
StmtStateFlag_bounds_check = GB_BIT(0),
|
StmtStateFlag_bounds_check = 1<<0,
|
||||||
StmtStateFlag_no_bounds_check = GB_BIT(1),
|
StmtStateFlag_no_bounds_check = 1<<1,
|
||||||
} StmtStateFlag;
|
} StmtStateFlag;
|
||||||
|
|
||||||
|
typedef enum FieldFlag {
|
||||||
|
FieldFlag_using = 1<<0,
|
||||||
|
FieldFlag_ellipsis = 1<<1,
|
||||||
|
} FieldListTag;
|
||||||
|
|
||||||
AstNodeArray make_ast_node_array(AstFile *f) {
|
AstNodeArray make_ast_node_array(AstFile *f) {
|
||||||
AstNodeArray a;
|
AstNodeArray a;
|
||||||
// array_init(&a, gb_arena_allocator(&f->arena));
|
// 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(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \
|
||||||
AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \
|
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 { \
|
AST_NODE_KIND(AsmOperand, "assembly operand", struct { \
|
||||||
Token string; \
|
Token string; \
|
||||||
AstNode *operand; \
|
AstNode *operand; \
|
||||||
@@ -283,7 +291,7 @@ AST_NODE_KIND(_DeclEnd, "", i32) \
|
|||||||
AST_NODE_KIND(Field, "field", struct { \
|
AST_NODE_KIND(Field, "field", struct { \
|
||||||
AstNodeArray names; \
|
AstNodeArray names; \
|
||||||
AstNode * type; \
|
AstNode * type; \
|
||||||
bool is_using; \
|
u32 flags; \
|
||||||
}) \
|
}) \
|
||||||
AST_NODE_KIND(_TypeBegin, "", i32) \
|
AST_NODE_KIND(_TypeBegin, "", i32) \
|
||||||
AST_NODE_KIND(HelperType, "type", struct { \
|
AST_NODE_KIND(HelperType, "type", struct { \
|
||||||
@@ -955,11 +963,11 @@ AstNode *make_bad_decl(AstFile *f, Token begin, Token end) {
|
|||||||
return result;
|
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);
|
AstNode *result = make_node(f, AstNode_Field);
|
||||||
result->Field.names = names;
|
result->Field.names = names;
|
||||||
result->Field.type = type;
|
result->Field.type = type;
|
||||||
result->Field.is_using = is_using;
|
result->Field.flags = flags;
|
||||||
return result;
|
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);
|
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) {
|
TokenKind separator, TokenKind follow) {
|
||||||
AstNodeArray params = make_ast_node_array(f);
|
AstNodeArray params = make_ast_node_array(f);
|
||||||
isize name_count = 0;
|
isize name_count = 0;
|
||||||
@@ -2240,7 +2248,7 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, bool allow_using,
|
|||||||
is_using = false;
|
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");
|
syntax_error(f->curr_token, "`using` is not allowed within this parameter list");
|
||||||
is_using = false;
|
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");
|
expect_token_after(f, Token_Colon, "parameter list");
|
||||||
|
|
||||||
AstNode *type = NULL;
|
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;
|
Token ellipsis = f->curr_token;
|
||||||
next_token(f);
|
next_token(f);
|
||||||
type = parse_type_attempt(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");
|
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);
|
array_add(¶ms, param);
|
||||||
|
|
||||||
if (separator == Token_Semicolon) {
|
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) {
|
AstNodeArray parse_record_fields(AstFile *f, isize *field_count_, u32 flags, String context) {
|
||||||
return parse_field_list(f, field_count_, allow_using, false, Token_Semicolon, Token_CloseBrace);
|
return parse_field_list(f, field_count_, flags, Token_Semicolon, Token_CloseBrace);
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNode *parse_identifier_or_type(AstFile *f) {
|
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");
|
Token open = expect_token_after(f, Token_OpenBrace, "struct");
|
||||||
isize decl_count = 0;
|
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);
|
Token close = expect_token(f, Token_CloseBrace);
|
||||||
|
|
||||||
return make_struct_type(f, token, decls, decl_count, is_packed, is_ordered);
|
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 token = expect_token(f, Token_union);
|
||||||
Token open = expect_token_after(f, Token_OpenBrace, "union");
|
Token open = expect_token_after(f, Token_OpenBrace, "union");
|
||||||
isize decl_count = 0;
|
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);
|
Token close = expect_token(f, Token_CloseBrace);
|
||||||
|
|
||||||
return make_union_type(f, token, decls, decl_count);
|
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 token = expect_token(f, Token_raw_union);
|
||||||
Token open = expect_token_after(f, Token_OpenBrace, "raw_union");
|
Token open = expect_token_after(f, Token_OpenBrace, "raw_union");
|
||||||
isize decl_count = 0;
|
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);
|
Token close = expect_token(f, Token_CloseBrace);
|
||||||
|
|
||||||
return make_raw_union_type(f, token, decls, decl_count);
|
return make_raw_union_type(f, token, decls, decl_count);
|
||||||
@@ -2479,7 +2491,7 @@ void parse_proc_signature(AstFile *f,
|
|||||||
AstNodeArray *params,
|
AstNodeArray *params,
|
||||||
AstNodeArray *results) {
|
AstNodeArray *results) {
|
||||||
expect_token(f, Token_OpenParen);
|
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");
|
expect_token_after(f, Token_CloseParen, "parameter list");
|
||||||
*results = parse_results(f);
|
*results = parse_results(f);
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-25
@@ -74,8 +74,11 @@ typedef struct TypeRecord {
|
|||||||
|
|
||||||
// All record types
|
// All record types
|
||||||
// Theses are arrays
|
// Theses are arrays
|
||||||
Entity **fields; // Entity_Variable (otherwise Entity_TypeName if union)
|
// Entity_Variable - struct/raw_union
|
||||||
i32 field_count; // == offset_count is struct
|
// Entity_TypeName - union
|
||||||
|
// Entity_Constant - enum
|
||||||
|
Entity **fields;
|
||||||
|
i32 field_count; // == struct_offsets count
|
||||||
AstNode *node;
|
AstNode *node;
|
||||||
|
|
||||||
i64 * struct_offsets;
|
i64 * struct_offsets;
|
||||||
@@ -84,10 +87,10 @@ typedef struct TypeRecord {
|
|||||||
bool struct_is_ordered;
|
bool struct_is_ordered;
|
||||||
Entity **fields_in_src_order; // Entity_Variable
|
Entity **fields_in_src_order; // Entity_Variable
|
||||||
|
|
||||||
Type * enum_base_type;
|
Type * enum_base_type;
|
||||||
Entity *enum_count;
|
Entity * enum_count;
|
||||||
Entity *enum_min_value;
|
Entity * enum_min_value;
|
||||||
Entity *enum_max_value;
|
Entity * enum_max_value;
|
||||||
} TypeRecord;
|
} TypeRecord;
|
||||||
|
|
||||||
#define TYPE_KINDS \
|
#define TYPE_KINDS \
|
||||||
@@ -481,28 +484,28 @@ bool is_type_named(Type *t) {
|
|||||||
return t->kind == Type_Named;
|
return t->kind == Type_Named;
|
||||||
}
|
}
|
||||||
bool is_type_boolean(Type *t) {
|
bool is_type_boolean(Type *t) {
|
||||||
t = base_type(t);
|
t = base_type(base_enum_type(t));
|
||||||
if (t->kind == Type_Basic) {
|
if (t->kind == Type_Basic) {
|
||||||
return (t->Basic.flags & BasicFlag_Boolean) != 0;
|
return (t->Basic.flags & BasicFlag_Boolean) != 0;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool is_type_integer(Type *t) {
|
bool is_type_integer(Type *t) {
|
||||||
t = base_type(t);
|
t = base_type(base_enum_type(t));
|
||||||
if (t->kind == Type_Basic) {
|
if (t->kind == Type_Basic) {
|
||||||
return (t->Basic.flags & BasicFlag_Integer) != 0;
|
return (t->Basic.flags & BasicFlag_Integer) != 0;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool is_type_unsigned(Type *t) {
|
bool is_type_unsigned(Type *t) {
|
||||||
t = base_type(t);
|
t = base_type(base_enum_type(t));
|
||||||
if (t->kind == Type_Basic) {
|
if (t->kind == Type_Basic) {
|
||||||
return (t->Basic.flags & BasicFlag_Unsigned) != 0;
|
return (t->Basic.flags & BasicFlag_Unsigned) != 0;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool is_type_numeric(Type *t) {
|
bool is_type_numeric(Type *t) {
|
||||||
t = base_type(t);
|
t = base_type(base_enum_type(t));
|
||||||
if (t->kind == Type_Basic) {
|
if (t->kind == Type_Basic) {
|
||||||
return (t->Basic.flags & BasicFlag_Numeric) != 0;
|
return (t->Basic.flags & BasicFlag_Numeric) != 0;
|
||||||
}
|
}
|
||||||
@@ -536,7 +539,7 @@ bool is_type_untyped(Type *t) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool is_type_ordered(Type *t) {
|
bool is_type_ordered(Type *t) {
|
||||||
t = base_type(t);
|
t = base_type(base_enum_type(t));
|
||||||
if (t->kind == Type_Basic) {
|
if (t->kind == Type_Basic) {
|
||||||
return (t->Basic.flags & BasicFlag_Ordered) != 0;
|
return (t->Basic.flags & BasicFlag_Ordered) != 0;
|
||||||
}
|
}
|
||||||
@@ -553,21 +556,21 @@ bool is_type_constant_type(Type *t) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool is_type_float(Type *t) {
|
bool is_type_float(Type *t) {
|
||||||
t = base_type(t);
|
t = base_type(base_enum_type(t));
|
||||||
if (t->kind == Type_Basic) {
|
if (t->kind == Type_Basic) {
|
||||||
return (t->Basic.flags & BasicFlag_Float) != 0;
|
return (t->Basic.flags & BasicFlag_Float) != 0;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool is_type_f32(Type *t) {
|
bool is_type_f32(Type *t) {
|
||||||
t = base_type(t);
|
t = base_type(base_enum_type(t));
|
||||||
if (t->kind == Type_Basic) {
|
if (t->kind == Type_Basic) {
|
||||||
return t->Basic.kind == Basic_f32;
|
return t->Basic.kind == Basic_f32;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool is_type_f64(Type *t) {
|
bool is_type_f64(Type *t) {
|
||||||
t = base_type(t);
|
t = base_type(base_enum_type(t));
|
||||||
if (t->kind == Type_Basic) {
|
if (t->kind == Type_Basic) {
|
||||||
return t->Basic.kind == Basic_f64;
|
return t->Basic.kind == Basic_f64;
|
||||||
}
|
}
|
||||||
@@ -677,8 +680,13 @@ bool is_type_indexable(Type *t) {
|
|||||||
bool type_has_nil(Type *t) {
|
bool type_has_nil(Type *t) {
|
||||||
t = base_type(t);
|
t = base_type(t);
|
||||||
switch (t->kind) {
|
switch (t->kind) {
|
||||||
case Type_Basic:
|
case Type_Basic: {
|
||||||
return is_type_rawptr(t);
|
switch (t->Basic.kind) {
|
||||||
|
case Basic_rawptr:
|
||||||
|
case Basic_any:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
case Type_Slice:
|
case Type_Slice:
|
||||||
case Type_Proc:
|
case Type_Proc:
|
||||||
case Type_Pointer:
|
case Type_Pointer:
|
||||||
@@ -693,24 +701,22 @@ bool is_type_comparable(Type *t) {
|
|||||||
t = base_type(t);
|
t = base_type(t);
|
||||||
switch (t->kind) {
|
switch (t->kind) {
|
||||||
case Type_Basic:
|
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:
|
case Type_Pointer:
|
||||||
return true;
|
return true;
|
||||||
case Type_Record: {
|
case Type_Record: {
|
||||||
if (false && is_type_struct(t)) {
|
if (is_type_enum(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)) {
|
|
||||||
return is_type_comparable(base_enum_type(t));
|
return is_type_comparable(base_enum_type(t));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} break;
|
} break;
|
||||||
case Type_Array:
|
case Type_Array:
|
||||||
return false;
|
return false;
|
||||||
// return is_type_comparable(t->Array.elem);
|
|
||||||
case Type_Vector:
|
case Type_Vector:
|
||||||
return is_type_comparable(t->Vector.elem);
|
return is_type_comparable(t->Vector.elem);
|
||||||
case Type_Proc:
|
case Type_Proc:
|
||||||
@@ -1189,6 +1195,9 @@ i64 align_formula(i64 size, i64 align) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||||
|
if (t == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
i64 size;
|
i64 size;
|
||||||
TypePath path = {0};
|
TypePath path = {0};
|
||||||
type_path_init(&path);
|
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) {
|
i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||||
|
if (t == NULL) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
i64 align;
|
i64 align;
|
||||||
TypePath path = {0};
|
TypePath path = {0};
|
||||||
type_path_init(&path);
|
type_path_init(&path);
|
||||||
@@ -1214,6 +1226,17 @@ i64 type_align_of_internal(BaseTypeSizes s, gbAllocator allocator, Type *t, Type
|
|||||||
t = base_type(t);
|
t = base_type(t);
|
||||||
|
|
||||||
switch (t->kind) {
|
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: {
|
case Type_Array: {
|
||||||
Type *elem = t->Array.elem;
|
Type *elem = t->Array.elem;
|
||||||
type_path_push(path, elem);
|
type_path_push(path, elem);
|
||||||
|
|||||||
Reference in New Issue
Block a user