Commit 14b9cbd43c

Andrew Kelley <superjoe30@gmail.com>
2016-01-09 04:59:47
add restrict qualifier on pointer arguments
1 parent d14a311
doc/vim/syntax/zig.vim
@@ -8,7 +8,7 @@ if exists("b:current_syntax")
 endif
 
 syn keyword zigOperator as
-syn keyword zigStorage const var extern volatile export pub
+syn keyword zigStorage const var extern volatile export pub restrict
 syn keyword zigStructure struct enum type
 syn keyword zigStatement goto break return continue asm
 syn keyword zigConditional if else match
doc/langref.md
@@ -68,11 +68,11 @@ CompilerFnExpr : token(NumberSign) token(Symbol) token(LParen) Expression token(
 
 CompilerFnType : token(NumberSign) token(Symbol) token(LParen) Type token(RParen)
 
-PointerType : token(Ampersand) option(token(Const)) Type
+PointerType : token(Ampersand) option(token(Const)) option(token(Restrict)) Type
 
 MaybeType : token(Question) Type
 
-ArrayType : token(LBracket) option(Expression) token(RBracket) option(token(Const)) Type
+ArrayType : token(LBracket) option(Expression) token(RBracket) option(token(Const)) option(token(Restrict)) Type
 
 Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace)
 
src/analyze.cpp
@@ -135,10 +135,8 @@ static TypeTableEntry *get_number_literal_type_unsigned(CodeGen *g, uint64_t x)
     return g->num_lit_types[get_number_literal_kind_unsigned(x)];
 }
 
-TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) {
-    TypeTableEntry **parent_pointer = is_const ?
-        &child_type->pointer_const_parent :
-        &child_type->pointer_mut_parent;
+TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const, bool is_restrict) {
+    TypeTableEntry **parent_pointer = &child_type->pointer_parent[(is_const ? 1 : 0)][(is_restrict ? 1 : 0)];
     if (*parent_pointer) {
         return *parent_pointer;
     } else {
@@ -153,6 +151,7 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool
                 entry->size_in_bits, entry->align_in_bits, buf_ptr(&entry->name));
         entry->data.pointer.child_type = child_type;
         entry->data.pointer.is_const = is_const;
+        entry->data.pointer.is_restrict = is_restrict;
 
         *parent_pointer = entry;
         return entry;
@@ -241,11 +240,9 @@ static TypeTableEntry *get_array_type(CodeGen *g, ImportTableEntry *import,
 }
 
 static TypeTableEntry *get_unknown_size_array_type(CodeGen *g, ImportTableEntry *import,
-        TypeTableEntry *child_type, bool is_const)
+        TypeTableEntry *child_type, bool is_const, bool is_restrict)
 {
-    TypeTableEntry **parent_pointer = is_const ?
-        &child_type->unknown_size_array_const_parent :
-        &child_type->unknown_size_array_mut_parent;
+    TypeTableEntry **parent_pointer = &child_type->unknown_size_array_parent[(is_const ? 1 : 0)][(is_restrict ? 1 : 0)];
     if (*parent_pointer) {
         return *parent_pointer;
     } else {
@@ -255,7 +252,7 @@ static TypeTableEntry *get_unknown_size_array_type(CodeGen *g, ImportTableEntry
         buf_appendf(&entry->name, "[]%s", buf_ptr(&child_type->name));
         entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&entry->name));
 
-        TypeTableEntry *pointer_type = get_pointer_to_type(g, child_type, is_const);
+        TypeTableEntry *pointer_type = get_pointer_to_type(g, child_type, is_const, is_restrict);
 
         unsigned element_count = 2;
         LLVMTypeRef element_types[] = {
@@ -430,7 +427,9 @@ static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
     }
 }
 
