| author | |
| committer | |
| log | e0aa0736be47e5e1383d8fb25f744f95c5a830ba |
| tree | 0ae86d7782de9e73d21449fd70a9fc3383cdad0a |
| parent | 22c52f1eb67a46e60be876fda332ad7d2f338ee7 |
6 files changed, 77 insertions(+), 6 deletions(-)
doc/langref.md+2-2| ... | ... | @@ -154,7 +154,7 @@ FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) |
| 154 | 154 | |
| 155 | 155 | ArrayAccessExpression : token(LBracket) Expression token(RBracket) |
| 156 | 156 | |
| 157 | PrefixOp : token(Not) | token(Dash) | token(Tilde) | (token(Ampersand) option(token(Const))) | |
| 157 | PrefixOp : token(Not) | token(Dash) | token(Tilde) | token(Star) | (token(Ampersand) option(token(Const))) | |
| 158 | 158 | |
| 159 | 159 | PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType |
| 160 | 160 | |
| ... | ... | @@ -173,7 +173,7 @@ KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) |
| 173 | 173 | |
| 174 | 174 | ``` |
| 175 | 175 | x() x[] x.y |
| 176 | !x -x ~x &x &const x | |
| 176 | !x -x ~x *x &x &const x | |
| 177 | 177 | as |
| 178 | 178 | * / % |
| 179 | 179 | + - |
src/analyze.cpp+38-2| ... | ... | @@ -1271,31 +1271,51 @@ static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, Bloc |
| 1271 | 1271 | if (purpose == LValPurposeAssign && var->is_const) { |
| 1272 | 1272 | add_node_error(g, lhs_node, |
| 1273 | 1273 | buf_sprintf("cannot assign to constant")); |
| 1274 | expected_rhs_type = g->builtin_types.entry_invalid; | |
| 1274 | 1275 | } else if (purpose == LValPurposeAddressOf && var->is_const && !is_ptr_const) { |
| 1275 | 1276 | add_node_error(g, lhs_node, |
| 1276 | 1277 | buf_sprintf("must use &const to get address of constant")); |
| 1278 | expected_rhs_type = g->builtin_types.entry_invalid; | |
| 1277 | 1279 | } else { |
| 1278 | 1280 | expected_rhs_type = var->type; |
| 1279 | 1281 | } |
| 1280 | 1282 | } else { |
| 1281 | 1283 | add_node_error(g, lhs_node, |
| 1282 | 1284 | buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name))); |
| 1285 | expected_rhs_type = g->builtin_types.entry_invalid; | |
| 1283 | 1286 | } |
| 1284 | 1287 | } else if (lhs_node->type == NodeTypeArrayAccessExpr) { |
| 1285 | 1288 | expected_rhs_type = analyze_array_access_expr(g, import, block_context, lhs_node); |
| 1286 | 1289 | } else if (lhs_node->type == NodeTypeFieldAccessExpr) { |
| 1287 | 1290 | alloc_codegen_node(lhs_node); |
| 1288 | 1291 | expected_rhs_type = analyze_field_access_expr(g, import, block_context, lhs_node); |
| 1292 | } else if (lhs_node->type == NodeTypePrefixOpExpr && | |
| 1293 | lhs_node->data.prefix_op_expr.prefix_op == PrefixOpDereference) | |
| 1294 | { | |
| 1295 | assert(purpose == LValPurposeAssign); | |
| 1296 | AstNode *target_node = lhs_node->data.prefix_op_expr.primary_expr; | |
| 1297 | TypeTableEntry *type_entry = analyze_expression(g, import, block_context, nullptr, target_node); | |
| 1298 | if (type_entry->id == TypeTableEntryIdInvalid) { | |
| 1299 | expected_rhs_type = type_entry; | |
| 1300 | } else if (type_entry->id == TypeTableEntryIdPointer) { | |
| 1301 | expected_rhs_type = type_entry->data.pointer.child_type; | |
| 1302 | } else { | |
| 1303 | add_node_error(g, target_node, | |
| 1304 | buf_sprintf("indirection requires pointer operand ('%s' invalid)", | |
| 1305 | buf_ptr(&type_entry->name))); | |
| 1306 | expected_rhs_type = g->builtin_types.entry_invalid; | |
| 1307 | } | |
| 1289 | 1308 | } else { |
| 1290 | 1309 | if (purpose == LValPurposeAssign) { |
| 1291 | 1310 | add_node_error(g, lhs_node, |
| 1292 | buf_sprintf("assignment target must be variable, field, or array element")); | |
| 1311 | buf_sprintf("invalid assignment target")); | |
| 1293 | 1312 | } else if (purpose == LValPurposeAddressOf) { |
| 1294 | 1313 | add_node_error(g, lhs_node, |
| 1295 | buf_sprintf("addressof target must be variable, field, or array element")); | |
| 1314 | buf_sprintf("invalid addressof target")); | |
| 1296 | 1315 | } |
| 1297 | 1316 | expected_rhs_type = g->builtin_types.entry_invalid; |
| 1298 | 1317 | } |
| 1318 | assert(expected_rhs_type); | |
| 1299 | 1319 | return expected_rhs_type; |
| 1300 | 1320 | } |
| 1301 | 1321 | |
| ... | ... | @@ -1940,6 +1960,22 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 1940 | 1960 | return_type = get_pointer_to_type(g, child_type, is_const); |
| 1941 | 1961 | break; |
| 1942 | 1962 | } |
| 1963 | case PrefixOpDereference: | |
| 1964 | { | |
| 1965 | TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, | |
| 1966 | node->data.prefix_op_expr.primary_expr); | |
| 1967 | if (type_entry->id == TypeTableEntryIdInvalid) { | |
| 1968 | return_type = type_entry; | |
| 1969 | } else if (type_entry->id == TypeTableEntryIdPointer) { | |
| 1970 | return_type = type_entry->data.pointer.child_type; | |
| 1971 | } else { | |
| 1972 | add_node_error(g, node->data.prefix_op_expr.primary_expr, | |
| 1973 | buf_sprintf("indirection requires pointer operand ('%s' invalid)", | |
| 1974 | buf_ptr(&type_entry->name))); | |
| 1975 | return_type = g->builtin_types.entry_invalid; | |
| 1976 | } | |
| 1977 | break; | |
| 1978 | } | |
| 1943 | 1979 | } |
| 1944 | 1980 | break; |
| 1945 | 1981 | case NodeTypeIfBoolExpr: |
src/codegen.cpp+13-1| ... | ... | @@ -359,6 +359,13 @@ static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node, |
| 359 | 359 | } |
| 360 | 360 | } else if (node->type == NodeTypeFieldAccessExpr) { |
| 361 | 361 | target_ref = gen_field_ptr(g, node, out_type_entry); |
| 362 | } else if (node->type == NodeTypePrefixOpExpr) { | |
| 363 | assert(node->data.prefix_op_expr.prefix_op == PrefixOpDereference); | |
| 364 | AstNode *target_expr = node->data.prefix_op_expr.primary_expr; | |
| 365 | TypeTableEntry *type_entry = get_expr_type(target_expr); | |
| 366 | assert(type_entry->id == TypeTableEntryIdPointer); | |
| 367 | *out_type_entry = type_entry->data.pointer.child_type; | |
| 368 | return gen_expr(g, target_expr); | |
| 362 | 369 | } else { |
| 363 | 370 | zig_panic("bad assign target"); |
| 364 | 371 | } |
| ... | ... | @@ -397,11 +404,16 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { |
| 397 | 404 | case PrefixOpAddressOf: |
| 398 | 405 | case PrefixOpConstAddressOf: |
| 399 | 406 | { |
| 400 | add_debug_source_node(g, node); | |
| 401 | 407 | TypeTableEntry *lvalue_type; |
| 402 | 408 | return gen_lvalue(g, node, expr_node, &lvalue_type); |
| 403 | 409 | } |
| 404 | 410 | |
| 411 | case PrefixOpDereference: | |
| 412 | { | |
| 413 | LLVMValueRef expr = gen_expr(g, expr_node); | |
| 414 | add_debug_source_node(g, node); | |
| 415 | return LLVMBuildLoad(g->builder, expr, ""); | |
| 416 | } | |
| 405 | 417 | } |
| 406 | 418 | zig_unreachable(); |
| 407 | 419 | } |
src/parser.cpp+2| ... | ... | @@ -60,6 +60,7 @@ static const char *prefix_op_str(PrefixOp prefix_op) { |
| 60 | 60 | case PrefixOpBinNot: return "~"; |
| 61 | 61 | case PrefixOpAddressOf: return "&"; |
| 62 | 62 | case PrefixOpConstAddressOf: return "&const"; |
| 63 | case PrefixOpDereference: return "*"; | |
| 63 | 64 | } |
| 64 | 65 | zig_unreachable(); |
| 65 | 66 | } |
| ... | ... | @@ -1422,6 +1423,7 @@ static PrefixOp tok_to_prefix_op(Token *token) { |
| 1422 | 1423 | case TokenIdDash: return PrefixOpNegation; |
| 1423 | 1424 | case TokenIdTilde: return PrefixOpBinNot; |
| 1424 | 1425 | case TokenIdAmpersand: return PrefixOpAddressOf; |
| 1426 | case TokenIdStar: return PrefixOpDereference; | |
| 1425 | 1427 | default: return PrefixOpInvalid; |
| 1426 | 1428 | } |
| 1427 | 1429 | } |
src/parser.hpp+1| ... | ... | @@ -212,6 +212,7 @@ enum PrefixOp { |
| 212 | 212 | PrefixOpNegation, |
| 213 | 213 | PrefixOpAddressOf, |
| 214 | 214 | PrefixOpConstAddressOf, |
| 215 | PrefixOpDereference, | |
| 215 | 216 | }; |
| 216 | 217 | |
| 217 | 218 | struct AstNodePrefixOpExpr { |
test/run_tests.cpp+21-1| ... | ... | @@ -755,6 +755,26 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { |
| 755 | 755 | } |
| 756 | 756 | print_str("OK\n"); |
| 757 | 757 | return 0; |
| 758 | } | |
| 759 | )SOURCE", "OK\n"); | |
| 760 | ||
| 761 | add_simple_case("pointer dereferencing", R"SOURCE( | |
| 762 | use "std.zig"; | |
| 763 | ||
| 764 | pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { | |
| 765 | var x = 3 as i32; | |
| 766 | const y = &x; | |
| 767 | ||
| 768 | *y += 1; | |
| 769 | ||
| 770 | if (x != 4) { | |
| 771 | print_str("BAD\n"); | |
| 772 | } | |
| 773 | if (*y != 4) { | |
| 774 | print_str("BAD\n"); | |
| 775 | } | |
| 776 | print_str("OK\n"); | |
| 777 | return 0; | |
| 758 | 778 | } |
| 759 | 779 | )SOURCE", "OK\n"); |
| 760 | 780 | } |
| ... | ... | @@ -904,7 +924,7 @@ a_label: |
| 904 | 924 | fn f() { |
| 905 | 925 | 3 = 3; |
| 906 | 926 | } |
| 907 | )SOURCE", 1, ".tmp_source.zig:3:5: error: assignment target must be variable, field, or array element"); | |
| 927 | )SOURCE", 1, ".tmp_source.zig:3:5: error: invalid assignment target"); | |
| 908 | 928 | |
| 909 | 929 | add_compile_fail_case("assign to constant variable", R"SOURCE( |
| 910 | 930 | fn f() { |