authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-09-26 22:33:33-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-09-26 22:33:33-04:00
log87b7c28c9aa618eda5589693ee79a5737d907bac
treee152ba0b645aaac3f541973781b5c5c5baae111c
parent7ce7e2c9d1d43886c93f34b67d32ef77cc0d8a6e

cstr.len and cstr.cmp can run at compile time

closes #140

3 files changed, 167 insertions(+), 104 deletions(-)

src/all_types.hpp-27
......@@ -1111,33 +1111,6 @@ struct FnTableEntry {
11111111 ZigList<AstNode *> goto_list;
11121112};
11131113
1114struct EvalVar {
1115 Buf *name;
1116 ConstExprValue value;
1117};
1118
1119struct EvalScope {
1120 BlockContext *block_context;
1121 ZigList<EvalVar> vars;
1122};
1123
1124struct EvalFnRoot {
1125 CodeGen *codegen;
1126 FnTableEntry *fn;
1127 AstNode *call_node;
1128 size_t branch_quota;
1129 size_t branches_used;
1130 AstNode *exceeded_quota_node;
1131 bool abort;
1132};
1133
1134struct EvalFn {
1135 EvalFnRoot *root;
1136 FnTableEntry *fn;
1137 ConstExprValue *return_expr;
1138 ZigList<EvalScope*> scope_stack;
1139};
1140
11411114enum BuiltinFnId {
11421115 BuiltinFnIdInvalid,
11431116 BuiltinFnIdMemcpy,
src/eval.cpp+149-69
......@@ -2,6 +2,34 @@
22#include "analyze.hpp"
33#include "error.hpp"
44
5struct EvalVar {
6 Buf *name;
7 ConstExprValue value;
8};
9
10struct EvalScope {
11 BlockContext *block_context;
12 ZigList<EvalVar> vars;
13};
14
15struct EvalFnRoot {
16 CodeGen *codegen;
17 FnTableEntry *fn;
18 AstNode *call_node;
19 size_t branch_quota;
20 size_t branches_used;
21 AstNode *exceeded_quota_node;
22 bool abort;
23};
24
25struct EvalFn {
26 EvalFnRoot *root;
27 FnTableEntry *fn;
28 ConstExprValue *return_expr;
29 ZigList<EvalScope*> scope_stack;
30};
31
32
533static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args, ConstExprValue *out_val);
634
735bool const_values_equal(ConstExprValue *a, ConstExprValue *b, TypeTableEntry *type_entry) {
......@@ -94,9 +122,9 @@ static bool eval_return(EvalFn *ef, AstNode *node, ConstExprValue *out) {
94122}
95123
96124static bool eval_bool_bin_op_bool(bool a, BinOpType bin_op, bool b) {
97 if (bin_op == BinOpTypeBoolOr) {
125 if (bin_op == BinOpTypeBoolOr || bin_op == BinOpTypeAssignBoolOr) {
98126 return a || b;
99 } else if (bin_op == BinOpTypeBoolAnd) {
127 } else if (bin_op == BinOpTypeBoolAnd || bin_op == BinOpTypeAssignBoolAnd) {
100128 return a && b;
101129 } else {
102130 zig_unreachable();
......@@ -180,6 +208,31 @@ static int eval_const_expr_bin_op_bignum(ConstExprValue *op1_val, ConstExprValue
180208 return 0;
181209}
182210
211bool eval_const_expr_bin_op_handle_errors(EvalFn *ef, AstNode *node,
212 ConstExprValue *op1_val, TypeTableEntry *op1_type,
213 BinOpType bin_op, ConstExprValue *op2_val, TypeTableEntry *op2_type, ConstExprValue *out_val)
214{
215 int err;
216 if ((err = eval_const_expr_bin_op(op1_val, op1_type, bin_op, op2_val, op2_type, out_val))) {
217 ef->root->abort = true;
218 if (err == ErrorDivByZero) {
219 ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node,
220 buf_sprintf("function evaluation caused division by zero"));
221 add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here"));
222 add_error_note(ef->root->codegen, msg, node, buf_sprintf("division by zero here"));
223 } else if (err == ErrorOverflow) {
224 ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node,
225 buf_sprintf("function evaluation caused overflow"));
226 add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here"));
227 add_error_note(ef->root->codegen, msg, node, buf_sprintf("overflow occurred here"));
228 } else {
229 zig_unreachable();
230 }
231 return true;
232 }
233 return false;
234}
235
183236int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
184237 BinOpType bin_op, ConstExprValue *op2_val, TypeTableEntry *op2_type, ConstExprValue *out_val)
185238{
......@@ -190,25 +243,12 @@ int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
190243
191244 switch (bin_op) {
192245 case BinOpTypeAssign:
193 case BinOpTypeAssignTimes:
194 case BinOpTypeAssignTimesWrap:
195 case BinOpTypeAssignDiv:
196 case BinOpTypeAssignMod:
197 case BinOpTypeAssignPlus:
198 case BinOpTypeAssignPlusWrap:
199 case BinOpTypeAssignMinus:
200 case BinOpTypeAssignMinusWrap:
201 case BinOpTypeAssignBitShiftLeft:
202 case BinOpTypeAssignBitShiftLeftWrap:
203 case BinOpTypeAssignBitShiftRight:
204 case BinOpTypeAssignBitAnd:
205 case BinOpTypeAssignBitXor:
206 case BinOpTypeAssignBitOr:
207 case BinOpTypeAssignBoolAnd:
208 case BinOpTypeAssignBoolOr:
209 zig_unreachable();
246 *out_val = *op2_val;
247 return 0;
210248 case BinOpTypeBoolOr:
211249 case BinOpTypeBoolAnd:
250 case BinOpTypeAssignBoolAnd:
251 case BinOpTypeAssignBoolOr:
212252 assert(op1_type->id == TypeTableEntryIdBool);
213253 assert(op2_type->id == TypeTableEntryIdBool);
214254 out_val->data.x_bool = eval_bool_bin_op_bool(op1_val->data.x_bool, bin_op, op2_val->data.x_bool);
......@@ -264,30 +304,43 @@ int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
264304 return 0;
265305 }
266306 case BinOpTypeAdd:
307 case BinOpTypeAssignPlus:
267308 return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_add, op1_type, false);
268309 case BinOpTypeAddWrap:
310 case BinOpTypeAssignPlusWrap:
269311 return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_add, op1_type, true);
270312 case BinOpTypeBinOr:
313 case BinOpTypeAssignBitOr:
271314 return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_or, op1_type, false);
272315 case BinOpTypeBinXor:
316 case BinOpTypeAssignBitXor:
273317 return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_xor, op1_type, false);
274318 case BinOpTypeBinAnd:
319 case BinOpTypeAssignBitAnd:
275320 return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_and, op1_type, false);
276321 case BinOpTypeBitShiftLeft:
322 case BinOpTypeAssignBitShiftLeft:
277323 return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_shl, op1_type, false);
278324 case BinOpTypeBitShiftLeftWrap:
325 case BinOpTypeAssignBitShiftLeftWrap:
279326 return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_shl, op1_type, true);
280327 case BinOpTypeBitShiftRight:
328 case BinOpTypeAssignBitShiftRight:
281329 return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_shr, op1_type, false);
282330 case BinOpTypeSub:
331 case BinOpTypeAssignMinus:
283332 return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_sub, op1_type, false);
284333 case BinOpTypeSubWrap:
334 case BinOpTypeAssignMinusWrap:
285335 return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_sub, op1_type, true);
286336 case BinOpTypeMult:
337 case BinOpTypeAssignTimes:
287338 return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mul, op1_type, false);
288339 case BinOpTypeMultWrap:
340 case BinOpTypeAssignTimesWrap:
289341 return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mul, op1_type, true);
290342 case BinOpTypeDiv:
343 case BinOpTypeAssignDiv:
291344 {
292345 bool is_int = false;
293346 bool is_float = false;
......@@ -309,6 +362,7 @@ int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
309362 }
310363 }
311364 case BinOpTypeMod:
365 case BinOpTypeAssignMod:
312366 return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mod, op1_type, false);
313367 case BinOpTypeUnwrapMaybe:
314368 zig_panic("TODO");
......@@ -320,13 +374,66 @@ int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
320374 zig_unreachable();
321375}
322376
323static bool eval_bin_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) {
324 assert(node->type == NodeTypeBinOpExpr);
377static EvalVar *find_var(EvalFn *ef, Buf *name) {
378 size_t scope_index = ef->scope_stack.length - 1;
379 while (scope_index != SIZE_MAX) {
380 EvalScope *scope = ef->scope_stack.at(scope_index);
381 for (size_t var_i = 0; var_i < scope->vars.length; var_i += 1) {
382 EvalVar *var = &scope->vars.at(var_i);
383 if (buf_eql_buf(var->name, name)) {
384 return var;
385 }
386 }
387 scope_index -= 1;
388 }
325389
390 return nullptr;
391}
392
393static bool eval_get_lvalue(EvalFn *ef, AstNode *node, ConstExprValue **lvalue) {
394 if (node->type == NodeTypeSymbol) {
395 Buf *name = node->data.symbol_expr.symbol;
396 EvalVar *var = find_var(ef, name);
397 assert(var);
398 *lvalue = &var->value;
399 } else {
400 zig_panic("TODO eval other lvalue types");
401 }
402 return false;
403}
404
405static bool eval_bin_op_assign(EvalFn *ef, AstNode *node, ConstExprValue *out_val) {
326406 AstNode *op1 = node->data.bin_op_expr.op1;
327407 AstNode *op2 = node->data.bin_op_expr.op2;
328408 BinOpType bin_op = node->data.bin_op_expr.bin_op;
329409
410 TypeTableEntry *op2_type = get_resolved_expr(op2)->type_entry;
411 assert(op2_type);
412
413 ConstExprValue *assign_result_val;
414 if (eval_get_lvalue(ef, op1, &assign_result_val)) return true;
415
416 ConstExprValue op1_val = *assign_result_val;
417
418 ConstExprValue op2_val = {0};
419 if (eval_expr(ef, op2, &op2_val)) return true;
420
421 if (eval_const_expr_bin_op_handle_errors(ef, node, &op1_val, op2_type, bin_op, &op2_val, op2_type,
422 assign_result_val))
423 {
424 return true;
425 }
426
427 out_val->ok = true;
428 out_val->depends_on_compile_var = false;
429 return false;
430}
431
432static bool eval_bin_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) {
433 assert(node->type == NodeTypeBinOpExpr);
434
435 BinOpType bin_op = node->data.bin_op_expr.bin_op;
436
330437 switch (bin_op) {
331438 case BinOpTypeAssign:
332439 case BinOpTypeAssignTimes:
......@@ -345,7 +452,7 @@ static bool eval_bin_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val)
345452 case BinOpTypeAssignBitOr:
346453 case BinOpTypeAssignBoolAnd:
347454 case BinOpTypeAssignBoolOr:
348 zig_panic("TODO");
455 return eval_bin_op_assign(ef, node, out_val);
349456 case BinOpTypeBoolOr:
350457 case BinOpTypeBoolAnd:
351458 case BinOpTypeCmpEq:
......@@ -376,6 +483,10 @@ static bool eval_bin_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val)
376483 zig_unreachable();
377484 }
378485
486 AstNode *op1 = node->data.bin_op_expr.op1;
487 AstNode *op2 = node->data.bin_op_expr.op2;
488
489
379490 TypeTableEntry *op1_type = get_resolved_expr(op1)->type_entry;
380491 TypeTableEntry *op2_type = get_resolved_expr(op2)->type_entry;
381492
......@@ -388,22 +499,7 @@ static bool eval_bin_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val)
388499 ConstExprValue op2_val = {0};
389500 if (eval_expr(ef, op2, &op2_val)) return true;
390501
391 int err;
392 if ((err = eval_const_expr_bin_op(&op1_val, op1_type, bin_op, &op2_val, op2_type, out_val))) {
393 ef->root->abort = true;
394 if (err == ErrorDivByZero) {
395 ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node,
396 buf_sprintf("function evaluation caused division by zero"));
397 add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here"));
398 add_error_note(ef->root->codegen, msg, node, buf_sprintf("division by zero here"));
399 } else if (err == ErrorOverflow) {
400 ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node,
401 buf_sprintf("function evaluation caused overflow"));
402 add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here"));
403 add_error_note(ef->root->codegen, msg, node, buf_sprintf("overflow occurred here"));
404 } else {
405 zig_unreachable();
406 }
502 if (eval_const_expr_bin_op_handle_errors(ef, node, &op1_val, op1_type, bin_op, &op2_val, op2_type, out_val)) {
407503 return true;
408504 }
409505
......@@ -412,22 +508,6 @@ static bool eval_bin_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val)
412508 return false;
413509}
414510
415static EvalVar *find_var(EvalFn *ef, Buf *name) {
416 size_t scope_index = ef->scope_stack.length - 1;
417 while (scope_index != SIZE_MAX) {
418 EvalScope *scope = ef->scope_stack.at(scope_index);
419 for (size_t var_i = 0; var_i < scope->vars.length; var_i += 1) {
420 EvalVar *var = &scope->vars.at(var_i);
421 if (buf_eql_buf(var->name, name)) {
422 return var;
423 }
424 }
425 scope_index -= 1;
426 }
427
428 return nullptr;
429}
430
431511static bool eval_symbol_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) {
432512 assert(node->type == NodeTypeSymbol);
433513
......@@ -456,7 +536,7 @@ static bool eval_container_init_expr(EvalFn *ef, AstNode *node, ConstExprValue *
456536 ContainerInitKind kind = container_init_expr->kind;
457537
458538 if (container_init_expr->enum_type) {
459 zig_panic("TODO");
539 zig_panic("TODO eval enum init");
460540 }
461541
462542 TypeTableEntry *container_type = resolve_expr_type(container_init_expr->type);
......@@ -514,7 +594,7 @@ static bool eval_container_init_expr(EvalFn *ef, AstNode *node, ConstExprValue *
514594 elem_val->depends_on_compile_var;
515595 }
516596 } else {
517 zig_panic("TODO");
597 zig_panic("TODO init more container kinds");
518598 }
519599
520600
......@@ -874,7 +954,7 @@ static bool eval_fn_call_builtin(EvalFn *ef, AstNode *node, ConstExprValue *out_
874954 case BuiltinFnIdEmbedFile:
875955 case BuiltinFnIdCmpExchange:
876956 case BuiltinFnIdTruncate:
877 zig_panic("TODO");
957 zig_panic("TODO builtin function");
878958 case BuiltinFnIdBreakpoint:
879959 case BuiltinFnIdInvalid:
880960 case BuiltinFnIdFrameAddress:
......@@ -909,7 +989,7 @@ static bool eval_fn_call_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val
909989 if (fn_ref_expr->type == NodeTypeFieldAccessExpr &&
910990 fn_ref_expr->data.field_access_expr.is_member_fn)
911991 {
912 zig_panic("TODO");
992 zig_panic("TODO field access member fn");
913993 }
914994
915995 if (!fn_table_entry) {
......@@ -941,7 +1021,7 @@ static bool eval_field_access_expr(EvalFn *ef, AstNode *node, ConstExprValue *ou
9411021 if (struct_type->id == TypeTableEntryIdArray) {
9421022 Buf *name = node->data.field_access_expr.field_name;
9431023 assert(buf_eql_str(name, "len"));
944 zig_panic("TODO");
1024 zig_panic("TODO field access array");
9451025 } else if (struct_type->id == TypeTableEntryIdStruct || (struct_type->id == TypeTableEntryIdPointer &&
9461026 struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct))
9471027 {
......@@ -954,17 +1034,17 @@ static bool eval_field_access_expr(EvalFn *ef, AstNode *node, ConstExprValue *ou
9541034 *out_val = *field_value;
9551035 assert(out_val->ok);
9561036 } else {
957 zig_panic("TODO");
1037 zig_panic("TODO field access struct");
9581038 }
9591039 } else if (struct_type->id == TypeTableEntryIdMetaType) {
9601040 TypeTableEntry *child_type = resolve_expr_type(struct_expr);
9611041 if (child_type->id == TypeTableEntryIdPureError) {
9621042 *out_val = get_resolved_expr(node)->const_val;
9631043 } else {
964 zig_panic("TODO");
1044 zig_panic("TODO field access meta type");
9651045 }
9661046 } else if (struct_type->id == TypeTableEntryIdNamespace) {
967 zig_panic("TODO");
1047 zig_panic("TODO field access namespace");
9681048 } else {
9691049 zig_unreachable();
9701050 }
......@@ -989,7 +1069,7 @@ static bool eval_for_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) {
9891069 Buf *elem_var_name = elem_node->data.symbol_expr.symbol;
9901070
9911071 if (node->data.for_expr.elem_is_ptr) {
992 zig_panic("TODO");
1072 zig_panic("TODO for elem is ptr");
9931073 }
9941074
9951075 Buf *index_var_name = nullptr;
......@@ -1062,7 +1142,7 @@ static bool eval_array_access_expr(EvalFn *ef, AstNode *node, ConstExprValue *ou
10621142
10631143 if (array_type->id == TypeTableEntryIdPointer) {
10641144 if (index_int >= array_val.data.x_ptr.len) {
1065 zig_panic("TODO");
1145 zig_panic("TODO array access pointer");
10661146 }
10671147 *out_val = *array_val.data.x_ptr.ptr[index_int];
10681148 } else if (array_type->id == TypeTableEntryIdStruct) {
......@@ -1071,7 +1151,7 @@ static bool eval_array_access_expr(EvalFn *ef, AstNode *node, ConstExprValue *ou
10711151 ConstExprValue *len_value = array_val.data.x_struct.fields[1];
10721152 uint64_t len_int = len_value->data.x_bignum.data.x_uint;
10731153 if (index_int >= len_int) {
1074 zig_panic("TODO");
1154 zig_panic("TODO array access slice");
10751155 }
10761156
10771157 ConstExprValue *ptr_value = array_val.data.x_struct.fields[0];
......@@ -1079,7 +1159,7 @@ static bool eval_array_access_expr(EvalFn *ef, AstNode *node, ConstExprValue *ou
10791159 } else if (array_type->id == TypeTableEntryIdArray) {
10801160 uint64_t array_len = array_type->data.array.len;
10811161 if (index_int >= array_len) {
1082 zig_panic("TODO");
1162 zig_panic("TODO array access array");
10831163 }
10841164 *out_val = *array_val.data.x_array.fields[index_int];
10851165 } else {
......@@ -1152,7 +1232,7 @@ static bool eval_prefix_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_v
11521232 return true;
11531233 }
11541234 } else if (expr_type->id == TypeTableEntryIdFloat) {
1155 zig_panic("TODO");
1235 zig_panic("TODO prefix op on floats");
11561236 } else {
11571237 zig_unreachable();
11581238 }
......@@ -1162,7 +1242,7 @@ static bool eval_prefix_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_v
11621242 case PrefixOpError:
11631243 case PrefixOpUnwrapError:
11641244 case PrefixOpUnwrapMaybe:
1165 zig_panic("TODO");
1245 zig_panic("TODO more prefix operations");
11661246 case PrefixOpInvalid:
11671247 zig_unreachable();
11681248 }
......@@ -1308,7 +1388,7 @@ static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) {
13081388 case NodeTypeErrorType:
13091389 case NodeTypeTypeLiteral:
13101390 case NodeTypeVarLiteral:
1311 zig_panic("TODO");
1391 zig_panic("TODO expr node");
13121392 case NodeTypeRoot:
13131393 case NodeTypeFnProto:
13141394 case NodeTypeFnDef:
std/cstr.zig+18-8
......@@ -6,22 +6,22 @@ const assert = debug.assert;
66
77const strlen = len;
88
9// TODO fix https://github.com/andrewrk/zig/issues/140
10// and then make this able to run at compile time
11#static_eval_enable(false)
129pub fn len(ptr: &const u8) -> usize {
1310 var count: usize = 0;
1411 while (ptr[count] != 0; count += 1) {}
1512 return count;
1613}
1714
18// TODO fix https://github.com/andrewrk/zig/issues/140
19// and then make this able to run at compile time
20#static_eval_enable(false)
21pub fn cmp(a: &const u8, b: &const u8) -> i32 {
15pub fn cmp(a: &const u8, b: &const u8) -> i8 {
2216 var index: usize = 0;
2317 while (a[index] == b[index] && a[index] != 0; index += 1) {}
24 return a[index] - b[index];
18 return if (a[index] > b[index]) {
19 1
20 } else if (a[index] < b[index]) {
21 -1
22 } else {
23 0
24 };
2525}
2626
2727pub fn toSliceConst(str: &const u8) -> []const u8 {
......@@ -145,3 +145,13 @@ fn testSimpleCBuf() {
145145 %%buf2.resize(4);
146146 assert(buf.startsWithCBuf(&buf2));
147147}
148
149#attribute("test")
150fn testCompileTimeStrCmp() {
151 assert(@constEval(cmp(c"aoeu", c"aoez") == -1));
152}
153
154#attribute("test")
155fn testCompileTimeStrLen() {
156 assert(@constEval(len(c"123456789") == 9));
157}