-static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node, ImportTableEntry *import, BlockContext *context) {
+static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node, ImportTableEntry *import,
+        BlockContext *context, bool restrict_allowed)
+{
     assert(node->type == NodeTypeType);
     alloc_codegen_node(node);
     TypeNode *type_node = &node->codegen_node->data.type_node;
@@ -450,21 +449,47 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node, ImportTableEntry
             }
         case AstNodeTypeTypePointer:
             {
-                resolve_type(g, node->data.type.child_type, import, context);
+                bool use_restrict = false;
+                if (node->data.type.is_restrict) {
+                    if (!restrict_allowed) {
+                        add_node_error(g, node,
+                                buf_create_from_str("invalid restrict qualifier"));
+                    } else {
+                        use_restrict = true;
+                    }
+                }
+
+                resolve_type(g, node->data.type.child_type, import, context, false);
                 TypeTableEntry *child_type = node->data.type.child_type->codegen_node->data.type_node.entry;
                 assert(child_type);
                 if (child_type->id == TypeTableEntryIdUnreachable) {
                     add_node_error(g, node,
                             buf_create_from_str("pointer to unreachable not allowed"));
+                    type_node->entry = g->builtin_types.entry_invalid;
+                    return type_node->entry;
                 } else if (child_type->id == TypeTableEntryIdInvalid) {
+                    type_node->entry = child_type;
                     return child_type;
+                } else {
+                    type_node->entry = get_pointer_to_type(g, child_type, node->data.type.is_const, use_restrict);
+                    return type_node->entry;
                 }
-                type_node->entry = get_pointer_to_type(g, child_type, node->data.type.is_const);
-                return type_node->entry;
             }
         case AstNodeTypeTypeArray:
             {
-                TypeTableEntry *child_type = resolve_type(g, node->data.type.child_type, import, context);
+                AstNode *size_node = node->data.type.array_size;
+
+                bool use_restrict = false;
+                if (node->data.type.is_restrict) {
+                    if (!restrict_allowed || size_node) {
+                        add_node_error(g, node,
+                                buf_create_from_str("invalid restrict qualifier"));
+                    } else {
+                        use_restrict = true;
+                    }
+                }
+
+                TypeTableEntry *child_type = resolve_type(g, node->data.type.child_type, import, context, false);
                 if (child_type->id == TypeTableEntryIdUnreachable) {
                     add_node_error(g, node,
                             buf_create_from_str("array of unreachable not allowed"));
@@ -472,8 +497,6 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node, ImportTableEntry
                     return type_node->entry;
                 }
 
-                AstNode *size_node = node->data.type.array_size;
-
                 if (size_node) {
                     TypeTableEntry *size_type = analyze_expression(g, import, context,
                             g->builtin_types.entry_usize, size_node);
@@ -501,14 +524,14 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node, ImportTableEntry
                     return type_node->entry;
                 } else {
                     type_node->entry = get_unknown_size_array_type(g, import, child_type,
-                            node->data.type.is_const);
+                            node->data.type.is_const, use_restrict);
                     return type_node->entry;
                 }
 
             }
         case AstNodeTypeTypeMaybe:
             {
-                resolve_type(g, node->data.type.child_type, import, context);
+                resolve_type(g, node->data.type.child_type, import, context, false);
                 TypeTableEntry *child_type = node->data.type.child_type->codegen_node->data.type_node.entry;
                 assert(child_type);
                 if (child_type->id == TypeTableEntryIdUnreachable) {
@@ -571,7 +594,8 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
     for (int i = 0; i < node->data.fn_proto.params.length; i += 1) {
         AstNode *child = node->data.fn_proto.params.at(i);
         assert(child->type == NodeTypeParamDecl);
-        TypeTableEntry *type_entry = resolve_type(g, child->data.param_decl.type, import, import->block_context);
+        TypeTableEntry *type_entry = resolve_type(g, child->data.param_decl.type,
+                import, import->block_context, true);
         if (type_entry->id == TypeTableEntryIdUnreachable) {
             add_node_error(g, child->data.param_decl.type,
                 buf_sprintf("parameter of type 'unreachable' not allowed"));
@@ -583,7 +607,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
         }
     }
 
-    resolve_type(g, node->data.fn_proto.return_type, import, import->block_context);
+    resolve_type(g, node->data.fn_proto.return_type, import, import->block_context, true);
 }
 
 static void preview_function_labels(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry) {
@@ -644,7 +668,8 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE
         AstNode *field_node = decl_node->data.struct_decl.fields.at(i);
         TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
         type_struct_field->name = &field_node->data.struct_field.name;
-        type_struct_field->type_entry = resolve_type(g, field_node->data.struct_field.type, import, import->block_context);
+        type_struct_field->type_entry = resolve_type(g, field_node->data.struct_field.type,
+                import, import->block_context, false);
 
         if (type_struct_field->type_entry->id == TypeTableEntryIdStruct) {
             resolve_struct_type(g, import, type_struct_field->type_entry);
@@ -1196,15 +1221,18 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont
         return expected_type;
     }
 
-    // implicit non-const to const
+    // implicit non-const to const and ignore restrict
     if (expected_type->id == TypeTableEntryIdPointer &&
         actual_type->id == TypeTableEntryIdPointer &&
-        expected_type->data.pointer.is_const &&
-        !actual_type->data.pointer.is_const)
+        (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const))
     {
-        return resolve_type_compatibility(g, context, node,
+        TypeTableEntry *resolved_type = resolve_type_compatibility(g, context, node,
                 expected_type->data.pointer.child_type,
                 actual_type->data.pointer.child_type);
+        if (resolved_type->id == TypeTableEntryIdInvalid) {
+            return resolved_type;
+        }
+        return expected_type;
     }
 
     add_node_error(g, first_executing_node(node),
@@ -1335,7 +1363,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
             return_type = g->builtin_types.entry_usize;
         } else if (buf_eql_str(name, "ptr")) {
             // TODO determine whether the pointer should be const
-            return_type = get_pointer_to_type(g, struct_type->data.array.child_type, false);
+            return_type = get_pointer_to_type(g, struct_type->data.array.child_type, false, false);
         } else {
             add_node_error(g, node,
                 buf_sprintf("no member named '%s' in '%s'", buf_ptr(name),
@@ -1365,16 +1393,16 @@ static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import,
         return_type = g->builtin_types.entry_invalid;
     } else if (array_type->id == TypeTableEntryIdArray) {
         return_type = get_unknown_size_array_type(g, import, array_type->data.array.child_type,
-                node->data.slice_expr.is_const);
+                node->data.slice_expr.is_const, false);
     } else if (array_type->id == TypeTableEntryIdPointer) {
         return_type = get_unknown_size_array_type(g, import, array_type->data.pointer.child_type,
-                node->data.slice_expr.is_const);
+                node->data.slice_expr.is_const, false);
     } else if (array_type->id == TypeTableEntryIdStruct &&
                array_type->data.structure.is_unknown_size_array)
     {
         return_type = get_unknown_size_array_type(g, import,
                 array_type->data.structure.fields[0].type_entry->data.pointer.child_type,
-                node->data.slice_expr.is_const);
+                node->data.slice_expr.is_const, false);
     } else {
         add_node_error(g, node,
             buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name)));
