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