diff --git a/doc/langref.html.in b/doc/langref.html.in index 16fafdaad9..9fb2ebf9f5 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5958,10 +5958,12 @@ MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%" PrefixOpExpression = PrefixOp TypeExpr | SuffixOpExpression -SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) +SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | PtrDerefExpression) FieldAccessExpression = "." Symbol +PtrDerefExpression = ".*" + FnCallExpression = "(" list(Expression, ",") ")" ArrayAccessExpression = "[" Expression "]" @@ -5974,7 +5976,7 @@ ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",") StructLiteralField = "." Symbol "=" Expression -PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" +PrefixOp = "!" | "-" | "~" | ("*" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType diff --git a/src/all_types.hpp b/src/all_types.hpp index d1b2ad61d2..2993589f7b 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -379,6 +379,7 @@ enum NodeType { NodeTypeArrayAccessExpr, NodeTypeSliceExpr, NodeTypeFieldAccessExpr, + NodeTypePtrDeref, NodeTypeUse, NodeTypeBoolLiteral, NodeTypeNullLiteral, @@ -603,6 +604,10 @@ struct AstNodeFieldAccessExpr { Buf *field_name; }; +struct AstNodePtrDerefExpr { + AstNode *target; +}; + enum PrefixOp { PrefixOpInvalid, PrefixOpBoolNot, @@ -911,6 +916,7 @@ struct AstNode { AstNodeCompTime comptime_expr; AstNodeAsmExpr asm_expr; AstNodeFieldAccessExpr field_access_expr; + AstNodePtrDerefExpr ptr_deref_expr; AstNodeContainerDecl container_decl; AstNodeStructField struct_field; AstNodeStringLiteral string_literal; diff --git a/src/analyze.cpp b/src/analyze.cpp index 1ecfe32f4c..99712cbfaf 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3275,6 +3275,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeUnreachable: case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: + case NodeTypePtrDeref: case NodeTypeStructField: case NodeTypeContainerInitExpr: case NodeTypeStructValueField: diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 2c3e1fc873..3e5ef0fcdb 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -222,6 +222,8 @@ static const char *node_type_str(NodeType node_type) { return "AsmExpr"; case NodeTypeFieldAccessExpr: return "FieldAccessExpr"; + case NodeTypePtrDeref: + return "PtrDerefExpr"; case NodeTypeContainerDecl: return "ContainerDecl"; case NodeTypeStructField: @@ -696,6 +698,13 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { print_symbol(ar, rhs); break; } + case NodeTypePtrDeref: + { + AstNode *lhs = node->data.ptr_deref_expr.target; + render_node_ungrouped(ar, lhs); + fprintf(ar->f, ".*"); + break; + } case NodeTypeUndefinedLiteral: fprintf(ar->f, "undefined"); break; diff --git a/src/ir.cpp b/src/ir.cpp index 469900bf07..8c7232722e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4548,8 +4548,14 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode } static IrInstruction *ir_gen_prefix_op_id_lval(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id, LVal lval) { - assert(node->type == NodeTypePrefixOpExpr); - AstNode *expr_node = node->data.prefix_op_expr.primary_expr; + AstNode *expr_node; + if (node->type == NodeTypePrefixOpExpr) { + expr_node = node->data.prefix_op_expr.primary_expr; + } else if (node->type == NodeTypePtrDeref) { + expr_node = node->data.ptr_deref_expr.target; + } else { + zig_unreachable(); + } IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval); if (value == irb->codegen->invalid_instruction) @@ -6527,6 +6533,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_build_load_ptr(irb, scope, node, ptr_instruction); } + case NodeTypePtrDeref: + return ir_gen_prefix_op_id_lval(irb, scope, node, IrUnOpDereference, lval); case NodeTypeThisLiteral: return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval); case NodeTypeBoolLiteral: diff --git a/src/parser.cpp b/src/parser.cpp index 4b70e904b8..c02546a99d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1046,11 +1046,12 @@ static AstNode *ast_parse_fn_proto_partial(ParseContext *pc, size_t *token_index } /* -SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) +SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | PtrDerefExpression | SliceExpression) FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) ArrayAccessExpression : token(LBracket) Expression token(RBracket) SliceExpression = "[" Expression ".." option(Expression) "]" FieldAccessExpression : token(Dot) token(Symbol) +PtrDerefExpression = ".*" StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression */ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { @@ -1131,13 +1132,27 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, } else if (first_token->id == TokenIdDot) { *token_index += 1; - Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol); + Token *token = &pc->tokens->at(*token_index); - AstNode *node = ast_create_node(pc, NodeTypeFieldAccessExpr, first_token); - node->data.field_access_expr.struct_expr = primary_expr; - node->data.field_access_expr.field_name = token_buf(name_token); + if (token->id == TokenIdSymbol) { + *token_index += 1; + + AstNode *node = ast_create_node(pc, NodeTypeFieldAccessExpr, first_token); + node->data.field_access_expr.struct_expr = primary_expr; + node->data.field_access_expr.field_name = token_buf(token); + + primary_expr = node; + } else if (token->id == TokenIdStar) { + *token_index += 1; + + AstNode *node = ast_create_node(pc, NodeTypePtrDeref, first_token); + node->data.ptr_deref_expr.target = primary_expr; + + primary_expr = node; + } else { + ast_invalid_token_error(pc, token); + } - primary_expr = node; } else { return primary_expr; } @@ -3012,6 +3027,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeFieldAccessExpr: visit_field(&node->data.field_access_expr.struct_expr, visit, context); break; + case NodeTypePtrDeref: + visit_field(&node->data.ptr_deref_expr.target, visit, context); + break; case NodeTypeUse: visit_field(&node->data.use.expr, visit, context); break; diff --git a/test/behavior.zig b/test/behavior.zig index 2c10c6d71b..cb484b39a5 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -33,6 +33,7 @@ comptime { _ = @import("cases/misc.zig"); _ = @import("cases/namespace_depends_on_compile_var/index.zig"); _ = @import("cases/null.zig"); + _ = @import("cases/pointers.zig"); _ = @import("cases/pub_enum/index.zig"); _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig"); _ = @import("cases/reflection.zig"); diff --git a/test/cases/pointers.zig b/test/cases/pointers.zig new file mode 100644 index 0000000000..87b3d25a74 --- /dev/null +++ b/test/cases/pointers.zig @@ -0,0 +1,14 @@ +const std = @import("std"); +const assert = std.debug.assert; + +test "dereference pointer" { + comptime testDerefPtr(); + testDerefPtr(); +} + +fn testDerefPtr() void { + var x: i32 = 1234; + var y = &x; + y.* += 1; + assert(x == 1235); +}