@@ -1490,7 +1518,7 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) {
 static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
-    TypeTableEntry *wanted_type = resolve_type(g, node->data.cast_expr.type, import, context);
+    TypeTableEntry *wanted_type = resolve_type(g, node->data.cast_expr.type, import, context, false);
     TypeTableEntry *actual_type = analyze_expression(g, import, context, nullptr, node->data.cast_expr.expr);
 
     if (wanted_type->id == TypeTableEntryIdInvalid ||
@@ -1713,7 +1741,7 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa
 {
     TypeTableEntry *explicit_type = nullptr;
     if (variable_declaration->type != nullptr) {
-        explicit_type = resolve_type(g, variable_declaration->type, import, context);
+        explicit_type = resolve_type(g, variable_declaration->type, import, context, false);
         if (explicit_type->id == TypeTableEntryIdUnreachable) {
             add_node_error(g, variable_declaration->type,
                 buf_sprintf("variable of type 'unreachable' not allowed"));
@@ -1837,7 +1865,7 @@ static TypeTableEntry *analyze_struct_val_expr(CodeGen *g, ImportTableEntry *imp
 
     AstNodeStructValueExpr *struct_val_expr = &node->data.struct_val_expr;
 
-    TypeTableEntry *type_entry = resolve_type(g, struct_val_expr->type, import, context);
+    TypeTableEntry *type_entry = resolve_type(g, struct_val_expr->type, import, context, false);
 
     if (type_entry->id == TypeTableEntryIdInvalid) {
         return g->builtin_types.entry_invalid;
@@ -2017,7 +2045,7 @@ static TypeTableEntry *analyze_compiler_fn_type(CodeGen *g, ImportTableEntry *im
     assert(node->type == NodeTypeCompilerFnType);
 
     Buf *name = &node->data.compiler_fn_type.name;
-    TypeTableEntry *type_entry = resolve_type(g, node->data.compiler_fn_type.type, import, context);
+    TypeTableEntry *type_entry = resolve_type(g, node->data.compiler_fn_type.type, import, context, false);
 
     if (buf_eql_str(name, "sizeof")) {
         uint64_t size_in_bytes = type_entry->size_in_bits / 8;
@@ -2221,7 +2249,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
                     AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
                     if (asm_output->return_type) {
                         node->data.asm_expr.return_count += 1;
-                        return_type = resolve_type(g, asm_output->return_type, import, context);
+                        return_type = resolve_type(g, asm_output->return_type, import, context, false);
                         if (node->data.asm_expr.return_count > 1) {
                             add_node_error(g, node,
                                 buf_sprintf("inline assembly allows up to one output value"));
@@ -2357,7 +2385,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
                             break;
                         }
 
-                        return_type = get_pointer_to_type(g, child_type, is_const);
+                        return_type = get_pointer_to_type(g, child_type, is_const, false);
                         break;
                     }
                 case PrefixOpDereference:
src/analyze.hpp
@@ -23,6 +23,7 @@ struct StructValExprNode;
 struct TypeTableEntryPointer {
     TypeTableEntry *child_type;
     bool is_const;
+    bool is_restrict;
 };
 
 struct TypeTableEntryInt {
@@ -97,12 +98,10 @@ struct TypeTableEntry {
     } data;
 
     // use these fields to make sure we don't duplicate type table entries for the same type
-    TypeTableEntry *pointer_const_parent;
-    TypeTableEntry *pointer_mut_parent;
+    TypeTableEntry *pointer_parent[2][2]; // 0 - const. 1 - restrict
+    TypeTableEntry *unknown_size_array_parent[2][2]; // 0 - const. 1 - restrict
     HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size;
     TypeTableEntry *maybe_parent;
-    TypeTableEntry *unknown_size_array_const_parent;
-    TypeTableEntry *unknown_size_array_mut_parent;
 
 };
 
@@ -379,7 +378,7 @@ void semantic_analyze(CodeGen *g);
 void add_node_error(CodeGen *g, AstNode *node, Buf *msg);
 void alloc_codegen_node(AstNode *node);
 TypeTableEntry *new_type_table_entry(TypeTableEntryId id);
-TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const);
+TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const, bool is_restrict);
 VariableTableEntry *find_variable(BlockContext *context, Buf *name);
 BlockContext *new_block_context(AstNode *node, BlockContext *parent);
 
src/codegen.cpp
@@ -77,13 +77,13 @@ static TypeTableEntry *get_type_for_type_node(CodeGen *g, AstNode *type_node) {
     return type_node->codegen_node->data.type_node.entry;
 }
 
-static LLVMTypeRef fn_proto_type_from_type_node(CodeGen *g, AstNode *type_node) {
+static TypeTableEntry *fn_proto_type_from_type_node(CodeGen *g, AstNode *type_node) {
     TypeTableEntry *type_entry = get_type_for_type_node(g, type_node);
 
     if (type_entry->id == TypeTableEntryIdStruct || type_entry->id == TypeTableEntryIdArray) {
-        return get_pointer_to_type(g, type_entry, true)->type_ref;
+        return get_pointer_to_type(g, type_entry, true, true);
     } else {
-        return type_entry->type_ref;
+        return type_entry;
     }
 }
 
@@ -1763,7 +1763,7 @@ static void do_code_gen(CodeGen *g) {
             if (is_param_decl_type_void(g, param_node))
                 continue;
             AstNode *type_node = param_node->data.param_decl.type;
-            param_types[gen_param_index] = fn_proto_type_from_type_node(g, type_node);
+            param_types[gen_param_index] = fn_proto_type_from_type_node(g, type_node)->type_ref;
             gen_param_index += 1;
         }
         LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, param_count, fn_proto->is_var_args);
@@ -1785,6 +1785,28 @@ static void do_code_gen(CodeGen *g) {
             LLVMAddFunctionAttr(fn, LLVMNoUnwindAttribute);
         }
 
+        // set parameter attributes
+        gen_param_index = 0;
+        for (int param_decl_i = 0; param_decl_i < fn_proto->params.length; param_decl_i += 1) {
+            AstNode *param_node = fn_proto->params.at(param_decl_i);
+            assert(param_node->type == NodeTypeParamDecl);
+            if (is_param_decl_type_void(g, param_node))
+                continue;
+            AstNode *type_node = param_node->data.param_decl.type;
+            TypeTableEntry *param_type = fn_proto_type_from_type_node(g, type_node);
+            LLVMValueRef argument_val = LLVMGetParam(fn, gen_param_index);
+            if (param_type->id == TypeTableEntryIdPointer &&
+                param_type->data.pointer.is_restrict)
+            {
+                LLVMAddAttribute(argument_val, LLVMNoAliasAttribute);
+            } else if (param_type->id == TypeTableEntryIdPointer &&
+                       param_type->data.pointer.is_const)
+            {
+                LLVMAddAttribute(argument_val, LLVMReadOnlyAttribute);
+            }
+            gen_param_index += 1;
+        }
+
         fn_table_entry->fn_value = fn;
     }
 
@@ -2032,7 +2054,7 @@ static void define_builtin_types(CodeGen *g) {
                 LLVMZigEncoding_DW_ATE_unsigned());
         g->builtin_types.entry_u64 = entry;
     }
-    g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
+    g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true, false);
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
         entry->type_ref = LLVMInt8Type();
src/parser.cpp
@@ -224,16 +224,18 @@ void ast_print(AstNode *node, int indent) {
                     }
                 case AstNodeTypeTypePointer:
                     {
-                        const char *const_or_mut_str = node->data.type.is_const ? "const" : "var";
-                        fprintf(stderr, "'%s' PointerType\n", const_or_mut_str);
+                        const char *const_or_mut_str = node->data.type.is_const ? "const " : "";
+                        const char *restrict_or_not_str = node->data.type.is_restrict ? "restrict " : "";
+                        fprintf(stderr, "%s%s PointerType\n", const_or_mut_str, restrict_or_not_str);
 
                         ast_print(node->data.type.child_type, indent + 2);
                         break;
                     }
                 case AstNodeTypeTypeArray:
                     {
-                        const char *const_or_mut_str = node->data.type.is_const ? "const" : "var";
-                        fprintf(stderr, "'%s' ArrayType\n", const_or_mut_str);
+                        const char *const_or_mut_str = node->data.type.is_const ? "const " : "";
+                        const char *restrict_or_not_str = node->data.type.is_restrict ? "restrict " : "";
+                        fprintf(stderr, "%s%s ArrayType\n", const_or_mut_str, restrict_or_not_str);
                         if (node->data.type.array_size)
                             ast_print(node->data.type.array_size, indent + 2);
                         ast_print(node->data.type.child_type, indent + 2);
@@ -1022,6 +1024,13 @@ static void ast_parse_type_assume_amp(ParseContext *pc, int *token_index, AstNod
         node->data.type.is_const = true;
         *token_index += 1;
         first_type_token = &pc->tokens->at(*token_index);
+        if (first_type_token->id == TokenIdKeywordRestrict) {
+            node->data.type.is_restrict = true;
+            *token_index += 1;
+        }
+    } else if (first_type_token->id == TokenIdKeywordRestrict) {
+        node->data.type.is_restrict = true;
+        *token_index += 1;
     }
 
     node->data.type.child_type = ast_parse_type(pc, token_index);
@@ -1079,8 +1088,8 @@ static AstNode *ast_parse_compiler_fn_call(ParseContext *pc, int *token_index, b
 
 /*
 Type : token(Symbol) | token(Unreachable) | token(Void) | PointerType | ArrayType | MaybeType | CompilerFnExpr
-PointerType : token(Ampersand) option(token(Const)) Type
-ArrayType : token(LBracket) option(Expression) token(RBracket) Type
+PointerType : token(Ampersand) option(token(Const)) option(token(Restrict)) Type
+ArrayType : token(LBracket) option(Expression) token(RBracket) option(token(Const)) option(token(Restrict)) Type
 */
 static AstNode *ast_parse_type(ParseContext *pc, int *token_index) {
     Token *token = &pc->tokens->at(*token_index);
@@ -1129,6 +1138,15 @@ static AstNode *ast_parse_type(ParseContext *pc, int *token_index) {
         if (const_tok->id == TokenIdKeywordConst) {
             *token_index += 1;
             node->data.type.is_const = true;
+
+            Token *next_tok = &pc->tokens->at(*token_index);
+            if (next_tok->id == TokenIdKeywordRestrict) {
+                *token_index += 1;
+                node->data.type.is_restrict = true;
+            }
+        } else if (const_tok->id == TokenIdKeywordRestrict) {
+            *token_index += 1;
+            node->data.type.is_restrict = true;
         }
 
         node->data.type.child_type = ast_parse_type(pc, token_index);
@@ -1476,7 +1494,7 @@ static PrefixOp tok_to_prefix_op(Token *token) {
 }
 
 /*
-PrefixOp : token(Not) | token(Dash) | token(Tilde) | (token(Ampersand) option(token(Const)))
+PrefixOp : token(Not) | token(Dash) | token(Tilde) | token(Star) | (token(Ampersand) option(token(Const)))
 */
 static PrefixOp ast_parse_prefix_op(ParseContext *pc, int *token_index, bool mandatory) {
     Token *token = &pc->tokens->at(*token_index);
src/parser.hpp
@@ -110,6 +110,7 @@ struct AstNodeType {
     AstNode *child_type;
     AstNode *array_size; // can be null
     bool is_const;
+    bool is_restrict;
     AstNode *compiler_expr;
 };
 
src/tokenizer.cpp
@@ -243,6 +243,8 @@ static void end_token(Tokenize *t) {
         t->cur_tok->id = TokenIdKeywordBreak;
     } else if (mem_eql_str(token_mem, token_len, "null")) {
         t->cur_tok->id = TokenIdKeywordNull;
+    } else if (mem_eql_str(token_mem, token_len, "restrict")) {
+        t->cur_tok->id = TokenIdKeywordRestrict;
     }
 
     t->cur_tok = nullptr;
@@ -1019,6 +1021,7 @@ static const char * token_name(Token *token) {
         case TokenIdKeywordContinue: return "Continue";
         case TokenIdKeywordBreak: return "Break";
         case TokenIdKeywordNull: return "Null";
+        case TokenIdKeywordRestrict: return "Restrict";
         case TokenIdLParen: return "LParen";
         case TokenIdRParen: return "RParen";
         case TokenIdComma: return "Comma";
src/tokenizer.hpp
@@ -36,6 +36,7 @@ enum TokenId {
     TokenIdKeywordContinue,
     TokenIdKeywordBreak,
     TokenIdKeywordNull,
+    TokenIdKeywordRestrict,
     TokenIdLParen,
     TokenIdRParen,
     TokenIdComma,
std/builtin.zig
@@ -10,8 +10,7 @@ export fn memset(dest: &u8, c: u8, n: usize) -> &u8 {
     return dest;
 }
 
-// TODO annotate parameters with noalias
-export fn memcpy(dest: &u8, src: &const u8, n: usize) -> &u8 {
+export fn memcpy(dest: &restrict u8, src: &const restrict u8, n: usize) -> &u8 {
     var index : #typeof(n) = 0;
     while (index != n) {
         dest[index] = src[index];