diff --git a/doc/langref.md b/doc/langref.md index 7d8efef421..3930b3f9b2 100644 --- a/doc/langref.md +++ b/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) diff --git a/doc/vim/syntax/zig.vim b/doc/vim/syntax/zig.vim index ffb08fc388..c505a7e58c 100644 --- a/doc/vim/syntax/zig.vim +++ b/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 diff --git a/src/analyze.cpp b/src/analyze.cpp index 68bfd6c3c7..18afb9eb05 100644 --- a/src/analyze.cpp +++ b/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: diff --git a/src/analyze.hpp b/src/analyze.hpp index 886bc5ec97..fd2d11cb71 100644 --- a/src/analyze.hpp +++ b/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 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); diff --git a/src/codegen.cpp b/src/codegen.cpp index dcb543bbf9..5ea7ff401f 100644 --- a/src/codegen.cpp +++ b/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(); diff --git a/src/parser.cpp b/src/parser.cpp index e8da95bb3d..0a1fb654b3 100644 --- a/src/parser.cpp +++ b/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); diff --git a/src/parser.hpp b/src/parser.hpp index 75d0f822ef..182ac13fd0 100644 --- a/src/parser.hpp +++ b/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; }; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index b70bea7057..94f10c6e03 100644 --- a/src/tokenizer.cpp +++ b/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"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 2169db9c1b..f9224de98c 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -36,6 +36,7 @@ enum TokenId { TokenIdKeywordContinue, TokenIdKeywordBreak, TokenIdKeywordNull, + TokenIdKeywordRestrict, TokenIdLParen, TokenIdRParen, TokenIdComma, diff --git a/std/builtin.zig b/std/builtin.zig index 572adf00cc..b57c440c07 100644 --- a/std/builtin.zig +++ b/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];