From 5bf9ffdc5be02e67b57fe9398ad9d13147bfb0c8 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 26 Jan 2019 05:10:40 +0900 Subject: Hint at use of and/or when &&/|| is improperly used (#1886) --- src/parser.cpp | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 077365995e..9425df2430 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -122,19 +122,37 @@ static AstNode *ast_parse_container_decl_type(ParseContext *pc); static AstNode *ast_parse_byte_align(ParseContext *pc); ATTRIBUTE_PRINTF(3, 4) -ATTRIBUTE_NORETURN -static void ast_error(ParseContext *pc, Token *token, const char *format, ...) { +static ErrorMsg *ast_error(ParseContext *pc, Token *token, const char *format, ...) { va_list ap; va_start(ap, format); Buf *msg = buf_vprintf(format, ap); va_end(ap); + ErrorMsg *err = err_msg_create_with_line(pc->owner->path, token->start_line, token->start_column, + pc->owner->source_code, pc->owner->line_offsets, msg); + err->line_start = token->start_line; + err->column_start = token->start_column; + + return err; +} + +ATTRIBUTE_PRINTF(4, 5) +ATTRIBUTE_NORETURN +static void ast_error_exit(ParseContext *pc, Token *token, ErrorMsg *note, const char *format, ...) { + va_list ap; + va_start(ap, format); + Buf *msg = buf_vprintf(format, ap); + va_end(ap); ErrorMsg *err = err_msg_create_with_line(pc->owner->path, token->start_line, token->start_column, pc->owner->source_code, pc->owner->line_offsets, msg); err->line_start = token->start_line; err->column_start = token->start_column; + if (note) { + err->notes.append(note); + } + print_err_msg(err, pc->err_color); exit(EXIT_FAILURE); } @@ -164,7 +182,7 @@ static Buf ast_token_str(Buf *input, Token *token) { ATTRIBUTE_NORETURN static void ast_invalid_token_error(ParseContext *pc, Token *token) { Buf token_value = ast_token_str(pc->buf, token); - ast_error(pc, token, "invalid token: '%s'", buf_ptr(&token_value)); + ast_error_exit(pc, token, NULL, "invalid token: '%s'", buf_ptr(&token_value)); } static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) { @@ -214,8 +232,13 @@ static Token *eat_token_if(ParseContext *pc, TokenId id) { static Token *expect_token(ParseContext *pc, TokenId id) { Token *res = eat_token(pc); - if (res->id != id) - ast_error(pc, res, "expected token '%s', found '%s'", token_name(id), token_name(res->id)); + if (res->id != id) { + ErrorMsg *note = NULL; + if (res->id == TokenIdAmpersandAmpersand) { + note = ast_error(pc, res, "did you mean to use `and`?"); + } + ast_error_exit(pc, res, note, "expected token '%s', found '%s'", token_name(id), token_name(res->id)); + } return res; } @@ -837,7 +860,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) { if (param_decl->data.param_decl.is_var_args) res->data.fn_proto.is_var_args = true; if (i != params.length - 1 && res->data.fn_proto.is_var_args) - ast_error(pc, first, "Function prototype have varargs as a none last paramter."); + ast_error_exit(pc, first, NULL, "Function prototype have varargs as a none last paramter."); } return res; } -- cgit v1.2.3 From 9c328b42916d463465b134457c7f13b5c65da406 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 Jan 2019 22:28:33 -0500 Subject: simpler implementation of `&&` and `||` hints This accomplishes the same goal, but with less changes, so that I can backport copy elision stuff easier. --- doc/langref.html.in | 2 +- src/ir.cpp | 235 +++++++++++++++++++++++++++++------------------- src/parser.cpp | 35 ++------ src/tokenizer.cpp | 15 +--- src/tokenizer.hpp | 1 - test/compile_errors.zig | 152 +++++++++++++++---------------- 6 files changed, 228 insertions(+), 212 deletions(-) (limited to 'src/parser.cpp') diff --git a/doc/langref.html.in b/doc/langref.html.in index 1fb751cef4..6e03d3ec6d 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6024,7 +6024,7 @@ fn add(a: i32, b: i32) i32 { This is typically used for type safety when interacting with C code that does not expose struct details. Example:

- {#code_begin|test_err|expected '*Derp' type, found '*Wat'#} + {#code_begin|test_err|expected type '*Derp', found '*Wat'#} const Derp = @OpaqueType(); const Wat = @OpaqueType(); diff --git a/src/ir.cpp b/src/ir.cpp index df2c8cc9be..b184252a2e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9771,19 +9771,13 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node return ir_exec_const_result(codegen, analyzed_executable); } -static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value, ZigTypeId wanted_type) { +static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) { if (type_is_invalid(type_value->value.type)) return ira->codegen->builtin_types.entry_invalid; - const char *expected_type_str = type_id_name(ZigTypeIdMetaType); - - if (wanted_type != ZigTypeIdInvalid) { - expected_type_str = type_id_name(wanted_type); - } - if (type_value->value.type->id != ZigTypeIdMetaType) { - ir_add_error( ira, type_value, - buf_sprintf("expected %s type, found '%s'", expected_type_str, buf_ptr(&type_value->value.type->name))); + ir_add_error(ira, type_value, + buf_sprintf("expected type 'type', found '%s'", buf_ptr(&type_value->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } @@ -9792,16 +9786,35 @@ static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value, ZigTy return ira->codegen->builtin_types.entry_invalid; assert(const_val->data.x_type != nullptr); + return const_val->data.x_type; +} - ZigType *out_type = const_val->data.x_type; +static ZigType *ir_resolve_error_set_type(IrAnalyze *ira, IrInstruction *op_source, IrInstruction *type_value) { + if (type_is_invalid(type_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; - if (wanted_type != ZigTypeIdInvalid && out_type->id != wanted_type) { - ir_add_error(ira, type_value, - buf_sprintf( "expected %s type, found '%s'", expected_type_str, buf_ptr(&out_type->name))); + if (type_value->value.type->id != ZigTypeIdMetaType) { + ErrorMsg *msg = ir_add_error(ira, type_value, + buf_sprintf("expected error set type, found '%s'", buf_ptr(&type_value->value.type->name))); + add_error_note(ira->codegen, msg, op_source->source_node, + buf_sprintf("`||` merges error sets; `or` performs boolean OR")); return ira->codegen->builtin_types.entry_invalid; } - return out_type; + ConstExprValue *const_val = ir_resolve_const(ira, type_value, UndefBad); + if (!const_val) + return ira->codegen->builtin_types.entry_invalid; + + assert(const_val->data.x_type != nullptr); + ZigType *result_type = const_val->data.x_type; + if (result_type->id != ZigTypeIdErrorSet) { + ErrorMsg *msg = ir_add_error(ira, type_value, + buf_sprintf("expected error set type, found type '%s'", buf_ptr(&result_type->name))); + add_error_note(ira->codegen, msg, op_source->source_node, + buf_sprintf("`||` merges error sets; `or` performs boolean OR")); + return ira->codegen->builtin_types.entry_invalid; + } + return result_type; } static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { @@ -11001,7 +11014,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node, - buf_sprintf("expected '%s' type, found '%s'", + buf_sprintf("expected type '%s', found '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name))); report_recursive_error(ira, source_instr->source_node, &const_cast_result, parent_msg); @@ -12229,7 +12242,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i size_t op2_array_end; if (op2_type->id == ZigTypeIdArray) { if (op2_type->data.array.child_type != child_type) { - ir_add_error(ira, op2, buf_sprintf("expected array of '%s' type, found '%s'", + ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", buf_ptr(&child_type->name), buf_ptr(&op2->value.type->name))); return ira->codegen->invalid_instruction; @@ -12243,7 +12256,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i op2_val->data.x_ptr.data.base_array.is_cstr) { if (child_type != ira->codegen->builtin_types.entry_u8) { - ir_add_error(ira, op2, buf_sprintf("expected array of '%s' type, found '%s'", + ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", buf_ptr(&child_type->name), buf_ptr(&op2->value.type->name))); return ira->codegen->invalid_instruction; @@ -12254,7 +12267,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i } else if (is_slice(op2_type)) { ZigType *ptr_type = op2_type->data.structure.fields[slice_ptr_index].type_entry; if (ptr_type->data.pointer.child_type != child_type) { - ir_add_error(ira, op2, buf_sprintf("expected array of '%s' type, found '%s'", + ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", buf_ptr(&child_type->name), buf_ptr(&op2->value.type->name))); return ira->codegen->invalid_instruction; @@ -12268,7 +12281,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i op2_array_end = bigint_as_unsigned(&len_val->data.x_bigint); } else { ir_add_error(ira, op2, - buf_sprintf("expected array or C string literal type, found '%s'", buf_ptr(&op2->value.type->name))); + buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op2->value.type->name))); return ira->codegen->invalid_instruction; } @@ -12403,19 +12416,11 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp * } static IrInstruction *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstructionBinOp *instruction) { - ZigType *op1_type = ir_resolve_type(ira, instruction->op1->child, ZigTypeIdErrorSet); - if (type_is_invalid(op1_type)) { - if (ira->codegen->errors.length != 0) { - add_error_note( ira->codegen - , ira->codegen->errors.last() - , instruction->base.source_node - , buf_sprintf("did you mean to use `or`?") - ); - } + ZigType *op1_type = ir_resolve_error_set_type(ira, &instruction->base, instruction->op1->child); + if (type_is_invalid(op1_type)) return ira->codegen->invalid_instruction; - } - ZigType *op2_type = ir_resolve_type(ira, instruction->op2->child, ZigTypeIdErrorSet); + ZigType *op2_type = ir_resolve_error_set_type(ira, &instruction->base, instruction->op2->child); if (type_is_invalid(op2_type)) return ira->codegen->invalid_instruction; @@ -12506,7 +12511,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct IrInstruction *var_type = nullptr; if (decl_var_instruction->var_type != nullptr) { var_type = decl_var_instruction->var_type->child; - ZigType *proposed_type = ir_resolve_type(ira, var_type, ZigTypeIdInvalid); + ZigType *proposed_type = ir_resolve_type(ira, var_type); explicit_type = validate_var_type(ira->codegen, var_type->source_node, proposed_type); if (type_is_invalid(explicit_type)) { var->value->type = ira->codegen->builtin_types.entry_invalid; @@ -12835,14 +12840,21 @@ static IrInstruction *ir_analyze_instruction_error_union(IrAnalyze *ira, { Error err; - ZigType *err_set_type = ir_resolve_type(ira, instruction->err_set->child, ZigTypeIdErrorSet); + ZigType *err_set_type = ir_resolve_type(ira, instruction->err_set->child); if (type_is_invalid(err_set_type)) return ira->codegen->invalid_instruction; - ZigType *payload_type = ir_resolve_type(ira, instruction->payload->child, ZigTypeIdInvalid); + ZigType *payload_type = ir_resolve_type(ira, instruction->payload->child); if (type_is_invalid(payload_type)) return ira->codegen->invalid_instruction; + if (err_set_type->id != ZigTypeIdErrorSet) { + ir_add_error(ira, instruction->err_set->child, + buf_sprintf("expected error set type, found type '%s'", + buf_ptr(&err_set_type->name))); + return ira->codegen->invalid_instruction; + } + if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; ZigType *result_type = get_error_union_type(ira->codegen, err_set_type, payload_type); @@ -12904,7 +12916,7 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *c ZigType *alloc_fn_type = ptr_to_alloc_fn_type->data.pointer.child_type; if (alloc_fn_type->id != ZigTypeIdFn) { ir_add_error(ira, &call_instruction->base, - buf_sprintf("expected allocation function type, found '%s'", buf_ptr(&alloc_fn_type->name))); + buf_sprintf("expected allocation function, found '%s'", buf_ptr(&alloc_fn_type->name))); return ira->codegen->invalid_instruction; } @@ -13696,7 +13708,7 @@ static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionC if (is_comptime || instr_is_comptime(fn_ref)) { if (fn_ref->value.type->id == ZigTypeIdMetaType) { - ZigType *dest_type = ir_resolve_type(ira, fn_ref, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, fn_ref); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -13821,7 +13833,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { Error err; IrInstruction *value = un_op_instruction->value->child; - ZigType *type_entry = ir_resolve_type(ira, value, ZigTypeIdInvalid); + ZigType *type_entry = ir_resolve_type(ira, value); if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; if ((err = ensure_complete_type(ira->codegen, type_entry))) @@ -15304,10 +15316,16 @@ static IrInstruction *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira, IrInstructionPtrTypeChild *ptr_type_child_instruction) { IrInstruction *type_value = ptr_type_child_instruction->value->child; - ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdPointer); + ZigType *type_entry = ir_resolve_type(ira, type_value); if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; + if (type_entry->id != ZigTypeIdPointer) { + ir_add_error_node(ira, ptr_type_child_instruction->base.source_node, + buf_sprintf("expected pointer type, found '%s'", buf_ptr(&type_entry->name))); + return ira->codegen->invalid_instruction; + } + return ir_const_type(ira, &ptr_type_child_instruction->base, type_entry->data.pointer.child_type); } @@ -15460,7 +15478,7 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, return ira->codegen->invalid_instruction; } - ZigType *child_type = ir_resolve_type(ira, slice_type_instruction->child_type->child, ZigTypeIdInvalid); + ZigType *child_type = ir_resolve_type(ira, slice_type_instruction->child_type->child); if (type_is_invalid(child_type)) return ira->codegen->invalid_instruction; @@ -15543,7 +15561,7 @@ static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAs AsmOutput *asm_output = asm_expr->output_list.at(i); if (asm_output->return_type) { output_types[i] = asm_instruction->output_types[i]->child; - return_type = ir_resolve_type(ira, output_types[i], ZigTypeIdInvalid); + return_type = ir_resolve_type(ira, output_types[i]); if (type_is_invalid(return_type)) return ira->codegen->invalid_instruction; } @@ -15558,7 +15576,7 @@ static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAs (input_value->value.type->id == ZigTypeIdComptimeInt || input_value->value.type->id == ZigTypeIdComptimeFloat)) { ir_add_error_node(ira, input_value->source_node, - buf_sprintf("expected sized integer or sized float type, found %s", buf_ptr(&input_value->value.type->name))); + buf_sprintf("expected sized integer or sized float, found %s", buf_ptr(&input_value->value.type->name))); return ira->codegen->invalid_instruction; } @@ -15584,7 +15602,7 @@ static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira, return ira->codegen->invalid_instruction; IrInstruction *child_type_value = array_type_instruction->child_type->child; - ZigType *child_type = ir_resolve_type(ira, child_type_value, ZigTypeIdInvalid); + ZigType *child_type = ir_resolve_type(ira, child_type_value); if (type_is_invalid(child_type)) return ira->codegen->invalid_instruction; switch (child_type->id) { @@ -15633,7 +15651,7 @@ static IrInstruction *ir_analyze_instruction_promise_type(IrAnalyze *ira, IrInst if (instruction->payload_type == nullptr) { promise_type = ira->codegen->builtin_types.entry_promise; } else { - ZigType *payload_type = ir_resolve_type(ira, instruction->payload_type->child, ZigTypeIdInvalid); + ZigType *payload_type = ir_resolve_type(ira, instruction->payload_type->child); if (type_is_invalid(payload_type)) return ira->codegen->invalid_instruction; @@ -15648,7 +15666,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, { Error err; IrInstruction *type_value = size_of_instruction->type_value->child; - ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdInvalid); + ZigType *type_entry = ir_resolve_type(ira, type_value); if ((err = ensure_complete_type(ira->codegen, type_entry))) return ira->codegen->invalid_instruction; @@ -16483,7 +16501,7 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, size_t elem_count = instruction->item_count; if (container_type_value->value.type->id == ZigTypeIdMetaType) { - ZigType *container_type = ir_resolve_type(ira, container_type_value, ZigTypeIdInvalid); + ZigType *container_type = ir_resolve_type(ira, container_type_value); if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; @@ -16599,7 +16617,7 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, IrInstructionContainerInitFields *instruction) { IrInstruction *container_type_value = instruction->container_type->child; - ZigType *container_type = ir_resolve_type(ira, container_type_value, ZigTypeIdInvalid); + ZigType *container_type = ir_resolve_type(ira, container_type_value); if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; @@ -16717,7 +16735,7 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, { Error err; IrInstruction *type_value = instruction->type_value->child; - ZigType *container_type = ir_resolve_type(ira, type_value, ZigTypeIdStruct); + ZigType *container_type = ir_resolve_type(ira, type_value); if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; @@ -16730,6 +16748,12 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, if (type_is_invalid(field_ptr->value.type)) return ira->codegen->invalid_instruction; + if (container_type->id != ZigTypeIdStruct) { + ir_add_error(ira, type_value, + buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name))); + return ira->codegen->invalid_instruction; + } + if ((err = ensure_complete_type(ira->codegen, container_type))) return ira->codegen->invalid_instruction; @@ -16743,7 +16767,7 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, if (field_ptr->value.type->id != ZigTypeIdPointer) { ir_add_error(ira, field_ptr, - buf_sprintf("expected Pointer type, found '%s'", buf_ptr(&field_ptr->value.type->name))); + buf_sprintf("expected pointer, found '%s'", buf_ptr(&field_ptr->value.type->name))); return ira->codegen->invalid_instruction; } @@ -16802,9 +16826,9 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, static TypeStructField *validate_byte_offset(IrAnalyze *ira, IrInstruction *type_value, IrInstruction *field_name_value, - size_t *byte_offset) + size_t *byte_offset) { - ZigType *container_type = ir_resolve_type(ira, type_value, ZigTypeIdStruct); + ZigType *container_type = ir_resolve_type(ira, type_value); if (type_is_invalid(container_type)) return nullptr; @@ -16816,6 +16840,12 @@ static TypeStructField *validate_byte_offset(IrAnalyze *ira, if (!field_name) return nullptr; + if (container_type->id != ZigTypeIdStruct) { + ir_add_error(ira, type_value, + buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name))); + return nullptr; + } + TypeStructField *field = find_struct_type_field(container_type, field_name); if (field == nullptr) { ir_add_error(ira, field_name_value, @@ -17793,7 +17823,7 @@ static IrInstruction *ir_analyze_instruction_type_info(IrAnalyze *ira, { Error err; IrInstruction *type_value = instruction->type_value->child; - ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdInvalid); + ZigType *type_entry = ir_resolve_type(ira, type_value); if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; @@ -17821,7 +17851,7 @@ static IrInstruction *ir_analyze_instruction_type_id(IrAnalyze *ira, IrInstructionTypeId *instruction) { IrInstruction *type_value = instruction->type_value->child; - ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdInvalid); + ZigType *type_entry = ir_resolve_type(ira, type_value); if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; @@ -17856,7 +17886,7 @@ static IrInstruction *ir_analyze_instruction_set_eval_branch_quota(IrAnalyze *ir static IrInstruction *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstructionTypeName *instruction) { IrInstruction *type_value = instruction->type_value->child; - ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdInvalid); + ZigType *type_entry = ir_resolve_type(ira, type_value); if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; @@ -18133,7 +18163,7 @@ static IrInstruction *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstruction static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstructionTruncate *instruction) { IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -18186,7 +18216,7 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct } static IrInstruction *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstructionIntCast *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -18219,7 +18249,7 @@ static IrInstruction *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstruct } static IrInstruction *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstructionFloatCast *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -18259,10 +18289,16 @@ static IrInstruction *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstru } static IrInstruction *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInstructionErrSetCast *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child, ZigTypeIdErrorSet); + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; + if (dest_type->id != ZigTypeIdErrorSet) { + ir_add_error(ira, instruction->dest_type, + buf_sprintf("expected error set type, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_instruction; + } + IrInstruction *target = instruction->target->child; if (type_is_invalid(target->value.type)) return ira->codegen->invalid_instruction; @@ -18291,7 +18327,7 @@ static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_ali static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionFromBytes *instruction) { Error err; - ZigType *dest_child_type = ir_resolve_type(ira, instruction->dest_child_type->child, ZigTypeIdInvalid); + ZigType *dest_child_type = ir_resolve_type(ira, instruction->dest_child_type->child); if (type_is_invalid(dest_child_type)) return ira->codegen->invalid_instruction; @@ -18388,7 +18424,7 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct if (!is_slice(target->value.type)) { ir_add_error(ira, instruction->target, - buf_sprintf("expected slice type, found '%s'", buf_ptr(&target->value.type->name))); + buf_sprintf("expected slice, found '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->invalid_instruction; } @@ -18407,7 +18443,7 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct } static IrInstruction *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -18425,7 +18461,7 @@ static IrInstruction *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInst } static IrInstruction *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrInstructionFloatToInt *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -18481,7 +18517,7 @@ static IrInstruction *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstr return ira->codegen->invalid_instruction; if (target->value.type->id != ZigTypeIdBool) { - ir_add_error(ira, instruction->target, buf_sprintf("expected bool type, found '%s'", + ir_add_error(ira, instruction->target, buf_sprintf("expected bool, found '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->invalid_instruction; } @@ -19062,7 +19098,7 @@ static IrInstruction *ir_analyze_instruction_member_count(IrAnalyze *ira, IrInst IrInstruction *container = instruction->container->child; if (type_is_invalid(container->value.type)) return ira->codegen->invalid_instruction; - ZigType *container_type = ir_resolve_type(ira, container, ZigTypeIdInvalid); + ZigType *container_type = ir_resolve_type(ira, container); if ((err = ensure_complete_type(ira->codegen, container_type))) return ira->codegen->invalid_instruction; @@ -19096,7 +19132,7 @@ static IrInstruction *ir_analyze_instruction_member_count(IrAnalyze *ira, IrInst static IrInstruction *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInstructionMemberType *instruction) { Error err; IrInstruction *container_type_value = instruction->container_type->child; - ZigType *container_type = ir_resolve_type(ira, container_type_value, ZigTypeIdInvalid); + ZigType *container_type = ir_resolve_type(ira, container_type_value); if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; @@ -19139,7 +19175,7 @@ static IrInstruction *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInstr static IrInstruction *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInstructionMemberName *instruction) { Error err; IrInstruction *container_type_value = instruction->container_type->child; - ZigType *container_type = ir_resolve_type(ira, container_type_value, ZigTypeIdInvalid); + ZigType *container_type = ir_resolve_type(ira, container_type_value); if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; @@ -19232,7 +19268,7 @@ static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruct IrInstruction *type_value = instruction->type_value->child; if (type_is_invalid(type_value->value.type)) return ira->codegen->invalid_instruction; - ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdInvalid); + ZigType *type_entry = ir_resolve_type(ira, type_value); if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_instruction; @@ -19282,10 +19318,16 @@ static IrInstruction *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstr if (type_is_invalid(type_value->value.type)) return ira->codegen->invalid_instruction; - ZigType *dest_type = ir_resolve_type(ira, type_value, ZigTypeIdInt); + ZigType *dest_type = ir_resolve_type(ira, type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; + if (dest_type->id != ZigTypeIdInt) { + ir_add_error(ira, type_value, + buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_instruction; + } + IrInstruction *op1 = instruction->op1->child; if (type_is_invalid(op1->value.type)) return ira->codegen->invalid_instruction; @@ -19555,7 +19597,7 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct IrInstruction *param_type_value = instruction->param_types[fn_type_id.next_param_index]->child; if (type_is_invalid(param_type_value->value.type)) return ira->codegen->invalid_instruction; - ZigType *param_type = ir_resolve_type(ira, param_type_value, ZigTypeIdInvalid); + ZigType *param_type = ir_resolve_type(ira, param_type_value); switch (type_requires_comptime(ira->codegen, param_type)) { case ReqCompTimeYes: if (!calling_convention_allows_zig_types(fn_type_id.cc)) { @@ -19589,7 +19631,7 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct } IrInstruction *return_type_value = instruction->return_type->child; - fn_type_id.return_type = ir_resolve_type(ira, return_type_value, ZigTypeIdInvalid); + fn_type_id.return_type = ir_resolve_type(ira, return_type_value); if (type_is_invalid(fn_type_id.return_type)) return ira->codegen->invalid_instruction; if (fn_type_id.return_type->id == ZigTypeIdOpaque) { @@ -19605,7 +19647,7 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; } IrInstruction *async_allocator_type_value = instruction->async_allocator_type_value->child; - fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value, ZigTypeIdInvalid); + fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value); if (type_is_invalid(fn_type_id.async_allocator_type)) return ira->codegen->invalid_instruction; } @@ -19913,7 +19955,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 result_type = get_slice_type(ira->codegen, result_ptr_type); } else { ir_add_error(ira, target, - buf_sprintf("expected pointer or slice type, found '%s'", buf_ptr(&target_type->name))); + buf_sprintf("expected pointer or slice, found '%s'", buf_ptr(&target_type->name))); return ira->codegen->invalid_instruction; } @@ -19959,13 +20001,13 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ // validate src_type and dest_type. if (get_src_ptr_type(src_type) == nullptr) { - ir_add_error(ira, ptr, buf_sprintf("expected Pointer type, found '%s'", buf_ptr(&src_type->name))); + ir_add_error(ira, ptr, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name))); return ira->codegen->invalid_instruction; } if (get_src_ptr_type(dest_type) == nullptr) { ir_add_error(ira, dest_type_src, - buf_sprintf("expected Pointer type, found '%s'", buf_ptr(&dest_type->name))); + buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_instruction; } @@ -20033,7 +20075,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCast *instruction) { IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -20229,7 +20271,7 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) { Error err; IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -20326,13 +20368,13 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) { Error err; IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; // We explicitly check for the size, so we can use get_src_ptr_type if (get_src_ptr_type(dest_type) == nullptr) { - ir_add_error(ira, dest_type_value, buf_sprintf("expected Pointer type, found '%s'", buf_ptr(&dest_type->name))); + ir_add_error(ira, dest_type_value, buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_instruction; } @@ -20435,7 +20477,7 @@ static IrInstruction *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstru // We check size explicitly so we can use get_src_ptr_type here. if (get_src_ptr_type(target->value.type) == nullptr) { ir_add_error(ira, target, - buf_sprintf("expected Pointer type, found '%s'", buf_ptr(&target->value.type->name))); + buf_sprintf("expected pointer, found '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->invalid_instruction; } @@ -20466,7 +20508,7 @@ static IrInstruction *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstru static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstructionPtrType *instruction) { Error err; - ZigType *child_type = ir_resolve_type(ira, instruction->child_type->child, ZigTypeIdInvalid); + ZigType *child_type = ir_resolve_type(ira, instruction->child_type->child); if (type_is_invalid(child_type)) return ira->codegen->invalid_instruction; @@ -20565,7 +20607,7 @@ static IrInstruction *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrI static IrInstruction *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstructionArgType *instruction) { IrInstruction *fn_type_inst = instruction->fn_type->child; - ZigType *fn_type = ir_resolve_type(ira, fn_type_inst, ZigTypeIdFn); + ZigType *fn_type = ir_resolve_type(ira, fn_type_inst); if (type_is_invalid(fn_type)) return ira->codegen->invalid_instruction; @@ -20574,6 +20616,11 @@ static IrInstruction *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruct if (!ir_resolve_usize(ira, arg_index_inst, &arg_index)) return ira->codegen->invalid_instruction; + if (fn_type->id != ZigTypeIdFn) { + ir_add_error(ira, fn_type_inst, buf_sprintf("expected function, found '%s'", buf_ptr(&fn_type->name))); + return ira->codegen->invalid_instruction; + } + FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; if (arg_index >= fn_type_id->param_count) { ir_add_error(ira, arg_index_inst, @@ -20598,7 +20645,7 @@ static IrInstruction *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruct static IrInstruction *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstructionTagType *instruction) { Error err; IrInstruction *target_inst = instruction->target->child; - ZigType *enum_type = ir_resolve_type(ira, target_inst, ZigTypeIdInvalid); + ZigType *enum_type = ir_resolve_type(ira, target_inst); if (type_is_invalid(enum_type)) return ira->codegen->invalid_instruction; @@ -20623,7 +20670,7 @@ static IrInstruction *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; } } else { - ir_add_error(ira, target_inst, buf_sprintf("expected enum or union type, found '%s'", + ir_add_error(ira, target_inst, buf_sprintf("expected enum or union, found '%s'", buf_ptr(&enum_type->name))); return ira->codegen->invalid_instruction; } @@ -20777,7 +20824,7 @@ static IrInstruction *ir_analyze_instruction_coro_promise(IrAnalyze *ira, IrInst if (coro_handle->value.type->id != ZigTypeIdPromise || coro_handle->value.type->data.promise.result_type == nullptr) { - ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T type, found '%s'", + ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T, found '%s'", buf_ptr(&coro_handle->value.type->name))); return ira->codegen->invalid_instruction; } @@ -20808,7 +20855,7 @@ static IrInstruction *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, I } static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op) { - ZigType *operand_type = ir_resolve_type(ira, op, ZigTypeIdInvalid); + ZigType *operand_type = ir_resolve_type(ira, op); if (type_is_invalid(operand_type)) return ira->codegen->builtin_types.entry_invalid; @@ -20938,12 +20985,12 @@ static IrInstruction *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstr } static IrInstruction *ir_analyze_instruction_promise_result_type(IrAnalyze *ira, IrInstructionPromiseResultType *instruction) { - ZigType *promise_type = ir_resolve_type(ira, instruction->promise_type->child, ZigTypeIdInvalid); + ZigType *promise_type = ir_resolve_type(ira, instruction->promise_type->child); if (type_is_invalid(promise_type)) return ira->codegen->invalid_instruction; if (promise_type->id != ZigTypeIdPromise || promise_type->data.promise.result_type == nullptr) { - ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T type, found '%s'", + ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T, found '%s'", buf_ptr(&promise_type->name))); return ira->codegen->invalid_instruction; } @@ -20952,7 +20999,7 @@ static IrInstruction *ir_analyze_instruction_promise_result_type(IrAnalyze *ira, } static IrInstruction *ir_analyze_instruction_await_bookkeeping(IrAnalyze *ira, IrInstructionAwaitBookkeeping *instruction) { - ZigType *promise_result_type = ir_resolve_type(ira, instruction->promise_result_type->child, ZigTypeIdInvalid); + ZigType *promise_result_type = ir_resolve_type(ira, instruction->promise_result_type->child); if (type_is_invalid(promise_result_type)) return ira->codegen->invalid_instruction; @@ -21015,7 +21062,7 @@ static IrInstruction *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *i } static IrInstruction *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) { - ZigType *float_type = ir_resolve_type(ira, instruction->type->child, ZigTypeIdInvalid); + ZigType *float_type = ir_resolve_type(ira, instruction->type->child); if (type_is_invalid(float_type)) return ira->codegen->invalid_instruction; @@ -21082,7 +21129,7 @@ static IrInstruction *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionS } static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstructionBswap *instruction) { - ZigType *int_type = ir_resolve_type(ira, instruction->type->child, ZigTypeIdInvalid); + ZigType *int_type = ir_resolve_type(ira, instruction->type->child); if (type_is_invalid(int_type)) return ira->codegen->invalid_instruction; @@ -21138,7 +21185,7 @@ static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstruction } static IrInstruction *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstructionBitReverse *instruction) { - ZigType *int_type = ir_resolve_type(ira, instruction->type->child, ZigTypeIdInvalid); + ZigType *int_type = ir_resolve_type(ira, instruction->type->child); if (type_is_invalid(int_type)) return ira->codegen->invalid_instruction; @@ -21209,7 +21256,7 @@ static IrInstruction *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstr if (target->value.type->id != ZigTypeIdEnum) { ir_add_error(ira, instruction->target, - buf_sprintf("expected enum type, found '%s'", buf_ptr(&target->value.type->name))); + buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->invalid_instruction; } @@ -21224,10 +21271,16 @@ static IrInstruction *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstr static IrInstruction *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstructionIntToEnum *instruction) { Error err; IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value, ZigTypeIdEnum); + ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; + if (dest_type->id != ZigTypeIdEnum) { + ir_add_error(ira, instruction->dest_type, + buf_sprintf("expected enum, found type '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_instruction; + } + if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; diff --git a/src/parser.cpp b/src/parser.cpp index 9425df2430..077365995e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -122,37 +122,19 @@ static AstNode *ast_parse_container_decl_type(ParseContext *pc); static AstNode *ast_parse_byte_align(ParseContext *pc); ATTRIBUTE_PRINTF(3, 4) -static ErrorMsg *ast_error(ParseContext *pc, Token *token, const char *format, ...) { - va_list ap; - va_start(ap, format); - Buf *msg = buf_vprintf(format, ap); - va_end(ap); - - ErrorMsg *err = err_msg_create_with_line(pc->owner->path, token->start_line, token->start_column, - pc->owner->source_code, pc->owner->line_offsets, msg); - err->line_start = token->start_line; - err->column_start = token->start_column; - - return err; -} - -ATTRIBUTE_PRINTF(4, 5) ATTRIBUTE_NORETURN -static void ast_error_exit(ParseContext *pc, Token *token, ErrorMsg *note, const char *format, ...) { +static void ast_error(ParseContext *pc, Token *token, const char *format, ...) { va_list ap; va_start(ap, format); Buf *msg = buf_vprintf(format, ap); va_end(ap); + ErrorMsg *err = err_msg_create_with_line(pc->owner->path, token->start_line, token->start_column, pc->owner->source_code, pc->owner->line_offsets, msg); err->line_start = token->start_line; err->column_start = token->start_column; - if (note) { - err->notes.append(note); - } - print_err_msg(err, pc->err_color); exit(EXIT_FAILURE); } @@ -182,7 +164,7 @@ static Buf ast_token_str(Buf *input, Token *token) { ATTRIBUTE_NORETURN static void ast_invalid_token_error(ParseContext *pc, Token *token) { Buf token_value = ast_token_str(pc->buf, token); - ast_error_exit(pc, token, NULL, "invalid token: '%s'", buf_ptr(&token_value)); + ast_error(pc, token, "invalid token: '%s'", buf_ptr(&token_value)); } static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) { @@ -232,13 +214,8 @@ static Token *eat_token_if(ParseContext *pc, TokenId id) { static Token *expect_token(ParseContext *pc, TokenId id) { Token *res = eat_token(pc); - if (res->id != id) { - ErrorMsg *note = NULL; - if (res->id == TokenIdAmpersandAmpersand) { - note = ast_error(pc, res, "did you mean to use `and`?"); - } - ast_error_exit(pc, res, note, "expected token '%s', found '%s'", token_name(id), token_name(res->id)); - } + if (res->id != id) + ast_error(pc, res, "expected token '%s', found '%s'", token_name(id), token_name(res->id)); return res; } @@ -860,7 +837,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) { if (param_decl->data.param_decl.is_var_args) res->data.fn_proto.is_var_args = true; if (i != params.length - 1 && res->data.fn_proto.is_var_args) - ast_error_exit(pc, first, NULL, "Function prototype have varargs as a none last paramter."); + ast_error(pc, first, "Function prototype have varargs as a none last paramter."); } return res; } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 464412d443..6215541876 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -199,7 +199,6 @@ enum TokenizeState { TokenizeStateSawDash, TokenizeStateSawMinusPercent, TokenizeStateSawAmpersand, - TokenizeStateSawAmpersandAmpersand, TokenizeStateSawCaret, TokenizeStateSawBar, TokenizeStateSawBarBar, @@ -887,15 +886,14 @@ void tokenize(Buf *buf, Tokenization *out) { break; case TokenizeStateSawAmpersand: switch (c) { + case '&': + tokenize_error(&t, "`&&` is invalid. Note that `and` is boolean AND."); + break; case '=': set_token_id(&t, t.cur_tok, TokenIdBitAndEq); end_token(&t); t.state = TokenizeStateStart; break; - case '&': - set_token_id(&t, t.cur_tok, TokenIdAmpersandAmpersand); - t.state = TokenizeStateSawAmpersandAmpersand; - break; default: t.pos -= 1; end_token(&t); @@ -903,11 +901,6 @@ void tokenize(Buf *buf, Tokenization *out) { continue; } break; - case TokenizeStateSawAmpersandAmpersand: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; case TokenizeStateSawCaret: switch (c) { case '=': @@ -1478,7 +1471,6 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateSawPlus: case TokenizeStateSawDash: case TokenizeStateSawAmpersand: - case TokenizeStateSawAmpersandAmpersand: case TokenizeStateSawCaret: case TokenizeStateSawBar: case TokenizeStateSawEq: @@ -1526,7 +1518,6 @@ void tokenize(Buf *buf, Tokenization *out) { const char * token_name(TokenId id) { switch (id) { case TokenIdAmpersand: return "&"; - case TokenIdAmpersandAmpersand: return "&&"; case TokenIdArrow: return "->"; case TokenIdAtSign: return "@"; case TokenIdBang: return "!"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 2e872ce4de..1574e95571 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -14,7 +14,6 @@ enum TokenId { TokenIdAmpersand, - TokenIdAmpersandAmpersand, TokenIdArrow, TokenIdAtSign, TokenIdBang, diff --git a/test/compile_errors.zig b/test/compile_errors.zig index d262405feb..0754f223e8 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,33 +2,28 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add( - "Use of && in place of `and`", - \\export fn entry() void { - \\ if (true && false) return; - \\} - , - ".tmp_source.zig:2:14: error: expected token ')', found '&&'", - ".tmp_source.zig:2:14: note: did you mean to use `and`?", - ); - - cases.add( - "Use of || in place of `or` (using comptime_int)", - \\export fn entry() void { - \\ if (1 || 0) return; + "attempted `&&`", + \\export fn entry(a: bool, b: bool) i32 { + \\ if (a && b) { + \\ return 1234; + \\ } + \\ return 5678; \\} , - ".tmp_source.zig:2:9: error: expected ErrorSet type, found 'comptime_int'", - ".tmp_source.zig:2:11: note: did you mean to use `or`?", + ".tmp_source.zig:2:11: error: `&&` is invalid. Note that `and` is boolean AND.", ); cases.add( - "Use of || in place of `or` (using booleans)", - \\export fn entry() void { - \\ if (true || false) return; + "attempted `||` on boolean values", + \\export fn entry(a: bool, b: bool) i32 { + \\ if (a || b) { + \\ return 1234; + \\ } + \\ return 5678; \\} , - ".tmp_source.zig:2:9: error: expected ErrorSet type, found 'bool'", - ".tmp_source.zig:2:14: note: did you mean to use `or`?", + ".tmp_source.zig:2:9: error: expected error set type, found 'bool'", + ".tmp_source.zig:2:11: note: `||` merges error sets; `or` performs boolean OR", ); cases.add( @@ -98,7 +93,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ do_the_thing(bar); \\} , - ".tmp_source.zig:4:18: error: expected 'fn(i32) void' type, found 'fn(bool) void", + ".tmp_source.zig:4:18: error: expected type 'fn(i32) void', found 'fn(bool) void", ".tmp_source.zig:4:18: note: parameter 0: 'bool' cannot cast into 'i32'", ); @@ -134,7 +129,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:3:31: error: integer value 300 cannot be implicitly casted to type 'u8'", ".tmp_source.zig:7:22: error: integer value 300 cannot be implicitly casted to type 'u8'", - ".tmp_source.zig:11:20: error: expected 'u8' type, found 'u16'", + ".tmp_source.zig:11:20: error: expected type 'u8', found 'u16'", ); cases.add( @@ -154,7 +149,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(y)); } , - ".tmp_source.zig:2:14: error: expected 'f32' type, found 'f64'", + ".tmp_source.zig:2:14: error: expected type 'f32', found 'f64'", ); cases.add( @@ -202,7 +197,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var ptr2: *c_void = &b; \\} , - ".tmp_source.zig:5:26: error: expected '*c_void' type, found '**u32'", + ".tmp_source.zig:5:26: error: expected type '*c_void', found '**u32'", ); cases.add( @@ -252,7 +247,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const sliceA: []u8 = &buffer; \\} , - ".tmp_source.zig:3:27: error: expected '[]u8' type, found '*const [1]u8'", + ".tmp_source.zig:3:27: error: expected type '[]u8', found '*const [1]u8'", ); cases.add( @@ -297,9 +292,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const Errors = error{} || u16; \\} , - ".tmp_source.zig:2:20: error: expected ErrorSet type, found 'u8'", - ".tmp_source.zig:2:23: note: did you mean to use `or`?", - ".tmp_source.zig:5:31: error: expected ErrorSet type, found 'u16'", + ".tmp_source.zig:2:20: error: expected error set type, found type 'u8'", + ".tmp_source.zig:2:23: note: `||` merges error sets; `or` performs boolean OR", + ".tmp_source.zig:5:31: error: expected error set type, found type 'u16'", + ".tmp_source.zig:5:28: note: `||` merges error sets; `or` performs boolean OR", ); cases.add( @@ -794,7 +790,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\fn bar(x: *b.Foo) void {} , - ".tmp_source.zig:6:10: error: expected '*Foo' type, found '*Foo'", + ".tmp_source.zig:6:10: error: expected type '*Foo', found '*Foo'", ".tmp_source.zig:6:10: note: pointer type child 'Foo' cannot cast into pointer type child 'Foo'", "a.zig:1:17: note: Foo declared here", "b.zig:1:17: note: Foo declared here", @@ -864,7 +860,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var y = p.*; \\} , - ".tmp_source.zig:4:23: error: expected '*?*i32' type, found '**i32'", + ".tmp_source.zig:4:23: error: expected type '*?*i32', found '**i32'", ); cases.add( @@ -873,7 +869,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const x: [*]const bool = true; \\} , - ".tmp_source.zig:2:30: error: expected '[*]const bool' type, found 'bool'", + ".tmp_source.zig:2:30: error: expected type '[*]const bool', found 'bool'", ); cases.add( @@ -918,7 +914,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var rule_set = try Foo.init(); \\} , - ".tmp_source.zig:2:13: error: expected 'i32' type, found 'type'", + ".tmp_source.zig:2:13: error: expected type 'i32', found 'type'", ); cases.add( @@ -952,7 +948,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return null; \\} , - ".tmp_source.zig:5:34: error: expected '?NextError!i32' type, found '?OtherError!i32'", + ".tmp_source.zig:5:34: error: expected type '?NextError!i32', found '?OtherError!i32'", ".tmp_source.zig:5:34: note: optional type child 'OtherError!i32' cannot cast into optional type child 'NextError!i32'", ".tmp_source.zig:5:34: note: error set 'OtherError' cannot cast into error set 'NextError'", ".tmp_source.zig:2:26: note: 'error.OutOfMemory' not a member of destination error set", @@ -1022,7 +1018,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ @panic(e); \\} , - ".tmp_source.zig:3:12: error: expected '[]const u8' type, found 'error{Foo}'", + ".tmp_source.zig:3:12: error: expected type '[]const u8', found 'error{Foo}'", ); cases.add( @@ -1050,7 +1046,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return error.ShouldBeCompileError; \\} , - ".tmp_source.zig:6:17: error: expected 'void' type, found 'error{ShouldBeCompileError}'", + ".tmp_source.zig:6:17: error: expected type 'void', found 'error{ShouldBeCompileError}'", ); cases.add( @@ -1093,7 +1089,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ a(c); \\} , - ".tmp_source.zig:8:7: error: expected 'fn(*const u8) void' type, found 'fn(u8) void'", + ".tmp_source.zig:8:7: error: expected type 'fn(*const u8) void', found 'fn(u8) void'", ); cases.add( @@ -1175,7 +1171,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ E.One => {}, \\ } \\} - , ".tmp_source.zig:9:10: error: expected 'usize' type, found 'E'"); + , ".tmp_source.zig:9:10: error: expected type 'usize', found 'E'"); cases.add( "range operator in switch used on error set", @@ -1221,7 +1217,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const z = i32!i32; \\} , - ".tmp_source.zig:2:15: error: expected ErrorSet type, found 'i32'", + ".tmp_source.zig:2:15: error: expected error set type, found type 'i32'", ); cases.add( @@ -1271,7 +1267,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return error.B; \\} , - ".tmp_source.zig:3:35: error: expected 'SmallErrorSet!i32' type, found 'anyerror!i32'", + ".tmp_source.zig:3:35: error: expected type 'SmallErrorSet!i32', found 'anyerror!i32'", ".tmp_source.zig:3:35: note: error set 'anyerror' cannot cast into error set 'SmallErrorSet'", ".tmp_source.zig:3:35: note: cannot cast global error set into smaller set", ); @@ -1286,7 +1282,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return error.B; \\} , - ".tmp_source.zig:3:31: error: expected 'SmallErrorSet' type, found 'anyerror'", + ".tmp_source.zig:3:31: error: expected type 'SmallErrorSet', found 'anyerror'", ".tmp_source.zig:3:31: note: cannot cast global error set into smaller set", ); @@ -1313,7 +1309,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x: Set2 = set1; \\} , - ".tmp_source.zig:7:19: error: expected 'Set2' type, found 'Set1'", + ".tmp_source.zig:7:19: error: expected type 'Set2', found 'Set1'", ".tmp_source.zig:1:23: note: 'error.B' not a member of destination error set", ); @@ -1853,7 +1849,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn a() noreturn {return;} \\export fn entry() void { a(); } , - ".tmp_source.zig:1:18: error: expected 'noreturn' type, found 'void'", + ".tmp_source.zig:1:18: error: expected type 'noreturn', found 'void'", ); cases.add( @@ -1861,7 +1857,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn a() i32 {} \\export fn entry() void { _ = a(); } , - ".tmp_source.zig:1:12: error: expected 'i32' type, found 'void'", + ".tmp_source.zig:1:12: error: expected type 'i32', found 'void'", ); cases.add( @@ -1967,7 +1963,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return a; \\} , - ".tmp_source.zig:3:12: error: expected 'i32' type, found '[*]const u8'", + ".tmp_source.zig:3:12: error: expected type 'i32', found '[*]const u8'", ); cases.add( @@ -1976,7 +1972,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ if (0) {} \\} , - ".tmp_source.zig:2:9: error: expected 'bool' type, found 'comptime_int'", + ".tmp_source.zig:2:9: error: expected type 'bool', found 'comptime_int'", ); cases.add( @@ -2071,8 +2067,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ array[bad] = array[bad]; \\} , - ".tmp_source.zig:4:11: error: expected 'usize' type, found 'bool'", - ".tmp_source.zig:4:24: error: expected 'usize' type, found 'bool'", + ".tmp_source.zig:4:11: error: expected type 'usize', found 'bool'", + ".tmp_source.zig:4:24: error: expected type 'usize', found 'bool'", ); cases.add( @@ -2486,7 +2482,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn foo() *const i32 { return y; } \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:3:30: error: expected '*const i32' type, found '*const comptime_int'", + ".tmp_source.zig:3:30: error: expected type '*const i32', found '*const comptime_int'", ); cases.add( @@ -2564,7 +2560,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn c() i32 {return 2;} \\export fn entry() usize { return @sizeOf(@typeOf(fns)); } , - ".tmp_source.zig:1:27: error: expected 'fn() void' type, found 'fn() i32'", + ".tmp_source.zig:1:27: error: expected type 'fn() void', found 'fn() i32'", ); cases.add( @@ -2576,7 +2572,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(fns)); } , - ".tmp_source.zig:1:36: error: expected 'fn(i32) i32' type, found 'extern fn(i32) i32'", + ".tmp_source.zig:1:36: error: expected type 'fn(i32) i32', found 'extern fn(i32) i32'", ); cases.add( @@ -2680,7 +2676,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(a)); } , - ".tmp_source.zig:1:16: error: expected '*u8' type, found '(null)'", + ".tmp_source.zig:1:16: error: expected type '*u8', found '(null)'", ); cases.add( @@ -3320,7 +3316,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\fn something() anyerror!void { } , - ".tmp_source.zig:2:5: error: expected 'void' type, found 'anyerror'", + ".tmp_source.zig:2:5: error: expected type 'void', found 'anyerror'", ".tmp_source.zig:1:15: note: return type declared here", ); @@ -3499,7 +3495,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ derp.init(); \\} , - ".tmp_source.zig:14:5: error: expected 'i32' type, found 'Foo'", + ".tmp_source.zig:14:5: error: expected type 'i32', found 'Foo'", ); cases.add( @@ -3529,7 +3525,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ x.init(); \\} , - ".tmp_source.zig:23:5: error: expected '*Allocator' type, found '*List'", + ".tmp_source.zig:23:5: error: expected type '*Allocator', found '*List'", ); cases.add( @@ -3701,7 +3697,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:8:26: error: expected '*const u3' type, found '*align(:3:1) const u3'", + ".tmp_source.zig:8:26: error: expected type '*const u3', found '*align(:3:1) const u3'", ); cases.add( @@ -3830,7 +3826,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:4:19: error: expected '*[]const u8' type, found '*const []const u8'", + ".tmp_source.zig:4:19: error: expected type '*[]const u8', found '*const []const u8'", ); cases.addCase(x: { @@ -3862,7 +3858,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ foo(global_array); \\} , - ".tmp_source.zig:4:9: error: expected '[]i32' type, found '[10]i32'", + ".tmp_source.zig:4:9: error: expected type '[]i32', found '[10]i32'", ); cases.add( @@ -3871,7 +3867,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return @ptrCast(usize, a); \\} , - ".tmp_source.zig:2:21: error: expected Pointer type, found 'usize'", + ".tmp_source.zig:2:21: error: expected pointer, found 'usize'", ); cases.add( @@ -3918,7 +3914,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return @fieldParentPtr(Foo, "a", a); \\} , - ".tmp_source.zig:3:28: error: expected Struct type, found 'i32'", + ".tmp_source.zig:3:28: error: expected struct type, found 'i32'", ); cases.add( @@ -3942,7 +3938,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return @fieldParentPtr(Foo, "a", a); \\} , - ".tmp_source.zig:5:38: error: expected Pointer type, found 'i32'", + ".tmp_source.zig:5:38: error: expected pointer, found 'i32'", ); cases.add( @@ -3983,7 +3979,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return @byteOffsetOf(Foo, "a",); \\} , - ".tmp_source.zig:3:26: error: expected Struct type, found 'i32'", + ".tmp_source.zig:3:26: error: expected struct type, found 'i32'", ); cases.add( @@ -4097,7 +4093,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\fn bar() ?i32 { return 1; } , - ".tmp_source.zig:2:15: error: expected 'bool' type, found '?i32'", + ".tmp_source.zig:2:15: error: expected type 'bool', found '?i32'", ); cases.add( @@ -4107,7 +4103,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\fn bar() anyerror!i32 { return 1; } , - ".tmp_source.zig:2:15: error: expected 'bool' type, found 'anyerror!i32'", + ".tmp_source.zig:2:15: error: expected type 'bool', found 'anyerror!i32'", ); cases.add( @@ -4368,7 +4364,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return @ptrToInt(x); \\} , - ".tmp_source.zig:2:22: error: expected Pointer type, found 'i32'", + ".tmp_source.zig:2:22: error: expected pointer, found 'i32'", ); cases.add( @@ -4404,7 +4400,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return x << y; \\} , - ".tmp_source.zig:2:17: error: expected 'u3' type, found 'u8'", + ".tmp_source.zig:2:17: error: expected type 'u3', found 'u8'", ); cases.add( @@ -4433,7 +4429,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ x.* += 1; \\} , - ".tmp_source.zig:8:13: error: expected '*u32' type, found '*align(1) u32'", + ".tmp_source.zig:8:13: error: expected type '*u32', found '*align(1) u32'", ); cases.add( @@ -4477,7 +4473,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ @alignCast(4, u32(3)); \\} , - ".tmp_source.zig:2:22: error: expected pointer or slice type, found 'u32'", + ".tmp_source.zig:2:22: error: expected pointer or slice, found 'u32'", ); cases.add( @@ -4490,7 +4486,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\fn alignedSmall() align(4) i32 { return 1234; } , - ".tmp_source.zig:2:35: error: expected 'fn() align(8) i32' type, found 'fn() align(4) i32'", + ".tmp_source.zig:2:35: error: expected type 'fn() align(8) i32', found 'fn() align(4) i32'", ); cases.add( @@ -4502,7 +4498,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return x == 5678; \\} , - ".tmp_source.zig:4:32: error: expected '*i32' type, found '*align(1) i32'", + ".tmp_source.zig:4:32: error: expected type '*i32', found '*align(1) i32'", ); cases.add( @@ -4537,7 +4533,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ bar(@ptrCast(*c_void, &x)); \\} , - ".tmp_source.zig:5:9: error: expected '*Derp' type, found '*c_void'", + ".tmp_source.zig:5:9: error: expected type '*Derp', found '*c_void'", ); cases.add( @@ -4583,7 +4579,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, u32(1234), u32(1234))) {} \\} , - ".tmp_source.zig:3:50: error: expected 'AtomicOrder' type, found 'u32'", + ".tmp_source.zig:3:50: error: expected type 'AtomicOrder', found 'u32'", ); cases.add( @@ -4593,7 +4589,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ @export("entry", entry, u32(1234)); \\} , - ".tmp_source.zig:3:32: error: expected 'GlobalLinkage' type, found 'u32'", + ".tmp_source.zig:3:32: error: expected type 'GlobalLinkage', found 'u32'", ); cases.add( @@ -4770,7 +4766,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = @ArgType(i32, 3); \\} , - ".tmp_source.zig:2:18: error: expected Fn type, found 'i32'", + ".tmp_source.zig:2:18: error: expected function, found 'i32'", ); cases.add( @@ -4868,7 +4864,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\pub extern fn foo(format: *const u8, ...) void; , - ".tmp_source.zig:2:9: error: expected '*const u8' type, found '[5]u8'", + ".tmp_source.zig:2:9: error: expected type '*const u8', found '[5]u8'", ); cases.add( @@ -4937,7 +4933,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x: u2 = Small.Two; \\} , - ".tmp_source.zig:9:22: error: expected 'u2' type, found 'Small'", + ".tmp_source.zig:9:22: error: expected type 'u2', found 'Small'", ); cases.add( @@ -4954,7 +4950,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x = @intToEnum(Small, y); \\} , - ".tmp_source.zig:10:31: error: expected 'u2' type, found 'u3'", + ".tmp_source.zig:10:31: error: expected type 'u2', found 'u3'", ); cases.add( @@ -5351,7 +5347,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ asm volatile ("" : : [bar]"r"(3) : ""); \\} , - ".tmp_source.zig:2:35: error: expected sized integer or sized float type, found comptime_int", + ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_int", ); cases.add( @@ -5360,6 +5356,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ asm volatile ("" : : [bar]"r"(3.17) : ""); \\} , - ".tmp_source.zig:2:35: error: expected sized integer or sized float type, found comptime_float", + ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_float", ); } -- cgit v1.2.3 From 581edd643fb18a66c472f77e2f8cd3f4cea524a2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 Jan 2019 21:47:26 -0500 Subject: backport copy elision changes This commit contains everything from the copy-elision-2 branch that does not have to do with copy elision directly, but is generally useful for master branch. * All const values know their parents, when applicable, not just structs and unions. * Null pointers in const values are represented explicitly, rather than as a HardCodedAddr value of 0. * Rename "maybe" to "optional" in various code locations. * Separate DeclVarSrc and DeclVarGen * Separate PtrCastSrc and PtrCastGen * Separate CmpxchgSrc and CmpxchgGen * Represent optional error set as an integer, using the 0 value. In a const value, it uses nullptr. * Introduce type_has_one_possible_value and use it where applicable. * Fix debug builds not setting memory to 0xaa when storing undefined. * Separate the type of a variable from the const value of a variable. * Use copy_const_val where appropriate. * Rearrange structs to pack data more efficiently. * Move test/cases/* to test/behavior/* * Use `std.debug.assertOrPanic` in behavior tests instead of `std.debug.assert`. * Fix outdated slice syntax in docs. --- build.zig | 5 +- doc/langref.html.in | 13 +- src/all_types.hpp | 165 ++- src/analyze.cpp | 297 ++-- src/analyze.hpp | 9 +- src/ast_render.cpp | 8 +- src/codegen.cpp | 385 +++-- src/ir.cpp | 1471 +++++++++++--------- src/ir.hpp | 2 +- src/ir_print.cpp | 83 +- src/parser.cpp | 8 +- std/event/fs.zig | 49 +- test/behavior.zig | 82 -- test/cases/align.zig | 230 --- test/cases/alignof.zig | 17 - test/cases/array.zig | 173 --- test/cases/asm.zig | 48 - test/cases/atomics.zig | 71 - test/cases/bit_shifting.zig | 88 -- test/cases/bitcast.zig | 37 - test/cases/bitreverse.zig | 81 -- test/cases/bool.zig | 35 - test/cases/bswap.zig | 32 - test/cases/bugs/1076.zig | 16 - test/cases/bugs/1111.zig | 12 - test/cases/bugs/1277.zig | 15 - test/cases/bugs/1322.zig | 19 - test/cases/bugs/1381.zig | 21 - test/cases/bugs/1421.zig | 14 - test/cases/bugs/1442.zig | 11 - test/cases/bugs/1486.zig | 11 - test/cases/bugs/394.zig | 18 - test/cases/bugs/655.zig | 12 - test/cases/bugs/655_other_file.zig | 1 - test/cases/bugs/656.zig | 31 - test/cases/bugs/726.zig | 16 - test/cases/bugs/828.zig | 33 - test/cases/bugs/920.zig | 65 - test/cases/byval_arg_var.zig | 27 - test/cases/cancel.zig | 92 -- test/cases/cast.zig | 472 ------- test/cases/const_slice_child.zig | 45 - test/cases/coroutine_await_struct.zig | 47 - test/cases/coroutines.zig | 258 ---- test/cases/defer.zig | 78 -- test/cases/enum.zig | 894 ------------ test/cases/enum_with_members.zig | 27 - test/cases/error.zig | 245 ---- test/cases/eval.zig | 782 ----------- test/cases/field_parent_ptr.zig | 41 - test/cases/fn.zig | 207 --- test/cases/fn_in_struct_in_comptime.zig | 17 - test/cases/for.zig | 106 -- test/cases/generics.zig | 151 -- test/cases/if.zig | 37 - test/cases/import.zig | 10 - test/cases/import/a_namespace.zig | 3 - test/cases/incomplete_struct_param_tld.zig | 30 - test/cases/inttoptr.zig | 27 - test/cases/ir_block_deps.zig | 21 - test/cases/math.zig | 500 ------- test/cases/merge_error_sets.zig | 21 - test/cases/misc.zig | 681 --------- test/cases/namespace_depends_on_compile_var/a.zig | 1 - test/cases/namespace_depends_on_compile_var/b.zig | 1 - .../namespace_depends_on_compile_var/index.zig | 14 - test/cases/new_stack_call.zig | 26 - test/cases/null.zig | 162 --- test/cases/optional.zig | 30 - test/cases/pointers.zig | 44 - test/cases/popcount.zig | 24 - test/cases/ptrcast.zig | 52 - test/cases/pub_enum/index.zig | 13 - test/cases/pub_enum/other.zig | 6 - .../ref_var_in_if_after_if_2nd_switch_prong.zig | 37 - test/cases/reflection.zig | 95 -- test/cases/sizeof_and_typeof.zig | 69 - test/cases/slice.zig | 40 - test/cases/struct.zig | 470 ------- test/cases/struct_contains_null_ptr_itself.zig | 21 - test/cases/struct_contains_slice_of_itself.zig | 85 -- test/cases/switch.zig | 271 ---- test/cases/switch_prong_err_enum.zig | 30 - test/cases/switch_prong_implicit_cast.zig | 22 - test/cases/syntax.zig | 59 - test/cases/this.zig | 34 - test/cases/truncate.zig | 8 - test/cases/try.zig | 43 - test/cases/type_info.zig | 264 ---- test/cases/undefined.zig | 68 - test/cases/underscore.zig | 28 - test/cases/union.zig | 352 ----- test/cases/var_args.zig | 84 -- test/cases/void.zig | 30 - test/cases/while.zig | 227 --- test/cases/widening.zig | 27 - test/compile_errors.zig | 4 +- test/stage1/behavior.zig | 80 ++ test/stage1/behavior/align.zig | 230 +++ test/stage1/behavior/alignof.zig | 18 + test/stage1/behavior/array.zig | 270 ++++ test/stage1/behavior/asm.zig | 92 ++ test/stage1/behavior/atomics.zig | 71 + test/stage1/behavior/bit_shifting.zig | 88 ++ test/stage1/behavior/bitcast.zig | 36 + test/stage1/behavior/bitreverse.zig | 81 ++ test/stage1/behavior/bool.zig | 35 + test/stage1/behavior/bswap.zig | 32 + test/stage1/behavior/bugs/1076.zig | 16 + test/stage1/behavior/bugs/1111.zig | 12 + test/stage1/behavior/bugs/1277.zig | 15 + test/stage1/behavior/bugs/1322.zig | 19 + test/stage1/behavior/bugs/1381.zig | 21 + test/stage1/behavior/bugs/1421.zig | 14 + test/stage1/behavior/bugs/1442.zig | 11 + test/stage1/behavior/bugs/1486.zig | 11 + test/stage1/behavior/bugs/394.zig | 18 + test/stage1/behavior/bugs/655.zig | 12 + test/stage1/behavior/bugs/655_other_file.zig | 1 + test/stage1/behavior/bugs/656.zig | 31 + test/stage1/behavior/bugs/726.zig | 16 + test/stage1/behavior/bugs/828.zig | 33 + test/stage1/behavior/bugs/920.zig | 65 + test/stage1/behavior/byval_arg_var.zig | 27 + test/stage1/behavior/cancel.zig | 92 ++ test/stage1/behavior/cast.zig | 473 +++++++ test/stage1/behavior/const_slice_child.zig | 45 + test/stage1/behavior/coroutine_await_struct.zig | 47 + test/stage1/behavior/coroutines.zig | 258 ++++ test/stage1/behavior/defer.zig | 78 ++ test/stage1/behavior/enum.zig | 894 ++++++++++++ test/stage1/behavior/enum_with_members.zig | 27 + test/stage1/behavior/error.zig | 332 +++++ test/stage1/behavior/eval.zig | 784 +++++++++++ test/stage1/behavior/field_parent_ptr.zig | 41 + test/stage1/behavior/fn.zig | 208 +++ test/stage1/behavior/fn_in_struct_in_comptime.zig | 17 + test/stage1/behavior/for.zig | 106 ++ test/stage1/behavior/generics.zig | 151 ++ test/stage1/behavior/if.zig | 37 + test/stage1/behavior/import.zig | 10 + test/stage1/behavior/import/a_namespace.zig | 3 + .../behavior/incomplete_struct_param_tld.zig | 30 + test/stage1/behavior/inttoptr.zig | 26 + test/stage1/behavior/ir_block_deps.zig | 21 + test/stage1/behavior/math.zig | 500 +++++++ test/stage1/behavior/merge_error_sets.zig | 21 + test/stage1/behavior/misc.zig | 687 +++++++++ .../namespace_depends_on_compile_var/a.zig | 1 + .../namespace_depends_on_compile_var/b.zig | 1 + .../namespace_depends_on_compile_var/index.zig | 14 + test/stage1/behavior/new_stack_call.zig | 26 + test/stage1/behavior/null.zig | 162 +++ test/stage1/behavior/optional.zig | 81 ++ test/stage1/behavior/pointers.zig | 44 + test/stage1/behavior/popcount.zig | 25 + test/stage1/behavior/ptrcast.zig | 52 + test/stage1/behavior/pub_enum/index.zig | 13 + test/stage1/behavior/pub_enum/other.zig | 6 + .../ref_var_in_if_after_if_2nd_switch_prong.zig | 37 + test/stage1/behavior/reflection.zig | 96 ++ test/stage1/behavior/sizeof_and_typeof.zig | 69 + test/stage1/behavior/slice.zig | 40 + test/stage1/behavior/struct.zig | 470 +++++++ .../behavior/struct_contains_null_ptr_itself.zig | 21 + .../behavior/struct_contains_slice_of_itself.zig | 85 ++ test/stage1/behavior/switch.zig | 271 ++++ test/stage1/behavior/switch_prong_err_enum.zig | 30 + .../stage1/behavior/switch_prong_implicit_cast.zig | 22 + test/stage1/behavior/syntax.zig | 60 + test/stage1/behavior/this.zig | 35 + test/stage1/behavior/truncate.zig | 8 + test/stage1/behavior/try.zig | 43 + test/stage1/behavior/type_info.zig | 264 ++++ test/stage1/behavior/undefined.zig | 69 + test/stage1/behavior/underscore.zig | 28 + test/stage1/behavior/union.zig | 352 +++++ test/stage1/behavior/var_args.zig | 84 ++ test/stage1/behavior/void.zig | 35 + test/stage1/behavior/while.zig | 228 +++ test/stage1/behavior/widening.zig | 28 + 181 files changed, 10595 insertions(+), 9692 deletions(-) delete mode 100644 test/behavior.zig delete mode 100644 test/cases/align.zig delete mode 100644 test/cases/alignof.zig delete mode 100644 test/cases/array.zig delete mode 100644 test/cases/asm.zig delete mode 100644 test/cases/atomics.zig delete mode 100644 test/cases/bit_shifting.zig delete mode 100644 test/cases/bitcast.zig delete mode 100644 test/cases/bitreverse.zig delete mode 100644 test/cases/bool.zig delete mode 100644 test/cases/bswap.zig delete mode 100644 test/cases/bugs/1076.zig delete mode 100644 test/cases/bugs/1111.zig delete mode 100644 test/cases/bugs/1277.zig delete mode 100644 test/cases/bugs/1322.zig delete mode 100644 test/cases/bugs/1381.zig delete mode 100644 test/cases/bugs/1421.zig delete mode 100644 test/cases/bugs/1442.zig delete mode 100644 test/cases/bugs/1486.zig delete mode 100644 test/cases/bugs/394.zig delete mode 100644 test/cases/bugs/655.zig delete mode 100644 test/cases/bugs/655_other_file.zig delete mode 100644 test/cases/bugs/656.zig delete mode 100644 test/cases/bugs/726.zig delete mode 100644 test/cases/bugs/828.zig delete mode 100644 test/cases/bugs/920.zig delete mode 100644 test/cases/byval_arg_var.zig delete mode 100644 test/cases/cancel.zig delete mode 100644 test/cases/cast.zig delete mode 100644 test/cases/const_slice_child.zig delete mode 100644 test/cases/coroutine_await_struct.zig delete mode 100644 test/cases/coroutines.zig delete mode 100644 test/cases/defer.zig delete mode 100644 test/cases/enum.zig delete mode 100644 test/cases/enum_with_members.zig delete mode 100644 test/cases/error.zig delete mode 100644 test/cases/eval.zig delete mode 100644 test/cases/field_parent_ptr.zig delete mode 100644 test/cases/fn.zig delete mode 100644 test/cases/fn_in_struct_in_comptime.zig delete mode 100644 test/cases/for.zig delete mode 100644 test/cases/generics.zig delete mode 100644 test/cases/if.zig delete mode 100644 test/cases/import.zig delete mode 100644 test/cases/import/a_namespace.zig delete mode 100644 test/cases/incomplete_struct_param_tld.zig delete mode 100644 test/cases/inttoptr.zig delete mode 100644 test/cases/ir_block_deps.zig delete mode 100644 test/cases/math.zig delete mode 100644 test/cases/merge_error_sets.zig delete mode 100644 test/cases/misc.zig delete mode 100644 test/cases/namespace_depends_on_compile_var/a.zig delete mode 100644 test/cases/namespace_depends_on_compile_var/b.zig delete mode 100644 test/cases/namespace_depends_on_compile_var/index.zig delete mode 100644 test/cases/new_stack_call.zig delete mode 100644 test/cases/null.zig delete mode 100644 test/cases/optional.zig delete mode 100644 test/cases/pointers.zig delete mode 100644 test/cases/popcount.zig delete mode 100644 test/cases/ptrcast.zig delete mode 100644 test/cases/pub_enum/index.zig delete mode 100644 test/cases/pub_enum/other.zig delete mode 100644 test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig delete mode 100644 test/cases/reflection.zig delete mode 100644 test/cases/sizeof_and_typeof.zig delete mode 100644 test/cases/slice.zig delete mode 100644 test/cases/struct.zig delete mode 100644 test/cases/struct_contains_null_ptr_itself.zig delete mode 100644 test/cases/struct_contains_slice_of_itself.zig delete mode 100644 test/cases/switch.zig delete mode 100644 test/cases/switch_prong_err_enum.zig delete mode 100644 test/cases/switch_prong_implicit_cast.zig delete mode 100644 test/cases/syntax.zig delete mode 100644 test/cases/this.zig delete mode 100644 test/cases/truncate.zig delete mode 100644 test/cases/try.zig delete mode 100644 test/cases/type_info.zig delete mode 100644 test/cases/undefined.zig delete mode 100644 test/cases/underscore.zig delete mode 100644 test/cases/union.zig delete mode 100644 test/cases/var_args.zig delete mode 100644 test/cases/void.zig delete mode 100644 test/cases/while.zig delete mode 100644 test/cases/widening.zig create mode 100644 test/stage1/behavior.zig create mode 100644 test/stage1/behavior/align.zig create mode 100644 test/stage1/behavior/alignof.zig create mode 100644 test/stage1/behavior/array.zig create mode 100644 test/stage1/behavior/asm.zig create mode 100644 test/stage1/behavior/atomics.zig create mode 100644 test/stage1/behavior/bit_shifting.zig create mode 100644 test/stage1/behavior/bitcast.zig create mode 100644 test/stage1/behavior/bitreverse.zig create mode 100644 test/stage1/behavior/bool.zig create mode 100644 test/stage1/behavior/bswap.zig create mode 100644 test/stage1/behavior/bugs/1076.zig create mode 100644 test/stage1/behavior/bugs/1111.zig create mode 100644 test/stage1/behavior/bugs/1277.zig create mode 100644 test/stage1/behavior/bugs/1322.zig create mode 100644 test/stage1/behavior/bugs/1381.zig create mode 100644 test/stage1/behavior/bugs/1421.zig create mode 100644 test/stage1/behavior/bugs/1442.zig create mode 100644 test/stage1/behavior/bugs/1486.zig create mode 100644 test/stage1/behavior/bugs/394.zig create mode 100644 test/stage1/behavior/bugs/655.zig create mode 100644 test/stage1/behavior/bugs/655_other_file.zig create mode 100644 test/stage1/behavior/bugs/656.zig create mode 100644 test/stage1/behavior/bugs/726.zig create mode 100644 test/stage1/behavior/bugs/828.zig create mode 100644 test/stage1/behavior/bugs/920.zig create mode 100644 test/stage1/behavior/byval_arg_var.zig create mode 100644 test/stage1/behavior/cancel.zig create mode 100644 test/stage1/behavior/cast.zig create mode 100644 test/stage1/behavior/const_slice_child.zig create mode 100644 test/stage1/behavior/coroutine_await_struct.zig create mode 100644 test/stage1/behavior/coroutines.zig create mode 100644 test/stage1/behavior/defer.zig create mode 100644 test/stage1/behavior/enum.zig create mode 100644 test/stage1/behavior/enum_with_members.zig create mode 100644 test/stage1/behavior/error.zig create mode 100644 test/stage1/behavior/eval.zig create mode 100644 test/stage1/behavior/field_parent_ptr.zig create mode 100644 test/stage1/behavior/fn.zig create mode 100644 test/stage1/behavior/fn_in_struct_in_comptime.zig create mode 100644 test/stage1/behavior/for.zig create mode 100644 test/stage1/behavior/generics.zig create mode 100644 test/stage1/behavior/if.zig create mode 100644 test/stage1/behavior/import.zig create mode 100644 test/stage1/behavior/import/a_namespace.zig create mode 100644 test/stage1/behavior/incomplete_struct_param_tld.zig create mode 100644 test/stage1/behavior/inttoptr.zig create mode 100644 test/stage1/behavior/ir_block_deps.zig create mode 100644 test/stage1/behavior/math.zig create mode 100644 test/stage1/behavior/merge_error_sets.zig create mode 100644 test/stage1/behavior/misc.zig create mode 100644 test/stage1/behavior/namespace_depends_on_compile_var/a.zig create mode 100644 test/stage1/behavior/namespace_depends_on_compile_var/b.zig create mode 100644 test/stage1/behavior/namespace_depends_on_compile_var/index.zig create mode 100644 test/stage1/behavior/new_stack_call.zig create mode 100644 test/stage1/behavior/null.zig create mode 100644 test/stage1/behavior/optional.zig create mode 100644 test/stage1/behavior/pointers.zig create mode 100644 test/stage1/behavior/popcount.zig create mode 100644 test/stage1/behavior/ptrcast.zig create mode 100644 test/stage1/behavior/pub_enum/index.zig create mode 100644 test/stage1/behavior/pub_enum/other.zig create mode 100644 test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig create mode 100644 test/stage1/behavior/reflection.zig create mode 100644 test/stage1/behavior/sizeof_and_typeof.zig create mode 100644 test/stage1/behavior/slice.zig create mode 100644 test/stage1/behavior/struct.zig create mode 100644 test/stage1/behavior/struct_contains_null_ptr_itself.zig create mode 100644 test/stage1/behavior/struct_contains_slice_of_itself.zig create mode 100644 test/stage1/behavior/switch.zig create mode 100644 test/stage1/behavior/switch_prong_err_enum.zig create mode 100644 test/stage1/behavior/switch_prong_implicit_cast.zig create mode 100644 test/stage1/behavior/syntax.zig create mode 100644 test/stage1/behavior/this.zig create mode 100644 test/stage1/behavior/truncate.zig create mode 100644 test/stage1/behavior/try.zig create mode 100644 test/stage1/behavior/type_info.zig create mode 100644 test/stage1/behavior/undefined.zig create mode 100644 test/stage1/behavior/underscore.zig create mode 100644 test/stage1/behavior/union.zig create mode 100644 test/stage1/behavior/var_args.zig create mode 100644 test/stage1/behavior/void.zig create mode 100644 test/stage1/behavior/while.zig create mode 100644 test/stage1/behavior/widening.zig (limited to 'src/parser.cpp') diff --git a/build.zig b/build.zig index 16185eebf4..d99165a6de 100644 --- a/build.zig +++ b/build.zig @@ -104,7 +104,7 @@ pub fn build(b: *Builder) !void { } const modes = chosen_modes[0..chosen_mode_index]; - test_step.dependOn(tests.addPkgTests(b, test_filter, "test/behavior.zig", "behavior", "Run the behavior tests", modes)); + test_step.dependOn(tests.addPkgTests(b, test_filter, "test/stage1/behavior.zig", "behavior", "Run the behavior tests", modes)); test_step.dependOn(tests.addPkgTests(b, test_filter, "std/index.zig", "std", "Run the standard library tests", modes)); @@ -299,8 +299,7 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { } else if (exe.target.isFreeBSD()) { try addCxxKnownPath(b, ctx, exe, "libc++.a", null); exe.linkSystemLibrary("pthread"); - } - else if (exe.target.isDarwin()) { + } else if (exe.target.isDarwin()) { if (addCxxKnownPath(b, ctx, exe, "libgcc_eh.a", "")) { // Compiler is GCC. try addCxxKnownPath(b, ctx, exe, "libstdc++.a", null); diff --git a/doc/langref.html.in b/doc/langref.html.in index 6e03d3ec6d..909c0f5817 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4327,7 +4327,7 @@ fn gimmeTheBiggerInteger(a: u64, b: u64) u64 {

For example, if we were to introduce another function to the above snippet:

- {#code_begin|test_err|unable to evaluate constant expression#} + {#code_begin|test_err|values of type 'type' must be comptime known#} fn max(comptime T: type, a: T, b: T) T { return if (a > b) a else b; } @@ -5905,13 +5905,13 @@ fn add(a: i32, b: i32) i32 { return a + b; } This function is a low level intrinsic with no safety mechanisms. Most code should not use this function, instead using something like this:

-
{#syntax#}for (source[0...byte_count]) |b, i| dest[i] = b;{#endsyntax#}
+
{#syntax#}for (source[0..byte_count]) |b, i| dest[i] = b;{#endsyntax#}

The optimizer is intelligent enough to turn the above snippet into a memcpy.

There is also a standard library function for this:

{#syntax#}const mem = @import("std").mem;
-mem.copy(u8, dest[0...byte_count], source[0...byte_count]);{#endsyntax#}
+mem.copy(u8, dest[0..byte_count], source[0..byte_count]);{#endsyntax#} {#header_close#} {#header_open|@memset#} @@ -5923,7 +5923,7 @@ mem.copy(u8, dest[0...byte_count], source[0...byte_count]);{#endsyntax#} This function is a low level intrinsic with no safety mechanisms. Most code should not use this function, instead using something like this:

-
{#syntax#}for (dest[0...byte_count]) |*b| b.* = c;{#endsyntax#}
+
{#syntax#}for (dest[0..byte_count]) |*b| b.* = c;{#endsyntax#}

The optimizer is intelligent enough to turn the above snippet into a memset.

@@ -6592,9 +6592,10 @@ pub const TypeInfo = union(TypeId) { {#header_close#} {#header_open|@typeName#} -
{#syntax#}@typeName(T: type) []u8{#endsyntax#}
+
{#syntax#}@typeName(T: type) [N]u8{#endsyntax#}

- This function returns the string representation of a type. + This function returns the string representation of a type, as + an array. It is equivalent to a string literal of the type name.

{#header_close#} diff --git a/src/all_types.hpp b/src/all_types.hpp index 91b24e3110..4b134361a3 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -56,9 +56,6 @@ struct IrExecutable { size_t next_debug_id; size_t *backward_branch_count; size_t backward_branch_quota; - bool invalid; - bool is_inline; - bool is_generic_instantiation; ZigFn *fn_entry; Buf *c_import_buf; AstNode *source_node; @@ -78,6 +75,10 @@ struct IrExecutable { IrBasicBlock *coro_suspend_block; IrBasicBlock *coro_final_cleanup_block; ZigVar *coro_allocator_var; + + bool invalid; + bool is_inline; + bool is_generic_instantiation; }; enum OutType { @@ -90,6 +91,9 @@ enum OutType { enum ConstParentId { ConstParentIdNone, ConstParentIdStruct, + ConstParentIdErrUnionCode, + ConstParentIdErrUnionPayload, + ConstParentIdOptionalPayload, ConstParentIdArray, ConstParentIdUnion, ConstParentIdScalar, @@ -107,6 +111,15 @@ struct ConstParent { ConstExprValue *struct_val; size_t field_index; } p_struct; + struct { + ConstExprValue *err_union_val; + } p_err_union_code; + struct { + ConstExprValue *err_union_val; + } p_err_union_payload; + struct { + ConstExprValue *optional_val; + } p_optional_payload; struct { ConstExprValue *union_val; } p_union; @@ -118,13 +131,11 @@ struct ConstParent { struct ConstStructValue { ConstExprValue *fields; - ConstParent parent; }; struct ConstUnionValue { BigInt tag; ConstExprValue *payload; - ConstParent parent; }; enum ConstArraySpecial { @@ -138,7 +149,6 @@ struct ConstArrayValue { union { struct { ConstExprValue *elements; - ConstParent parent; } s_none; Buf *s_buf; } data; @@ -153,19 +163,29 @@ enum ConstPtrSpecial { ConstPtrSpecialBaseArray, // The pointer points to a field in an underlying struct. ConstPtrSpecialBaseStruct, + // The pointer points to the error set field of an error union + ConstPtrSpecialBaseErrorUnionCode, + // The pointer points to the payload field of an error union + ConstPtrSpecialBaseErrorUnionPayload, + // The pointer points to the payload field of an optional + ConstPtrSpecialBaseOptionalPayload, // This means that we did a compile-time pointer reinterpret and we cannot // understand the value of pointee at compile time. However, we will still // emit a binary with a compile time known address. // In this case index is the numeric address value. - // We also use this for null pointer. We need the data layout for ConstCastOnly == true - // types to be the same, so all optionals of pointer types use x_ptr - // instead of x_optional ConstPtrSpecialHardCodedAddr, // This means that the pointer represents memory of assigning to _. // That is, storing discards the data, and loading is invalid. ConstPtrSpecialDiscard, // This is actually a function. ConstPtrSpecialFunction, + // This means the pointer is null. This is only allowed when the type is ?*T. + // We use this instead of ConstPtrSpecialHardCodedAddr because often we check + // for that value to avoid doing comptime work. + // We need the data layout for ConstCastOnly == true + // types to be the same, so all optionals of pointer types use x_ptr + // instead of x_optional. + ConstPtrSpecialNull, }; enum ConstPtrMut { @@ -199,6 +219,15 @@ struct ConstPtrValue { ConstExprValue *struct_val; size_t field_index; } base_struct; + struct { + ConstExprValue *err_union_val; + } base_err_union_code; + struct { + ConstExprValue *err_union_val; + } base_err_union_payload; + struct { + ConstExprValue *optional_val; + } base_optional_payload; struct { uint64_t addr; } hard_coded_addr; @@ -209,7 +238,7 @@ struct ConstPtrValue { }; struct ConstErrValue { - ErrorTableEntry *err; + ConstExprValue *error_set; ConstExprValue *payload; }; @@ -265,6 +294,7 @@ struct ConstGlobalRefs { struct ConstExprValue { ZigType *type; ConstValSpecial special; + ConstParent parent; ConstGlobalRefs *global_refs; union { @@ -433,7 +463,7 @@ enum NodeType { NodeTypeArrayType, NodeTypeErrorType, NodeTypeIfErrorExpr, - NodeTypeTestExpr, + NodeTypeIfOptional, NodeTypeErrorSetDecl, NodeTypeCancel, NodeTypeResume, @@ -677,7 +707,7 @@ struct AstNodeUse { AstNode *expr; TldResolution resolution; - IrInstruction *value; + ConstExprValue *value; }; struct AstNodeIfBoolExpr { @@ -1610,7 +1640,7 @@ struct CodeGen { HashMap fn_type_table; HashMap error_table; HashMap generic_table; - HashMap memoized_fn_eval_table; + HashMap memoized_fn_eval_table; HashMap llvm_fn_table; HashMap exported_symbol_names; HashMap external_prototypes; @@ -1802,10 +1832,9 @@ enum VarLinkage { struct ZigVar { Buf name; - ConstExprValue *value; + ConstExprValue *const_value; + ZigType *var_type; LLVMValueRef value_ref; - bool src_is_const; - bool gen_is_const; IrInstruction *is_comptime; // which node is the declaration of the variable AstNode *decl_node; @@ -1815,17 +1844,21 @@ struct ZigVar { Scope *parent_scope; Scope *child_scope; LLVMValueRef param_value_ref; - bool shadowable; size_t mem_slot_index; IrExecutable *owner_exec; size_t ref_count; - VarLinkage linkage; - uint32_t align_bytes; // In an inline loop, multiple variables may be created, // In this case, a reference to a variable should follow // this pointer to the redefined variable. ZigVar *next_var; + + uint32_t align_bytes; + VarLinkage linkage; + + bool shadowable; + bool src_is_const; + bool gen_is_const; }; struct ErrorTableEntry { @@ -1891,10 +1924,11 @@ struct ScopeBlock { ZigList *incoming_values; ZigList *incoming_blocks; - bool safety_off; AstNode *safety_set_node; - bool fast_math_on; AstNode *fast_math_set_node; + + bool safety_off; + bool fast_math_on; }; // This scope is created from every defer expression. @@ -2030,8 +2064,19 @@ struct IrBasicBlock { IrInstruction *must_be_comptime_source_instr; }; +// These instructions are in transition to having "pass 1" instructions +// and "pass 2" instructions. The pass 1 instructions are suffixed with Src +// and pass 2 are suffixed with Gen. +// Once all instructions are separated in this way, they'll have different +// base types for better type safety. +// Src instructions are generated by ir_gen_* functions in ir.cpp from AST. +// ir_analyze_* functions consume Src instructions and produce Gen instructions. +// ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR. +// Src instructions do not have type information; Gen instructions do. enum IrInstructionId { IrInstructionIdInvalid, + IrInstructionIdDeclVarSrc, + IrInstructionIdDeclVarGen, IrInstructionIdBr, IrInstructionIdCondBr, IrInstructionIdSwitchBr, @@ -2040,7 +2085,6 @@ enum IrInstructionId { IrInstructionIdPhi, IrInstructionIdUnOp, IrInstructionIdBinOp, - IrInstructionIdDeclVar, IrInstructionIdLoadPtr, IrInstructionIdStorePtr, IrInstructionIdFieldPtr, @@ -2069,7 +2113,7 @@ enum IrInstructionId { IrInstructionIdAsm, IrInstructionIdSizeOf, IrInstructionIdTestNonNull, - IrInstructionIdUnwrapOptional, + IrInstructionIdOptionalUnwrapPtr, IrInstructionIdOptionalWrap, IrInstructionIdUnionTag, IrInstructionIdClz, @@ -2085,7 +2129,8 @@ enum IrInstructionId { IrInstructionIdCompileLog, IrInstructionIdErrName, IrInstructionIdEmbedFile, - IrInstructionIdCmpxchg, + IrInstructionIdCmpxchgSrc, + IrInstructionIdCmpxchgGen, IrInstructionIdFence, IrInstructionIdTruncate, IrInstructionIdIntCast, @@ -2114,7 +2159,8 @@ enum IrInstructionId { IrInstructionIdErrWrapPayload, IrInstructionIdFnProto, IrInstructionIdTestComptime, - IrInstructionIdPtrCast, + IrInstructionIdPtrCastSrc, + IrInstructionIdPtrCastGen, IrInstructionIdBitCast, IrInstructionIdWidenOrShorten, IrInstructionIdIntToPtr, @@ -2194,6 +2240,22 @@ struct IrInstruction { bool is_gen; }; +struct IrInstructionDeclVarSrc { + IrInstruction base; + + ZigVar *var; + IrInstruction *var_type; + IrInstruction *align_value; + IrInstruction *init_value; +}; + +struct IrInstructionDeclVarGen { + IrInstruction base; + + ZigVar *var; + IrInstruction *init_value; +}; + struct IrInstructionCondBr { IrInstruction base; @@ -2302,20 +2364,11 @@ struct IrInstructionBinOp { IrInstruction base; IrInstruction *op1; - IrBinOp op_id; IrInstruction *op2; + IrBinOp op_id; bool safety_check_on; }; -struct IrInstructionDeclVar { - IrInstruction base; - - ZigVar *var; - IrInstruction *var_type; - IrInstruction *align_value; - IrInstruction *init_value; -}; - struct IrInstructionLoadPtr { IrInstruction base; @@ -2335,7 +2388,6 @@ struct IrInstructionFieldPtr { IrInstruction *container_ptr; Buf *field_name_buffer; IrInstruction *field_name_expr; - bool is_const; }; struct IrInstructionStructFieldPtr { @@ -2378,13 +2430,13 @@ struct IrInstructionCall { ZigFn *fn_entry; size_t arg_count; IrInstruction **args; - bool is_comptime; LLVMValueRef tmp_ptr; - FnInline fn_inline; - bool is_async; IrInstruction *async_allocator; IrInstruction *new_stack; + FnInline fn_inline; + bool is_async; + bool is_comptime; }; struct IrInstructionConst { @@ -2527,9 +2579,9 @@ struct IrInstructionSliceType { IrInstruction base; IrInstruction *align_value; + IrInstruction *child_type; bool is_const; bool is_volatile; - IrInstruction *child_type; }; struct IrInstructionAsm { @@ -2557,10 +2609,12 @@ struct IrInstructionTestNonNull { IrInstruction *value; }; -struct IrInstructionUnwrapOptional { +// Takes a pointer to an optional value, returns a pointer +// to the payload. +struct IrInstructionOptionalUnwrapPtr { IrInstruction base; - IrInstruction *value; + IrInstruction *base_ptr; bool safety_check_on; }; @@ -2651,7 +2705,7 @@ struct IrInstructionEmbedFile { IrInstruction *name; }; -struct IrInstructionCmpxchg { +struct IrInstructionCmpxchgSrc { IrInstruction base; IrInstruction *type_value; @@ -2661,14 +2715,19 @@ struct IrInstructionCmpxchg { IrInstruction *success_order_value; IrInstruction *failure_order_value; - // if this instruction gets to runtime then we know these values: - ZigType *type; - AtomicOrder success_order; - AtomicOrder failure_order; - bool is_weak; +}; +struct IrInstructionCmpxchgGen { + IrInstruction base; + + IrInstruction *ptr; + IrInstruction *cmp_value; + IrInstruction *new_value; LLVMValueRef tmp_ptr; + AtomicOrder success_order; + AtomicOrder failure_order; + bool is_weak; }; struct IrInstructionFence { @@ -2851,7 +2910,7 @@ struct IrInstructionTestErr { struct IrInstructionUnwrapErrCode { IrInstruction base; - IrInstruction *value; + IrInstruction *err_union; }; struct IrInstructionUnwrapErrPayload { @@ -2899,13 +2958,19 @@ struct IrInstructionTestComptime { IrInstruction *value; }; -struct IrInstructionPtrCast { +struct IrInstructionPtrCastSrc { IrInstruction base; IrInstruction *dest_type; IrInstruction *ptr; }; +struct IrInstructionPtrCastGen { + IrInstruction base; + + IrInstruction *ptr; +}; + struct IrInstructionBitCast { IrInstruction base; diff --git a/src/analyze.cpp b/src/analyze.cpp index 15370983fc..194888068c 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -570,7 +570,7 @@ ZigType *get_optional_type(CodeGen *g, ZigType *child_type) { if (child_type->zero_bits) { entry->type_ref = LLVMInt1Type(); entry->di_type = g->builtin_types.entry_bool->di_type; - } else if (type_is_codegen_pointer(child_type)) { + } else if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) { assert(child_type->di_type); // this is an optimization but also is necessary for calling C // functions where all pointers are maybe pointers @@ -1278,7 +1278,9 @@ ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind return entry; } -static IrInstruction *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, Buf *type_name) { +static ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, + Buf *type_name) +{ size_t backward_branch_count = 0; return ir_eval_const_value(g, scope, node, type_entry, &backward_branch_count, default_backward_branch_quota, @@ -1286,12 +1288,12 @@ static IrInstruction *analyze_const_value(CodeGen *g, Scope *scope, AstNode *nod } ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) { - IrInstruction *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type, nullptr); - if (result->value.type->id == ZigTypeIdInvalid) + ConstExprValue *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type, nullptr); + if (type_is_invalid(result->type)) return g->builtin_types.entry_invalid; - assert(result->value.special != ConstValSpecialRuntime); - return result->value.data.x_type; + assert(result->special != ConstValSpecialRuntime); + return result->data.x_type; } ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) { @@ -1342,11 +1344,11 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou } static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_t *result) { - IrInstruction *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g), nullptr); - if (type_is_invalid(align_result->value.type)) + ConstExprValue *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g), nullptr); + if (type_is_invalid(align_result->type)) return false; - uint32_t align_bytes = bigint_as_unsigned(&align_result->value.data.x_bigint); + uint32_t align_bytes = bigint_as_unsigned(&align_result->data.x_bigint); if (align_bytes == 0) { add_node_error(g, node, buf_sprintf("alignment must be >= 1")); return false; @@ -1364,12 +1366,12 @@ static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf ** ZigType *ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, PtrLenUnknown, 0, 0, 0); ZigType *str_type = get_slice_type(g, ptr_type); - IrInstruction *instr = analyze_const_value(g, scope, node, str_type, nullptr); - if (type_is_invalid(instr->value.type)) + ConstExprValue *result_val = analyze_const_value(g, scope, node, str_type, nullptr); + if (type_is_invalid(result_val->type)) return false; - ConstExprValue *ptr_field = &instr->value.data.x_struct.fields[slice_ptr_index]; - ConstExprValue *len_field = &instr->value.data.x_struct.fields[slice_len_index]; + ConstExprValue *ptr_field = &result_val->data.x_struct.fields[slice_ptr_index]; + ConstExprValue *len_field = &result_val->data.x_struct.fields[slice_len_index]; assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray); ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val; @@ -2504,20 +2506,20 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { // In this first pass we resolve explicit tag values. // In a second pass we will fill in the unspecified ones. if (tag_value != nullptr) { - IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); - if (result_inst->value.type->id == ZigTypeIdInvalid) { + ConstExprValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); + if (type_is_invalid(result->type)) { enum_type->data.enumeration.is_invalid = true; continue; } - assert(result_inst->value.special != ConstValSpecialRuntime); - assert(result_inst->value.type->id == ZigTypeIdInt || - result_inst->value.type->id == ZigTypeIdComptimeInt); - auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value); + assert(result->special != ConstValSpecialRuntime); + assert(result->type->id == ZigTypeIdInt || + result->type->id == ZigTypeIdComptimeInt); + auto entry = occupied_tag_values.put_unique(result->data.x_bigint, tag_value); if (entry == nullptr) { - bigint_init_bigint(&type_enum_field->value, &result_inst->value.data.x_bigint); + bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint); } else { Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10); + bigint_append_buf(val_buf, &result->data.x_bigint, 10); ErrorMsg *msg = add_node_error(g, tag_value, buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); @@ -2944,19 +2946,19 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { // In a second pass we will fill in the unspecified ones. if (tag_value != nullptr) { ZigType *tag_int_type = tag_type->data.enumeration.tag_int_type; - IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); - if (result_inst->value.type->id == ZigTypeIdInvalid) { + ConstExprValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); + if (type_is_invalid(result->type)) { union_type->data.unionation.is_invalid = true; continue; } - assert(result_inst->value.special != ConstValSpecialRuntime); - assert(result_inst->value.type->id == ZigTypeIdInt); - auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value); + assert(result->special != ConstValSpecialRuntime); + assert(result->type->id == ZigTypeIdInt); + auto entry = occupied_tag_values.put_unique(result->data.x_bigint, tag_value); if (entry == nullptr) { - bigint_init_bigint(&union_field->enum_field->value, &result_inst->value.data.x_bigint); + bigint_init_bigint(&union_field->enum_field->value, &result->data.x_bigint); } else { Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10); + bigint_append_buf(val_buf, &result->data.x_bigint, 10); ErrorMsg *msg = add_node_error(g, tag_value, buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); @@ -3419,7 +3421,8 @@ void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value) { resolve_top_level_decl(g, tld, false, tld->source_node); assert(tld->id == TldIdVar); TldVar *tld_var = (TldVar *)tld; - tld_var->var->value = value; + tld_var->var->const_value = value; + tld_var->var->var_type = value->type; tld_var->var->align_bytes = get_abi_alignment(g, value->type); } @@ -3513,7 +3516,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeArrayType: case NodeTypeErrorType: case NodeTypeIfErrorExpr: - case NodeTypeTestExpr: + case NodeTypeIfOptional: case NodeTypeErrorSetDecl: case NodeTypeCancel: case NodeTypeResume: @@ -3582,13 +3585,15 @@ ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry // Set name to nullptr to make the variable anonymous (not visible to programmer). // TODO merge with definition of add_local_var in ir.cpp ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name, - bool is_const, ConstExprValue *value, Tld *src_tld) + bool is_const, ConstExprValue *const_value, Tld *src_tld, ZigType *var_type) { Error err; - assert(value); + assert(const_value != nullptr); + assert(var_type != nullptr); ZigVar *variable_entry = allocate(1); - variable_entry->value = value; + variable_entry->const_value = const_value; + variable_entry->var_type = var_type; variable_entry->parent_scope = parent_scope; variable_entry->shadowable = false; variable_entry->mem_slot_index = SIZE_MAX; @@ -3597,23 +3602,23 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf assert(name); buf_init_from_buf(&variable_entry->name, name); - if ((err = type_resolve(g, value->type, ResolveStatusAlignmentKnown))) { - variable_entry->value->type = g->builtin_types.entry_invalid; + if ((err = type_resolve(g, var_type, ResolveStatusAlignmentKnown))) { + variable_entry->var_type = g->builtin_types.entry_invalid; } else { - variable_entry->align_bytes = get_abi_alignment(g, value->type); + variable_entry->align_bytes = get_abi_alignment(g, var_type); ZigVar *existing_var = find_variable(g, parent_scope, name, nullptr); if (existing_var && !existing_var->shadowable) { ErrorMsg *msg = add_node_error(g, source_node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); - variable_entry->value->type = g->builtin_types.entry_invalid; + variable_entry->var_type = g->builtin_types.entry_invalid; } else { ZigType *type; if (get_primitive_type(g, name, &type) != ErrorPrimitiveTypeNotFound) { add_node_error(g, source_node, buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name))); - variable_entry->value->type = g->builtin_types.entry_invalid; + variable_entry->var_type = g->builtin_types.entry_invalid; } else { Scope *search_scope = nullptr; if (src_tld == nullptr) { @@ -3627,7 +3632,7 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf ErrorMsg *msg = add_node_error(g, source_node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); add_error_note(g, msg, tld->source_node, buf_sprintf("previous definition is here")); - variable_entry->value->type = g->builtin_types.entry_invalid; + variable_entry->var_type = g->builtin_types.entry_invalid; } } } @@ -3677,7 +3682,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { linkage = VarLinkageInternal; } - IrInstruction *init_value = nullptr; + ConstExprValue *init_value = nullptr; // TODO more validation for types that can't be used for export/extern variables ZigType *implicit_type = nullptr; @@ -3686,7 +3691,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { } else if (var_decl->expr) { init_value = analyze_const_value(g, tld_var->base.parent_scope, var_decl->expr, explicit_type, var_decl->symbol); assert(init_value); - implicit_type = init_value->value.type; + implicit_type = init_value->type; if (implicit_type->id == ZigTypeIdUnreachable) { add_node_error(g, source_node, buf_sprintf("variable initialization is unreachable")); @@ -3704,7 +3709,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { add_node_error(g, source_node, buf_sprintf("variable of type 'type' must be constant")); implicit_type = g->builtin_types.entry_invalid; } - assert(implicit_type->id == ZigTypeIdInvalid || init_value->value.special != ConstValSpecialRuntime); + assert(implicit_type->id == ZigTypeIdInvalid || init_value->special != ConstValSpecialRuntime); } else if (linkage != VarLinkageExternal) { add_node_error(g, source_node, buf_sprintf("variables must be initialized")); implicit_type = g->builtin_types.entry_invalid; @@ -3713,19 +3718,19 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { ZigType *type = explicit_type ? explicit_type : implicit_type; assert(type != nullptr); // should have been caught by the parser - ConstExprValue *init_val = init_value ? &init_value->value : create_const_runtime(type); + ConstExprValue *init_val = (init_value != nullptr) ? init_value : create_const_runtime(type); tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol, - is_const, init_val, &tld_var->base); + is_const, init_val, &tld_var->base, type); tld_var->var->linkage = linkage; if (implicit_type != nullptr && type_is_invalid(implicit_type)) { - tld_var->var->value->type = g->builtin_types.entry_invalid; + tld_var->var->var_type = g->builtin_types.entry_invalid; } if (var_decl->align_expr != nullptr) { if (!analyze_const_align(g, tld_var->base.parent_scope, var_decl->align_expr, &tld_var->var->align_bytes)) { - tld_var->var->value->type = g->builtin_types.entry_invalid; + tld_var->var->var_type = g->builtin_types.entry_invalid; } } @@ -4090,7 +4095,7 @@ static void define_local_param_variables(CodeGen *g, ZigFn *fn_table_entry) { } ZigVar *var = add_variable(g, param_decl_node, fn_table_entry->child_scope, - param_name, true, create_const_runtime(param_type), nullptr); + param_name, true, create_const_runtime(param_type), nullptr, param_type); var->src_arg_index = i; fn_table_entry->child_scope = var->child_scope; var->shadowable = var->shadowable || is_var_args; @@ -4228,18 +4233,17 @@ static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode * preview_use_decl(g, src_use_node); } - IrInstruction *use_target_value = src_use_node->data.use.value; - if (use_target_value->value.type->id == ZigTypeIdInvalid) { + ConstExprValue *use_target_value = src_use_node->data.use.value; + if (type_is_invalid(use_target_value->type)) { dst_use_node->owner->any_imports_failed = true; return; } dst_use_node->data.use.resolution = TldResolutionOk; - ConstExprValue *const_val = &use_target_value->value; - assert(const_val->special != ConstValSpecialRuntime); + assert(use_target_value->special != ConstValSpecialRuntime); - ImportTableEntry *target_import = const_val->data.x_import; + ImportTableEntry *target_import = use_target_value->data.x_import; assert(target_import); if (target_import->any_imports_failed) { @@ -4302,10 +4306,10 @@ void preview_use_decl(CodeGen *g, AstNode *node) { } node->data.use.resolution = TldResolutionResolving; - IrInstruction *result = analyze_const_value(g, &node->owner->decls_scope->base, + ConstExprValue *result = analyze_const_value(g, &node->owner->decls_scope->base, node->data.use.expr, g->builtin_types.entry_namespace, nullptr); - if (result->value.type->id == ZigTypeIdInvalid) + if (type_is_invalid(result->type)) node->owner->any_imports_failed = true; node->data.use.value = result; @@ -4486,7 +4490,8 @@ bool handle_is_ptr(ZigType *type_entry) { return type_has_bits(type_entry->data.error_union.payload_type); case ZigTypeIdOptional: return type_has_bits(type_entry->data.maybe.child_type) && - !type_is_codegen_pointer(type_entry->data.maybe.child_type); + !type_is_codegen_pointer(type_entry->data.maybe.child_type) && + type_entry->data.maybe.child_type->id != ZigTypeIdErrorSet; case ZigTypeIdUnion: assert(type_entry->data.unionation.zero_bits_known); if (type_entry->data.unionation.gen_field_count == 0) @@ -4732,6 +4737,11 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) { return true; } +static uint32_t hash_const_val_error_set(ConstExprValue *const_val) { + assert(const_val->data.x_err_set != nullptr); + return const_val->data.x_err_set->value ^ 2630160122; +} + static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { uint32_t hash_val = 0; switch (const_val->data.x_ptr.mut) { @@ -4763,6 +4773,18 @@ static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val); hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index); return hash_val; + case ConstPtrSpecialBaseErrorUnionCode: + hash_val += (uint32_t)2994743799; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_code.err_union_val); + return hash_val; + case ConstPtrSpecialBaseErrorUnionPayload: + hash_val += (uint32_t)3456080131; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_payload.err_union_val); + return hash_val; + case ConstPtrSpecialBaseOptionalPayload: + hash_val += (uint32_t)3163140517; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_optional_payload.optional_val); + return hash_val; case ConstPtrSpecialHardCodedAddr: hash_val += (uint32_t)4048518294; hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr); @@ -4774,6 +4796,9 @@ static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { hash_val += (uint32_t)2590901619; hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry); return hash_val; + case ConstPtrSpecialNull: + hash_val += (uint32_t)1486246455; + return hash_val; } zig_unreachable(); } @@ -4872,7 +4897,9 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { return 2709806591; case ZigTypeIdOptional: if (get_codegen_ptr_type(const_val->type) != nullptr) { - return hash_const_val(const_val) * 1992916303; + return hash_const_val_ptr(const_val) * 1992916303; + } else if (const_val->type->data.maybe.child_type->id == ZigTypeIdErrorSet) { + return hash_const_val_error_set(const_val) * 3147031929; } else { if (const_val->data.x_optional) { return hash_const_val(const_val->data.x_optional) * 1992916303; @@ -4884,8 +4911,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { // TODO better hashing algorithm return 3415065496; case ZigTypeIdErrorSet: - assert(const_val->data.x_err_set != nullptr); - return const_val->data.x_err_set->value ^ 2630160122; + return hash_const_val_error_set(const_val); case ZigTypeIdNamespace: return hash_ptr(const_val->data.x_import); case ZigTypeIdBoundFn: @@ -4987,7 +5013,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { return can_mutate_comptime_var_state(value->data.x_optional); case ZigTypeIdErrorUnion: - if (value->data.x_err_union.err != nullptr) + if (value->data.x_err_union.error_set->data.x_err_set != nullptr) return false; assert(value->data.x_err_union.payload != nullptr); return can_mutate_comptime_var_state(value->data.x_err_union.payload); @@ -5048,9 +5074,9 @@ bool fn_eval_cacheable(Scope *scope, ZigType *return_type) { while (scope) { if (scope->id == ScopeIdVarDecl) { ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; - if (type_is_invalid(var_scope->var->value->type)) + if (type_is_invalid(var_scope->var->var_type)) return false; - if (can_mutate_comptime_var_state(var_scope->var->value)) + if (can_mutate_comptime_var_state(var_scope->var->const_value)) return false; } else if (scope->id == ScopeIdFnDef) { return true; @@ -5068,7 +5094,7 @@ uint32_t fn_eval_hash(Scope* scope) { while (scope) { if (scope->id == ScopeIdVarDecl) { ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; - result += hash_const_val(var_scope->var->value); + result += hash_const_val(var_scope->var->const_value); } else if (scope->id == ScopeIdFnDef) { ScopeFnDef *fn_scope = (ScopeFnDef *)scope; result += hash_ptr(fn_scope->fn_entry); @@ -5092,10 +5118,16 @@ bool fn_eval_eql(Scope *a, Scope *b) { if (a->id == ScopeIdVarDecl) { ScopeVarDecl *a_var_scope = (ScopeVarDecl *)a; ScopeVarDecl *b_var_scope = (ScopeVarDecl *)b; - if (a_var_scope->var->value->type != b_var_scope->var->value->type) - return false; - if (!const_values_equal(a->codegen, a_var_scope->var->value, b_var_scope->var->value)) + if (a_var_scope->var->var_type != b_var_scope->var->var_type) return false; + if (a_var_scope->var->var_type == a_var_scope->var->const_value->type && + b_var_scope->var->var_type == b_var_scope->var->const_value->type) + { + if (!const_values_equal(a->codegen, a_var_scope->var->const_value, b_var_scope->var->const_value)) + return false; + } else { + zig_panic("TODO comptime ptr reinterpret for fn_eval_eql"); + } } else if (a->id == ScopeIdFnDef) { ScopeFnDef *a_fn_scope = (ScopeFnDef *)a; ScopeFnDef *b_fn_scope = (ScopeFnDef *)b; @@ -5113,6 +5145,7 @@ bool fn_eval_eql(Scope *a, Scope *b) { return false; } +// Whether the type has bits at runtime. bool type_has_bits(ZigType *type_entry) { assert(type_entry); assert(!type_is_invalid(type_entry)); @@ -5120,6 +5153,65 @@ bool type_has_bits(ZigType *type_entry) { return !type_entry->zero_bits; } +// Whether you can infer the value based solely on the type. +OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { + assert(type_entry != nullptr); + Error err; + if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) + return OnePossibleValueInvalid; + switch (type_entry->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdOpaque: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdMetaType: + case ZigTypeIdNamespace: + case ZigTypeIdBoundFn: + case ZigTypeIdArgTuple: + case ZigTypeIdOptional: + case ZigTypeIdFn: + case ZigTypeIdBool: + case ZigTypeIdFloat: + case ZigTypeIdPromise: + case ZigTypeIdErrorUnion: + return OnePossibleValueNo; + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdVoid: + case ZigTypeIdUnreachable: + return OnePossibleValueYes; + case ZigTypeIdArray: + if (type_entry->data.array.len == 0) + return OnePossibleValueYes; + return type_has_one_possible_value(g, type_entry->data.array.child_type); + case ZigTypeIdStruct: + for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { + TypeStructField *field = &type_entry->data.structure.fields[i]; + switch (type_has_one_possible_value(g, field->type_entry)) { + case OnePossibleValueInvalid: + return OnePossibleValueInvalid; + case OnePossibleValueNo: + return OnePossibleValueNo; + case OnePossibleValueYes: + continue; + } + } + return OnePossibleValueYes; + case ZigTypeIdErrorSet: + case ZigTypeIdEnum: + case ZigTypeIdInt: + return type_has_bits(type_entry) ? OnePossibleValueNo : OnePossibleValueYes; + case ZigTypeIdPointer: + return type_has_one_possible_value(g, type_entry->data.pointer.child_type); + case ZigTypeIdUnion: + if (type_entry->data.unionation.src_field_count > 1) + return OnePossibleValueNo; + return type_has_one_possible_value(g, type_entry->data.unionation.fields[0].type_entry); + } + zig_unreachable(); +} + ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry) { Error err; if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) @@ -5574,6 +5666,33 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index) return false; return true; + case ConstPtrSpecialBaseErrorUnionCode: + if (a->data.x_ptr.data.base_err_union_code.err_union_val != + b->data.x_ptr.data.base_err_union_code.err_union_val && + a->data.x_ptr.data.base_err_union_code.err_union_val->global_refs != + b->data.x_ptr.data.base_err_union_code.err_union_val->global_refs) + { + return false; + } + return true; + case ConstPtrSpecialBaseErrorUnionPayload: + if (a->data.x_ptr.data.base_err_union_payload.err_union_val != + b->data.x_ptr.data.base_err_union_payload.err_union_val && + a->data.x_ptr.data.base_err_union_payload.err_union_val->global_refs != + b->data.x_ptr.data.base_err_union_payload.err_union_val->global_refs) + { + return false; + } + return true; + case ConstPtrSpecialBaseOptionalPayload: + if (a->data.x_ptr.data.base_optional_payload.optional_val != + b->data.x_ptr.data.base_optional_payload.optional_val && + a->data.x_ptr.data.base_optional_payload.optional_val->global_refs != + b->data.x_ptr.data.base_optional_payload.optional_val->global_refs) + { + return false; + } + return true; case ConstPtrSpecialHardCodedAddr: if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr) return false; @@ -5582,6 +5701,8 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { return true; case ConstPtrSpecialFunction: return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry; + case ConstPtrSpecialNull: + return true; } zig_unreachable(); } @@ -5750,7 +5871,7 @@ void eval_min_max_value(CodeGen *g, ZigType *type_entry, ConstExprValue *const_v } } -void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigType *type_entry) { +static void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigType *type_entry) { assert(type_entry->id == ZigTypeIdPointer); if (type_entry->data.pointer.child_type->id == ZigTypeIdOpaque) { @@ -5763,6 +5884,9 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigTy zig_unreachable(); case ConstPtrSpecialRef: case ConstPtrSpecialBaseStruct: + case ConstPtrSpecialBaseErrorUnionCode: + case ConstPtrSpecialBaseErrorUnionPayload: + case ConstPtrSpecialBaseOptionalPayload: buf_appendf(buf, "*"); // TODO we need a source node for const_ptr_pointee because it can generate compile errors render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr)); @@ -5790,10 +5914,21 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigTy buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name)); return; } + case ConstPtrSpecialNull: + buf_append_str(buf, "null"); + return; } zig_unreachable(); } +static void render_const_val_err_set(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigType *type_entry) { + if (const_val->data.x_err_set == nullptr) { + buf_append_str(buf, "null"); + } else { + buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name)); + } +} + void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { switch (const_val->special) { case ConstValSpecialRuntime: @@ -5921,6 +6056,8 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { { if (get_codegen_ptr_type(const_val->type) != nullptr) return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type); + if (type_entry->data.maybe.child_type->id == ZigTypeIdErrorSet) + return render_const_val_err_set(g, buf, const_val, type_entry->data.maybe.child_type); if (const_val->data.x_optional) { render_const_value(g, buf, const_val->data.x_optional); } else { @@ -5958,11 +6095,12 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { case ZigTypeIdErrorUnion: { buf_appendf(buf, "%s(", buf_ptr(&type_entry->name)); - if (const_val->data.x_err_union.err == nullptr) { + ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set; + if (err_set == nullptr) { render_const_value(g, buf, const_val->data.x_err_union.payload); } else { buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->data.error_union.err_set_type->name), - buf_ptr(&const_val->data.x_err_union.err->name)); + buf_ptr(&err_set->name)); } buf_appendf(buf, ")"); return; @@ -5977,10 +6115,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { return; } case ZigTypeIdErrorSet: - { - buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name)); - return; - } + return render_const_val_err_set(g, buf, const_val, type_entry); case ZigTypeIdArgTuple: { buf_appendf(buf, "(args value)"); @@ -6172,6 +6307,10 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) { // Canonicalize the array value as ConstArraySpecialNone void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { assert(const_val->type->id == ZigTypeIdArray); + if (const_val->special == ConstValSpecialUndef) { + const_val->special = ConstValSpecialStatic; + const_val->data.x_array.special = ConstArraySpecialUndef; + } switch (const_val->data.x_array.special) { case ConstArraySpecialNone: return; @@ -6215,17 +6354,7 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { } ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) { - assert(value->type); - ZigType *type_entry = value->type; - if (type_entry->id == ZigTypeIdArray) { - expand_undef_array(g, value); - return &value->data.x_array.data.s_none.parent; - } else if (type_entry->id == ZigTypeIdStruct) { - return &value->data.x_struct.parent; - } else if (type_entry->id == ZigTypeIdUnion) { - return &value->data.x_union.parent; - } - return nullptr; + return &value->parent; } static const ZigTypeId all_type_ids[] = { @@ -6453,7 +6582,7 @@ ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { resolve_top_level_decl(codegen, tld, false, nullptr); assert(tld->id == TldIdVar); TldVar *tld_var = (TldVar *)tld; - ConstExprValue *var_value = tld_var->var->value; + ConstExprValue *var_value = tld_var->var->const_value; assert(var_value != nullptr); return var_value; } diff --git a/src/analyze.hpp b/src/analyze.hpp index efbc065a63..1bac15ebcc 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -81,7 +81,7 @@ ZigFn *scope_fn_entry(Scope *scope); ImportTableEntry *get_scope_import(Scope *scope); void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node, Scope *parent_scope); ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name, - bool is_const, ConstExprValue *init_value, Tld *src_tld); + bool is_const, ConstExprValue *init_value, Tld *src_tld, ZigType *var_type); ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node); ZigFn *create_fn(CodeGen *g, AstNode *proto_node); ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value); @@ -222,6 +222,13 @@ enum ReqCompTime { }; ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry); +enum OnePossibleValue { + OnePossibleValueInvalid, + OnePossibleValueNo, + OnePossibleValueYes, +}; +OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry); + Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, ConstExprValue *const_val, ZigType *wanted_type); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index f7eb646e16..994ba5f5b1 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -233,8 +233,8 @@ static const char *node_type_str(NodeType node_type) { return "ErrorType"; case NodeTypeIfErrorExpr: return "IfErrorExpr"; - case NodeTypeTestExpr: - return "TestExpr"; + case NodeTypeIfOptional: + return "IfOptional"; case NodeTypeErrorSetDecl: return "ErrorSetDecl"; case NodeTypeCancel: @@ -387,7 +387,7 @@ static bool statement_terminates_without_semicolon(AstNode *node) { if (node->data.if_err_expr.else_node) return statement_terminates_without_semicolon(node->data.if_err_expr.else_node); return node->data.if_err_expr.then_node->type == NodeTypeBlock; - case NodeTypeTestExpr: + case NodeTypeIfOptional: if (node->data.test_expr.else_node) return statement_terminates_without_semicolon(node->data.test_expr.else_node); return node->data.test_expr.then_node->type == NodeTypeBlock; @@ -974,7 +974,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { } break; } - case NodeTypeTestExpr: + case NodeTypeIfOptional: { fprintf(ar->f, "if ("); render_node_grouped(ar, node->data.test_expr.target_node); diff --git a/src/codegen.cpp b/src/codegen.cpp index 47f2aa103f..2f360735dd 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -313,6 +313,8 @@ static void render_const_val(CodeGen *g, ConstExprValue *const_val, const char * static void render_const_val_global(CodeGen *g, ConstExprValue *const_val, const char *name); static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name); static void generate_error_name_table(CodeGen *g); +static bool value_is_all_undef(ConstExprValue *const_val); +static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr); static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) { unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name)); @@ -461,6 +463,21 @@ static void maybe_import_dll(CodeGen *g, LLVMValueRef global_value, GlobalLinkag } } +static bool cc_want_sret_attr(CallingConvention cc) { + switch (cc) { + case CallingConventionNaked: + zig_unreachable(); + case CallingConventionC: + case CallingConventionCold: + case CallingConventionStdcall: + return true; + case CallingConventionAsync: + case CallingConventionUnspecified: + return false; + } + zig_unreachable(); +} + static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { if (fn_table_entry->llvm_value) return fn_table_entry->llvm_value; @@ -598,9 +615,9 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { } else if (type_is_codegen_pointer(return_type)) { addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull"); } else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) { - addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull"); - if (cc == CallingConventionC) { + addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); + if (cc_want_sret_attr(cc)) { addLLVMArgAttr(fn_table_entry->llvm_value, 0, "noalias"); } init_gen_i = 1; @@ -2200,10 +2217,10 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { assert(variable); assert(variable->value_ref); - if (!handle_is_ptr(variable->value->type)) { + if (!handle_is_ptr(variable->var_type)) { clear_debug_source_node(g); - gen_store_untyped(g, LLVMGetParam(llvm_fn, (unsigned)variable->gen_arg_index), variable->value_ref, - variable->align_bytes, false); + gen_store_untyped(g, LLVMGetParam(llvm_fn, (unsigned)variable->gen_arg_index), + variable->value_ref, variable->align_bytes, false); } if (variable->decl_node) { @@ -2961,7 +2978,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, } static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable, - IrInstructionPtrCast *instruction) + IrInstructionPtrCastGen *instruction) { ZigType *wanted_type = instruction->base.value.type; if (!type_has_bits(wanted_type)) { @@ -3149,11 +3166,11 @@ static LLVMValueRef ir_render_bool_not(CodeGen *g, IrExecutable *executable, IrI } static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, - IrInstructionDeclVar *decl_var_instruction) + IrInstructionDeclVarGen *decl_var_instruction) { ZigVar *var = decl_var_instruction->var; - if (!type_has_bits(var->value->type)) + if (!type_has_bits(var->var_type)) return nullptr; if (var->ref_count == 0 && g->build_mode != BuildModeDebug) @@ -3161,34 +3178,16 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, IrInstruction *init_value = decl_var_instruction->init_value; - bool have_init_expr = false; - - ConstExprValue *const_val = &init_value->value; - if (const_val->special == ConstValSpecialRuntime || const_val->special == ConstValSpecialStatic) - have_init_expr = true; + bool have_init_expr = !value_is_all_undef(&init_value->value); if (have_init_expr) { - assert(var->value->type == init_value->value.type); - ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false, + ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->var_type, false, false, PtrLenSingle, var->align_bytes, 0, 0); LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value); gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val); - } else { - bool want_safe = ir_want_runtime_safety(g, &decl_var_instruction->base); - if (want_safe) { - ZigType *usize = g->builtin_types.entry_usize; - uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, var->value->type->type_ref); - assert(size_bytes > 0); - - assert(var->align_bytes > 0); - - // memset uninitialized memory to 0xa - LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); - LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); - LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, var->value_ref, ptr_u8, ""); - LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false); - ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, var->align_bytes, false); - } + } else if (ir_want_runtime_safety(g, &decl_var_instruction->base)) { + uint32_t align_bytes = (var->align_bytes == 0) ? get_abi_alignment(g, var->var_type) : var->align_bytes; + gen_undef_init(g, align_bytes, var->var_type, var->value_ref); } gen_var_debug_decl(g, var); @@ -3225,21 +3224,75 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, ""); } -static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) { - LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); - LLVMValueRef value = ir_llvm_value(g, instruction->value); +static bool value_is_all_undef(ConstExprValue *const_val) { + switch (const_val->special) { + case ConstValSpecialRuntime: + return false; + case ConstValSpecialUndef: + return true; + case ConstValSpecialStatic: + if (const_val->type->id == ZigTypeIdStruct) { + for (size_t i = 0; i < const_val->type->data.structure.src_field_count; i += 1) { + if (!value_is_all_undef(&const_val->data.x_struct.fields[i])) + return false; + } + return true; + } else if (const_val->type->id == ZigTypeIdArray) { + switch (const_val->data.x_array.special) { + case ConstArraySpecialUndef: + return true; + case ConstArraySpecialBuf: + return false; + case ConstArraySpecialNone: + for (size_t i = 0; i < const_val->type->data.array.len; i += 1) { + if (!value_is_all_undef(&const_val->data.x_array.data.s_none.elements[i])) + return false; + } + return true; + } + zig_unreachable(); + } else { + return false; + } + } + zig_unreachable(); +} - assert(instruction->ptr->value.type->id == ZigTypeIdPointer); - ZigType *ptr_type = instruction->ptr->value.type; +static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) { + assert(type_has_bits(value_type)); + uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, value_type->type_ref); + assert(size_bytes > 0); + assert(ptr_align_bytes > 0); + // memset uninitialized memory to 0xaa + LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); + LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); + LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, ptr, ptr_u8, ""); + ZigType *usize = g->builtin_types.entry_usize; + LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false); + ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false); +} - gen_assign_raw(g, ptr, ptr_type, value); +static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) { + ZigType *ptr_type = instruction->ptr->value.type; + assert(ptr_type->id == ZigTypeIdPointer); + if (!type_has_bits(ptr_type)) + return nullptr; + bool have_init_expr = !value_is_all_undef(&instruction->value->value); + if (have_init_expr) { + LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); + LLVMValueRef value = ir_llvm_value(g, instruction->value); + gen_assign_raw(g, ptr, ptr_type, value); + } else if (ir_want_runtime_safety(g, &instruction->base)) { + gen_undef_init(g, get_ptr_align(g, ptr_type), instruction->value->value.type, + ir_llvm_value(g, instruction->ptr)); + } return nullptr; } static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrInstructionVarPtr *instruction) { ZigVar *var = instruction->var; - if (type_has_bits(var->value->type)) { + if (type_has_bits(var->var_type)) { assert(var->value_ref); return var->value_ref; } else { @@ -3553,7 +3606,8 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab LLVMPositionBuilderAtEnd(g->builder, ok_block); } - LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_union_index, ""); + LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, + union_type->data.unionation.gen_union_index, ""); LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, ""); return bitcasted_union_field_ptr; } @@ -3715,8 +3769,8 @@ static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueR if (child_type->zero_bits) { return maybe_handle; } else { - bool maybe_is_ptr = type_is_codegen_pointer(child_type); - if (maybe_is_ptr) { + bool is_scalar = type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet; + if (is_scalar) { return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(maybe_type->type_ref), ""); } else { LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_handle, maybe_null_index, ""); @@ -3731,17 +3785,17 @@ static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable return gen_non_null_bit(g, instruction->value->value.type, ir_llvm_value(g, instruction->value)); } -static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, - IrInstructionUnwrapOptional *instruction) +static LLVMValueRef ir_render_optional_unwrap_ptr(CodeGen *g, IrExecutable *executable, + IrInstructionOptionalUnwrapPtr *instruction) { - ZigType *ptr_type = instruction->value->value.type; + ZigType *ptr_type = instruction->base_ptr->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *maybe_type = ptr_type->data.pointer.child_type; assert(maybe_type->id == ZigTypeIdOptional); ZigType *child_type = maybe_type->data.maybe.child_type; - LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->value); - LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); + LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->base_ptr); if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) { + LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle); LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk"); LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail"); @@ -3755,8 +3809,8 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, if (child_type->zero_bits) { return nullptr; } else { - bool maybe_is_ptr = type_is_codegen_pointer(child_type); - if (maybe_is_ptr) { + bool is_scalar = type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet; + if (is_scalar) { return maybe_ptr; } else { LLVMValueRef maybe_struct_ref = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); @@ -4174,7 +4228,7 @@ static LLVMAtomicRMWBinOp to_LLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed) zig_unreachable(); } -static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrInstructionCmpxchg *instruction) { +static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrInstructionCmpxchgGen *instruction) { LLVMValueRef ptr_val = ir_llvm_value(g, instruction->ptr); LLVMValueRef cmp_val = ir_llvm_value(g, instruction->cmp_value); LLVMValueRef new_val = ir_llvm_value(g, instruction->new_value); @@ -4189,18 +4243,18 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn assert(maybe_type->id == ZigTypeIdOptional); ZigType *child_type = maybe_type->data.maybe.child_type; - if (type_is_codegen_pointer(child_type)) { + if (!handle_is_ptr(maybe_type)) { LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); return LLVMBuildSelect(g->builder, success_bit, LLVMConstNull(child_type->type_ref), payload_val, ""); } assert(instruction->tmp_ptr != nullptr); - assert(type_has_bits(instruction->type)); + assert(type_has_bits(child_type)); LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, ""); - gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val); + gen_assign_raw(g, val_ptr, get_pointer_to_type(g, child_type, false), payload_val); LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, ""); @@ -4351,6 +4405,7 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst assert(array_type->data.structure.is_slice); assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); + assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(tmp_struct_ptr))) == LLVMStructTypeKind); size_t ptr_index = array_type->data.structure.fields[slice_ptr_index].gen_index; assert(ptr_index != SIZE_MAX); @@ -4540,12 +4595,14 @@ static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutable *executable, IrI return LLVMBuildICmp(g->builder, LLVMIntNE, err_val, zero, ""); } -static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrCode *instruction) { - ZigType *ptr_type = instruction->value->value.type; +static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executable, + IrInstructionUnwrapErrCode *instruction) +{ + ZigType *ptr_type = instruction->err_union->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *err_union_type = ptr_type->data.pointer.child_type; ZigType *payload_type = err_union_type->data.error_union.payload_type; - LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value); + LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->err_union); LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type); if (type_has_bits(payload_type)) { @@ -4556,7 +4613,13 @@ static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executab } } -static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrPayload *instruction) { +static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *executable, + IrInstructionUnwrapErrPayload *instruction) +{ + bool want_safety = ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && + g->errors_by_index.length > 1; + if (!want_safety && !type_has_bits(instruction->base.value.type)) + return nullptr; ZigType *ptr_type = instruction->value->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *err_union_type = ptr_type->data.pointer.child_type; @@ -4568,7 +4631,7 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu return err_union_handle; } - if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && g->errors_by_index.length > 1) { + if (want_safety) { LLVMValueRef err_val; if (type_has_bits(payload_type)) { LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); @@ -4607,7 +4670,7 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I } LLVMValueRef payload_val = ir_llvm_value(g, instruction->value); - if (type_is_codegen_pointer(child_type)) { + if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) { return payload_val; } @@ -5184,12 +5247,15 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdToBytes: case IrInstructionIdEnumToInt: case IrInstructionIdCheckRuntimeScope: + case IrInstructionIdDeclVarSrc: + case IrInstructionIdPtrCastSrc: + case IrInstructionIdCmpxchgSrc: zig_unreachable(); + case IrInstructionIdDeclVarGen: + return ir_render_decl_var(g, executable, (IrInstructionDeclVarGen *)instruction); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); - case IrInstructionIdDeclVar: - return ir_render_decl_var(g, executable, (IrInstructionDeclVar *)instruction); case IrInstructionIdBinOp: return ir_render_bin_op(g, executable, (IrInstructionBinOp *)instruction); case IrInstructionIdCast: @@ -5220,8 +5286,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_asm(g, executable, (IrInstructionAsm *)instruction); case IrInstructionIdTestNonNull: return ir_render_test_non_null(g, executable, (IrInstructionTestNonNull *)instruction); - case IrInstructionIdUnwrapOptional: - return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapOptional *)instruction); + case IrInstructionIdOptionalUnwrapPtr: + return ir_render_optional_unwrap_ptr(g, executable, (IrInstructionOptionalUnwrapPtr *)instruction); case IrInstructionIdClz: return ir_render_clz(g, executable, (IrInstructionClz *)instruction); case IrInstructionIdCtz: @@ -5236,8 +5302,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_ref(g, executable, (IrInstructionRef *)instruction); case IrInstructionIdErrName: return ir_render_err_name(g, executable, (IrInstructionErrName *)instruction); - case IrInstructionIdCmpxchg: - return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchg *)instruction); + case IrInstructionIdCmpxchgGen: + return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchgGen *)instruction); case IrInstructionIdFence: return ir_render_fence(g, executable, (IrInstructionFence *)instruction); case IrInstructionIdTruncate: @@ -5278,8 +5344,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction); case IrInstructionIdUnionInit: return ir_render_union_init(g, executable, (IrInstructionUnionInit *)instruction); - case IrInstructionIdPtrCast: - return ir_render_ptr_cast(g, executable, (IrInstructionPtrCast *)instruction); + case IrInstructionIdPtrCastGen: + return ir_render_ptr_cast(g, executable, (IrInstructionPtrCastGen *)instruction); case IrInstructionIdBitCast: return ir_render_bit_cast(g, executable, (IrInstructionBitCast *)instruction); case IrInstructionIdWidenOrShorten: @@ -5377,6 +5443,9 @@ static void ir_render(CodeGen *g, ZigFn *fn_entry) { static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index); static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index); static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val); +static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExprValue *err_union_const_val); +static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ConstExprValue *err_union_const_val); +static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ConstExprValue *optional_const_val); static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) { switch (parent->id) { @@ -5387,6 +5456,12 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent case ConstParentIdStruct: return gen_const_ptr_struct_recursive(g, parent->data.p_struct.struct_val, parent->data.p_struct.field_index); + case ConstParentIdErrUnionCode: + return gen_const_ptr_err_union_code_recursive(g, parent->data.p_err_union_code.err_union_val); + case ConstParentIdErrUnionPayload: + return gen_const_ptr_err_union_payload_recursive(g, parent->data.p_err_union_payload.err_union_val); + case ConstParentIdOptionalPayload: + return gen_const_ptr_optional_payload_recursive(g, parent->data.p_optional_payload.optional_val); case ConstParentIdArray: return gen_const_ptr_array_recursive(g, parent->data.p_array.array_val, parent->data.p_array.elem_index); @@ -5402,7 +5477,7 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index) { expand_undef_array(g, array_const_val); - ConstParent *parent = &array_const_val->data.x_array.data.s_none.parent; + ConstParent *parent = &array_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, array_const_val, parent); LLVMTypeKind el_type = LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(base_ptr))); @@ -5427,7 +5502,7 @@ static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *ar } static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index) { - ConstParent *parent = &struct_const_val->data.x_struct.parent; + ConstParent *parent = &struct_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, struct_const_val, parent); ZigType *u32 = g->builtin_types.entry_u32; @@ -5438,8 +5513,44 @@ static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *s return LLVMConstInBoundsGEP(base_ptr, indices, 2); } +static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExprValue *err_union_const_val) { + ConstParent *parent = &err_union_const_val->parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent); + + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(u32->type_ref), + LLVMConstInt(u32->type_ref, err_union_err_index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); +} + +static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ConstExprValue *err_union_const_val) { + ConstParent *parent = &err_union_const_val->parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent); + + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(u32->type_ref), + LLVMConstInt(u32->type_ref, err_union_payload_index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); +} + +static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ConstExprValue *optional_const_val) { + ConstParent *parent = &optional_const_val->parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, optional_const_val, parent); + + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(u32->type_ref), + LLVMConstInt(u32->type_ref, maybe_child_index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); +} + static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val) { - ConstParent *parent = &union_const_val->data.x_union.parent; + ConstParent *parent = &union_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent); ZigType *u32 = g->builtin_types.entry_u32; @@ -5609,6 +5720,63 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con render_const_val_global(g, const_val, ""); return ptr_val; } + case ConstPtrSpecialBaseErrorUnionCode: + { + render_const_val_global(g, const_val, name); + ConstExprValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_code.err_union_val; + assert(err_union_const_val->type->id == ZigTypeIdErrorUnion); + if (err_union_const_val->type->zero_bits) { + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), + const_val->type->type_ref); + render_const_val_global(g, const_val, ""); + return const_val->global_refs->llvm_value; + } + LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_code_recursive(g, err_union_const_val); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); + const_val->global_refs->llvm_value = ptr_val; + render_const_val_global(g, const_val, ""); + return ptr_val; + } + case ConstPtrSpecialBaseErrorUnionPayload: + { + render_const_val_global(g, const_val, name); + ConstExprValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_payload.err_union_val; + assert(err_union_const_val->type->id == ZigTypeIdErrorUnion); + if (err_union_const_val->type->zero_bits) { + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), + const_val->type->type_ref); + render_const_val_global(g, const_val, ""); + return const_val->global_refs->llvm_value; + } + LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_payload_recursive(g, err_union_const_val); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); + const_val->global_refs->llvm_value = ptr_val; + render_const_val_global(g, const_val, ""); + return ptr_val; + } + case ConstPtrSpecialBaseOptionalPayload: + { + render_const_val_global(g, const_val, name); + ConstExprValue *optional_const_val = const_val->data.x_ptr.data.base_optional_payload.optional_val; + assert(optional_const_val->type->id == ZigTypeIdOptional); + if (optional_const_val->type->zero_bits) { + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), + const_val->type->type_ref); + render_const_val_global(g, const_val, ""); + return const_val->global_refs->llvm_value; + } + LLVMValueRef uncasted_ptr_val = gen_const_ptr_optional_payload_recursive(g, optional_const_val); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); + const_val->global_refs->llvm_value = ptr_val; + render_const_val_global(g, const_val, ""); + return ptr_val; + } case ConstPtrSpecialHardCodedAddr: { render_const_val_global(g, const_val, name); @@ -5621,10 +5789,17 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con } case ConstPtrSpecialFunction: return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref); + case ConstPtrSpecialNull: + return LLVMConstNull(const_val->type->type_ref); } zig_unreachable(); } +static LLVMValueRef gen_const_val_err_set(CodeGen *g, ConstExprValue *const_val, const char *name) { + uint64_t value = (const_val->data.x_err_set == nullptr) ? 0 : const_val->data.x_err_set->value; + return LLVMConstInt(g->builtin_types.entry_global_error_set->type_ref, value, false); +} + static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) { Error err; @@ -5644,9 +5819,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c case ZigTypeIdInt: return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigint); case ZigTypeIdErrorSet: - assert(const_val->data.x_err_set != nullptr); - return LLVMConstInt(g->builtin_types.entry_global_error_set->type_ref, - const_val->data.x_err_set->value, false); + return gen_const_val_err_set(g, const_val, name); case ZigTypeIdFloat: switch (type_entry->data.floating.bit_count) { case 16: @@ -5680,6 +5853,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c return LLVMConstInt(LLVMInt1Type(), const_val->data.x_optional ? 1 : 0, false); } else if (type_is_codegen_pointer(child_type)) { return gen_const_val_ptr(g, const_val, name); + } else if (child_type->id == ZigTypeIdErrorSet) { + return gen_const_val_err_set(g, const_val, name); } else { LLVMValueRef child_val; LLVMValueRef maybe_val; @@ -5914,7 +6089,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c ZigType *err_set_type = type_entry->data.error_union.err_set_type; if (!type_has_bits(payload_type)) { assert(type_has_bits(err_set_type)); - uint64_t value = const_val->data.x_err_union.err ? const_val->data.x_err_union.err->value : 0; + ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set; + uint64_t value = (err_set == nullptr) ? 0 : err_set->value; return LLVMConstInt(g->err_tag_type->type_ref, value, false); } else if (!type_has_bits(err_set_type)) { assert(type_has_bits(payload_type)); @@ -5923,8 +6099,9 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c LLVMValueRef err_tag_value; LLVMValueRef err_payload_value; bool make_unnamed_struct; - if (const_val->data.x_err_union.err) { - err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, const_val->data.x_err_union.err->value, false); + ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set; + if (err_set != nullptr) { + err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, err_set->value, false); err_payload_value = LLVMConstNull(payload_type->type_ref); make_unnamed_struct = false; } else { @@ -6130,10 +6307,13 @@ static void do_code_gen(CodeGen *g) { TldVar *tld_var = g->global_vars.at(i); ZigVar *var = tld_var->var; - if (var->value->type->id == ZigTypeIdComptimeFloat) { + if (var->var_type->id == ZigTypeIdComptimeFloat) { // Generate debug info for it but that's it. - ConstExprValue *const_val = var->value; + ConstExprValue *const_val = var->const_value; assert(const_val->special != ConstValSpecialRuntime); + if (const_val->type != var->var_type) { + zig_panic("TODO debug info for var with ptr casted value"); + } ZigType *var_type = g->builtin_types.entry_f128; ConstExprValue coerced_value; coerced_value.special = ConstValSpecialStatic; @@ -6144,10 +6324,13 @@ static void do_code_gen(CodeGen *g) { continue; } - if (var->value->type->id == ZigTypeIdComptimeInt) { + if (var->var_type->id == ZigTypeIdComptimeInt) { // Generate debug info for it but that's it. - ConstExprValue *const_val = var->value; + ConstExprValue *const_val = var->const_value; assert(const_val->special != ConstValSpecialRuntime); + if (const_val->type != var->var_type) { + zig_panic("TODO debug info for var with ptr casted value"); + } size_t bits_needed = bigint_bits_needed(&const_val->data.x_bigint); if (bits_needed < 8) { bits_needed = 8; @@ -6158,7 +6341,7 @@ static void do_code_gen(CodeGen *g) { continue; } - if (!type_has_bits(var->value->type)) + if (!type_has_bits(var->var_type)) continue; assert(var->decl_node); @@ -6167,9 +6350,9 @@ static void do_code_gen(CodeGen *g) { if (var->linkage == VarLinkageExternal) { LLVMValueRef existing_llvm_var = LLVMGetNamedGlobal(g->module, buf_ptr(&var->name)); if (existing_llvm_var) { - global_value = LLVMConstBitCast(existing_llvm_var, LLVMPointerType(var->value->type->type_ref, 0)); + global_value = LLVMConstBitCast(existing_llvm_var, LLVMPointerType(var->var_type->type_ref, 0)); } else { - global_value = LLVMAddGlobal(g->module, var->value->type->type_ref, buf_ptr(&var->name)); + global_value = LLVMAddGlobal(g->module, var->var_type->type_ref, buf_ptr(&var->name)); // TODO debug info for the extern variable LLVMSetLinkage(global_value, LLVMExternalLinkage); @@ -6180,9 +6363,9 @@ static void do_code_gen(CodeGen *g) { } else { bool exported = (var->linkage == VarLinkageExport); const char *mangled_name = buf_ptr(get_mangled_name(g, &var->name, exported)); - render_const_val(g, var->value, mangled_name); - render_const_val_global(g, var->value, mangled_name); - global_value = var->value->global_refs->llvm_global; + render_const_val(g, var->const_value, mangled_name); + render_const_val_global(g, var->const_value, mangled_name); + global_value = var->const_value->global_refs->llvm_global; if (exported) { LLVMSetLinkage(global_value, LLVMExternalLinkage); @@ -6194,8 +6377,10 @@ static void do_code_gen(CodeGen *g) { LLVMSetAlignment(global_value, var->align_bytes); // TODO debug info for function pointers - if (var->gen_is_const && var->value->type->id != ZigTypeIdFn) { - gen_global_var(g, var, var->value->global_refs->llvm_value, var->value->type); + // Here we use const_value->type because that's the type of the llvm global, + // which we const ptr cast upon use to whatever it needs to be. + if (var->gen_is_const && var->const_value->type->id != ZigTypeIdFn) { + gen_global_var(g, var, var->const_value->global_refs->llvm_value, var->const_value->type); } LLVMSetGlobalConstant(global_value, var->gen_is_const); @@ -6281,8 +6466,8 @@ static void do_code_gen(CodeGen *g) { } else if (instruction->id == IrInstructionIdErrWrapCode) { IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction; slot = &err_wrap_code_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdCmpxchg) { - IrInstructionCmpxchg *cmpxchg_instruction = (IrInstructionCmpxchg *)instruction; + } else if (instruction->id == IrInstructionIdCmpxchgGen) { + IrInstructionCmpxchgGen *cmpxchg_instruction = (IrInstructionCmpxchgGen *)instruction; slot = &cmpxchg_instruction->tmp_ptr; } else { zig_unreachable(); @@ -6304,12 +6489,12 @@ static void do_code_gen(CodeGen *g) { for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) { ZigVar *var = fn_table_entry->variable_list.at(var_i); - if (!type_has_bits(var->value->type)) { + if (!type_has_bits(var->var_type)) { continue; } if (ir_get_var_is_comptime(var)) continue; - switch (type_requires_comptime(g, var->value->type)) { + switch (type_requires_comptime(g, var->var_type)) { case ReqCompTimeInvalid: zig_unreachable(); case ReqCompTimeYes: @@ -6319,11 +6504,11 @@ static void do_code_gen(CodeGen *g) { } if (var->src_arg_index == SIZE_MAX) { - var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes); + var->value_ref = build_alloca(g, var->var_type, buf_ptr(&var->name), var->align_bytes); var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope), buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1), - var->value->type->di_type, !g->strip_debug_symbols, 0); + var->var_type->di_type, !g->strip_debug_symbols, 0); } else if (is_c_abi) { fn_walk_var.data.vars.var = var; @@ -6333,16 +6518,16 @@ static void do_code_gen(CodeGen *g) { ZigType *gen_type; FnGenParamInfo *gen_info = &fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index]; - if (handle_is_ptr(var->value->type)) { + if (handle_is_ptr(var->var_type)) { if (gen_info->is_byval) { - gen_type = var->value->type; + gen_type = var->var_type; } else { gen_type = gen_info->type; } var->value_ref = LLVMGetParam(fn, (unsigned)var->gen_arg_index); } else { - gen_type = var->value->type; - var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes); + gen_type = var->var_type; + var->value_ref = build_alloca(g, var->var_type, buf_ptr(&var->name), var->align_bytes); } if (var->decl_node) { var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), @@ -7458,9 +7643,9 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) { ConstExprValue *this_val = &test_fn_array->data.x_array.data.s_none.elements[i]; this_val->special = ConstValSpecialStatic; this_val->type = struct_type; - this_val->data.x_struct.parent.id = ConstParentIdArray; - this_val->data.x_struct.parent.data.p_array.array_val = test_fn_array; - this_val->data.x_struct.parent.data.p_array.elem_index = i; + this_val->parent.id = ConstParentIdArray; + this_val->parent.data.p_array.array_val = test_fn_array; + this_val->parent.data.p_array.elem_index = i; this_val->data.x_struct.fields = create_const_vals(2); ConstExprValue *name_field = &this_val->data.x_struct.fields[0]; diff --git a/src/ir.cpp b/src/ir.cpp index b184252a2e..06eb4a47f1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -167,6 +167,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ ZigType *dest_type, IrInstruction *dest_type_src); static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed); static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs); +static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align); static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); @@ -178,15 +179,28 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c case ConstPtrSpecialRef: result = const_val->data.x_ptr.data.ref.pointee; break; - case ConstPtrSpecialBaseArray: - expand_undef_array(g, const_val->data.x_ptr.data.base_array.array_val); - result = &const_val->data.x_ptr.data.base_array.array_val->data.x_array.data.s_none.elements[ - const_val->data.x_ptr.data.base_array.elem_index]; + case ConstPtrSpecialBaseArray: { + ConstExprValue *array_val = const_val->data.x_ptr.data.base_array.array_val; + expand_undef_array(g, array_val); + result = &array_val->data.x_array.data.s_none.elements[const_val->data.x_ptr.data.base_array.elem_index]; break; + } case ConstPtrSpecialBaseStruct: result = &const_val->data.x_ptr.data.base_struct.struct_val->data.x_struct.fields[ const_val->data.x_ptr.data.base_struct.field_index]; break; + case ConstPtrSpecialBaseErrorUnionCode: + result = const_val->data.x_ptr.data.base_err_union_code.err_union_val->data.x_err_union.error_set; + break; + case ConstPtrSpecialBaseErrorUnionPayload: + result = const_val->data.x_ptr.data.base_err_union_payload.err_union_val->data.x_err_union.payload; + break; + case ConstPtrSpecialBaseOptionalPayload: + result = const_val->data.x_ptr.data.base_optional_payload.optional_val->data.x_optional; + break; + case ConstPtrSpecialNull: + result = const_val; + break; case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialDiscard: @@ -198,6 +212,11 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c return result; } +static bool is_opt_err_set(ZigType *ty) { + return ty->id == ZigTypeIdErrorSet || + (ty->id == ZigTypeIdOptional && ty->data.maybe.child_type->id == ZigTypeIdErrorSet); +} + static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { if (a == b) return true; @@ -208,15 +227,10 @@ static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { if (get_codegen_ptr_type(a) != nullptr && get_codegen_ptr_type(b) != nullptr) return true; - return false; -} + if (is_opt_err_set(a) && is_opt_err_set(b)) + return true; -ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { - ConstExprValue *result = const_ptr_pointee_unchecked(g, const_val); - if (const_val->type->id == ZigTypeIdPointer) { - assert(types_have_same_zig_comptime_repr(const_val->type->data.pointer.child_type, result->type)); - } - return result; + return false; } static bool ir_should_inline(IrExecutable *exec, Scope *scope) { @@ -305,6 +319,14 @@ static IrBasicBlock *ir_build_bb_from(IrBuilder *irb, IrBasicBlock *other_bb) { return new_bb; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVarSrc *) { + return IrInstructionIdDeclVarSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVarGen *) { + return IrInstructionIdDeclVarGen; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionCondBr *) { return IrInstructionIdCondBr; } @@ -337,10 +359,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionBinOp *) { return IrInstructionIdBinOp; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVar *) { - return IrInstructionIdDeclVar; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionExport *) { return IrInstructionIdExport; } @@ -449,8 +467,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNonNull *) { return IrInstructionIdTestNonNull; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapOptional *) { - return IrInstructionIdUnwrapOptional; +static constexpr IrInstructionId ir_instruction_id(IrInstructionOptionalUnwrapPtr *) { + return IrInstructionIdOptionalUnwrapPtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionClz *) { @@ -517,8 +535,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionEmbedFile *) { return IrInstructionIdEmbedFile; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchg *) { - return IrInstructionIdCmpxchg; +static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchgSrc *) { + return IrInstructionIdCmpxchgSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchgGen *) { + return IrInstructionIdCmpxchgGen; } static constexpr IrInstructionId ir_instruction_id(IrInstructionFence *) { @@ -649,8 +671,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestComptime *) return IrInstructionIdTestComptime; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCast *) { - return IrInstructionIdPtrCast; +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCastSrc *) { + return IrInstructionIdPtrCastSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCastGen *) { + return IrInstructionIdPtrCastGen; } static constexpr IrInstructionId ir_instruction_id(IrInstructionBitCast *) { @@ -915,7 +941,7 @@ static IrInstruction *ir_build_cond_br(IrBuilder *irb, Scope *scope, AstNode *so ir_ref_instruction(condition, irb->current_basic_block); ir_ref_bb(then_block); ir_ref_bb(else_block); - if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block); + if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block); return &cond_br_instruction->base; } @@ -931,16 +957,6 @@ static IrInstruction *ir_build_return(IrBuilder *irb, Scope *scope, AstNode *sou return &return_instruction->base; } -static IrInstruction *ir_create_const(IrBuilder *irb, Scope *scope, AstNode *source_node, - ZigType *type_entry) -{ - assert(type_entry); - IrInstructionConst *const_instruction = ir_create_instruction(irb, scope, source_node); - const_instruction->base.value.type = type_entry; - const_instruction->base.value.special = ConstValSpecialStatic; - return &const_instruction->base; -} - static IrInstruction *ir_build_const_void(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.value.type = irb->codegen->builtin_types.entry_void; @@ -1188,14 +1204,11 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc call_instruction->async_allocator = async_allocator; call_instruction->new_stack = new_stack; - if (fn_ref) - ir_ref_instruction(fn_ref, irb->current_basic_block); + if (fn_ref != nullptr) ir_ref_instruction(fn_ref, irb->current_basic_block); for (size_t i = 0; i < arg_count; i += 1) ir_ref_instruction(args[i], irb->current_basic_block); - if (async_allocator) - ir_ref_instruction(async_allocator, irb->current_basic_block); - if (new_stack != nullptr) - ir_ref_instruction(new_stack, irb->current_basic_block); + if (async_allocator != nullptr) ir_ref_instruction(async_allocator, irb->current_basic_block); + if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block); return &call_instruction->base; } @@ -1280,7 +1293,7 @@ static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope, container_init_list_instruction->item_count = item_count; container_init_list_instruction->items = items; - ir_ref_instruction(container_type, irb->current_basic_block); + if (container_type != nullptr) ir_ref_instruction(container_type, irb->current_basic_block); for (size_t i = 0; i < item_count; i += 1) { ir_ref_instruction(items[i], irb->current_basic_block); } @@ -1355,10 +1368,10 @@ static IrInstruction *ir_build_store_ptr(IrBuilder *irb, Scope *scope, AstNode * return &instruction->base; } -static IrInstruction *ir_build_var_decl(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_var_decl_src(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var, IrInstruction *var_type, IrInstruction *align_value, IrInstruction *init_value) { - IrInstructionDeclVar *decl_var_instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionDeclVarSrc *decl_var_instruction = ir_build_instruction(irb, scope, source_node); decl_var_instruction->base.value.special = ConstValSpecialStatic; decl_var_instruction->base.value.type = irb->codegen->builtin_types.entry_void; decl_var_instruction->var = var; @@ -1366,13 +1379,28 @@ static IrInstruction *ir_build_var_decl(IrBuilder *irb, Scope *scope, AstNode *s decl_var_instruction->align_value = align_value; decl_var_instruction->init_value = init_value; - if (var_type) ir_ref_instruction(var_type, irb->current_basic_block); - if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); + if (var_type != nullptr) ir_ref_instruction(var_type, irb->current_basic_block); + if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); ir_ref_instruction(init_value, irb->current_basic_block); return &decl_var_instruction->base; } +static IrInstruction *ir_build_var_decl_gen(IrAnalyze *ira, IrInstruction *source_instruction, + ZigVar *var, IrInstruction *init_value) +{ + IrInstructionDeclVarGen *decl_var_instruction = ir_build_instruction(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + decl_var_instruction->base.value.special = ConstValSpecialStatic; + decl_var_instruction->base.value.type = ira->codegen->builtin_types.entry_void; + decl_var_instruction->var = var; + decl_var_instruction->init_value = init_value; + + ir_ref_instruction(init_value, ira->new_irb.current_basic_block); + + return &decl_var_instruction->base; +} + static IrInstruction *ir_build_export(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name, IrInstruction *target, IrInstruction *linkage) { @@ -1542,14 +1570,14 @@ static IrInstruction *ir_build_test_nonnull(IrBuilder *irb, Scope *scope, AstNod return &instruction->base; } -static IrInstruction *ir_build_unwrap_maybe(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value, - bool safety_check_on) +static IrInstruction *ir_build_optional_unwrap_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *base_ptr, bool safety_check_on) { - IrInstructionUnwrapOptional *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value = value; + IrInstructionOptionalUnwrapPtr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->base_ptr = base_ptr; instruction->safety_check_on = safety_check_on; - ir_ref_instruction(value, irb->current_basic_block); + ir_ref_instruction(base_ptr, irb->current_basic_block); return &instruction->base; } @@ -1765,13 +1793,12 @@ static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } -static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value, - IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, +static IrInstruction *ir_build_cmpxchg_src(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *type_value, IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value, - bool is_weak, - ZigType *type, AtomicOrder success_order, AtomicOrder failure_order) + bool is_weak) { - IrInstructionCmpxchg *instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionCmpxchgSrc *instruction = ir_build_instruction(irb, scope, source_node); instruction->type_value = type_value; instruction->ptr = ptr; instruction->cmp_value = cmp_value; @@ -1779,16 +1806,33 @@ static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *so instruction->success_order_value = success_order_value; instruction->failure_order_value = failure_order_value; instruction->is_weak = is_weak; - instruction->type = type; - instruction->success_order = success_order; - instruction->failure_order = failure_order; - if (type_value != nullptr) ir_ref_instruction(type_value, irb->current_basic_block); + ir_ref_instruction(type_value, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(cmp_value, irb->current_basic_block); ir_ref_instruction(new_value, irb->current_basic_block); - if (type_value != nullptr) ir_ref_instruction(success_order_value, irb->current_basic_block); - if (type_value != nullptr) ir_ref_instruction(failure_order_value, irb->current_basic_block); + ir_ref_instruction(success_order_value, irb->current_basic_block); + ir_ref_instruction(failure_order_value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_cmpxchg_gen(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, + AtomicOrder success_order, AtomicOrder failure_order, bool is_weak) +{ + IrInstructionCmpxchgGen *instruction = ir_build_instruction(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->ptr = ptr; + instruction->cmp_value = cmp_value; + instruction->new_value = new_value; + instruction->success_order = success_order; + instruction->failure_order = failure_order; + instruction->is_weak = is_weak; + + ir_ref_instruction(ptr, ira->new_irb.current_basic_block); + ir_ref_instruction(cmp_value, ira->new_irb.current_basic_block); + ir_ref_instruction(new_value, ira->new_irb.current_basic_block); return &instruction->base; } @@ -2060,12 +2104,12 @@ static IrInstruction *ir_build_test_err(IrBuilder *irb, Scope *scope, AstNode *s } static IrInstruction *ir_build_unwrap_err_code(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *value) + IrInstruction *err_union) { IrInstructionUnwrapErrCode *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value = value; + instruction->err_union = err_union; - ir_ref_instruction(value, irb->current_basic_block); + ir_ref_instruction(err_union, irb->current_basic_block); return &instruction->base; } @@ -2115,20 +2159,33 @@ static IrInstruction *ir_build_test_comptime(IrBuilder *irb, Scope *scope, AstNo return &instruction->base; } -static IrInstruction *ir_build_ptr_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_ptr_cast_src(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *ptr) { - IrInstructionPtrCast *instruction = ir_build_instruction( + IrInstructionPtrCastSrc *instruction = ir_build_instruction( irb, scope, source_node); instruction->dest_type = dest_type; instruction->ptr = ptr; - if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(dest_type, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); return &instruction->base; } +static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *source_instruction, + ZigType *ptr_type, IrInstruction *ptr) +{ + IrInstructionPtrCastGen *instruction = ir_build_instruction( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + instruction->base.value.type = ptr_type; + instruction->ptr = ptr; + + ir_ref_instruction(ptr, ira->new_irb.current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_bit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *value) { @@ -2807,10 +2864,13 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o Scope *defer_expr_scope = defer_node->data.defer.expr_scope; IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); if (defer_expr_value != irb->codegen->invalid_instruction) { - if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == ZigTypeIdUnreachable) { + if (defer_expr_value->value.type != nullptr && + defer_expr_value->value.type->id == ZigTypeIdUnreachable) + { is_noreturn = true; } else { - ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value)); + ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, + defer_expr_value)); } } } @@ -3065,7 +3125,7 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s variable_entry->mem_slot_index = SIZE_MAX; variable_entry->is_comptime = is_comptime; variable_entry->src_arg_index = SIZE_MAX; - variable_entry->value = create_const_vals(1); + variable_entry->const_value = create_const_vals(1); if (is_comptime != nullptr) { is_comptime->ref_count += 1; @@ -3080,20 +3140,20 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s ErrorMsg *msg = add_node_error(codegen, node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); - variable_entry->value->type = codegen->builtin_types.entry_invalid; + variable_entry->var_type = codegen->builtin_types.entry_invalid; } else { ZigType *type; if (get_primitive_type(codegen, name, &type) != ErrorPrimitiveTypeNotFound) { add_node_error(codegen, node, buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name))); - variable_entry->value->type = codegen->builtin_types.entry_invalid; + variable_entry->var_type = codegen->builtin_types.entry_invalid; } else { Tld *tld = find_decl(codegen, parent_scope, name); if (tld != nullptr) { ErrorMsg *msg = add_node_error(codegen, node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here")); - variable_entry->value->type = codegen->builtin_types.entry_invalid; + variable_entry->var_type = codegen->builtin_types.entry_invalid; } } } @@ -3156,7 +3216,8 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode scope_block->incoming_blocks = &incoming_blocks; scope_block->incoming_values = &incoming_values; scope_block->end_block = ir_create_basic_block(irb, parent_scope, "BlockEnd"); - scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, ir_should_inline(irb->exec, parent_scope)); + scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, + ir_should_inline(irb->exec, parent_scope)); } bool is_continuation_unreachable = false; @@ -3174,9 +3235,9 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode // defer starts a new scope child_scope = statement_node->data.defer.child_scope; assert(child_scope); - } else if (statement_value->id == IrInstructionIdDeclVar) { + } else if (statement_value->id == IrInstructionIdDeclVarSrc) { // variable declarations start a new scope - IrInstructionDeclVar *decl_var_instruction = (IrInstructionDeclVar *)statement_value; + IrInstructionDeclVarSrc *decl_var_instruction = (IrInstructionDeclVarSrc *)statement_value; child_scope = decl_var_instruction->var->child_scope; } else if (statement_value != irb->codegen->invalid_instruction && !is_continuation_unreachable) { // this statement's value must be void @@ -3331,7 +3392,7 @@ static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } -static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); AstNode *op1_node = node->data.bin_op_expr.op1; @@ -3365,7 +3426,7 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, ok_block); - IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, parent_scope, node, maybe_ptr, false); + IrInstruction *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, parent_scope, node, maybe_ptr, false); IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); IrBasicBlock *after_ok_block = irb->current_basic_block; ir_build_br(irb, parent_scope, node, end_block, is_comptime); @@ -3483,7 +3544,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) case BinOpTypeMergeErrorSets: return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets); case BinOpTypeUnwrapOptional: - return ir_gen_maybe_ok_or(irb, scope, node); + return ir_gen_orelse(irb, scope, node); case BinOpTypeErrorUnion: return ir_gen_error_union(irb, scope, node); } @@ -3542,6 +3603,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, buf_ptr(variable_name))); return irb->codegen->invalid_instruction; } + assert(err == ErrorPrimitiveTypeNotFound); } else { IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_type); if (lval == LValPtr) { @@ -3904,9 +3966,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg5_value == irb->codegen->invalid_instruction) return arg5_value; - IrInstruction *cmpxchg = ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value, - arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak), - nullptr, AtomicOrderUnordered, AtomicOrderUnordered); + IrInstruction *cmpxchg = ir_build_cmpxchg_src(irb, scope, node, arg0_value, arg1_value, + arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak)); return ir_lval_wrap(irb, scope, cmpxchg, lval); } case BuiltinFnIdFence: @@ -4346,7 +4407,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *ptr_cast = ir_build_ptr_cast(irb, scope, node, arg0_value, arg1_value); + IrInstruction *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, ptr_cast, lval); } case BuiltinFnIdBitCast: @@ -4784,7 +4845,8 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node } } - IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr); + IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, + is_async, async_allocator, nullptr); return ir_lval_wrap(irb, scope, fn_call, lval); } @@ -4793,7 +4855,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode IrInstruction *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope); if (condition == irb->codegen->invalid_instruction) - return condition; + return irb->codegen->invalid_instruction; IrInstruction *is_comptime; if (ir_should_inline(irb->exec, scope)) { @@ -4816,7 +4878,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); IrInstruction *then_expr_result = ir_gen_node(irb, then_node, subexpr_scope); if (then_expr_result == irb->codegen->invalid_instruction) - return then_expr_result; + return irb->codegen->invalid_instruction; IrBasicBlock *after_then_block = irb->current_basic_block; if (!instr_is_unreachable(then_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); @@ -4826,7 +4888,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode if (else_node) { else_expr_result = ir_gen_node(irb, else_node, subexpr_scope); if (else_expr_result == irb->codegen->invalid_instruction) - return else_expr_result; + return irb->codegen->invalid_instruction; } else { else_expr_result = ir_build_const_void(irb, scope, node); } @@ -4927,7 +4989,7 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode ptr_len, align_value, bit_offset_start, host_int_bytes); } -static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, +static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, LVal lval) { IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); @@ -5053,11 +5115,11 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime); ZigVar *var = ir_create_var(irb, node, scope, variable_declaration->symbol, is_const, is_const, is_shadowable, is_comptime); - // we detect IrInstructionIdDeclVar in gen_block to make sure the next node + // we detect IrInstructionIdDeclVarSrc in gen_block to make sure the next node // is inside var->child_scope if (!is_extern && !variable_declaration->expr) { - var->value->type = irb->codegen->builtin_types.entry_invalid; + var->var_type = irb->codegen->builtin_types.entry_invalid; add_node_error(irb->codegen, node, buf_sprintf("variables must be initialized")); return irb->codegen->invalid_instruction; } @@ -5084,7 +5146,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod if (init_value == irb->codegen->invalid_instruction) return init_value; - return ir_build_var_decl(irb, scope, node, var, type_instruction, align_value, init_value); + return ir_build_var_decl_src(irb, scope, node, var, type_instruction, align_value, init_value); } static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -5140,7 +5202,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n err_val_ptr, false); IrInstruction *var_value = node->data.while_expr.var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, payload_scope, symbol_node, var_ptr_value); - ir_build_var_decl(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_value); + ir_build_var_decl_src(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_value); } ZigList incoming_values = {0}; @@ -5180,7 +5242,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n true, false, false, is_comptime); Scope *err_scope = err_var->child_scope; IrInstruction *err_var_value = ir_build_unwrap_err_code(irb, err_scope, err_symbol_node, err_val_ptr); - ir_build_var_decl(irb, err_scope, symbol_node, err_var, nullptr, nullptr, err_var_value); + ir_build_var_decl_src(irb, err_scope, symbol_node, err_var, nullptr, nullptr, err_var_value); IrInstruction *else_result = ir_gen_node(irb, else_node, err_scope); if (else_result == irb->codegen->invalid_instruction) @@ -5220,10 +5282,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n } ir_set_cursor_at_end_and_append_block(irb, body_block); - IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, child_scope, symbol_node, maybe_val_ptr, false); + IrInstruction *var_ptr_value = ir_build_optional_unwrap_ptr(irb, child_scope, symbol_node, maybe_val_ptr, false); IrInstruction *var_value = node->data.while_expr.var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, child_scope, symbol_node, var_ptr_value); - ir_build_var_decl(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_value); + ir_build_var_decl_src(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_value); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; @@ -5380,7 +5442,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo Scope *child_scope = elem_var->child_scope; IrInstruction *undefined_value = ir_build_const_undefined(irb, child_scope, elem_node); - ir_build_var_decl(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, undefined_value); + ir_build_var_decl_src(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, undefined_value); IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, child_scope, node, elem_var); AstNode *index_var_source_node; @@ -5398,7 +5460,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo IrInstruction *usize = ir_build_const_type(irb, child_scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *zero = ir_build_const_usize(irb, child_scope, node, 0); IrInstruction *one = ir_build_const_usize(irb, child_scope, node, 1); - ir_build_var_decl(irb, child_scope, index_var_source_node, index_var, usize, nullptr, zero); + ir_build_var_decl_src(irb, child_scope, index_var_source_node, index_var, usize, nullptr, zero); IrInstruction *index_ptr = ir_build_var_ptr(irb, child_scope, node, index_var); @@ -5622,8 +5684,8 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_asm(irb, scope, node, input_list, output_types, output_vars, return_count, is_volatile); } -static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeTestExpr); +static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeIfOptional); Buf *var_symbol = node->data.test_expr.var_symbol; AstNode *expr_node = node->data.test_expr.target_node; @@ -5661,9 +5723,9 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_symbol, is_const, is_const, is_shadowable, is_comptime); - IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, subexpr_scope, node, maybe_val_ptr, false); + IrInstruction *var_ptr_value = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, maybe_val_ptr, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value); - ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value); var_scope = var->child_scope; } else { var_scope = subexpr_scope; @@ -5738,7 +5800,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_val_ptr, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value); - ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value); var_scope = var->child_scope; } else { var_scope = subexpr_scope; @@ -5763,7 +5825,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * err_symbol, is_const, is_const, is_shadowable, is_comptime); IrInstruction *var_value = ir_build_unwrap_err_code(irb, subexpr_scope, node, err_val_ptr); - ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value); err_var_scope = var->child_scope; } else { err_var_scope = subexpr_scope; @@ -5818,7 +5880,7 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit var_value = var_is_ptr ? target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr); } IrInstruction *var_type = nullptr; // infer the type - ir_build_var_decl(irb, scope, var_symbol_node, var, var_type, nullptr, var_value); + ir_build_var_decl_src(irb, scope, var_symbol_node, var, var_type, nullptr, var_value); } else { child_scope = scope; } @@ -6228,7 +6290,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) return ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true); } -static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeUnwrapErrorExpr); AstNode *op1_node = node->data.unwrap_err_expr.op1; @@ -6242,7 +6304,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name))); return irb->codegen->invalid_instruction; } - return ir_gen_err_assert_ok(irb, parent_scope, node, op1_node, LValNone); + return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, LValNone); } @@ -6276,7 +6338,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN is_const, is_const, is_shadowable, is_comptime); err_scope = var->child_scope; IrInstruction *err_val = ir_build_unwrap_err_code(irb, err_scope, node, err_union_ptr); - ir_build_var_decl(irb, err_scope, var_node, var, nullptr, nullptr, err_val); + ir_build_var_decl_src(irb, err_scope, var_node, var, nullptr, nullptr, err_val); } else { err_scope = parent_scope; } @@ -6312,7 +6374,8 @@ static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *o ScopeVarDecl *var_scope = (ScopeVarDecl *)inner_scope; if (need_comma) buf_append_char(name, ','); - render_const_value(codegen, name, var_scope->var->value); + // TODO: const ptr reinterpret here to make the var type agree with the value? + render_const_value(codegen, name, var_scope->var->const_value); return true; } @@ -6562,7 +6625,7 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 // TODO relies on Zig not re-ordering fields - IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); + IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst); IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, @@ -6640,7 +6703,7 @@ static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void)); // TODO relies on Zig not re-ordering fields - IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); + IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst); IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, @@ -6756,7 +6819,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst); IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type); ir_build_await_bookkeeping(irb, scope, node, promise_result_type); - ir_build_var_decl(irb, scope, node, result_var, promise_result_type, nullptr, undefined_value); + ir_build_var_decl_src(irb, scope, node, result_var, promise_result_type, nullptr, undefined_value); IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, scope, node, result_var); ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, my_result_var_ptr); IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle); @@ -7050,7 +7113,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop if (maybe_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true); + IrInstruction *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, maybe_ptr, true); if (lval == LValPtr) return unwrapped_ptr; @@ -7074,8 +7137,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval); case NodeTypeIfErrorExpr: return ir_lval_wrap(irb, scope, ir_gen_if_err_expr(irb, scope, node), lval); - case NodeTypeTestExpr: - return ir_lval_wrap(irb, scope, ir_gen_test_expr(irb, scope, node), lval); + case NodeTypeIfOptional: + return ir_lval_wrap(irb, scope, ir_gen_if_optional_expr(irb, scope, node), lval); case NodeTypeSwitchExpr: return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval); case NodeTypeCompTime: @@ -7093,7 +7156,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeSliceExpr: return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node), lval); case NodeTypeUnwrapErrorExpr: - return ir_lval_wrap(irb, scope, ir_gen_err_ok_or(irb, scope, node), lval); + return ir_lval_wrap(irb, scope, ir_gen_catch(irb, scope, node), lval); case NodeTypeContainerDecl: return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval); case NodeTypeFnProto: @@ -7152,6 +7215,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_ref_bb(irb->current_basic_block); ZigFn *fn_entry = exec_fn_entry(irb->exec); + bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; IrInstruction *coro_id; IrInstruction *u8_ptr_type; @@ -7172,27 +7236,27 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ZigType *coro_frame_type = get_promise_frame_type(irb->codegen, return_type); IrInstruction *coro_frame_type_value = ir_build_const_type(irb, coro_scope, node, coro_frame_type); // TODO mark this var decl as "no safety" e.g. disable initializing the undef value to 0xaa - ir_build_var_decl(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, undef); + ir_build_var_decl_src(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, undef); coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var); ZigVar *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node); IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node, get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); - ir_build_var_decl(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value); + ir_build_var_decl_src(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value); irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node, await_handle_var); u8_ptr_type = ir_build_const_type(irb, coro_scope, node, get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false)); - IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, coro_promise_ptr); + IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, coro_promise_ptr); coro_id = ir_build_coro_id(irb, coro_scope, node, promise_as_u8_ptr); coro_size_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *coro_size = ir_build_coro_size(irb, coro_scope, node); - ir_build_var_decl(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size); + ir_build_var_decl_src(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size); IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, coro_scope, node, ImplicitAllocatorIdArg); irb->exec->coro_allocator_var = ir_create_var(irb, node, coro_scope, nullptr, true, true, true, const_bool_false); - ir_build_var_decl(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr); + ir_build_var_decl_src(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr); Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, alloc_field_name); IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr); @@ -7208,7 +7272,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_build_return(irb, coro_scope, node, undef); ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block); - IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr); + IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr); irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); @@ -7286,8 +7350,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0)); IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr); - IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, result_ptr); - IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, + IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, result_ptr); + IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, irb->exec->coro_result_field_ptr); IrInstruction *return_type_inst = ir_build_const_type(irb, scope, node, fn_entry->type_entry->data.fn.fn_type_id.return_type); @@ -7303,7 +7367,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec // Before we destroy the coroutine frame, we need to load the target promise into // a register or local variable which does not get spilled into the frame, // otherwise llvm tries to access memory inside the destroyed frame. - IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node, + IrInstruction *unwrapped_await_handle_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, false); IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr); ir_build_br(irb, scope, node, check_free_block, const_bool_false); @@ -7338,7 +7402,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0)); - IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); + IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); @@ -7435,7 +7499,7 @@ ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprVal return val; } -static IrInstruction *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) { +static ConstExprValue *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) { IrBasicBlock *bb = exec->basic_block_list.at(0); for (size_t i = 0; i < bb->instruction_list.length; i += 1) { IrInstruction *instruction = bb->instruction_list.at(i); @@ -7445,16 +7509,16 @@ static IrInstruction *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) if (value->value.special == ConstValSpecialRuntime) { exec_add_error_node(codegen, exec, value->source_node, buf_sprintf("unable to evaluate constant expression")); - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; } - return value; + return &value->value; } else if (ir_has_side_effects(instruction)) { exec_add_error_node(codegen, exec, instruction->source_node, buf_sprintf("unable to evaluate constant expression")); - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; } } - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; } static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInstruction *source_instruction) { @@ -8768,13 +8832,13 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT size_t errors_count = 0; ZigType *err_set_type = nullptr; if (prev_inst->value.type->id == ZigTypeIdErrorSet) { + if (!resolve_inferred_error_set(ira->codegen, prev_inst->value.type, prev_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } if (type_is_global_error_set(prev_inst->value.type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; } else { err_set_type = prev_inst->value.type; - if (!resolve_inferred_error_set(ira->codegen, err_set_type, prev_inst->source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } update_errors_helper(ira->codegen, &errors, &errors_count); for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { @@ -8933,6 +8997,9 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT if (prev_type->id == ZigTypeIdArray) { convert_to_const_slice = true; } + if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } if (type_is_global_error_set(cur_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; continue; @@ -8940,9 +9007,6 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT if (err_set_type != nullptr && type_is_global_error_set(err_set_type)) { continue; } - if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } update_errors_helper(ira->codegen, &errors, &errors_count); @@ -9432,14 +9496,23 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ } return true; } + +static IrInstruction *ir_const(IrAnalyze *ira, IrInstruction *old_instruction, ZigType *ty) { + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, + old_instruction->scope, old_instruction->source_node); + IrInstruction *new_instruction = &const_instruction->base; + new_instruction->value.type = ty; + new_instruction->value.special = ConstValSpecialStatic; + return new_instruction; +} + static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type, CastOp cast_op, bool need_alloca) { if ((instr_is_comptime(value) || !type_has_bits(wanted_type)) && cast_op != CastOpResizeSlice) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, &value->value, value->value.type, &result->value, wanted_type)) { @@ -9476,9 +9549,7 @@ static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, if (pointee == nullptr) return ira->codegen->invalid_instruction; if (pointee->special != ConstValSpecialRuntime) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); - result->value.type = wanted_type; + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.data.x_ptr.special = ConstPtrSpecialBaseArray; result->value.data.x_ptr.mut = value->value.data.x_ptr.mut; result->value.data.x_ptr.data.base_array.array_val = pointee; @@ -9517,8 +9588,7 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc assert(is_slice(wanted_type)); bool is_const = wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_slice(ira->codegen, &result->value, pointee, 0, array_type->data.array.len, is_const); result->value.data.x_struct.fields[slice_ptr_index].data.x_ptr.mut = value->value.data.x_ptr.mut; @@ -9653,15 +9723,6 @@ static IrInstruction *ir_finish_anal(IrAnalyze *ira, IrInstruction *instruction) return instruction; } -static IrInstruction *ir_const(IrAnalyze *ira, IrInstruction *old_instruction, ZigType *ty) { - IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, - old_instruction->scope, old_instruction->source_node); - IrInstruction *new_instruction = &const_instruction->base; - new_instruction->value.type = ty; - new_instruction->value.special = ConstValSpecialStatic; - return new_instruction; -} - static IrInstruction *ir_const_type(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *ty) { IrInstruction *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_type); result->value.data.x_type = ty; @@ -9719,13 +9780,13 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un zig_unreachable(); } -IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, +ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota, ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name, IrExecutable *parent_exec) { if (expected_type != nullptr && type_is_invalid(expected_type)) - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; IrExecutable *ir_executable = allocate(1); ir_executable->source_node = source_node; @@ -9738,13 +9799,13 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node ir_gen(codegen, node, scope, ir_executable); if (ir_executable->invalid) - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; if (codegen->verbose_ir) { fprintf(stderr, "\nSource: "); ast_render(codegen, stderr, node, 4); fprintf(stderr, "\n{ // (IR)\n"); - ir_print(codegen, stderr, ir_executable, 4); + ir_print(codegen, stderr, ir_executable, 2); fprintf(stderr, "}\n"); } IrExecutable *analyzed_executable = allocate(1); @@ -9760,11 +9821,11 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node analyzed_executable->begin_scope = scope; ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, node); if (type_is_invalid(result_type)) - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; if (codegen->verbose_ir) { fprintf(stderr, "{ // (analyzed)\n"); - ir_print(codegen, stderr, analyzed_executable, 4); + ir_print(codegen, stderr, analyzed_executable, 2); fprintf(stderr, "}\n"); } @@ -9838,7 +9899,9 @@ static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { return const_val->data.x_ptr.data.fn.fn_entry; } -static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { +static IrInstruction *ir_analyze_optional_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, + ZigType *wanted_type) +{ assert(wanted_type->id == ZigTypeIdOptional); if (instr_is_comptime(value)) { @@ -9854,7 +9917,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.special = ConstValSpecialStatic; - if (get_codegen_ptr_type(wanted_type) != nullptr) { + if (types_have_same_zig_comptime_repr(wanted_type, payload_type)) { copy_const_val(&const_instruction->base.value, val, val->data.x_ptr.mut == ConstPtrMutComptimeConst); } else { const_instruction->base.value.data.x_optional = val; @@ -9885,11 +9948,16 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction if (!val) return ira->codegen->invalid_instruction; + ConstExprValue *err_set_val = create_const_vals(1); + err_set_val->type = wanted_type->data.error_union.err_set_type; + err_set_val->special = ConstValSpecialStatic; + err_set_val->data.x_err_set = nullptr; + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.type = wanted_type; const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_err_union.err = nullptr; + const_instruction->base.value.data.x_err_union.error_set = err_set_val; const_instruction->base.value.data.x_err_union.payload = val; return &const_instruction->base; } @@ -9954,11 +10022,16 @@ static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *so if (!val) return ira->codegen->invalid_instruction; + ConstExprValue *err_set_val = create_const_vals(1); + err_set_val->special = ConstValSpecialStatic; + err_set_val->type = wanted_type->data.error_union.err_set_type; + err_set_val->data.x_err_set = val->data.x_err_set; + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.type = wanted_type; const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_err_union.err = val->data.x_err_set; + const_instruction->base.value.data.x_err_union.error_set = err_set_val; const_instruction->base.value.data.x_err_union.payload = nullptr; return &const_instruction->base; } @@ -9980,8 +10053,9 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.special = ConstValSpecialStatic; if (get_codegen_ptr_type(wanted_type) != nullptr) { - const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr; - const_instruction->base.value.data.x_ptr.data.hard_coded_addr.addr = 0; + const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialNull; + } else if (is_opt_err_set(wanted_type)) { + const_instruction->base.value.data.x_err_set = nullptr; } else { const_instruction->base.value.data.x_optional = nullptr; } @@ -10014,7 +10088,7 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi source_instruction->source_node, value, is_const, is_volatile); new_instruction->value.type = ptr_type; new_instruction->value.data.rh_ptr = RuntimeHintPtrStack; - if (type_has_bits(ptr_type)) { + if (type_has_bits(ptr_type) && !handle_is_ptr(value->value.type)) { ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); assert(fn_entry); fn_entry->alloca_list.append(new_instruction); @@ -10040,20 +10114,17 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s ZigType *array_type = array->value.type; assert(array_type->id == ZigTypeIdArray); - if (instr_is_comptime(array)) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + if (instr_is_comptime(array) || array_type->data.array.len == 0) { + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_slice(ira->codegen, &result->value, &array->value, 0, array_type->data.array.len, true); result->value.type = wanted_type; return result; } - IrInstruction *start = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, ira->codegen->builtin_types.entry_usize); + IrInstruction *start = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize); init_const_usize(ira->codegen, &start->value, 0); - IrInstruction *end = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, ira->codegen->builtin_types.entry_usize); + IrInstruction *end = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize); init_const_usize(ira->codegen, &end->value, array_type->data.array.len); if (!array_ptr) array_ptr = ir_get_ref(ira, source_instr, array, true, false); @@ -10092,8 +10163,7 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_bigint(&result->value, wanted_type, &val->data.x_enum_tag); return result; } @@ -10103,8 +10173,7 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour actual_type->data.enumeration.src_field_count == 1) { assert(wanted_type== ira->codegen->builtin_types.entry_num_lit_int); - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_bigint(&result->value, wanted_type, &actual_type->data.enumeration.fields[0].value); return result; @@ -10127,8 +10196,7 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.special = ConstValSpecialStatic; result->value.type = wanted_type; bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_union.tag); @@ -10139,8 +10207,7 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou if (wanted_type->data.enumeration.layout == ContainerLayoutAuto && wanted_type->data.enumeration.src_field_count == 1) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.special = ConstValSpecialStatic; result->value.type = wanted_type; TypeEnumField *enum_field = target->value.type->data.unionation.fields[0].enum_field; @@ -10157,8 +10224,7 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *wanted_type) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_undefined(ira->codegen, &result->value); return result; } @@ -10190,8 +10256,7 @@ static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *so buf_sprintf("field '%s' declared here", buf_ptr(union_field->name))); return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.special = ConstValSpecialStatic; result->value.type = wanted_type; bigint_init_bigint(&result->value.data.x_union.tag, &val->data.x_enum_tag); @@ -10246,8 +10311,7 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction return ira->codegen->invalid_instruction; } } - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.type = wanted_type; if (wanted_type->id == ZigTypeIdInt) { bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint); @@ -10301,8 +10365,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_bigint); return result; } @@ -10320,8 +10383,7 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (wanted_type->id == ZigTypeIdComptimeFloat) { float_init_float(&result->value, val); } else if (wanted_type->id == ZigTypeIdComptimeInt) { @@ -10344,8 +10406,7 @@ static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *sourc if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) { return ira->codegen->invalid_instruction; @@ -10409,12 +10470,11 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); ErrorTableEntry *err; if (err_type->id == ZigTypeIdErrorUnion) { - err = val->data.x_err_union.err; + err = val->data.x_err_union.error_set->data.x_err_set; } else if (err_type->id == ZigTypeIdErrorSet) { err = val->data.x_err_set; } else { @@ -10449,15 +10509,11 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc return ira->codegen->invalid_instruction; } if (err_set_type->data.error_set.err_count == 0) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); - result->value.type = wanted_type; + IrInstruction *result = ir_const(ira, source_instr, wanted_type); bigint_init_unsigned(&result->value.data.x_bigint, 0); return result; } else if (err_set_type->data.error_set.err_count == 1) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); - result->value.type = wanted_type; + IrInstruction *result = ir_const(ira, source_instr, wanted_type); ErrorTableEntry *err = err_set_type->data.error_set.errors[0]; bigint_init_unsigned(&result->value.data.x_bigint, err->value); return result; @@ -10504,8 +10560,8 @@ static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *sou array_val->type = array_type; array_val->data.x_array.special = ConstArraySpecialNone; array_val->data.x_array.data.s_none.elements = pointee; - array_val->data.x_array.data.s_none.parent.id = ConstParentIdScalar; - array_val->data.x_array.data.s_none.parent.data.p_scalar.scalar_val = pointee; + array_val->parent.id = ConstParentIdScalar; + array_val->parent.data.p_scalar.scalar_val = pointee; IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); @@ -10653,12 +10709,12 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk) { - return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); + return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type); } else if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) { - return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); + return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type); } else { return ira->codegen->invalid_instruction; } @@ -10682,7 +10738,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_child_type); if (type_is_invalid(cast1->value.type)) return ira->codegen->invalid_instruction; - return ir_analyze_maybe_wrap(ira, source_instr, cast1, wanted_type); + return ir_analyze_optional_wrap(ira, source_instr, cast1, wanted_type); } } } @@ -10735,6 +10791,11 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst (wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdFloat || wanted_type->id == ZigTypeIdComptimeFloat)) { + if (value->value.special == ConstValSpecialUndef) { + IrInstruction *result = ir_const(ira, source_instr, wanted_type); + result->value.special = ConstValSpecialUndef; + return result; + } if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) { if (wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdInt) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); @@ -10788,6 +10849,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // cast from [N]T to []const T + // TODO: once https://github.com/ziglang/zig/issues/265 lands, remove this if (is_slice(wanted_type) && actual_type->id == ZigTypeIdArray) { ZigType *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == ZigTypeIdPointer); @@ -10800,6 +10862,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } // cast from [N]T to ?[]const T + // TODO: once https://github.com/ziglang/zig/issues/265 lands, remove this if (wanted_type->id == ZigTypeIdOptional && is_slice(wanted_type->data.maybe.child_type) && actual_type->id == ZigTypeIdArray) @@ -10894,7 +10957,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // cast from error set to error union type + // cast from E to E!T if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id == ZigTypeIdErrorSet) { @@ -11046,8 +11109,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc ZigType *child_type = type_entry->data.pointer.child_type; // dereferencing a *u0 is comptime known to be 0 if (child_type->id == ZigTypeIdInt && child_type->data.integral.bit_count == 0) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, child_type); + IrInstruction *result = ir_const(ira, source_instruction, child_type); init_const_unsigned_negative(&result->value, child_type, 0, false); return result; } @@ -11061,8 +11123,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc { ConstExprValue *pointee = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); if (pointee->special != ConstValSpecialRuntime) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, child_type); + IrInstruction *result = ir_const(ira, source_instruction, child_type); if ((err = ir_read_const_ptr(ira, ira->codegen, source_instruction->source_node, &result->value, &ptr->value))) @@ -11074,7 +11135,11 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc } } } - // TODO if the instruction is a const ref instruction we can skip it + // if the instruction is a const ref instruction we can skip it + if (ptr->id == IrInstructionIdRef) { + IrInstructionRef *ref_inst = reinterpret_cast(ptr); + return ref_inst->value; + } IrInstruction *load_ptr_instruction = ir_build_load_ptr(&ira->new_irb, source_instruction->scope, source_instruction->source_node, ptr); load_ptr_instruction->value.type = child_type; @@ -11321,8 +11386,7 @@ static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructio static IrInstruction *ir_analyze_instruction_const(IrAnalyze *ira, IrInstructionConst *instruction) { IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - // TODO determine if we need to use copy_const_val here - result->value = instruction->base.value; + copy_const_val(&result->value, &instruction->base.value, true); return result; } @@ -11397,6 +11461,8 @@ static bool optional_value_is_null(ConstExprValue *val) { if (get_codegen_ptr_type(val->type) != nullptr) { return val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && val->data.x_ptr.data.hard_coded_addr.addr == 0; + } else if (is_opt_err_set(val->type)) { + return val->data.x_err_set == nullptr; } else { return val->data.x_optional == nullptr; } @@ -11596,19 +11662,18 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->invalid_instruction; - bool requires_comptime; - switch (type_requires_comptime(ira->codegen, resolved_type)) { - case ReqCompTimeYes: - requires_comptime = true; + bool one_possible_value; + switch (type_has_one_possible_value(ira->codegen, resolved_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + one_possible_value = true; break; - case ReqCompTimeNo: - requires_comptime = false; + case OnePossibleValueNo: + one_possible_value = false; break; - case ReqCompTimeInvalid: - return ira->codegen->invalid_instruction; } - bool one_possible_value = !requires_comptime && !type_has_bits(resolved_type); if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) { ConstExprValue *op1_val = one_possible_value ? &casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) @@ -12497,13 +12562,15 @@ static IrInstruction *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructio zig_unreachable(); } -static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) { +static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, + IrInstructionDeclVarSrc *decl_var_instruction) +{ Error err; ZigVar *var = decl_var_instruction->var; IrInstruction *init_value = decl_var_instruction->init_value->child; if (type_is_invalid(init_value->value.type)) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; } @@ -12514,7 +12581,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct ZigType *proposed_type = ir_resolve_type(ira, var_type); explicit_type = validate_var_type(ira->codegen, var_type->source_node, proposed_type); if (type_is_invalid(explicit_type)) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; } } @@ -12539,7 +12606,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct case ReqCompTimeInvalid: result_type = ira->codegen->builtin_types.entry_invalid; break; - case ReqCompTimeYes: { + case ReqCompTimeYes: var_class_requires_const = true; if (!var->gen_is_const && !is_comptime_var) { ir_add_error_node(ira, source_node, @@ -12548,7 +12615,6 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct result_type = ira->codegen->builtin_types.entry_invalid; } break; - } case ReqCompTimeNo: if (casted_init_value->value.special == ConstValSpecialStatic && casted_init_value->value.type->id == ZigTypeIdFn && @@ -12567,7 +12633,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct break; } - if (var->value->type != nullptr && !is_comptime_var) { + if (var->var_type != nullptr && !is_comptime_var) { // This is at least the second time we've seen this variable declaration during analysis. // This means that this is actually a different variable due to, e.g. an inline while loop. // We make a new variable so that it can hold a different type, and so the debug info can @@ -12589,8 +12655,8 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct // This must be done after possibly creating a new variable above var->ref_count = 0; - var->value->type = result_type; - assert(var->value->type); + var->var_type = result_type; + assert(var->var_type); if (type_is_invalid(result_type)) { return ir_const_void(ira, &decl_var_instruction->base); @@ -12598,13 +12664,13 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct if (decl_var_instruction->align_value == nullptr) { if ((err = type_resolve(ira->codegen, result_type, ResolveStatusAlignmentKnown))) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ir_const_void(ira, &decl_var_instruction->base); } var->align_bytes = get_abi_alignment(ira->codegen, result_type); } else { if (!ir_resolve_align(ira, decl_var_instruction->align_value->child, &var->align_bytes)) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; } } @@ -12621,7 +12687,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct } else if (is_comptime_var) { ir_add_error(ira, &decl_var_instruction->base, buf_sprintf("cannot store runtime value in compile time variable")); - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; } @@ -12629,11 +12695,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct if (fn_entry) fn_entry->variable_list.append(var); - IrInstruction *result = ir_build_var_decl(&ira->new_irb, - decl_var_instruction->base.scope, decl_var_instruction->base.source_node, - var, var_type, nullptr, casted_init_value); - result->value.type = ira->codegen->builtin_types.entry_void; - return result; + return ir_build_var_decl_gen(ira, &decl_var_instruction->base, var, casted_init_value); } static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructionExport *instruction) { @@ -12963,7 +13025,7 @@ static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node Buf *param_name = param_decl_node->data.param_decl.name; ZigVar *var = add_variable(ira->codegen, param_decl_node, - *exec_scope, param_name, true, arg_val, nullptr); + *exec_scope, param_name, true, arg_val, nullptr, arg_val->type); *exec_scope = var->child_scope; *next_proto_i += 1; @@ -13021,7 +13083,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod if (!param_name) return false; if (!is_var_args) { ZigVar *var = add_variable(ira->codegen, param_decl_node, - *child_scope, param_name, true, arg_val, nullptr); + *child_scope, param_name, true, arg_val, nullptr, arg_val->type); *child_scope = var->child_scope; var->shadowable = !comptime_arg; @@ -13075,9 +13137,7 @@ static ZigVar *get_fn_var_by_index(ZigFn *fn_entry, size_t index) { return fn_entry->variable_list.at(next_var_i); } -static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, - ZigVar *var) -{ +static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var) { while (var->next_var != nullptr) { var = var->next_var; } @@ -13086,14 +13146,14 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, assert(ira->codegen->errors.length != 0); return ira->codegen->invalid_instruction; } - if (var->value->type == nullptr || type_is_invalid(var->value->type)) + if (var->var_type == nullptr || type_is_invalid(var->var_type)) return ira->codegen->invalid_instruction; bool comptime_var_mem = ir_get_var_is_comptime(var); ConstExprValue *mem_slot = nullptr; - if (var->value->special == ConstValSpecialStatic) { - mem_slot = var->value; + if (var->const_value->special == ConstValSpecialStatic) { + mem_slot = var->const_value; } else { if (var->mem_slot_index != SIZE_MAX && (comptime_var_mem || var->gen_is_const)) { // find the relevant exec_context @@ -13122,7 +13182,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, assert(!comptime_var_mem); ptr_mut = ConstPtrMutRuntimeVar; } - return ir_get_const_ptr(ira, instruction, mem_slot, var->value->type, + return ir_get_const_ptr(ira, instruction, mem_slot, var->var_type, ptr_mut, is_const, is_volatile, var->align_bytes); } } @@ -13133,7 +13193,7 @@ no_mem_slot: IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb, instruction->scope, instruction->source_node, var); - var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type, + var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->var_type, var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0); bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr); @@ -13142,6 +13202,96 @@ no_mem_slot: return var_ptr_instruction; } +static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *ptr, IrInstruction *uncasted_value) +{ + if (ptr->value.type->id != ZigTypeIdPointer) { + ir_add_error(ira, ptr, + buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr->value.type->name))); + return ira->codegen->invalid_instruction; + } + + if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { + return ir_const_void(ira, source_instr); + } + + if (ptr->value.type->data.pointer.is_const && !source_instr->is_gen) { + ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); + return ira->codegen->invalid_instruction; + } + + ZigType *child_type = ptr->value.type->data.pointer.child_type; + IrInstruction *value = ir_implicit_cast(ira, uncasted_value, child_type); + if (value == ira->codegen->invalid_instruction) + return ira->codegen->invalid_instruction; + + if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst) { + ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); + return ira->codegen->invalid_instruction; + } + if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) { + if (instr_is_comptime(value)) { + ConstExprValue *dest_val = const_ptr_pointee(ira, ira->codegen, &ptr->value, source_instr->source_node); + if (dest_val == nullptr) + return ira->codegen->invalid_instruction; + if (dest_val->special != ConstValSpecialRuntime) { + // TODO this allows a value stored to have the original value modified and then + // have that affect what should be a copy. We need some kind of advanced copy-on-write + // system to make these two tests pass at the same time: + // * "string literal used as comptime slice is memoized" + // * "comptime modification of const struct field" - except modified to avoid + // ConstPtrMutComptimeVar, thus defeating the logic below. + bool same_global_refs = ptr->value.data.x_ptr.mut != ConstPtrMutComptimeVar; + copy_const_val(dest_val, &value->value, same_global_refs); + if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { + switch (type_has_one_possible_value(ira->codegen, child_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueNo: + ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; + break; + case OnePossibleValueYes: + break; + } + } + return ir_const_void(ira, source_instr); + } + } + ir_add_error(ira, source_instr, + buf_sprintf("cannot store runtime value in compile time variable")); + ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); + dest_val->type = ira->codegen->builtin_types.entry_invalid; + + return ira->codegen->invalid_instruction; + } + } + + switch (type_requires_comptime(ira->codegen, child_type)) { + case ReqCompTimeInvalid: + return ira->codegen->invalid_instruction; + case ReqCompTimeYes: + switch (type_has_one_possible_value(ira->codegen, ptr->value.type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueNo: + ir_add_error(ira, source_instr, + buf_sprintf("cannot store runtime value in type '%s'", buf_ptr(&child_type->name))); + return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + return ir_const_void(ira, source_instr); + } + zig_unreachable(); + case ReqCompTimeNo: + break; + } + + IrInstruction *result = ir_build_store_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, + ptr, value); + result->value.type = ira->codegen->builtin_types.entry_void; + return result; +} + static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline) @@ -13286,7 +13436,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call } bool cacheable = fn_eval_cacheable(exec_scope, return_type); - IrInstruction *result = nullptr; + ConstExprValue *result = nullptr; if (cacheable) { auto entry = ira->codegen->memoized_fn_eval_table.maybe_get(exec_scope); if (entry) @@ -13302,18 +13452,19 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call if (inferred_err_set_type != nullptr) { inferred_err_set_type->data.error_set.infer_fn = nullptr; - if (result->value.type->id == ZigTypeIdErrorUnion) { - if (result->value.data.x_err_union.err != nullptr) { + if (result->type->id == ZigTypeIdErrorUnion) { + ErrorTableEntry *err = result->data.x_err_union.error_set->data.x_err_set; + if (err != nullptr) { inferred_err_set_type->data.error_set.err_count = 1; inferred_err_set_type->data.error_set.errors = allocate(1); - inferred_err_set_type->data.error_set.errors[0] = result->value.data.x_err_union.err; + inferred_err_set_type->data.error_set.errors[0] = err; } - ZigType *fn_inferred_err_set_type = result->value.type->data.error_union.err_set_type; + ZigType *fn_inferred_err_set_type = result->type->data.error_union.err_set_type; inferred_err_set_type->data.error_set.err_count = fn_inferred_err_set_type->data.error_set.err_count; inferred_err_set_type->data.error_set.errors = fn_inferred_err_set_type->data.error_set.errors; - } else if (result->value.type->id == ZigTypeIdErrorSet) { - inferred_err_set_type->data.error_set.err_count = result->value.type->data.error_set.err_count; - inferred_err_set_type->data.error_set.errors = result->value.type->data.error_set.errors; + } else if (result->type->id == ZigTypeIdErrorSet) { + inferred_err_set_type->data.error_set.err_count = result->type->data.error_set.err_count; + inferred_err_set_type->data.error_set.errors = result->type->data.error_set.errors; } } @@ -13321,13 +13472,12 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call ira->codegen->memoized_fn_eval_table.put(exec_scope, result); } - if (type_is_invalid(result->value.type)) + if (type_is_invalid(result->type)) return ira->codegen->invalid_instruction; } - IrInstruction *new_instruction = ir_const(ira, &call_instruction->base, result->value.type); - // TODO should we use copy_const_val? - new_instruction->value = result->value; + IrInstruction *new_instruction = ir_const(ira, &call_instruction->base, result->type); + copy_const_val(&new_instruction->value, result, true); new_instruction->value.type = return_type; return ir_finish_anal(ira, new_instruction); } @@ -13486,18 +13636,21 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call ConstExprValue *var_args_val = create_const_arg_tuple(ira->codegen, first_var_arg, inst_fn_type_id.param_count); ZigVar *var = add_variable(ira->codegen, param_decl_node, - impl_fn->child_scope, param_name, true, var_args_val, nullptr); + impl_fn->child_scope, param_name, true, var_args_val, nullptr, var_args_val->type); impl_fn->child_scope = var->child_scope; } if (fn_proto_node->data.fn_proto.align_expr != nullptr) { - IrInstruction *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope, + ConstExprValue *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr, get_align_amt_type(ira->codegen), ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec); + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, + impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr); + const_instruction->base.value = *align_result; uint32_t align_bytes = 0; - ir_resolve_align(ira, align_result, &align_bytes); + ir_resolve_align(ira, &const_instruction->base, &align_bytes); impl_fn->align_bytes = align_bytes; inst_fn_type_id.alignment = align_bytes; } @@ -13575,12 +13728,12 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call ira->codegen->fn_defs.append(impl_fn); } - ZigType *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type; - if (fn_type_can_fail(&impl_fn->type_entry->data.fn.fn_type_id)) { + FnTypeId *impl_fn_type_id = &impl_fn->type_entry->data.fn.fn_type_id; + if (fn_type_can_fail(impl_fn_type_id)) { parent_fn_entry->calls_or_awaits_errorable_fn = true; } - size_t impl_param_count = impl_fn->type_entry->data.fn.fn_type_id.param_count; + size_t impl_param_count = impl_fn_type_id->param_count; if (call_instruction->is_async) { IrInstruction *result = ir_analyze_async_call(ira, call_instruction, impl_fn, impl_fn->type_entry, fn_ref, casted_args, impl_param_count, async_allocator_inst); @@ -13593,9 +13746,9 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call call_instruction->base.scope, call_instruction->base.source_node, impl_fn, nullptr, impl_param_count, casted_args, false, fn_inline, call_instruction->is_async, nullptr, casted_new_stack); - new_call_instruction->value.type = return_type; + new_call_instruction->value.type = impl_fn_type_id->return_type; - ir_add_alloca(ira, new_call_instruction, return_type); + ir_add_alloca(ira, new_call_instruction, impl_fn_type_id->return_type); return ir_finish_anal(ira, new_call_instruction); } @@ -13790,6 +13943,13 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source switch (ptr_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: zig_unreachable(); + case ConstPtrSpecialNull: + if (dst_size == 0) + return ErrorNone; + opt_ir_add_error_node(ira, codegen, source_node, + buf_sprintf("attempt to read %zu bytes from null pointer", + dst_size)); + return ErrorSemanticAnalyzeFail; case ConstPtrSpecialRef: { opt_ir_add_error_node(ira, codegen, source_node, buf_sprintf("attempt to read %zu bytes from pointer to %s which is %zu bytes", @@ -13822,6 +13982,9 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source return ErrorNone; } case ConstPtrSpecialBaseStruct: + case ConstPtrSpecialBaseErrorUnionCode: + case ConstPtrSpecialBaseErrorUnionPayload: + case ConstPtrSpecialBaseOptionalPayload: case ConstPtrSpecialDiscard: case ConstPtrSpecialHardCodedAddr: case ConstPtrSpecialFunction: @@ -14017,9 +14180,14 @@ static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructi if (!ir_resolve_comptime(ira, cond_br_instruction->is_comptime->child, &is_comptime)) return ir_unreach_error(ira); - if (is_comptime || instr_is_comptime(condition)) { + ZigType *bool_type = ira->codegen->builtin_types.entry_bool; + IrInstruction *casted_condition = ir_implicit_cast(ira, condition, bool_type); + if (type_is_invalid(casted_condition->value.type)) + return ir_unreach_error(ira); + + if (is_comptime || instr_is_comptime(casted_condition)) { bool cond_is_true; - if (!ir_resolve_bool(ira, condition, &cond_is_true)) + if (!ir_resolve_bool(ira, casted_condition, &cond_is_true)) return ir_unreach_error(ira); IrBasicBlock *old_dest_block = cond_is_true ? @@ -14038,11 +14206,6 @@ static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructi return ir_finish_anal(ira, result); } - ZigType *bool_type = ira->codegen->builtin_types.entry_bool; - IrInstruction *casted_condition = ir_implicit_cast(ira, condition, bool_type); - if (casted_condition == ira->codegen->invalid_instruction) - return ir_unreach_error(ira); - assert(cond_br_instruction->then_block != cond_br_instruction->else_block); IrBasicBlock *new_then_block = ir_get_new_bb_runtime(ira, cond_br_instruction->then_block, &cond_br_instruction->base); if (new_then_block == nullptr) @@ -14081,8 +14244,7 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh if (value->value.special != ConstValSpecialRuntime) { IrInstruction *result = ir_const(ira, &phi_instruction->base, nullptr); - // TODO use copy_const_val? - result->value = value->value; + copy_const_val(&result->value, &value->value, true); return result; } else { return value; @@ -14131,14 +14293,24 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh if (type_is_invalid(resolved_type)) return ira->codegen->invalid_instruction; - if (resolved_type->id == ZigTypeIdComptimeFloat || - resolved_type->id == ZigTypeIdComptimeInt || - resolved_type->id == ZigTypeIdNull || - resolved_type->id == ZigTypeIdUndefined) - { + switch (type_has_one_possible_value(ira->codegen, resolved_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + return ir_const(ira, &phi_instruction->base, resolved_type); + case OnePossibleValueNo: + break; + } + + switch (type_requires_comptime(ira->codegen, resolved_type)) { + case ReqCompTimeInvalid: + return ira->codegen->invalid_instruction; + case ReqCompTimeYes: ir_add_error_node(ira, phi_instruction->base.source_node, - buf_sprintf("unable to infer expression type")); + buf_sprintf("values of type '%s' must be comptime known", buf_ptr(&resolved_type->name))); return ira->codegen->invalid_instruction; + case ReqCompTimeNo: + break; } bool all_stack_ptrs = (resolved_type->id == ZigTypeIdPointer); @@ -14428,10 +14600,18 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct } case ConstPtrSpecialBaseStruct: zig_panic("TODO elem ptr on a const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO elem ptr on a const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO elem ptr on a const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO elem ptr on a const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO element ptr of a function casted to a ptr"); + case ConstPtrSpecialNull: + zig_panic("TODO elem ptr on a null pointer"); } if (new_index >= mem_size) { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, @@ -14481,10 +14661,18 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct } case ConstPtrSpecialBaseStruct: zig_panic("TODO elem ptr on a slice backed by const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO elem ptr on a slice backed by const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO elem ptr on a slice backed by const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO elem ptr on a slice backed by const optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO elem ptr on a slice that was ptrcast from a function"); + case ConstPtrSpecialNull: + zig_panic("TODO elem ptr on a slice has a null pointer"); } return result; } else if (array_type->id == ZigTypeIdArray) { @@ -15171,74 +15359,23 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc } } -static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) { - IrInstruction *ptr = load_ptr_instruction->ptr->child; - if (type_is_invalid(ptr->value.type)) - return ira->codegen->invalid_instruction; - return ir_get_deref(ira, &load_ptr_instruction->base, ptr); -} - -static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *store_ptr_instruction) { - IrInstruction *ptr = store_ptr_instruction->ptr->child; +static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *instruction) { + IrInstruction *ptr = instruction->ptr->child; if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; - IrInstruction *value = store_ptr_instruction->value->child; + IrInstruction *value = instruction->value->child; if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - if (ptr->value.type->id != ZigTypeIdPointer) { - ir_add_error(ira, ptr, - buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr->value.type->name))); - return ira->codegen->invalid_instruction; - } - - if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { - return ir_const_void(ira, &store_ptr_instruction->base); - } - - if (ptr->value.type->data.pointer.is_const && !store_ptr_instruction->base.is_gen) { - ir_add_error(ira, &store_ptr_instruction->base, buf_sprintf("cannot assign to constant")); - return ira->codegen->invalid_instruction; - } + return ir_analyze_store_ptr(ira, &instruction->base, ptr, value); +} - ZigType *child_type = ptr->value.type->data.pointer.child_type; - IrInstruction *casted_value = ir_implicit_cast(ira, value, child_type); - if (casted_value == ira->codegen->invalid_instruction) +static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *instruction) { + IrInstruction *ptr = instruction->ptr->child; + if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; - - if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst) { - ir_add_error(ira, &store_ptr_instruction->base, buf_sprintf("cannot assign to constant")); - return ira->codegen->invalid_instruction; - } - if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) { - if (instr_is_comptime(casted_value)) { - ConstExprValue *dest_val = const_ptr_pointee(ira, ira->codegen, &ptr->value, store_ptr_instruction->base.source_node); - if (dest_val == nullptr) - return ira->codegen->invalid_instruction; - if (dest_val->special != ConstValSpecialRuntime) { - *dest_val = casted_value->value; - if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { - ira->new_irb.current_basic_block->must_be_comptime_source_instr = &store_ptr_instruction->base; - } - return ir_const_void(ira, &store_ptr_instruction->base); - } - } - ir_add_error(ira, &store_ptr_instruction->base, - buf_sprintf("cannot store runtime value in compile time variable")); - ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); - dest_val->type = ira->codegen->builtin_types.entry_invalid; - - return ira->codegen->invalid_instruction; - } - } - - IrInstruction *result = ir_build_store_ptr(&ira->new_irb, - store_ptr_instruction->base.scope, store_ptr_instruction->base.source_node, - ptr, casted_value); - result->value.type = ira->codegen->builtin_types.entry_void; - return result; + return ir_get_deref(ira, &instruction->base, ptr); } static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) { @@ -15709,11 +15846,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, zig_unreachable(); } -static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstructionTestNonNull *instruction) { - IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) - return ira->codegen->invalid_instruction; - +static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *value) { ZigType *type_entry = value->value.type; if (type_entry->id == ZigTypeIdOptional) { @@ -15722,60 +15855,66 @@ static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIns if (!maybe_val) return ira->codegen->invalid_instruction; - return ir_const_bool(ira, &instruction->base, !optional_value_is_null(maybe_val)); + return ir_const_bool(ira, source_inst, !optional_value_is_null(maybe_val)); } IrInstruction *result = ir_build_test_nonnull(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, value); + source_inst->scope, source_inst->source_node, value); result->value.type = ira->codegen->builtin_types.entry_bool; return result; } else if (type_entry->id == ZigTypeIdNull) { - return ir_const_bool(ira, &instruction->base, false); + return ir_const_bool(ira, source_inst, false); } else { - return ir_const_bool(ira, &instruction->base, true); + return ir_const_bool(ira, source_inst, true); } } -static IrInstruction *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, - IrInstructionUnwrapOptional *unwrap_maybe_instruction) -{ - IrInstruction *value = unwrap_maybe_instruction->value->child; +static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstructionTestNonNull *instruction) { + IrInstruction *value = instruction->value->child; if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - ZigType *ptr_type = value->value.type; + return ir_analyze_test_non_null(ira, &instruction->base, value); +} + +static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *base_ptr, bool safety_check_on) +{ + ZigType *ptr_type = base_ptr->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) { + if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - } else if (type_entry->id != ZigTypeIdOptional) { - ir_add_error_node(ira, unwrap_maybe_instruction->value->source_node, + + if (type_entry->id != ZigTypeIdOptional) { + ir_add_error_node(ira, base_ptr->source_node, buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } + ZigType *child_type = type_entry->data.maybe.child_type; ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); - if (instr_is_comptime(value)) { - ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); + if (instr_is_comptime(base_ptr)) { + ConstExprValue *val = ir_resolve_const(ira, base_ptr, UndefBad); if (!val) return ira->codegen->invalid_instruction; - ConstExprValue *maybe_val = const_ptr_pointee(ira, ira->codegen, val, unwrap_maybe_instruction->base.source_node); + ConstExprValue *maybe_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); if (maybe_val == nullptr) return ira->codegen->invalid_instruction; if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { if (optional_value_is_null(maybe_val)) { - ir_add_error(ira, &unwrap_maybe_instruction->base, buf_sprintf("unable to unwrap null")); + ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null")); return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_const(ira, &unwrap_maybe_instruction->base, result_type); + IrInstruction *result = ir_const(ira, source_instr, result_type); ConstExprValue *out_val = &result->value; out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.mut = val->data.x_ptr.mut; - if (type_is_codegen_pointer(child_type)) { + if (types_have_same_zig_comptime_repr(type_entry, child_type)) { out_val->data.x_ptr.data.ref.pointee = maybe_val; } else { out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_optional; @@ -15784,13 +15923,22 @@ static IrInstruction *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, } } - IrInstruction *result = ir_build_unwrap_maybe(&ira->new_irb, - unwrap_maybe_instruction->base.scope, unwrap_maybe_instruction->base.source_node, - value, unwrap_maybe_instruction->safety_check_on); + IrInstruction *result = ir_build_optional_unwrap_ptr(&ira->new_irb, source_instr->scope, + source_instr->source_node, base_ptr, safety_check_on); result->value.type = result_type; return result; } +static IrInstruction *ir_analyze_instruction_optional_unwrap_ptr(IrAnalyze *ira, + IrInstructionOptionalUnwrapPtr *instruction) +{ + IrInstruction *base_ptr = instruction->base_ptr->child; + if (type_is_invalid(base_ptr->value.type)) + return ira->codegen->invalid_instruction; + + return ir_analyze_unwrap_optional_payload(ira, &instruction->base, base_ptr, instruction->safety_check_on); +} + static IrInstruction *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionCtz *ctz_instruction) { IrInstruction *value = ctz_instruction->value->child; if (type_is_invalid(value->value.type)) { @@ -16091,9 +16239,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, return result; } - IrInstruction *result = ir_build_load_ptr(&ira->new_irb, - switch_target_instruction->base.scope, switch_target_instruction->base.source_node, - target_value_ptr); + IrInstruction *result = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr); result->value.type = target_type; return result; } @@ -16123,8 +16269,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, return result; } - IrInstruction *union_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr); + IrInstruction *union_value = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr); union_value->value.type = target_type; IrInstruction *union_tag_inst = ir_build_union_tag(&ira->new_irb, switch_target_instruction->base.scope, @@ -16148,8 +16293,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, return result; } - IrInstruction *enum_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr); + IrInstruction *enum_value = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr); enum_value->value.type = target_type; return enum_value; } @@ -16306,7 +16450,7 @@ static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrI Error err; assert(container_type->id == ZigTypeIdUnion); - if ((err = ensure_complete_type(ira->codegen, container_type))) + if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; if (instr_field_count != 1) { @@ -16350,12 +16494,8 @@ static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrI ConstExprValue *out_val = &result->value; out_val->data.x_union.payload = field_val; out_val->data.x_union.tag = type_field->enum_field->value; - - ConstParent *parent = get_const_val_parent(ira->codegen, field_val); - if (parent != nullptr) { - parent->id = ConstParentIdUnion; - parent->data.p_union.union_val = out_val; - } + out_val->parent.id = ConstParentIdUnion; + out_val->parent.data.p_union.union_val = out_val; return result; } @@ -16382,7 +16522,7 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc return ira->codegen->invalid_instruction; } - if ((err = ensure_complete_type(ira->codegen, container_type))) + if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; size_t actual_field_count = container_type->data.structure.src_field_count; @@ -16461,9 +16601,8 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc if (const_val.special == ConstValSpecialStatic) { IrInstruction *result = ir_const(ira, instruction, nullptr); ConstExprValue *out_val = &result->value; - // TODO copy_const_val? - *out_val = const_val; - result->value.type = container_type; + copy_const_val(out_val, &const_val, true); + out_val->type = container_type; for (size_t i = 0; i < instr_field_count; i += 1) { ConstExprValue *field_val = &out_val->data.x_struct.fields[i]; @@ -16495,127 +16634,119 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, IrInstructionContainerInitList *instruction) { - IrInstruction *container_type_value = instruction->container_type->child; - if (type_is_invalid(container_type_value->value.type)) + ZigType *container_type = ir_resolve_type(ira, instruction->container_type->child); + if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; size_t elem_count = instruction->item_count; - if (container_type_value->value.type->id == ZigTypeIdMetaType) { - ZigType *container_type = ir_resolve_type(ira, container_type_value); - if (type_is_invalid(container_type)) - return ira->codegen->invalid_instruction; - if (container_type->id == ZigTypeIdStruct && !is_slice(container_type) && elem_count == 0) { - return ir_analyze_container_init_fields(ira, &instruction->base, container_type, - 0, nullptr); - } else if (is_slice(container_type) || container_type->id == ZigTypeIdArray) { - // array is same as slice init but we make a compile error if the length is wrong - ZigType *child_type; - if (container_type->id == ZigTypeIdArray) { - child_type = container_type->data.array.child_type; - if (container_type->data.array.len != elem_count) { - ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count); + if (container_type->id == ZigTypeIdStruct && !is_slice(container_type) && elem_count == 0) { + return ir_analyze_container_init_fields(ira, &instruction->base, container_type, + 0, nullptr); + } else if (is_slice(container_type) || container_type->id == ZigTypeIdArray) { + // array is same as slice init but we make a compile error if the length is wrong + ZigType *child_type; + if (container_type->id == ZigTypeIdArray) { + child_type = container_type->data.array.child_type; + if (container_type->data.array.len != elem_count) { + ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count); - ir_add_error(ira, &instruction->base, - buf_sprintf("expected %s literal, found %s literal", - buf_ptr(&container_type->name), buf_ptr(&literal_type->name))); - return ira->codegen->invalid_instruction; - } - } else { - ZigType *pointer_type = container_type->data.structure.fields[slice_ptr_index].type_entry; - assert(pointer_type->id == ZigTypeIdPointer); - child_type = pointer_type->data.pointer.child_type; + ir_add_error(ira, &instruction->base, + buf_sprintf("expected %s literal, found %s literal", + buf_ptr(&container_type->name), buf_ptr(&literal_type->name))); + return ira->codegen->invalid_instruction; } + } else { + ZigType *pointer_type = container_type->data.structure.fields[slice_ptr_index].type_entry; + assert(pointer_type->id == ZigTypeIdPointer); + child_type = pointer_type->data.pointer.child_type; + } - ZigType *fixed_size_array_type = get_array_type(ira->codegen, child_type, elem_count); + ZigType *fixed_size_array_type = get_array_type(ira->codegen, child_type, elem_count); - ConstExprValue const_val = {}; - const_val.special = ConstValSpecialStatic; - const_val.type = fixed_size_array_type; - const_val.data.x_array.data.s_none.elements = create_const_vals(elem_count); + ConstExprValue const_val = {}; + const_val.special = ConstValSpecialStatic; + const_val.type = fixed_size_array_type; + const_val.data.x_array.data.s_none.elements = create_const_vals(elem_count); - bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope); + bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope); - IrInstruction **new_items = allocate(elem_count); + IrInstruction **new_items = allocate(elem_count); - IrInstruction *first_non_const_instruction = nullptr; + IrInstruction *first_non_const_instruction = nullptr; - for (size_t i = 0; i < elem_count; i += 1) { - IrInstruction *arg_value = instruction->items[i]->child; - if (type_is_invalid(arg_value->value.type)) - return ira->codegen->invalid_instruction; + for (size_t i = 0; i < elem_count; i += 1) { + IrInstruction *arg_value = instruction->items[i]->child; + if (type_is_invalid(arg_value->value.type)) + return ira->codegen->invalid_instruction; - IrInstruction *casted_arg = ir_implicit_cast(ira, arg_value, child_type); - if (casted_arg == ira->codegen->invalid_instruction) - return ira->codegen->invalid_instruction; + IrInstruction *casted_arg = ir_implicit_cast(ira, arg_value, child_type); + if (casted_arg == ira->codegen->invalid_instruction) + return ira->codegen->invalid_instruction; - new_items[i] = casted_arg; + new_items[i] = casted_arg; - if (const_val.special == ConstValSpecialStatic) { - if (is_comptime || casted_arg->value.special != ConstValSpecialRuntime) { - ConstExprValue *elem_val = ir_resolve_const(ira, casted_arg, UndefBad); - if (!elem_val) - return ira->codegen->invalid_instruction; + if (const_val.special == ConstValSpecialStatic) { + if (is_comptime || casted_arg->value.special != ConstValSpecialRuntime) { + ConstExprValue *elem_val = ir_resolve_const(ira, casted_arg, UndefBad); + if (!elem_val) + return ira->codegen->invalid_instruction; - copy_const_val(&const_val.data.x_array.data.s_none.elements[i], elem_val, true); - } else { - first_non_const_instruction = casted_arg; - const_val.special = ConstValSpecialRuntime; - } + copy_const_val(&const_val.data.x_array.data.s_none.elements[i], elem_val, true); + } else { + first_non_const_instruction = casted_arg; + const_val.special = ConstValSpecialRuntime; } } + } - if (const_val.special == ConstValSpecialStatic) { - IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - ConstExprValue *out_val = &result->value; - // TODO copy_const_val? - *out_val = const_val; - result->value.type = fixed_size_array_type; - for (size_t i = 0; i < elem_count; i += 1) { - ConstExprValue *elem_val = &out_val->data.x_array.data.s_none.elements[i]; - ConstParent *parent = get_const_val_parent(ira->codegen, elem_val); - if (parent != nullptr) { - parent->id = ConstParentIdArray; - parent->data.p_array.array_val = out_val; - parent->data.p_array.elem_index = i; - } + if (const_val.special == ConstValSpecialStatic) { + IrInstruction *result = ir_const(ira, &instruction->base, nullptr); + ConstExprValue *out_val = &result->value; + copy_const_val(out_val, &const_val, true); + result->value.type = fixed_size_array_type; + for (size_t i = 0; i < elem_count; i += 1) { + ConstExprValue *elem_val = &out_val->data.x_array.data.s_none.elements[i]; + ConstParent *parent = get_const_val_parent(ira->codegen, elem_val); + if (parent != nullptr) { + parent->id = ConstParentIdArray; + parent->data.p_array.array_val = out_val; + parent->data.p_array.elem_index = i; } - return result; } + return result; + } - if (is_comptime) { - ir_add_error_node(ira, first_non_const_instruction->source_node, - buf_sprintf("unable to evaluate constant expression")); - return ira->codegen->invalid_instruction; - } + if (is_comptime) { + ir_add_error_node(ira, first_non_const_instruction->source_node, + buf_sprintf("unable to evaluate constant expression")); + return ira->codegen->invalid_instruction; + } - IrInstruction *new_instruction = ir_build_container_init_list(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, - container_type_value, elem_count, new_items); - new_instruction->value.type = fixed_size_array_type; - ir_add_alloca(ira, new_instruction, fixed_size_array_type); - return new_instruction; - } else if (container_type->id == ZigTypeIdVoid) { - if (elem_count != 0) { - ir_add_error_node(ira, instruction->base.source_node, - buf_sprintf("void expression expects no arguments")); - return ira->codegen->invalid_instruction; - } - return ir_const_void(ira, &instruction->base); - } else { + IrInstruction *new_instruction = ir_build_container_init_list(&ira->new_irb, + instruction->base.scope, instruction->base.source_node, + nullptr, elem_count, new_items); + new_instruction->value.type = fixed_size_array_type; + ir_add_alloca(ira, new_instruction, fixed_size_array_type); + return new_instruction; + } else if (container_type->id == ZigTypeIdVoid) { + if (elem_count != 0) { ir_add_error_node(ira, instruction->base.source_node, - buf_sprintf("type '%s' does not support array initialization", - buf_ptr(&container_type->name))); + buf_sprintf("void expression expects no arguments")); return ira->codegen->invalid_instruction; } + return ir_const_void(ira, &instruction->base); } else { - ir_add_error(ira, container_type_value, - buf_sprintf("expected type, found '%s' value", buf_ptr(&container_type_value->value.type->name))); + ir_add_error_node(ira, instruction->base.source_node, + buf_sprintf("type '%s' does not support array initialization", + buf_ptr(&container_type->name))); return ira->codegen->invalid_instruction; } } -static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, IrInstructionContainerInitFields *instruction) { +static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, + IrInstructionContainerInitFields *instruction) +{ IrInstruction *container_type_value = instruction->container_type->child; ZigType *container_type = ir_resolve_type(ira, container_type_value); if (type_is_invalid(container_type)) @@ -16675,7 +16806,7 @@ static IrInstruction *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruct if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - IrInstruction *casted_value = ir_implicit_cast(ira, value, value->value.type); + IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_global_error_set); if (type_is_invalid(casted_value->value.type)) return ira->codegen->invalid_instruction; @@ -16936,10 +17067,11 @@ static ZigType *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, Zig ZigVar *var = tld->var; - if ((err = ensure_complete_type(ira->codegen, var->value->type))) + if ((err = ensure_complete_type(ira->codegen, var->const_value->type))) return ira->codegen->builtin_types.entry_invalid; - assert(var->value->type->id == ZigTypeIdMetaType); - return var->value->data.x_type; + + assert(var->const_value->type->id == ZigTypeIdMetaType); + return var->const_value->data.x_type; } static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope) { @@ -16994,7 +17126,6 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco definition_array->special = ConstValSpecialStatic; definition_array->type = get_array_type(ira->codegen, type_info_definition_type, definition_count); definition_array->data.x_array.special = ConstArraySpecialNone; - definition_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; definition_array->data.x_array.data.s_none.elements = create_const_vals(definition_count); init_const_slice(ira->codegen, out_val, definition_array, 0, definition_count, false); @@ -17025,33 +17156,30 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco inner_fields[1].data.x_bool = curr_entry->value->visib_mod == VisibModPub; inner_fields[2].special = ConstValSpecialStatic; inner_fields[2].type = type_info_definition_data_type; - inner_fields[2].data.x_union.parent.id = ConstParentIdStruct; - inner_fields[2].data.x_union.parent.data.p_struct.struct_val = definition_val; - inner_fields[2].data.x_union.parent.data.p_struct.field_index = 1; + inner_fields[2].parent.id = ConstParentIdStruct; + inner_fields[2].parent.data.p_struct.struct_val = definition_val; + inner_fields[2].parent.data.p_struct.field_index = 1; switch (curr_entry->value->id) { case TldIdVar: { ZigVar *var = ((TldVar *)curr_entry->value)->var; - if ((err = ensure_complete_type(ira->codegen, var->value->type))) + if ((err = ensure_complete_type(ira->codegen, var->const_value->type))) return ErrorSemanticAnalyzeFail; - if (var->value->type->id == ZigTypeIdMetaType) - { + if (var->const_value->type->id == ZigTypeIdMetaType) { // We have a variable of type 'type', so it's actually a type definition. // 0: Data.Type: type bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0); - inner_fields[2].data.x_union.payload = var->value; - } - else - { + inner_fields[2].data.x_union.payload = var->const_value; + } else { // We have a variable of another type, so we store the type of the variable. // 1: Data.Var: type bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 1); ConstExprValue *payload = create_const_vals(1); payload->type = ira->codegen->builtin_types.entry_type; - payload->data.x_type = var->value->type; + payload->data.x_type = var->const_value->type; inner_fields[2].data.x_union.payload = payload; } @@ -17071,8 +17199,8 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco ConstExprValue *fn_def_val = create_const_vals(1); fn_def_val->special = ConstValSpecialStatic; fn_def_val->type = type_info_fn_def_type; - fn_def_val->data.x_struct.parent.id = ConstParentIdUnion; - fn_def_val->data.x_struct.parent.data.p_union.union_val = &inner_fields[2]; + fn_def_val->parent.id = ConstParentIdUnion; + fn_def_val->parent.data.p_union.union_val = &inner_fields[2]; ConstExprValue *fn_def_fields = create_const_vals(9); fn_def_val->data.x_struct.fields = fn_def_fields; @@ -17136,20 +17264,18 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco fn_arg_name_array->type = get_array_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr), fn_arg_count); fn_arg_name_array->data.x_array.special = ConstArraySpecialNone; - fn_arg_name_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; fn_arg_name_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count); init_const_slice(ira->codegen, &fn_def_fields[8], fn_arg_name_array, 0, fn_arg_count, false); - for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) - { + for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) { ZigVar *arg_var = fn_entry->variable_list.at(fn_arg_index); ConstExprValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.data.s_none.elements[fn_arg_index]; ConstExprValue *arg_name = create_const_str_lit(ira->codegen, &arg_var->name); init_const_slice(ira->codegen, fn_arg_name_val, arg_name, 0, buf_len(&arg_var->name), true); - fn_arg_name_val->data.x_struct.parent.id = ConstParentIdArray; - fn_arg_name_val->data.x_struct.parent.data.p_array.array_val = fn_arg_name_array; - fn_arg_name_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index; + fn_arg_name_val->parent.id = ConstParentIdArray; + fn_arg_name_val->parent.data.p_array.array_val = fn_arg_name_array; + fn_arg_name_val->parent.data.p_array.elem_index = fn_arg_index; } inner_fields[2].data.x_union.payload = fn_def_val; @@ -17442,7 +17568,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE enum_field_array->special = ConstValSpecialStatic; enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count); enum_field_array->data.x_array.special = ConstArraySpecialNone; - enum_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; enum_field_array->data.x_array.data.s_none.elements = create_const_vals(enum_field_count); init_const_slice(ira->codegen, &fields[2], enum_field_array, 0, enum_field_count, false); @@ -17452,9 +17577,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index]; ConstExprValue *enum_field_val = &enum_field_array->data.x_array.data.s_none.elements[enum_field_index]; make_enum_field_val(ira, enum_field_val, enum_field, type_info_enum_field_type); - enum_field_val->data.x_struct.parent.id = ConstParentIdArray; - enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array; - enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index; + enum_field_val->parent.id = ConstParentIdArray; + enum_field_val->parent.data.p_array.array_val = enum_field_array; + enum_field_val->parent.data.p_array.elem_index = enum_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 3); @@ -17481,7 +17606,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE error_array->special = ConstValSpecialStatic; error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count); error_array->data.x_array.special = ConstArraySpecialNone; - error_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; error_array->data.x_array.data.s_none.elements = create_const_vals(error_count); init_const_slice(ira->codegen, &fields[0], error_array, 0, error_count, false); @@ -17505,9 +17629,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE bigint_init_unsigned(&inner_fields[1].data.x_bigint, error->value); error_val->data.x_struct.fields = inner_fields; - error_val->data.x_struct.parent.id = ConstParentIdArray; - error_val->data.x_struct.parent.data.p_array.array_val = error_array; - error_val->data.x_struct.parent.data.p_array.elem_index = error_index; + error_val->parent.id = ConstParentIdArray; + error_val->parent.data.p_array.array_val = error_array; + error_val->parent.data.p_array.elem_index = error_index; } break; @@ -17576,7 +17700,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE union_field_array->special = ConstValSpecialStatic; union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count); union_field_array->data.x_array.special = ConstArraySpecialNone; - union_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; union_field_array->data.x_array.data.s_none.elements = create_const_vals(union_field_count); init_const_slice(ira->codegen, &fields[2], union_field_array, 0, union_field_count, false); @@ -17609,9 +17732,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(union_field->name), true); union_field_val->data.x_struct.fields = inner_fields; - union_field_val->data.x_struct.parent.id = ConstParentIdArray; - union_field_val->data.x_struct.parent.data.p_array.array_val = union_field_array; - union_field_val->data.x_struct.parent.data.p_array.elem_index = union_field_index; + union_field_val->parent.id = ConstParentIdArray; + union_field_val->parent.data.p_array.array_val = union_field_array; + union_field_val->parent.data.p_array.elem_index = union_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 3); @@ -17651,7 +17774,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE struct_field_array->special = ConstValSpecialStatic; struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count); struct_field_array->data.x_array.special = ConstArraySpecialNone; - struct_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; struct_field_array->data.x_array.data.s_none.elements = create_const_vals(struct_field_count); init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false); @@ -17685,9 +17807,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(struct_field->name), true); struct_field_val->data.x_struct.fields = inner_fields; - struct_field_val->data.x_struct.parent.id = ConstParentIdArray; - struct_field_val->data.x_struct.parent.data.p_array.array_val = struct_field_array; - struct_field_val->data.x_struct.parent.data.p_array.elem_index = struct_field_index; + struct_field_val->parent.id = ConstParentIdArray; + struct_field_val->parent.data.p_array.array_val = struct_field_array; + struct_field_val->parent.data.p_array.elem_index = struct_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 2); @@ -17757,7 +17879,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE fn_arg_array->special = ConstValSpecialStatic; fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count); fn_arg_array->data.x_array.special = ConstArraySpecialNone; - fn_arg_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; fn_arg_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count); init_const_slice(ira->codegen, &fields[5], fn_arg_array, 0, fn_arg_count, false); @@ -17794,9 +17915,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE } fn_arg_val->data.x_struct.fields = inner_fields; - fn_arg_val->data.x_struct.parent.id = ConstParentIdArray; - fn_arg_val->data.x_struct.parent.data.p_array.array_val = fn_arg_array; - fn_arg_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index; + fn_arg_val->parent.id = ConstParentIdArray; + fn_arg_val->parent.data.p_array.array_val = fn_arg_array; + fn_arg_val->parent.data.p_array.elem_index = fn_arg_index; } break; @@ -17840,8 +17961,8 @@ static IrInstruction *ir_analyze_instruction_type_info(IrAnalyze *ira, if (payload != nullptr) { assert(payload->type->id == ZigTypeIdStruct); - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = out_val; + payload->parent.id = ConstParentIdUnion; + payload->parent.data.p_union.union_val = out_val; } return result; @@ -17913,10 +18034,10 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct // Execute the C import block like an inline function ZigType *void_type = ira->codegen->builtin_types.entry_void; - IrInstruction *cimport_result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type, + ConstExprValue *cimport_result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type, ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, &cimport_scope->buf, block_node, nullptr, nullptr); - if (type_is_invalid(cimport_result->value.type)) + if (type_is_invalid(cimport_result->type)) return ira->codegen->invalid_instruction; find_libc_include_path(ira->codegen); @@ -18066,7 +18187,7 @@ static IrInstruction *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstru return result; } -static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) { +static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchgSrc *instruction) { ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->type_value->child); if (type_is_invalid(operand_type)) return ira->codegen->invalid_instruction; @@ -18138,9 +18259,9 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi zig_panic("TODO compile-time execution of cmpxchg"); } - IrInstruction *result = ir_build_cmpxchg(&ira->new_irb, instruction->base.scope, instruction->base.source_node, - nullptr, casted_ptr, casted_cmp_value, casted_new_value, nullptr, nullptr, instruction->is_weak, - operand_type, success_order, failure_order); + IrInstruction *result = ir_build_cmpxchg_gen(ira, &instruction->base, + casted_ptr, casted_cmp_value, casted_new_value, + success_order, failure_order, instruction->is_weak); result->value.type = get_optional_type(ira->codegen, operand_type); ir_add_alloca(ira, result, result->value.type); return result; @@ -18312,18 +18433,6 @@ static IrInstruction *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInst return ir_analyze_err_set_cast(ira, &instruction->base, target, dest_type); } -static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { - Error err; - - if (ty->id == ZigTypeIdPointer) { - if ((err = type_resolve(ira->codegen, ty->data.pointer.child_type, ResolveStatusAlignmentKnown))) - return err; - } - - *result_align = get_ptr_align(ira->codegen, ty); - return ErrorNone; -} - static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionFromBytes *instruction) { Error err; @@ -18442,6 +18551,20 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct return ir_resolve_cast(ira, &instruction->base, target, dest_slice_type, CastOpResizeSlice, true); } +static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { + Error err; + + ZigType *ptr_type = get_src_ptr_type(ty); + assert(ptr_type != nullptr); + if (ptr_type->id == ZigTypeIdPointer) { + if ((err = type_resolve(ira->codegen, ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return err; + } + + *result_align = get_ptr_align(ira->codegen, ty); + return ErrorNone; +} + static IrInstruction *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) { ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) @@ -18646,10 +18769,18 @@ static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructio } case ConstPtrSpecialBaseStruct: zig_panic("TODO memset on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memset on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memset on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memset on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memset on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memset on null ptr"); } size_t count = bigint_as_unsigned(&casted_count->value.data.x_bigint); @@ -18761,10 +18892,18 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio } case ConstPtrSpecialBaseStruct: zig_panic("TODO memcpy on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memcpy on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memcpy on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memcpy on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memcpy on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memcpy on null ptr"); } if (dest_start + count > dest_end) { @@ -18797,10 +18936,18 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio } case ConstPtrSpecialBaseStruct: zig_panic("TODO memcpy on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memcpy on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memcpy on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memcpy on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memcpy on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memcpy on null ptr"); } if (src_start + count > src_end) { @@ -18828,9 +18975,9 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction if (type_is_invalid(ptr_ptr->value.type)) return ira->codegen->invalid_instruction; - ZigType *ptr_type = ptr_ptr->value.type; - assert(ptr_type->id == ZigTypeIdPointer); - ZigType *array_type = ptr_type->data.pointer.child_type; + ZigType *ptr_ptr_type = ptr_ptr->value.type; + assert(ptr_ptr_type->id == ZigTypeIdPointer); + ZigType *array_type = ptr_ptr_type->data.pointer.child_type; IrInstruction *start = instruction->start->child; if (type_is_invalid(start->value.type)) @@ -18859,10 +19006,10 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction bool is_comptime_const = ptr_ptr->value.special == ConstValSpecialStatic && ptr_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst; ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, - ptr_type->data.pointer.is_const || is_comptime_const, - ptr_type->data.pointer.is_volatile, + ptr_ptr_type->data.pointer.is_const || is_comptime_const, + ptr_ptr_type->data.pointer.is_volatile, PtrLenUnknown, - ptr_type->data.pointer.explicit_alignment, 0, 0); + ptr_ptr_type->data.pointer.explicit_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else if (array_type->id == ZigTypeIdPointer) { if (array_type->data.pointer.ptr_len == PtrLenSingle) { @@ -18960,6 +19107,12 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialBaseStruct: zig_panic("TODO slice const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO slice const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO slice const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO slice const inner optional payload"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; @@ -18967,6 +19120,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialFunction: zig_panic("TODO slice of ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO slice of null ptr"); } } else if (is_slice(array_type)) { ConstExprValue *slice_ptr = const_ptr_pointee(ira, ira->codegen, &ptr_ptr->value, instruction->base.source_node); @@ -18997,6 +19152,12 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialBaseStruct: zig_panic("TODO slice const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO slice const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO slice const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO slice const inner optional payload"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; @@ -19004,6 +19165,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialFunction: zig_panic("TODO slice of slice cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO slice of null"); } } else { zig_unreachable(); @@ -19069,6 +19232,12 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction zig_unreachable(); case ConstPtrSpecialBaseStruct: zig_panic("TODO"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO"); case ConstPtrSpecialHardCodedAddr: init_const_ptr_hard_coded_addr(ira->codegen, ptr_val, parent_ptr->type->data.pointer.child_type, @@ -19077,6 +19246,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialFunction: zig_panic("TODO"); + case ConstPtrSpecialNull: + zig_panic("TODO"); } ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index]; @@ -19432,7 +19603,8 @@ static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; if (err_union_val->special != ConstValSpecialRuntime) { - return ir_const_bool(ira, &instruction->base, (err_union_val->data.x_err_union.err != nullptr)); + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; + return ir_const_bool(ira, &instruction->base, (err != nullptr)); } } @@ -19458,48 +19630,47 @@ static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruct } } -static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, - IrInstructionUnwrapErrCode *instruction) -{ - IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) +static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, IrInstructionUnwrapErrCode *instruction) { + IrInstruction *base_ptr = instruction->err_union->child; + if (type_is_invalid(base_ptr->value.type)) return ira->codegen->invalid_instruction; - ZigType *ptr_type = value->value.type; + ZigType *ptr_type = base_ptr->value.type; // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) { + if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - } else if (type_entry->id == ZigTypeIdErrorUnion) { - if (instr_is_comptime(value)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); - if (!ptr_val) - return ira->codegen->invalid_instruction; - ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node); - if (err_union_val == nullptr) - return ira->codegen->invalid_instruction; - if (err_union_val->special != ConstValSpecialRuntime) { - ErrorTableEntry *err = err_union_val->data.x_err_union.err; - assert(err); - - IrInstruction *result = ir_const(ira, &instruction->base, - type_entry->data.error_union.err_set_type); - result->value.data.x_err_set = err; - return result; - } - } - IrInstruction *result = ir_build_unwrap_err_code(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, value); - result->value.type = type_entry->data.error_union.err_set_type; - return result; - } else { - ir_add_error(ira, value, + if (type_entry->id != ZigTypeIdErrorUnion) { + ir_add_error(ira, base_ptr, buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } + + if (instr_is_comptime(base_ptr)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node); + if (err_union_val == nullptr) + return ira->codegen->invalid_instruction; + if (err_union_val->special != ConstValSpecialRuntime) { + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; + assert(err); + + IrInstruction *result = ir_const(ira, &instruction->base, + type_entry->data.error_union.err_set_type); + result->value.data.x_err_set = err; + return result; + } + } + + IrInstruction *result = ir_build_unwrap_err_code(&ira->new_irb, + instruction->base.scope, instruction->base.source_node, base_ptr); + result->value.type = type_entry->data.error_union.err_set_type; + return result; } static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, @@ -19515,48 +19686,48 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) { + if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - } else if (type_entry->id == ZigTypeIdErrorUnion) { - ZigType *payload_type = type_entry->data.error_union.payload_type; - if (type_is_invalid(payload_type)) { - return ira->codegen->invalid_instruction; - } - ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - PtrLenSingle, 0, 0, 0); - if (instr_is_comptime(value)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); - if (!ptr_val) - return ira->codegen->invalid_instruction; - ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node); - if (err_union_val == nullptr) - return ira->codegen->invalid_instruction; - if (err_union_val->special != ConstValSpecialRuntime) { - ErrorTableEntry *err = err_union_val->data.x_err_union.err; - if (err != nullptr) { - ir_add_error(ira, &instruction->base, - buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); - return ira->codegen->invalid_instruction; - } - IrInstruction *result = ir_const(ira, &instruction->base, result_type); - result->value.data.x_ptr.special = ConstPtrSpecialRef; - result->value.data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; - return result; - } - } - - IrInstruction *result = ir_build_unwrap_err_payload(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, value, instruction->safety_check_on); - result->value.type = result_type; - return result; - } else { + if (type_entry->id != ZigTypeIdErrorUnion) { ir_add_error(ira, value, buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } + ZigType *payload_type = type_entry->data.error_union.payload_type; + if (type_is_invalid(payload_type)) + return ira->codegen->invalid_instruction; + + ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + PtrLenSingle, 0, 0, 0); + if (instr_is_comptime(value)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node); + if (err_union_val == nullptr) + return ira->codegen->invalid_instruction; + if (err_union_val->special != ConstValSpecialRuntime) { + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; + if (err != nullptr) { + ir_add_error(ira, &instruction->base, + buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); + return ira->codegen->invalid_instruction; + } + + IrInstruction *result = ir_const(ira, &instruction->base, result_type); + result->value.data.x_ptr.special = ConstPtrSpecialRef; + result->value.data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; + return result; + } + } + + IrInstruction *result = ir_build_unwrap_err_payload(&ira->new_irb, + instruction->base.scope, instruction->base.source_node, value, instruction->safety_check_on); + result->value.type = result_type; + return result; } static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnProto *instruction) { @@ -19973,7 +20144,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_create_const(&ira->new_irb, target->scope, target->source_node, result_type); + IrInstruction *result = ir_const(ira, target, result_type); copy_const_val(&result->value, val, false); result->value.type = result_type; return result; @@ -20021,8 +20192,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, - dest_type); + IrInstruction *result = ir_const(ira, source_instr, dest_type); copy_const_val(&result->value, val, false); result->value.type = dest_type; return result; @@ -20045,9 +20215,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ return ira->codegen->invalid_instruction; } - IrInstruction *casted_ptr = ir_build_ptr_cast(&ira->new_irb, source_instr->scope, - source_instr->source_node, nullptr, ptr); - casted_ptr->value.type = dest_type; + IrInstruction *casted_ptr = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr); if (type_has_bits(dest_type) && !type_has_bits(src_type)) { ErrorMsg *msg = ir_add_error(ira, source_instr, @@ -20073,7 +20241,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ return result; } -static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCast *instruction) { +static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCastSrc *instruction) { IrInstruction *dest_type_value = instruction->dest_type->child; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) @@ -20211,15 +20379,29 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem))) return err; } - break; + return ErrorNone; case ConstArraySpecialUndef: zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type"); case ConstArraySpecialBuf: zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type"); } - - return ErrorNone; + zig_unreachable(); } + case ZigTypeIdEnum: + switch (val->type->data.enumeration.layout) { + case ContainerLayoutAuto: + zig_panic("TODO buf_read_value_bytes enum auto"); + case ContainerLayoutPacked: + zig_panic("TODO buf_read_value_bytes enum packed"); + case ContainerLayoutExtern: { + ZigType *tag_int_type = val->type->data.enumeration.tag_int_type; + assert(tag_int_type->id == ZigTypeIdInt); + bigint_read_twos_complement(&val->data.x_enum_tag, buf, tag_int_type->data.integral.bit_count, + codegen->is_big_endian, tag_int_type->data.integral.is_signed); + return ErrorNone; + } + } + zig_unreachable(); case ZigTypeIdStruct: switch (val->type->data.structure.layout) { case ContainerLayoutAuto: { @@ -20258,8 +20440,6 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou zig_panic("TODO buf_read_value_bytes error union"); case ZigTypeIdErrorSet: zig_panic("TODO buf_read_value_bytes pure error type"); - case ZigTypeIdEnum: - zig_panic("TODO buf_read_value_bytes enum type"); case ZigTypeIdFn: zig_panic("TODO buf_read_value_bytes fn type"); case ZigTypeIdUnion: @@ -20426,8 +20606,7 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, case TldIdContainer: case TldIdCompTime: zig_unreachable(); - case TldIdVar: - { + case TldIdVar: { TldVar *tld_var = (TldVar *)tld; ZigVar *var = tld_var->var; @@ -20445,8 +20624,7 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, return ir_get_deref(ira, &instruction->base, var_ptr); } } - case TldIdFn: - { + case TldIdFn: { TldFn *tld_fn = (TldFn *)tld; ZigFn *fn_entry = tld_fn->fn_entry; assert(fn_entry->type_entry); @@ -20492,8 +20670,7 @@ static IrInstruction *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstru if (!val) return ira->codegen->invalid_instruction; if (val->type->id == ZigTypeIdPointer && val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { - IrInstruction *result = ir_create_const(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, usize); + IrInstruction *result = ir_const(ira, &instruction->base, usize); bigint_init_unsigned(&result->value.data.x_bigint, val->data.x_ptr.data.hard_coded_addr.addr); result->value.type = usize; return result; @@ -21331,6 +21508,9 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: case IrInstructionIdCast: + case IrInstructionIdDeclVarGen: + case IrInstructionIdPtrCastGen: + case IrInstructionIdCmpxchgGen: zig_unreachable(); case IrInstructionIdReturn: @@ -21341,8 +21521,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_un_op(ira, (IrInstructionUnOp *)instruction); case IrInstructionIdBinOp: return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction); - case IrInstructionIdDeclVar: - return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVar *)instruction); + case IrInstructionIdDeclVarSrc: + return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVarSrc *)instruction); case IrInstructionIdLoadPtr: return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction); case IrInstructionIdStorePtr: @@ -21387,8 +21567,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction); case IrInstructionIdTestNonNull: return ir_analyze_instruction_test_non_null(ira, (IrInstructionTestNonNull *)instruction); - case IrInstructionIdUnwrapOptional: - return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapOptional *)instruction); + case IrInstructionIdOptionalUnwrapPtr: + return ir_analyze_instruction_optional_unwrap_ptr(ira, (IrInstructionOptionalUnwrapPtr *)instruction); case IrInstructionIdClz: return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction); case IrInstructionIdCtz: @@ -21429,8 +21609,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_c_undef(ira, (IrInstructionCUndef *)instruction); case IrInstructionIdEmbedFile: return ir_analyze_instruction_embed_file(ira, (IrInstructionEmbedFile *)instruction); - case IrInstructionIdCmpxchg: - return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchg *)instruction); + case IrInstructionIdCmpxchgSrc: + return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchgSrc *)instruction); case IrInstructionIdFence: return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction); case IrInstructionIdTruncate: @@ -21497,8 +21677,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_decl_ref(ira, (IrInstructionDeclRef *)instruction); case IrInstructionIdPanic: return ir_analyze_instruction_panic(ira, (IrInstructionPanic *)instruction); - case IrInstructionIdPtrCast: - return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCast *)instruction); + case IrInstructionIdPtrCastSrc: + return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCastSrc *)instruction); case IrInstructionIdBitCast: return ir_analyze_instruction_bit_cast(ira, (IrInstructionBitCast *)instruction); case IrInstructionIdIntToPtr: @@ -21682,7 +21862,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdBr: case IrInstructionIdCondBr: case IrInstructionIdSwitchBr: - case IrInstructionIdDeclVar: + case IrInstructionIdDeclVarSrc: + case IrInstructionIdDeclVarGen: case IrInstructionIdStorePtr: case IrInstructionIdCall: case IrInstructionIdReturn: @@ -21697,7 +21878,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCInclude: case IrInstructionIdCDefine: case IrInstructionIdCUndef: - case IrInstructionIdCmpxchg: case IrInstructionIdFence: case IrInstructionIdMemset: case IrInstructionIdMemcpy: @@ -21725,6 +21905,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdMergeErrRetTraces: case IrInstructionIdMarkErrRetTracePtr: case IrInstructionIdAtomicRmw: + case IrInstructionIdCmpxchgGen: + case IrInstructionIdCmpxchgSrc: return true; case IrInstructionIdPhi: @@ -21750,7 +21932,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdSliceType: case IrInstructionIdSizeOf: case IrInstructionIdTestNonNull: - case IrInstructionIdUnwrapOptional: + case IrInstructionIdOptionalUnwrapPtr: case IrInstructionIdClz: case IrInstructionIdCtz: case IrInstructionIdPopCount: @@ -21777,7 +21959,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdErrWrapPayload: case IrInstructionIdFnProto: case IrInstructionIdTestComptime: - case IrInstructionIdPtrCast: + case IrInstructionIdPtrCastSrc: + case IrInstructionIdPtrCastGen: case IrInstructionIdBitCast: case IrInstructionIdWidenOrShorten: case IrInstructionIdPtrToInt: diff --git a/src/ir.hpp b/src/ir.hpp index 7af1d7f52b..0a7c614812 100644 --- a/src/ir.hpp +++ b/src/ir.hpp @@ -13,7 +13,7 @@ bool ir_gen(CodeGen *g, AstNode *node, Scope *scope, IrExecutable *ir_executable); bool ir_gen_fn(CodeGen *g, ZigFn *fn_entry); -IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, +ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota, ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name, IrExecutable *parent_exec); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index b5099db86a..a3ec8e9d35 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -172,7 +172,7 @@ static void ir_print_bin_op(IrPrint *irp, IrInstructionBinOp *bin_op_instruction } } -static void ir_print_decl_var(IrPrint *irp, IrInstructionDeclVar *decl_var_instruction) { +static void ir_print_decl_var_src(IrPrint *irp, IrInstructionDeclVarSrc *decl_var_instruction) { const char *var_or_const = decl_var_instruction->var->gen_is_const ? "const" : "var"; const char *name = buf_ptr(&decl_var_instruction->var->name); if (decl_var_instruction->var_type) { @@ -332,8 +332,8 @@ static void ir_print_var_ptr(IrPrint *irp, IrInstructionVarPtr *instruction) { } static void ir_print_load_ptr(IrPrint *irp, IrInstructionLoadPtr *instruction) { - fprintf(irp->f, "*"); ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ".*"); } static void ir_print_store_ptr(IrPrint *irp, IrInstructionStorePtr *instruction) { @@ -479,15 +479,15 @@ static void ir_print_size_of(IrPrint *irp, IrInstructionSizeOf *instruction) { fprintf(irp->f, ")"); } -static void ir_print_test_null(IrPrint *irp, IrInstructionTestNonNull *instruction) { - fprintf(irp->f, "*"); +static void ir_print_test_non_null(IrPrint *irp, IrInstructionTestNonNull *instruction) { ir_print_other_instruction(irp, instruction->value); fprintf(irp->f, " != null"); } -static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapOptional *instruction) { - fprintf(irp->f, "&??*"); - ir_print_other_instruction(irp, instruction->value); +static void ir_print_optional_unwrap_ptr(IrPrint *irp, IrInstructionOptionalUnwrapPtr *instruction) { + fprintf(irp->f, "&"); + ir_print_other_instruction(irp, instruction->base_ptr); + fprintf(irp->f, ".*.?"); if (!instruction->safety_check_on) { fprintf(irp->f, " // no safety"); } @@ -613,7 +613,7 @@ static void ir_print_embed_file(IrPrint *irp, IrInstructionEmbedFile *instructio fprintf(irp->f, ")"); } -static void ir_print_cmpxchg(IrPrint *irp, IrInstructionCmpxchg *instruction) { +static void ir_print_cmpxchg_src(IrPrint *irp, IrInstructionCmpxchgSrc *instruction) { fprintf(irp->f, "@cmpxchg("); ir_print_other_instruction(irp, instruction->ptr); fprintf(irp->f, ", "); @@ -627,6 +627,16 @@ static void ir_print_cmpxchg(IrPrint *irp, IrInstructionCmpxchg *instruction) { fprintf(irp->f, ")"); } +static void ir_print_cmpxchg_gen(IrPrint *irp, IrInstructionCmpxchgGen *instruction) { + fprintf(irp->f, "@cmpxchg("); + ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->cmp_value); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->new_value); + fprintf(irp->f, ", TODO print atomic orders)"); +} + static void ir_print_fence(IrPrint *irp, IrInstructionFence *instruction) { fprintf(irp->f, "@fence("); ir_print_other_instruction(irp, instruction->order_value); @@ -820,13 +830,13 @@ static void ir_print_test_err(IrPrint *irp, IrInstructionTestErr *instruction) { } static void ir_print_unwrap_err_code(IrPrint *irp, IrInstructionUnwrapErrCode *instruction) { - fprintf(irp->f, "@unwrapErrorCode("); - ir_print_other_instruction(irp, instruction->value); + fprintf(irp->f, "UnwrapErrorCode("); + ir_print_other_instruction(irp, instruction->err_union); fprintf(irp->f, ")"); } static void ir_print_unwrap_err_payload(IrPrint *irp, IrInstructionUnwrapErrPayload *instruction) { - fprintf(irp->f, "@unwrapErrorPayload("); + fprintf(irp->f, "ErrorUnionFieldPayload("); ir_print_other_instruction(irp, instruction->value); fprintf(irp->f, ")"); if (!instruction->safety_check_on) { @@ -879,7 +889,7 @@ static void ir_print_test_comptime(IrPrint *irp, IrInstructionTestComptime *inst fprintf(irp->f, ")"); } -static void ir_print_ptr_cast(IrPrint *irp, IrInstructionPtrCast *instruction) { +static void ir_print_ptr_cast_src(IrPrint *irp, IrInstructionPtrCastSrc *instruction) { fprintf(irp->f, "@ptrCast("); if (instruction->dest_type) { ir_print_other_instruction(irp, instruction->dest_type); @@ -889,6 +899,12 @@ static void ir_print_ptr_cast(IrPrint *irp, IrInstructionPtrCast *instruction) { fprintf(irp->f, ")"); } +static void ir_print_ptr_cast_gen(IrPrint *irp, IrInstructionPtrCastGen *instruction) { + fprintf(irp->f, "@ptrCast("); + ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ")"); +} + static void ir_print_bit_cast(IrPrint *irp, IrInstructionBitCast *instruction) { fprintf(irp->f, "@bitCast("); if (instruction->dest_type) { @@ -900,7 +916,7 @@ static void ir_print_bit_cast(IrPrint *irp, IrInstructionBitCast *instruction) { } static void ir_print_widen_or_shorten(IrPrint *irp, IrInstructionWidenOrShorten *instruction) { - fprintf(irp->f, "@widenOrShorten("); + fprintf(irp->f, "WidenOrShorten("); ir_print_other_instruction(irp, instruction->target); fprintf(irp->f, ")"); } @@ -1323,6 +1339,20 @@ static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) { fprintf(irp->f, ")"); } +static void ir_print_decl_var_gen(IrPrint *irp, IrInstructionDeclVarGen *decl_var_instruction) { + ZigVar *var = decl_var_instruction->var; + const char *var_or_const = decl_var_instruction->var->gen_is_const ? "const" : "var"; + const char *name = buf_ptr(&decl_var_instruction->var->name); + fprintf(irp->f, "%s %s: %s align(%u) = ", var_or_const, name, buf_ptr(&var->var_type->name), + var->align_bytes); + + ir_print_other_instruction(irp, decl_var_instruction->init_value); + if (decl_var_instruction->var->is_comptime != nullptr) { + fprintf(irp->f, " // comptime = "); + ir_print_other_instruction(irp, decl_var_instruction->var->is_comptime); + } +} + static void ir_print_bswap(IrPrint *irp, IrInstructionBswap *instruction) { fprintf(irp->f, "@bswap("); if (instruction->type != nullptr) { @@ -1361,8 +1391,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdBinOp: ir_print_bin_op(irp, (IrInstructionBinOp *)instruction); break; - case IrInstructionIdDeclVar: - ir_print_decl_var(irp, (IrInstructionDeclVar *)instruction); + case IrInstructionIdDeclVarSrc: + ir_print_decl_var_src(irp, (IrInstructionDeclVarSrc *)instruction); break; case IrInstructionIdCast: ir_print_cast(irp, (IrInstructionCast *)instruction); @@ -1452,10 +1482,10 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_size_of(irp, (IrInstructionSizeOf *)instruction); break; case IrInstructionIdTestNonNull: - ir_print_test_null(irp, (IrInstructionTestNonNull *)instruction); + ir_print_test_non_null(irp, (IrInstructionTestNonNull *)instruction); break; - case IrInstructionIdUnwrapOptional: - ir_print_unwrap_maybe(irp, (IrInstructionUnwrapOptional *)instruction); + case IrInstructionIdOptionalUnwrapPtr: + ir_print_optional_unwrap_ptr(irp, (IrInstructionOptionalUnwrapPtr *)instruction); break; case IrInstructionIdCtz: ir_print_ctz(irp, (IrInstructionCtz *)instruction); @@ -1508,8 +1538,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdEmbedFile: ir_print_embed_file(irp, (IrInstructionEmbedFile *)instruction); break; - case IrInstructionIdCmpxchg: - ir_print_cmpxchg(irp, (IrInstructionCmpxchg *)instruction); + case IrInstructionIdCmpxchgSrc: + ir_print_cmpxchg_src(irp, (IrInstructionCmpxchgSrc *)instruction); + break; + case IrInstructionIdCmpxchgGen: + ir_print_cmpxchg_gen(irp, (IrInstructionCmpxchgGen *)instruction); break; case IrInstructionIdFence: ir_print_fence(irp, (IrInstructionFence *)instruction); @@ -1607,8 +1640,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdTestComptime: ir_print_test_comptime(irp, (IrInstructionTestComptime *)instruction); break; - case IrInstructionIdPtrCast: - ir_print_ptr_cast(irp, (IrInstructionPtrCast *)instruction); + case IrInstructionIdPtrCastSrc: + ir_print_ptr_cast_src(irp, (IrInstructionPtrCastSrc *)instruction); + break; + case IrInstructionIdPtrCastGen: + ir_print_ptr_cast_gen(irp, (IrInstructionPtrCastGen *)instruction); break; case IrInstructionIdBitCast: ir_print_bit_cast(irp, (IrInstructionBitCast *)instruction); @@ -1775,6 +1811,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdCheckRuntimeScope: ir_print_check_runtime_scope(irp, (IrInstructionCheckRuntimeScope *)instruction); break; + case IrInstructionIdDeclVarGen: + ir_print_decl_var_gen(irp, (IrInstructionDeclVarGen *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/src/parser.cpp b/src/parser.cpp index 077365995e..81bd469d1c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -381,7 +381,7 @@ static AstNode *ast_parse_if_expr_helper(ParseContext *pc, AstNode *(*body_parse else_body = ast_expect(pc, body_parser); } - assert(res->type == NodeTypeTestExpr); + assert(res->type == NodeTypeIfOptional); if (err_payload != nullptr) { AstNodeTestExpr old = res->data.test_expr; res->type = NodeTypeIfErrorExpr; @@ -990,7 +990,7 @@ static AstNode *ast_parse_if_statement(ParseContext *pc) { if (requires_semi && else_body == nullptr) expect_token(pc, TokenIdSemicolon); - assert(res->type == NodeTypeTestExpr); + assert(res->type == NodeTypeIfOptional); if (err_payload != nullptr) { AstNodeTestExpr old = res->data.test_expr; res->type = NodeTypeIfErrorExpr; @@ -2204,7 +2204,7 @@ static AstNode *ast_parse_if_prefix(ParseContext *pc) { Optional opt_payload = ast_parse_ptr_payload(pc); PtrPayload payload; - AstNode *res = ast_create_node(pc, NodeTypeTestExpr, first); + AstNode *res = ast_create_node(pc, NodeTypeIfOptional, first); res->data.test_expr.target_node = condition; if (opt_payload.unwrap(&payload)) { res->data.test_expr.var_symbol = token_buf(payload.payload); @@ -2999,7 +2999,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont visit_field(&node->data.if_err_expr.then_node, visit, context); visit_field(&node->data.if_err_expr.else_node, visit, context); break; - case NodeTypeTestExpr: + case NodeTypeIfOptional: visit_field(&node->data.test_expr.target_node, visit, context); visit_field(&node->data.test_expr.then_node, visit, context); visit_field(&node->data.test_expr.else_node, visit, context); diff --git a/std/event/fs.zig b/std/event/fs.zig index 1b8e1aa5dc..7e77b3e6e2 100644 --- a/std/event/fs.zig +++ b/std/event/fs.zig @@ -1307,32 +1307,29 @@ pub fn Watch(comptime V: type) type { const test_tmp_dir = "std_event_fs_test"; -test "write a file, watch it, write it again" { - if (builtin.os == builtin.Os.windows) { - // TODO this test is disabled on windows until the coroutine rewrite is finished. - // https://github.com/ziglang/zig/issues/1363 - return error.SkipZigTest; - } - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - const allocator = &da.allocator; - - // TODO move this into event loop too - try os.makePath(allocator, test_tmp_dir); - defer os.deleteTree(allocator, test_tmp_dir) catch {}; - - var loop: Loop = undefined; - try loop.initMultiThreaded(allocator); - defer loop.deinit(); - - var result: anyerror!void = error.ResultNeverWritten; - const handle = try async testFsWatchCantFail(&loop, &result); - defer cancel handle; - - loop.run(); - return result; -} +// TODO this test is disabled until the coroutine rewrite is finished. +//test "write a file, watch it, write it again" { +// return error.SkipZigTest; +// var da = std.heap.DirectAllocator.init(); +// defer da.deinit(); +// +// const allocator = &da.allocator; +// +// // TODO move this into event loop too +// try os.makePath(allocator, test_tmp_dir); +// defer os.deleteTree(allocator, test_tmp_dir) catch {}; +// +// var loop: Loop = undefined; +// try loop.initMultiThreaded(allocator); +// defer loop.deinit(); +// +// var result: anyerror!void = error.ResultNeverWritten; +// const handle = try async testFsWatchCantFail(&loop, &result); +// defer cancel handle; +// +// loop.run(); +// return result; +//} async fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void { result.* = await (async testFsWatch(loop) catch unreachable); diff --git a/test/behavior.zig b/test/behavior.zig deleted file mode 100644 index 10cd08dad7..0000000000 --- a/test/behavior.zig +++ /dev/null @@ -1,82 +0,0 @@ -const builtin = @import("builtin"); - -comptime { - _ = @import("cases/align.zig"); - _ = @import("cases/alignof.zig"); - _ = @import("cases/array.zig"); - _ = @import("cases/asm.zig"); - _ = @import("cases/atomics.zig"); - _ = @import("cases/bitcast.zig"); - _ = @import("cases/bool.zig"); - _ = @import("cases/bswap.zig"); - _ = @import("cases/bitreverse.zig"); - _ = @import("cases/bugs/1076.zig"); - _ = @import("cases/bugs/1111.zig"); - _ = @import("cases/bugs/1277.zig"); - _ = @import("cases/bugs/1322.zig"); - _ = @import("cases/bugs/1381.zig"); - _ = @import("cases/bugs/1421.zig"); - _ = @import("cases/bugs/1442.zig"); - _ = @import("cases/bugs/1486.zig"); - _ = @import("cases/bugs/394.zig"); - _ = @import("cases/bugs/655.zig"); - _ = @import("cases/bugs/656.zig"); - _ = @import("cases/bugs/726.zig"); - _ = @import("cases/bugs/828.zig"); - _ = @import("cases/bugs/920.zig"); - _ = @import("cases/byval_arg_var.zig"); - _ = @import("cases/cancel.zig"); - _ = @import("cases/cast.zig"); - _ = @import("cases/const_slice_child.zig"); - _ = @import("cases/coroutine_await_struct.zig"); - _ = @import("cases/coroutines.zig"); - _ = @import("cases/defer.zig"); - _ = @import("cases/enum.zig"); - _ = @import("cases/enum_with_members.zig"); - _ = @import("cases/error.zig"); - _ = @import("cases/eval.zig"); - _ = @import("cases/field_parent_ptr.zig"); - _ = @import("cases/fn.zig"); - _ = @import("cases/fn_in_struct_in_comptime.zig"); - _ = @import("cases/for.zig"); - _ = @import("cases/generics.zig"); - _ = @import("cases/if.zig"); - _ = @import("cases/import.zig"); - _ = @import("cases/incomplete_struct_param_tld.zig"); - _ = @import("cases/inttoptr.zig"); - _ = @import("cases/ir_block_deps.zig"); - _ = @import("cases/math.zig"); - _ = @import("cases/merge_error_sets.zig"); - _ = @import("cases/misc.zig"); - _ = @import("cases/namespace_depends_on_compile_var/index.zig"); - _ = @import("cases/new_stack_call.zig"); - _ = @import("cases/null.zig"); - _ = @import("cases/optional.zig"); - _ = @import("cases/pointers.zig"); - _ = @import("cases/popcount.zig"); - _ = @import("cases/ptrcast.zig"); - _ = @import("cases/pub_enum/index.zig"); - _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig"); - _ = @import("cases/reflection.zig"); - _ = @import("cases/sizeof_and_typeof.zig"); - _ = @import("cases/slice.zig"); - _ = @import("cases/struct.zig"); - _ = @import("cases/struct_contains_null_ptr_itself.zig"); - _ = @import("cases/struct_contains_slice_of_itself.zig"); - _ = @import("cases/switch.zig"); - _ = @import("cases/switch_prong_err_enum.zig"); - _ = @import("cases/switch_prong_implicit_cast.zig"); - _ = @import("cases/syntax.zig"); - _ = @import("cases/this.zig"); - _ = @import("cases/truncate.zig"); - _ = @import("cases/try.zig"); - _ = @import("cases/type_info.zig"); - _ = @import("cases/undefined.zig"); - _ = @import("cases/underscore.zig"); - _ = @import("cases/union.zig"); - _ = @import("cases/var_args.zig"); - _ = @import("cases/void.zig"); - _ = @import("cases/while.zig"); - _ = @import("cases/widening.zig"); - _ = @import("cases/bit_shifting.zig"); -} diff --git a/test/cases/align.zig b/test/cases/align.zig deleted file mode 100644 index 3dff57feb8..0000000000 --- a/test/cases/align.zig +++ /dev/null @@ -1,230 +0,0 @@ -const assert = @import("std").debug.assert; -const builtin = @import("builtin"); - -var foo: u8 align(4) = 100; - -test "global variable alignment" { - assert(@typeOf(&foo).alignment == 4); - assert(@typeOf(&foo) == *align(4) u8); - const slice = (*[1]u8)(&foo)[0..]; - assert(@typeOf(slice) == []align(4) u8); -} - -fn derp() align(@sizeOf(usize) * 2) i32 { - return 1234; -} -fn noop1() align(1) void {} -fn noop4() align(4) void {} - -test "function alignment" { - assert(derp() == 1234); - assert(@typeOf(noop1) == fn () align(1) void); - assert(@typeOf(noop4) == fn () align(4) void); - noop1(); - noop4(); -} - -var baz: packed struct { - a: u32, - b: u32, -} = undefined; - -test "packed struct alignment" { - assert(@typeOf(&baz.b) == *align(1) u32); -} - -const blah: packed struct { - a: u3, - b: u3, - c: u2, -} = undefined; - -test "bit field alignment" { - assert(@typeOf(&blah.b) == *align(1:3:1) const u3); -} - -test "default alignment allows unspecified in type syntax" { - assert(*u32 == *align(@alignOf(u32)) u32); -} - -test "implicitly decreasing pointer alignment" { - const a: u32 align(4) = 3; - const b: u32 align(8) = 4; - assert(addUnaligned(&a, &b) == 7); -} - -fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 { - return a.* + b.*; -} - -test "implicitly decreasing slice alignment" { - const a: u32 align(4) = 3; - const b: u32 align(8) = 4; - assert(addUnalignedSlice((*[1]u32)(&a)[0..], (*[1]u32)(&b)[0..]) == 7); -} -fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 { - return a[0] + b[0]; -} - -test "specifying alignment allows pointer cast" { - testBytesAlign(0x33); -} -fn testBytesAlign(b: u8) void { - var bytes align(4) = []u8{ - b, - b, - b, - b, - }; - const ptr = @ptrCast(*u32, &bytes[0]); - assert(ptr.* == 0x33333333); -} - -test "specifying alignment allows slice cast" { - testBytesAlignSlice(0x33); -} -fn testBytesAlignSlice(b: u8) void { - var bytes align(4) = []u8{ - b, - b, - b, - b, - }; - const slice: []u32 = @bytesToSlice(u32, bytes[0..]); - assert(slice[0] == 0x33333333); -} - -test "@alignCast pointers" { - var x: u32 align(4) = 1; - expectsOnly1(&x); - assert(x == 2); -} -fn expectsOnly1(x: *align(1) u32) void { - expects4(@alignCast(4, x)); -} -fn expects4(x: *align(4) u32) void { - x.* += 1; -} - -test "@alignCast slices" { - var array align(4) = []u32{ - 1, - 1, - }; - const slice = array[0..]; - sliceExpectsOnly1(slice); - assert(slice[0] == 2); -} -fn sliceExpectsOnly1(slice: []align(1) u32) void { - sliceExpects4(@alignCast(4, slice)); -} -fn sliceExpects4(slice: []align(4) u32) void { - slice[0] += 1; -} - -test "implicitly decreasing fn alignment" { - testImplicitlyDecreaseFnAlign(alignedSmall, 1234); - testImplicitlyDecreaseFnAlign(alignedBig, 5678); -} - -fn testImplicitlyDecreaseFnAlign(ptr: fn () align(1) i32, answer: i32) void { - assert(ptr() == answer); -} - -fn alignedSmall() align(8) i32 { - return 1234; -} -fn alignedBig() align(16) i32 { - return 5678; -} - -test "@alignCast functions" { - assert(fnExpectsOnly1(simple4) == 0x19); -} -fn fnExpectsOnly1(ptr: fn () align(1) i32) i32 { - return fnExpects4(@alignCast(4, ptr)); -} -fn fnExpects4(ptr: fn () align(4) i32) i32 { - return ptr(); -} -fn simple4() align(4) i32 { - return 0x19; -} - -test "generic function with align param" { - assert(whyWouldYouEverDoThis(1) == 0x1); - assert(whyWouldYouEverDoThis(4) == 0x1); - assert(whyWouldYouEverDoThis(8) == 0x1); -} - -fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { - return 0x1; -} - -test "@ptrCast preserves alignment of bigger source" { - var x: u32 align(16) = 1234; - const ptr = @ptrCast(*u8, &x); - assert(@typeOf(ptr) == *align(16) u8); -} - -test "runtime known array index has best alignment possible" { - // take full advantage of over-alignment - var array align(4) = []u8{ 1, 2, 3, 4 }; - assert(@typeOf(&array[0]) == *align(4) u8); - assert(@typeOf(&array[1]) == *u8); - assert(@typeOf(&array[2]) == *align(2) u8); - assert(@typeOf(&array[3]) == *u8); - - // because align is too small but we still figure out to use 2 - var bigger align(2) = []u64{ 1, 2, 3, 4 }; - assert(@typeOf(&bigger[0]) == *align(2) u64); - assert(@typeOf(&bigger[1]) == *align(2) u64); - assert(@typeOf(&bigger[2]) == *align(2) u64); - assert(@typeOf(&bigger[3]) == *align(2) u64); - - // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2 - var smaller align(2) = []u32{ 1, 2, 3, 4 }; - comptime assert(@typeOf(smaller[0..]) == []align(2) u32); - comptime assert(@typeOf(smaller[0..].ptr) == [*]align(2) u32); - testIndex(smaller[0..].ptr, 0, *align(2) u32); - testIndex(smaller[0..].ptr, 1, *align(2) u32); - testIndex(smaller[0..].ptr, 2, *align(2) u32); - testIndex(smaller[0..].ptr, 3, *align(2) u32); - - // has to use ABI alignment because index known at runtime only - testIndex2(array[0..].ptr, 0, *u8); - testIndex2(array[0..].ptr, 1, *u8); - testIndex2(array[0..].ptr, 2, *u8); - testIndex2(array[0..].ptr, 3, *u8); -} -fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) void { - comptime assert(@typeOf(&smaller[index]) == T); -} -fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) void { - comptime assert(@typeOf(&ptr[index]) == T); -} - -test "alignstack" { - assert(fnWithAlignedStack() == 1234); -} - -fn fnWithAlignedStack() i32 { - @setAlignStack(256); - return 1234; -} - -test "alignment of structs" { - assert(@alignOf(struct { - a: i32, - b: *i32, - }) == @alignOf(usize)); -} - -test "alignment of extern() void" { - var runtime_nothing = nothing; - const casted1 = @ptrCast(*const u8, runtime_nothing); - const casted2 = @ptrCast(extern fn () void, casted1); - casted2(); -} - -extern fn nothing() void {} diff --git a/test/cases/alignof.zig b/test/cases/alignof.zig deleted file mode 100644 index 433e86e45e..0000000000 --- a/test/cases/alignof.zig +++ /dev/null @@ -1,17 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const builtin = @import("builtin"); -const maxInt = std.math.maxInt; - -const Foo = struct { - x: u32, - y: u32, - z: u32, -}; - -test "@alignOf(T) before referencing T" { - comptime assert(@alignOf(Foo) != maxInt(usize)); - if (builtin.arch == builtin.Arch.x86_64) { - comptime assert(@alignOf(Foo) == 4); - } -} diff --git a/test/cases/array.zig b/test/cases/array.zig deleted file mode 100644 index 7c63a649a8..0000000000 --- a/test/cases/array.zig +++ /dev/null @@ -1,173 +0,0 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; - -test "arrays" { - var array: [5]u32 = undefined; - - var i: u32 = 0; - while (i < 5) { - array[i] = i + 1; - i = array[i]; - } - - i = 0; - var accumulator = u32(0); - while (i < 5) { - accumulator += array[i]; - - i += 1; - } - - assert(accumulator == 15); - assert(getArrayLen(array) == 5); -} -fn getArrayLen(a: []const u32) usize { - return a.len; -} - -test "void arrays" { - var array: [4]void = undefined; - array[0] = void{}; - array[1] = array[2]; - assert(@sizeOf(@typeOf(array)) == 0); - assert(array.len == 4); -} - -test "array literal" { - const hex_mult = []u16{ - 4096, - 256, - 16, - 1, - }; - - assert(hex_mult.len == 4); - assert(hex_mult[1] == 256); -} - -test "array dot len const expr" { - assert(comptime x: { - break :x some_array.len == 4; - }); -} - -const ArrayDotLenConstExpr = struct { - y: [some_array.len]u8, -}; -const some_array = []u8{ - 0, - 1, - 2, - 3, -}; - -test "nested arrays" { - const array_of_strings = [][]const u8{ - "hello", - "this", - "is", - "my", - "thing", - }; - for (array_of_strings) |s, i| { - if (i == 0) assert(mem.eql(u8, s, "hello")); - if (i == 1) assert(mem.eql(u8, s, "this")); - if (i == 2) assert(mem.eql(u8, s, "is")); - if (i == 3) assert(mem.eql(u8, s, "my")); - if (i == 4) assert(mem.eql(u8, s, "thing")); - } -} - -var s_array: [8]Sub = undefined; -const Sub = struct { - b: u8, -}; -const Str = struct { - a: []Sub, -}; -test "set global var array via slice embedded in struct" { - var s = Str{ .a = s_array[0..] }; - - s.a[0].b = 1; - s.a[1].b = 2; - s.a[2].b = 3; - - assert(s_array[0].b == 1); - assert(s_array[1].b == 2); - assert(s_array[2].b == 3); -} - -test "array literal with specified size" { - var array = [2]u8{ - 1, - 2, - }; - assert(array[0] == 1); - assert(array[1] == 2); -} - -test "array child property" { - var x: [5]i32 = undefined; - assert(@typeOf(x).Child == i32); -} - -test "array len property" { - var x: [5]i32 = undefined; - assert(@typeOf(x).len == 5); -} - -test "array len field" { - var arr = [4]u8{ 0, 0, 0, 0 }; - var ptr = &arr; - assert(arr.len == 4); - comptime assert(arr.len == 4); - assert(ptr.len == 4); - comptime assert(ptr.len == 4); -} - -test "single-item pointer to array indexing and slicing" { - testSingleItemPtrArrayIndexSlice(); - comptime testSingleItemPtrArrayIndexSlice(); -} - -fn testSingleItemPtrArrayIndexSlice() void { - var array = "aaaa"; - doSomeMangling(&array); - assert(mem.eql(u8, "azya", array)); -} - -fn doSomeMangling(array: *[4]u8) void { - array[1] = 'z'; - array[2..3][0] = 'y'; -} - -test "implicit cast single-item pointer" { - testImplicitCastSingleItemPtr(); - comptime testImplicitCastSingleItemPtr(); -} - -fn testImplicitCastSingleItemPtr() void { - var byte: u8 = 100; - const slice = (*[1]u8)(&byte)[0..]; - slice[0] += 1; - assert(byte == 101); -} - -fn testArrayByValAtComptime(b: [2]u8) u8 { - return b[0]; -} - -test "comptime evalutating function that takes array by value" { - const arr = []u8{ 0, 1 }; - _ = comptime testArrayByValAtComptime(arr); - _ = comptime testArrayByValAtComptime(arr); -} - -test "implicit comptime in array type size" { - var arr: [plusOne(10)]bool = undefined; - assert(arr.len == 11); -} - -fn plusOne(x: u32) u32 { - return x + 1; -} diff --git a/test/cases/asm.zig b/test/cases/asm.zig deleted file mode 100644 index 63e37c857c..0000000000 --- a/test/cases/asm.zig +++ /dev/null @@ -1,48 +0,0 @@ -const config = @import("builtin"); -const assert = @import("std").debug.assert; - -comptime { - if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { - asm volatile ( - \\.globl aoeu; - \\.type aoeu, @function; - \\.set aoeu, derp; - ); - } -} - -test "module level assembly" { - if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { - assert(aoeu() == 1234); - } -} - -test "output constraint modifiers" { - // This is only testing compilation. - var a: u32 = 3; - asm volatile ("" : [_]"=m,r"(a) : : ""); - asm volatile ("" : [_]"=r,m"(a) : : ""); -} - -test "alternative constraints" { - // Make sure we allow commas as a separator for alternative constraints. - var a: u32 = 3; - asm volatile ("" : [_]"=r,m"(a) : [_]"r,m"(a) : ""); -} - -test "sized integer/float in asm input" { - asm volatile ("" : : [_]"m"(usize(3)) : ""); - asm volatile ("" : : [_]"m"(i15(-3)) : ""); - asm volatile ("" : : [_]"m"(u3(3)) : ""); - asm volatile ("" : : [_]"m"(i3(3)) : ""); - asm volatile ("" : : [_]"m"(u121(3)) : ""); - asm volatile ("" : : [_]"m"(i121(3)) : ""); - asm volatile ("" : : [_]"m"(f32(3.17)) : ""); - asm volatile ("" : : [_]"m"(f64(3.17)) : ""); -} - -extern fn aoeu() i32; - -export fn derp() i32 { - return 1234; -} diff --git a/test/cases/atomics.zig b/test/cases/atomics.zig deleted file mode 100644 index 67c9ab3dd1..0000000000 --- a/test/cases/atomics.zig +++ /dev/null @@ -1,71 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const builtin = @import("builtin"); -const AtomicRmwOp = builtin.AtomicRmwOp; -const AtomicOrder = builtin.AtomicOrder; - -test "cmpxchg" { - var x: i32 = 1234; - if (@cmpxchgWeak(i32, &x, 99, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == 1234); - } else { - @panic("cmpxchg should have failed"); - } - - while (@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == 1234); - } - assert(x == 5678); - - assert(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); - assert(x == 42); -} - -test "fence" { - var x: i32 = 1234; - @fence(AtomicOrder.SeqCst); - x = 5678; -} - -test "atomicrmw and atomicload" { - var data: u8 = 200; - testAtomicRmw(&data); - assert(data == 42); - testAtomicLoad(&data); -} - -fn testAtomicRmw(ptr: *u8) void { - const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst); - assert(prev_value == 200); - comptime { - var x: i32 = 1234; - const y: i32 = 12345; - assert(@atomicLoad(i32, &x, AtomicOrder.SeqCst) == 1234); - assert(@atomicLoad(i32, &y, AtomicOrder.SeqCst) == 12345); - } -} - -fn testAtomicLoad(ptr: *u8) void { - const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst); - assert(x == 42); -} - -test "cmpxchg with ptr" { - var data1: i32 = 1234; - var data2: i32 = 5678; - var data3: i32 = 9101; - var x: *i32 = &data1; - if (@cmpxchgWeak(*i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == &data1); - } else { - @panic("cmpxchg should have failed"); - } - - while (@cmpxchgWeak(*i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == &data1); - } - assert(x == &data3); - - assert(@cmpxchgStrong(*i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); - assert(x == &data2); -} diff --git a/test/cases/bit_shifting.zig b/test/cases/bit_shifting.zig deleted file mode 100644 index 325e765bb0..0000000000 --- a/test/cases/bit_shifting.zig +++ /dev/null @@ -1,88 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; - -fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, comptime V: type) type { - assert(Key == @IntType(false, Key.bit_count)); - assert(Key.bit_count >= mask_bit_count); - const ShardKey = @IntType(false, mask_bit_count); - const shift_amount = Key.bit_count - ShardKey.bit_count; - return struct { - const Self = @This(); - shards: [1 << ShardKey.bit_count]?*Node, - - pub fn create() Self { - return Self{ .shards = []?*Node{null} ** (1 << ShardKey.bit_count) }; - } - - fn getShardKey(key: Key) ShardKey { - // https://github.com/ziglang/zig/issues/1544 - // this special case is needed because you can't u32 >> 32. - if (ShardKey == u0) return 0; - - // this can be u1 >> u0 - const shard_key = key >> shift_amount; - - // TODO: https://github.com/ziglang/zig/issues/1544 - // This cast could be implicit if we teach the compiler that - // u32 >> 30 -> u2 - return @intCast(ShardKey, shard_key); - } - - pub fn put(self: *Self, node: *Node) void { - const shard_key = Self.getShardKey(node.key); - node.next = self.shards[shard_key]; - self.shards[shard_key] = node; - } - - pub fn get(self: *Self, key: Key) ?*Node { - const shard_key = Self.getShardKey(key); - var maybe_node = self.shards[shard_key]; - while (maybe_node) |node| : (maybe_node = node.next) { - if (node.key == key) return node; - } - return null; - } - - pub const Node = struct { - key: Key, - value: V, - next: ?*Node, - - pub fn init(self: *Node, key: Key, value: V) void { - self.key = key; - self.value = value; - self.next = null; - } - }; - }; -} - -test "sharded table" { - // realistic 16-way sharding - testShardedTable(u32, 4, 8); - - testShardedTable(u5, 0, 32); // ShardKey == u0 - testShardedTable(u5, 2, 32); - testShardedTable(u5, 5, 32); - - testShardedTable(u1, 0, 2); - testShardedTable(u1, 1, 2); // this does u1 >> u0 - - testShardedTable(u0, 0, 1); -} -fn testShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, comptime node_count: comptime_int) void { - const Table = ShardedTable(Key, mask_bit_count, void); - - var table = Table.create(); - var node_buffer: [node_count]Table.Node = undefined; - for (node_buffer) |*node, i| { - const key = @intCast(Key, i); - assert(table.get(key) == null); - node.init(key, {}); - table.put(node); - } - - for (node_buffer) |*node, i| { - assert(table.get(@intCast(Key, i)) == node); - } -} diff --git a/test/cases/bitcast.zig b/test/cases/bitcast.zig deleted file mode 100644 index d85a84ed22..0000000000 --- a/test/cases/bitcast.zig +++ /dev/null @@ -1,37 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const maxInt = std.math.maxInt; - -test "@bitCast i32 -> u32" { - testBitCast_i32_u32(); - comptime testBitCast_i32_u32(); -} - -fn testBitCast_i32_u32() void { - assert(conv(-1) == maxInt(u32)); - assert(conv2(maxInt(u32)) == -1); -} - -fn conv(x: i32) u32 { - return @bitCast(u32, x); -} -fn conv2(x: u32) i32 { - return @bitCast(i32, x); -} - -test "@bitCast extern enum to its integer type" { - const SOCK = extern enum { - A, - B, - - fn testBitCastExternEnum() void { - var SOCK_DGRAM = @This().B; - var sock_dgram = @bitCast(c_int, SOCK_DGRAM); - assert(sock_dgram == 1); - } - }; - - SOCK.testBitCastExternEnum(); - comptime SOCK.testBitCastExternEnum(); -} - diff --git a/test/cases/bitreverse.zig b/test/cases/bitreverse.zig deleted file mode 100644 index 3721e68a94..0000000000 --- a/test/cases/bitreverse.zig +++ /dev/null @@ -1,81 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const minInt = std.math.minInt; - -test "@bitreverse" { - comptime testBitReverse(); - testBitReverse(); -} - -fn testBitReverse() void { - // using comptime_ints, unsigned - assert(@bitreverse(u0, 0) == 0); - assert(@bitreverse(u5, 0x12) == 0x9); - assert(@bitreverse(u8, 0x12) == 0x48); - assert(@bitreverse(u16, 0x1234) == 0x2c48); - assert(@bitreverse(u24, 0x123456) == 0x6a2c48); - assert(@bitreverse(u32, 0x12345678) == 0x1e6a2c48); - assert(@bitreverse(u40, 0x123456789a) == 0x591e6a2c48); - assert(@bitreverse(u48, 0x123456789abc) == 0x3d591e6a2c48); - assert(@bitreverse(u56, 0x123456789abcde) == 0x7b3d591e6a2c48); - assert(@bitreverse(u64, 0x123456789abcdef1) == 0x8f7b3d591e6a2c48); - assert(@bitreverse(u128, 0x123456789abcdef11121314151617181) == 0x818e868a828c84888f7b3d591e6a2c48); - - // using runtime uints, unsigned - var num0: u0 = 0; - assert(@bitreverse(u0, num0) == 0); - var num5: u5 = 0x12; - assert(@bitreverse(u5, num5) == 0x9); - var num8: u8 = 0x12; - assert(@bitreverse(u8, num8) == 0x48); - var num16: u16 = 0x1234; - assert(@bitreverse(u16, num16) == 0x2c48); - var num24: u24 = 0x123456; - assert(@bitreverse(u24, num24) == 0x6a2c48); - var num32: u32 = 0x12345678; - assert(@bitreverse(u32, num32) == 0x1e6a2c48); - var num40: u40 = 0x123456789a; - assert(@bitreverse(u40, num40) == 0x591e6a2c48); - var num48: u48 = 0x123456789abc; - assert(@bitreverse(u48, num48) == 0x3d591e6a2c48); - var num56: u56 = 0x123456789abcde; - assert(@bitreverse(u56, num56) == 0x7b3d591e6a2c48); - var num64: u64 = 0x123456789abcdef1; - assert(@bitreverse(u64, num64) == 0x8f7b3d591e6a2c48); - var num128: u128 = 0x123456789abcdef11121314151617181; - assert(@bitreverse(u128, num128) == 0x818e868a828c84888f7b3d591e6a2c48); - - // using comptime_ints, signed, positive - assert(@bitreverse(i0, 0) == 0); - assert(@bitreverse(i8, @bitCast(i8, u8(0x92))) == @bitCast(i8, u8( 0x49))); - assert(@bitreverse(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16( 0x2c48))); - assert(@bitreverse(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24( 0x6a2c48))); - assert(@bitreverse(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32( 0x1e6a2c48))); - assert(@bitreverse(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40( 0x591e6a2c48))); - assert(@bitreverse(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48( 0x3d591e6a2c48))); - assert(@bitreverse(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56( 0x7b3d591e6a2c48))); - assert(@bitreverse(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64,u64(0x8f7b3d591e6a2c48))); - assert(@bitreverse(i128, @bitCast(i128,u128(0x123456789abcdef11121314151617181))) == @bitCast(i128,u128(0x818e868a828c84888f7b3d591e6a2c48))); - - // using comptime_ints, signed, negative. Compare to runtime ints returned from llvm. - var neg5: i5 = minInt(i5) + 1; - assert(@bitreverse(i5, minInt(i5) + 1) == @bitreverse(i5, neg5)); - var neg8: i8 = -18; - assert(@bitreverse(i8, -18) == @bitreverse(i8, neg8)); - var neg16: i16 = -32694; - assert(@bitreverse(i16, -32694) == @bitreverse(i16, neg16)); - var neg24: i24 = -6773785; - assert(@bitreverse(i24, -6773785) == @bitreverse(i24, neg24)); - var neg32: i32 = -16773785; - assert(@bitreverse(i32, -16773785) == @bitreverse(i32, neg32)); - var neg40: i40 = minInt(i40) + 12345; - assert(@bitreverse(i40, minInt(i40) + 12345) == @bitreverse(i40, neg40)); - var neg48: i48 = minInt(i48) + 12345; - assert(@bitreverse(i48, minInt(i48) + 12345) == @bitreverse(i48, neg48)); - var neg56: i56 = minInt(i56) + 12345; - assert(@bitreverse(i56, minInt(i56) + 12345) == @bitreverse(i56, neg56)); - var neg64: i64 = minInt(i64) + 12345; - assert(@bitreverse(i64, minInt(i64) + 12345) == @bitreverse(i64, neg64)); - var neg128: i128 = minInt(i128) + 12345; - assert(@bitreverse(i128, minInt(i128) + 12345) == @bitreverse(i128, neg128)); -} diff --git a/test/cases/bool.zig b/test/cases/bool.zig deleted file mode 100644 index 3e4ac9c1cf..0000000000 --- a/test/cases/bool.zig +++ /dev/null @@ -1,35 +0,0 @@ -const assert = @import("std").debug.assert; - -test "bool literals" { - assert(true); - assert(!false); -} - -test "cast bool to int" { - const t = true; - const f = false; - assert(@boolToInt(t) == u32(1)); - assert(@boolToInt(f) == u32(0)); - nonConstCastBoolToInt(t, f); -} - -fn nonConstCastBoolToInt(t: bool, f: bool) void { - assert(@boolToInt(t) == u32(1)); - assert(@boolToInt(f) == u32(0)); -} - -test "bool cmp" { - assert(testBoolCmp(true, false) == false); -} -fn testBoolCmp(a: bool, b: bool) bool { - return a == b; -} - -const global_f = false; -const global_t = true; -const not_global_f = !global_f; -const not_global_t = !global_t; -test "compile time bool not" { - assert(not_global_f); - assert(!not_global_t); -} diff --git a/test/cases/bswap.zig b/test/cases/bswap.zig deleted file mode 100644 index 57993077e1..0000000000 --- a/test/cases/bswap.zig +++ /dev/null @@ -1,32 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; - -test "@bswap" { - comptime testByteSwap(); - testByteSwap(); -} - -fn testByteSwap() void { - assert(@bswap(u0, 0) == 0); - assert(@bswap(u8, 0x12) == 0x12); - assert(@bswap(u16, 0x1234) == 0x3412); - assert(@bswap(u24, 0x123456) == 0x563412); - assert(@bswap(u32, 0x12345678) == 0x78563412); - assert(@bswap(u40, 0x123456789a) == 0x9a78563412); - assert(@bswap(u48, 0x123456789abc) == 0xbc9a78563412); - assert(@bswap(u56, 0x123456789abcde) == 0xdebc9a78563412); - assert(@bswap(u64, 0x123456789abcdef1) == 0xf1debc9a78563412); - assert(@bswap(u128, 0x123456789abcdef11121314151617181) == 0x8171615141312111f1debc9a78563412); - - assert(@bswap(i0, 0) == 0); - assert(@bswap(i8, -50) == -50); - assert(@bswap(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16(0x3412))); - assert(@bswap(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24(0x563412))); - assert(@bswap(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32(0x78563412))); - assert(@bswap(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40(0x9a78563412))); - assert(@bswap(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48(0xbc9a78563412))); - assert(@bswap(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56(0xdebc9a78563412))); - assert(@bswap(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64, u64(0xf1debc9a78563412))); - assert(@bswap(i128, @bitCast(i128, u128(0x123456789abcdef11121314151617181))) == - @bitCast(i128, u128(0x8171615141312111f1debc9a78563412))); -} diff --git a/test/cases/bugs/1076.zig b/test/cases/bugs/1076.zig deleted file mode 100644 index 7b84312310..0000000000 --- a/test/cases/bugs/1076.zig +++ /dev/null @@ -1,16 +0,0 @@ -const std = @import("std"); -const mem = std.mem; -const assert = std.debug.assert; - -test "comptime code should not modify constant data" { - testCastPtrOfArrayToSliceAndPtr(); - comptime testCastPtrOfArrayToSliceAndPtr(); -} - -fn testCastPtrOfArrayToSliceAndPtr() void { - var array = "aoeu"; - const x: [*]u8 = &array; - x[0] += 1; - assert(mem.eql(u8, array[0..], "boeu")); -} - diff --git a/test/cases/bugs/1111.zig b/test/cases/bugs/1111.zig deleted file mode 100644 index f62107f9a3..0000000000 --- a/test/cases/bugs/1111.zig +++ /dev/null @@ -1,12 +0,0 @@ -const Foo = extern enum { - Bar = -1, -}; - -test "issue 1111 fixed" { - const v = Foo.Bar; - - switch (v) { - Foo.Bar => return, - else => return, - } -} diff --git a/test/cases/bugs/1277.zig b/test/cases/bugs/1277.zig deleted file mode 100644 index a83e7653e2..0000000000 --- a/test/cases/bugs/1277.zig +++ /dev/null @@ -1,15 +0,0 @@ -const std = @import("std"); - -const S = struct { - f: ?fn () i32, -}; - -const s = S{ .f = f }; - -fn f() i32 { - return 1234; -} - -test "don't emit an LLVM global for a const function when it's in an optional in a struct" { - std.debug.assertOrPanic(s.f.?() == 1234); -} diff --git a/test/cases/bugs/1322.zig b/test/cases/bugs/1322.zig deleted file mode 100644 index 2de92191ec..0000000000 --- a/test/cases/bugs/1322.zig +++ /dev/null @@ -1,19 +0,0 @@ -const std = @import("std"); - -const B = union(enum) { - c: C, - None, -}; - -const A = struct { - b: B, -}; - -const C = struct {}; - -test "tagged union with all void fields but a meaningful tag" { - var a: A = A{ .b = B{ .c = C{} } }; - std.debug.assert(@TagType(B)(a.b) == @TagType(B).c); - a = A{ .b = B.None }; - std.debug.assert(@TagType(B)(a.b) == @TagType(B).None); -} diff --git a/test/cases/bugs/1381.zig b/test/cases/bugs/1381.zig deleted file mode 100644 index 2d452da156..0000000000 --- a/test/cases/bugs/1381.zig +++ /dev/null @@ -1,21 +0,0 @@ -const std = @import("std"); - -const B = union(enum) { - D: u8, - E: u16, -}; - -const A = union(enum) { - B: B, - C: u8, -}; - -test "union that needs padding bytes inside an array" { - var as = []A{ - A{ .B = B{ .D = 1 } }, - A{ .B = B{ .D = 1 } }, - }; - - const a = as[0].B; - std.debug.assertOrPanic(a.D == 1); -} diff --git a/test/cases/bugs/1421.zig b/test/cases/bugs/1421.zig deleted file mode 100644 index fcbb8b70e4..0000000000 --- a/test/cases/bugs/1421.zig +++ /dev/null @@ -1,14 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const assert = std.debug.assert; - -const S = struct { - fn method() builtin.TypeInfo { - return @typeInfo(S); - } -}; - -test "functions with return type required to be comptime are generic" { - const ti = S.method(); - assert(builtin.TypeId(ti) == builtin.TypeId.Struct); -} diff --git a/test/cases/bugs/1442.zig b/test/cases/bugs/1442.zig deleted file mode 100644 index e9dfd5d2ce..0000000000 --- a/test/cases/bugs/1442.zig +++ /dev/null @@ -1,11 +0,0 @@ -const std = @import("std"); - -const Union = union(enum) { - Text: []const u8, - Color: u32, -}; - -test "const error union field alignment" { - var union_or_err: anyerror!Union = Union{ .Color = 1234 }; - std.debug.assertOrPanic((union_or_err catch unreachable).Color == 1234); -} diff --git a/test/cases/bugs/1486.zig b/test/cases/bugs/1486.zig deleted file mode 100644 index 98fae36d3a..0000000000 --- a/test/cases/bugs/1486.zig +++ /dev/null @@ -1,11 +0,0 @@ -const assert = @import("std").debug.assert; - -const ptr = &global; -var global: u64 = 123; - -test "constant pointer to global variable causes runtime load" { - global = 1234; - assert(&global == ptr); - assert(ptr.* == 1234); -} - diff --git a/test/cases/bugs/394.zig b/test/cases/bugs/394.zig deleted file mode 100644 index b0afec2357..0000000000 --- a/test/cases/bugs/394.zig +++ /dev/null @@ -1,18 +0,0 @@ -const E = union(enum) { - A: [9]u8, - B: u64, -}; -const S = struct { - x: u8, - y: E, -}; - -const assert = @import("std").debug.assert; - -test "bug 394 fixed" { - const x = S{ - .x = 3, - .y = E{ .B = 1 }, - }; - assert(x.x == 3); -} diff --git a/test/cases/bugs/655.zig b/test/cases/bugs/655.zig deleted file mode 100644 index ebb8da0658..0000000000 --- a/test/cases/bugs/655.zig +++ /dev/null @@ -1,12 +0,0 @@ -const std = @import("std"); -const other_file = @import("655_other_file.zig"); - -test "function with *const parameter with type dereferenced by namespace" { - const x: other_file.Integer = 1234; - comptime std.debug.assert(@typeOf(&x) == *const other_file.Integer); - foo(&x); -} - -fn foo(x: *const other_file.Integer) void { - std.debug.assert(x.* == 1234); -} diff --git a/test/cases/bugs/655_other_file.zig b/test/cases/bugs/655_other_file.zig deleted file mode 100644 index df1df44955..0000000000 --- a/test/cases/bugs/655_other_file.zig +++ /dev/null @@ -1 +0,0 @@ -pub const Integer = u32; diff --git a/test/cases/bugs/656.zig b/test/cases/bugs/656.zig deleted file mode 100644 index f93f0ac4d5..0000000000 --- a/test/cases/bugs/656.zig +++ /dev/null @@ -1,31 +0,0 @@ -const assert = @import("std").debug.assert; - -const PrefixOp = union(enum) { - Return, - AddrOf: Value, -}; - -const Value = struct { - align_expr: ?u32, -}; - -test "optional if after an if in a switch prong of a switch with 2 prongs in an else" { - foo(false, true); -} - -fn foo(a: bool, b: bool) void { - var prefix_op = PrefixOp{ - .AddrOf = Value{ .align_expr = 1234 }, - }; - if (a) {} else { - switch (prefix_op) { - PrefixOp.AddrOf => |addr_of_info| { - if (b) {} - if (addr_of_info.align_expr) |align_expr| { - assert(align_expr == 1234); - } - }, - PrefixOp.Return => {}, - } - } -} diff --git a/test/cases/bugs/726.zig b/test/cases/bugs/726.zig deleted file mode 100644 index 2acc91eb26..0000000000 --- a/test/cases/bugs/726.zig +++ /dev/null @@ -1,16 +0,0 @@ -const assert = @import("std").debug.assert; - -test "@ptrCast from const to nullable" { - const c: u8 = 4; - var x: ?*const u8 = @ptrCast(?*const u8, &c); - assert(x.?.* == 4); -} - -test "@ptrCast from var in empty struct to nullable" { - const container = struct { - var c: u8 = 4; - }; - var x: ?*const u8 = @ptrCast(?*const u8, &container.c); - assert(x.?.* == 4); -} - diff --git a/test/cases/bugs/828.zig b/test/cases/bugs/828.zig deleted file mode 100644 index 50ae0fd279..0000000000 --- a/test/cases/bugs/828.zig +++ /dev/null @@ -1,33 +0,0 @@ -const CountBy = struct { - a: usize, - - const One = CountBy{ .a = 1 }; - - pub fn counter(self: *const CountBy) Counter { - return Counter{ .i = 0 }; - } -}; - -const Counter = struct { - i: usize, - - pub fn count(self: *Counter) bool { - self.i += 1; - return self.i <= 10; - } -}; - -fn constCount(comptime cb: *const CountBy, comptime unused: u32) void { - comptime { - var cnt = cb.counter(); - if (cnt.i != 0) @compileError("Counter instance reused!"); - while (cnt.count()) {} - } -} - -test "comptime struct return should not return the same instance" { - //the first parameter must be passed by reference to trigger the bug - //a second parameter is required to trigger the bug - const ValA = constCount(&CountBy.One, 12); - const ValB = constCount(&CountBy.One, 15); -} diff --git a/test/cases/bugs/920.zig b/test/cases/bugs/920.zig deleted file mode 100644 index 2903f05a29..0000000000 --- a/test/cases/bugs/920.zig +++ /dev/null @@ -1,65 +0,0 @@ -const std = @import("std"); -const math = std.math; -const Random = std.rand.Random; - -const ZigTable = struct { - r: f64, - x: [257]f64, - f: [257]f64, - - pdf: fn (f64) f64, - is_symmetric: bool, - zero_case: fn (*Random, f64) f64, -}; - -fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn (f64) f64, comptime f_inv: fn (f64) f64, comptime zero_case: fn (*Random, f64) f64) ZigTable { - var tables: ZigTable = undefined; - - tables.is_symmetric = is_symmetric; - tables.r = r; - tables.pdf = f; - tables.zero_case = zero_case; - - tables.x[0] = v / f(r); - tables.x[1] = r; - - for (tables.x[2..256]) |*entry, i| { - const last = tables.x[2 + i - 1]; - entry.* = f_inv(v / last + f(last)); - } - tables.x[256] = 0; - - for (tables.f[0..]) |*entry, i| { - entry.* = f(tables.x[i]); - } - - return tables; -} - -const norm_r = 3.6541528853610088; -const norm_v = 0.00492867323399; - -fn norm_f(x: f64) f64 { - return math.exp(-x * x / 2.0); -} -fn norm_f_inv(y: f64) f64 { - return math.sqrt(-2.0 * math.ln(y)); -} -fn norm_zero_case(random: *Random, u: f64) f64 { - return 0.0; -} - -const NormalDist = blk: { - @setEvalBranchQuota(30000); - break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case); -}; - -test "bug 920 fixed" { - const NormalDist1 = blk: { - break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case); - }; - - for (NormalDist1.f) |_, i| { - std.debug.assert(NormalDist1.f[i] == NormalDist.f[i]); - } -} diff --git a/test/cases/byval_arg_var.zig b/test/cases/byval_arg_var.zig deleted file mode 100644 index 826b9cc9e5..0000000000 --- a/test/cases/byval_arg_var.zig +++ /dev/null @@ -1,27 +0,0 @@ -const std = @import("std"); - -var result: []const u8 = "wrong"; - -test "aoeu" { - start(); - blowUpStack(10); - - std.debug.assert(std.mem.eql(u8, result, "string literal")); -} - -fn start() void { - foo("string literal"); -} - -fn foo(x: var) void { - bar(x); -} - -fn bar(x: var) void { - result = x; -} - -fn blowUpStack(x: u32) void { - if (x == 0) return; - blowUpStack(x - 1); -} diff --git a/test/cases/cancel.zig b/test/cases/cancel.zig deleted file mode 100644 index c0f74fd34f..0000000000 --- a/test/cases/cancel.zig +++ /dev/null @@ -1,92 +0,0 @@ -const std = @import("std"); - -var defer_f1: bool = false; -var defer_f2: bool = false; -var defer_f3: bool = false; - -test "cancel forwards" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - const p = async<&da.allocator> f1() catch unreachable; - cancel p; - std.debug.assert(defer_f1); - std.debug.assert(defer_f2); - std.debug.assert(defer_f3); -} - -async fn f1() void { - defer { - defer_f1 = true; - } - await (async f2() catch unreachable); -} - -async fn f2() void { - defer { - defer_f2 = true; - } - await (async f3() catch unreachable); -} - -async fn f3() void { - defer { - defer_f3 = true; - } - suspend; -} - -var defer_b1: bool = false; -var defer_b2: bool = false; -var defer_b3: bool = false; -var defer_b4: bool = false; - -test "cancel backwards" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - const p = async<&da.allocator> b1() catch unreachable; - cancel p; - std.debug.assert(defer_b1); - std.debug.assert(defer_b2); - std.debug.assert(defer_b3); - std.debug.assert(defer_b4); -} - -async fn b1() void { - defer { - defer_b1 = true; - } - await (async b2() catch unreachable); -} - -var b4_handle: promise = undefined; - -async fn b2() void { - const b3_handle = async b3() catch unreachable; - resume b4_handle; - cancel b4_handle; - defer { - defer_b2 = true; - } - const value = await b3_handle; - @panic("unreachable"); -} - -async fn b3() i32 { - defer { - defer_b3 = true; - } - await (async b4() catch unreachable); - return 1234; -} - -async fn b4() void { - defer { - defer_b4 = true; - } - suspend { - b4_handle = @handle(); - } - suspend; -} diff --git a/test/cases/cast.zig b/test/cases/cast.zig deleted file mode 100644 index bd45bbc00f..0000000000 --- a/test/cases/cast.zig +++ /dev/null @@ -1,472 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const mem = std.mem; -const maxInt = std.math.maxInt; - -test "int to ptr cast" { - const x = usize(13); - const y = @intToPtr(*u8, x); - const z = @ptrToInt(y); - assert(z == 13); -} - -test "integer literal to pointer cast" { - const vga_mem = @intToPtr(*u16, 0xB8000); - assert(@ptrToInt(vga_mem) == 0xB8000); -} - -test "pointer reinterpret const float to int" { - const float: f64 = 5.99999999999994648725e-01; - const float_ptr = &float; - const int_ptr = @ptrCast(*const i32, float_ptr); - const int_val = int_ptr.*; - assert(int_val == 858993411); -} - -test "implicitly cast indirect pointer to maybe-indirect pointer" { - const S = struct { - const Self = @This(); - x: u8, - fn constConst(p: *const *const Self) u8 { - return p.*.x; - } - fn maybeConstConst(p: ?*const *const Self) u8 { - return p.?.*.x; - } - fn constConstConst(p: *const *const *const Self) u8 { - return p.*.*.x; - } - fn maybeConstConstConst(p: ?*const *const *const Self) u8 { - return p.?.*.*.x; - } - }; - const s = S{ .x = 42 }; - const p = &s; - const q = &p; - const r = &q; - assert(42 == S.constConst(q)); - assert(42 == S.maybeConstConst(q)); - assert(42 == S.constConstConst(r)); - assert(42 == S.maybeConstConstConst(r)); -} - -test "explicit cast from integer to error type" { - testCastIntToErr(error.ItBroke); - comptime testCastIntToErr(error.ItBroke); -} -fn testCastIntToErr(err: anyerror) void { - const x = @errorToInt(err); - const y = @intToError(x); - assert(error.ItBroke == y); -} - -test "peer resolve arrays of different size to const slice" { - assert(mem.eql(u8, boolToStr(true), "true")); - assert(mem.eql(u8, boolToStr(false), "false")); - comptime assert(mem.eql(u8, boolToStr(true), "true")); - comptime assert(mem.eql(u8, boolToStr(false), "false")); -} -fn boolToStr(b: bool) []const u8 { - return if (b) "true" else "false"; -} - -test "peer resolve array and const slice" { - testPeerResolveArrayConstSlice(true); - comptime testPeerResolveArrayConstSlice(true); -} -fn testPeerResolveArrayConstSlice(b: bool) void { - const value1 = if (b) "aoeu" else ([]const u8)("zz"); - const value2 = if (b) ([]const u8)("zz") else "aoeu"; - assert(mem.eql(u8, value1, "aoeu")); - assert(mem.eql(u8, value2, "zz")); -} - -test "implicitly cast from T to anyerror!?T" { - castToOptionalTypeError(1); - comptime castToOptionalTypeError(1); -} -const A = struct { - a: i32, -}; -fn castToOptionalTypeError(z: i32) void { - const x = i32(1); - const y: anyerror!?i32 = x; - assert((try y).? == 1); - - const f = z; - const g: anyerror!?i32 = f; - - const a = A{ .a = z }; - const b: anyerror!?A = a; - assert((b catch unreachable).?.a == 1); -} - -test "implicitly cast from int to anyerror!?T" { - implicitIntLitToOptional(); - comptime implicitIntLitToOptional(); -} -fn implicitIntLitToOptional() void { - const f: ?i32 = 1; - const g: anyerror!?i32 = 1; -} - -test "return null from fn() anyerror!?&T" { - const a = returnNullFromOptionalTypeErrorRef(); - const b = returnNullLitFromOptionalTypeErrorRef(); - assert((try a) == null and (try b) == null); -} -fn returnNullFromOptionalTypeErrorRef() anyerror!?*A { - const a: ?*A = null; - return a; -} -fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A { - return null; -} - -test "peer type resolution: ?T and T" { - assert(peerTypeTAndOptionalT(true, false).? == 0); - assert(peerTypeTAndOptionalT(false, false).? == 3); - comptime { - assert(peerTypeTAndOptionalT(true, false).? == 0); - assert(peerTypeTAndOptionalT(false, false).? == 3); - } -} -fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { - if (c) { - return if (b) null else usize(0); - } - - return usize(3); -} - -test "peer type resolution: [0]u8 and []const u8" { - assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); - assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); - comptime { - assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); - assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); - } -} -fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { - if (a) { - return []const u8{}; - } - - return slice[0..1]; -} - -test "implicitly cast from [N]T to ?[]const T" { - assert(mem.eql(u8, castToOptionalSlice().?, "hi")); - comptime assert(mem.eql(u8, castToOptionalSlice().?, "hi")); -} - -fn castToOptionalSlice() ?[]const u8 { - return "hi"; -} - -test "implicitly cast from [0]T to anyerror![]T" { - testCastZeroArrayToErrSliceMut(); - comptime testCastZeroArrayToErrSliceMut(); -} - -fn testCastZeroArrayToErrSliceMut() void { - assert((gimmeErrOrSlice() catch unreachable).len == 0); -} - -fn gimmeErrOrSlice() anyerror![]u8 { - return []u8{}; -} - -test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" { - { - var data = "hi"; - const slice = data[0..]; - assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); - assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); - } - comptime { - var data = "hi"; - const slice = data[0..]; - assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); - assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); - } -} -fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 { - if (a) { - return []u8{}; - } - - return slice[0..1]; -} - -test "resolve undefined with integer" { - testResolveUndefWithInt(true, 1234); - comptime testResolveUndefWithInt(true, 1234); -} -fn testResolveUndefWithInt(b: bool, x: i32) void { - const value = if (b) x else undefined; - if (b) { - assert(value == x); - } -} - -test "implicit cast from &const [N]T to []const T" { - testCastConstArrayRefToConstSlice(); - comptime testCastConstArrayRefToConstSlice(); -} - -fn testCastConstArrayRefToConstSlice() void { - const blah = "aoeu"; - const const_array_ref = &blah; - assert(@typeOf(const_array_ref) == *const [4]u8); - const slice: []const u8 = const_array_ref; - assert(mem.eql(u8, slice, "aoeu")); -} - -test "peer type resolution: error and [N]T" { - // TODO: implicit error!T to error!U where T can implicitly cast to U - //assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); - //comptime assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); - assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); - comptime assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); -} - -//fn testPeerErrorAndArray(x: u8) error![]const u8 { -// return switch (x) { -// 0x00 => "OK", -// else => error.BadValue, -// }; -//} -fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 { - return switch (x) { - 0x00 => "OK", - 0x01 => "OKK", - else => error.BadValue, - }; -} - -test "@floatToInt" { - testFloatToInts(); - comptime testFloatToInts(); -} - -fn testFloatToInts() void { - const x = i32(1e4); - assert(x == 10000); - const y = @floatToInt(i32, f32(1e4)); - assert(y == 10000); - expectFloatToInt(f16, 255.1, u8, 255); - expectFloatToInt(f16, 127.2, i8, 127); - expectFloatToInt(f16, -128.2, i8, -128); - expectFloatToInt(f32, 255.1, u8, 255); - expectFloatToInt(f32, 127.2, i8, 127); - expectFloatToInt(f32, -128.2, i8, -128); - expectFloatToInt(comptime_int, 1234, i16, 1234); -} - -fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) void { - assert(@floatToInt(I, f) == i); -} - -test "cast u128 to f128 and back" { - comptime testCast128(); - testCast128(); -} - -fn testCast128() void { - assert(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); -} - -fn cast128Int(x: f128) u128 { - return @bitCast(u128, x); -} - -fn cast128Float(x: u128) f128 { - return @bitCast(f128, x); -} - -test "const slice widen cast" { - const bytes align(4) = []u8{ - 0x12, - 0x12, - 0x12, - 0x12, - }; - - const u32_value = @bytesToSlice(u32, bytes[0..])[0]; - assert(u32_value == 0x12121212); - - assert(@bitCast(u32, bytes) == 0x12121212); -} - -test "single-item pointer of array to slice and to unknown length pointer" { - testCastPtrOfArrayToSliceAndPtr(); - comptime testCastPtrOfArrayToSliceAndPtr(); -} - -fn testCastPtrOfArrayToSliceAndPtr() void { - var array = "aoeu"; - const x: [*]u8 = &array; - x[0] += 1; - assert(mem.eql(u8, array[0..], "boeu")); - const y: []u8 = &array; - y[0] += 1; - assert(mem.eql(u8, array[0..], "coeu")); -} - -test "cast *[1][*]const u8 to [*]const ?[*]const u8" { - const window_name = [1][*]const u8{c"window name"}; - const x: [*]const ?[*]const u8 = &window_name; - assert(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); -} - -test "@intCast comptime_int" { - const result = @intCast(i32, 1234); - assert(@typeOf(result) == i32); - assert(result == 1234); -} - -test "@floatCast comptime_int and comptime_float" { - { - const result = @floatCast(f16, 1234); - assert(@typeOf(result) == f16); - assert(result == 1234.0); - } - { - const result = @floatCast(f16, 1234.0); - assert(@typeOf(result) == f16); - assert(result == 1234.0); - } - { - const result = @floatCast(f32, 1234); - assert(@typeOf(result) == f32); - assert(result == 1234.0); - } - { - const result = @floatCast(f32, 1234.0); - assert(@typeOf(result) == f32); - assert(result == 1234.0); - } -} - -test "comptime_int @intToFloat" { - { - const result = @intToFloat(f16, 1234); - assert(@typeOf(result) == f16); - assert(result == 1234.0); - } - { - const result = @intToFloat(f32, 1234); - assert(@typeOf(result) == f32); - assert(result == 1234.0); - } -} - -test "@bytesToSlice keeps pointer alignment" { - var bytes = []u8{ 0x01, 0x02, 0x03, 0x04 }; - const numbers = @bytesToSlice(u32, bytes[0..]); - comptime assert(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); -} - -test "@intCast i32 to u7" { - var x: u128 = maxInt(u128); - var y: i32 = 120; - var z = x >> @intCast(u7, y); - assert(z == 0xff); -} - -test "implicit cast undefined to optional" { - assert(MakeType(void).getNull() == null); - assert(MakeType(void).getNonNull() != null); -} - -fn MakeType(comptime T: type) type { - return struct { - fn getNull() ?T { - return null; - } - - fn getNonNull() ?T { - return T(undefined); - } - }; -} - -test "implicit cast from *[N]T to ?[*]T" { - var x: ?[*]u16 = null; - var y: [4]u16 = [4]u16{ 0, 1, 2, 3 }; - - x = &y; - assert(std.mem.eql(u16, x.?[0..4], y[0..4])); - x.?[0] = 8; - y[3] = 6; - assert(std.mem.eql(u16, x.?[0..4], y[0..4])); -} - -test "implicit cast from *T to ?*c_void" { - var a: u8 = 1; - incrementVoidPtrValue(&a); - std.debug.assert(a == 2); -} - -fn incrementVoidPtrValue(value: ?*c_void) void { - @ptrCast(*u8, value.?).* += 1; -} - -test "implicit cast from [*]T to ?*c_void" { - var a = []u8{ 3, 2, 1 }; - incrementVoidPtrArray(a[0..].ptr, 3); - assert(std.mem.eql(u8, a, []u8{ 4, 3, 2 })); -} - -fn incrementVoidPtrArray(array: ?*c_void, len: usize) void { - var n: usize = 0; - while (n < len) : (n += 1) { - @ptrCast([*]u8, array.?)[n] += 1; - } -} - -test "*usize to *void" { - var i = usize(0); - var v = @ptrCast(*void, &i); - v.* = {}; -} - -test "compile time int to ptr of function" { - foobar(FUNCTION_CONSTANT); -} - -pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize)); -pub const PFN_void = extern fn (*c_void) void; - -fn foobar(func: PFN_void) void { - std.debug.assert(@ptrToInt(func) == maxInt(usize)); -} - -test "implicit ptr to *c_void" { - var a: u32 = 1; - var ptr: *c_void = &a; - var b: *u32 = @ptrCast(*u32, ptr); - assert(b.* == 1); - var ptr2: ?*c_void = &a; - var c: *u32 = @ptrCast(*u32, ptr2.?); - assert(c.* == 1); -} - -test "@intCast to comptime_int" { - assert(@intCast(comptime_int, 0) == 0); -} - -test "implicit cast comptime numbers to any type when the value fits" { - const a: u64 = 255; - var b: u8 = a; - assert(b == 255); -} - -test "@intToEnum passed a comptime_int to an enum with one item" { - const E = enum { - A, - }; - const x = @intToEnum(E, 0); - assert(x == E.A); -} diff --git a/test/cases/const_slice_child.zig b/test/cases/const_slice_child.zig deleted file mode 100644 index 07d02d5df0..0000000000 --- a/test/cases/const_slice_child.zig +++ /dev/null @@ -1,45 +0,0 @@ -const debug = @import("std").debug; -const assert = debug.assert; - -var argv: [*]const [*]const u8 = undefined; - -test "const slice child" { - const strs = ([][*]const u8){ - c"one", - c"two", - c"three", - }; - // TODO this should implicitly cast - argv = @ptrCast([*]const [*]const u8, &strs); - bar(strs.len); -} - -fn foo(args: [][]const u8) void { - assert(args.len == 3); - assert(streql(args[0], "one")); - assert(streql(args[1], "two")); - assert(streql(args[2], "three")); -} - -fn bar(argc: usize) void { - const args = debug.global_allocator.alloc([]const u8, argc) catch unreachable; - for (args) |_, i| { - const ptr = argv[i]; - args[i] = ptr[0..strlen(ptr)]; - } - foo(args); -} - -fn strlen(ptr: [*]const u8) usize { - var count: usize = 0; - while (ptr[count] != 0) : (count += 1) {} - return count; -} - -fn streql(a: []const u8, b: []const u8) bool { - if (a.len != b.len) return false; - for (a) |item, index| { - if (b[index] != item) return false; - } - return true; -} diff --git a/test/cases/coroutine_await_struct.zig b/test/cases/coroutine_await_struct.zig deleted file mode 100644 index 79168715d8..0000000000 --- a/test/cases/coroutine_await_struct.zig +++ /dev/null @@ -1,47 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const assert = std.debug.assert; - -const Foo = struct { - x: i32, -}; - -var await_a_promise: promise = undefined; -var await_final_result = Foo{ .x = 0 }; - -test "coroutine await struct" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - await_seq('a'); - const p = async<&da.allocator> await_amain() catch unreachable; - await_seq('f'); - resume await_a_promise; - await_seq('i'); - assert(await_final_result.x == 1234); - assert(std.mem.eql(u8, await_points, "abcdefghi")); -} -async fn await_amain() void { - await_seq('b'); - const p = async await_another() catch unreachable; - await_seq('e'); - await_final_result = await p; - await_seq('h'); -} -async fn await_another() Foo { - await_seq('c'); - suspend { - await_seq('d'); - await_a_promise = @handle(); - } - await_seq('g'); - return Foo{ .x = 1234 }; -} - -var await_points = []u8{0} ** "abcdefghi".len; -var await_seq_index: usize = 0; - -fn await_seq(c: u8) void { - await_points[await_seq_index] = c; - await_seq_index += 1; -} diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig deleted file mode 100644 index 89490ebc2c..0000000000 --- a/test/cases/coroutines.zig +++ /dev/null @@ -1,258 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const assert = std.debug.assert; - -var x: i32 = 1; - -test "create a coroutine and cancel it" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - const p = try async<&da.allocator> simpleAsyncFn(); - comptime assert(@typeOf(p) == promise->void); - cancel p; - assert(x == 2); -} -async fn simpleAsyncFn() void { - x += 1; - suspend; - x += 1; -} - -test "coroutine suspend, resume, cancel" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - seq('a'); - const p = try async<&da.allocator> testAsyncSeq(); - seq('c'); - resume p; - seq('f'); - cancel p; - seq('g'); - - assert(std.mem.eql(u8, points, "abcdefg")); -} -async fn testAsyncSeq() void { - defer seq('e'); - - seq('b'); - suspend; - seq('d'); -} -var points = []u8{0} ** "abcdefg".len; -var index: usize = 0; - -fn seq(c: u8) void { - points[index] = c; - index += 1; -} - -test "coroutine suspend with block" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - const p = try async<&da.allocator> testSuspendBlock(); - std.debug.assert(!result); - resume a_promise; - std.debug.assert(result); - cancel p; -} - -var a_promise: promise = undefined; -var result = false; -async fn testSuspendBlock() void { - suspend { - comptime assert(@typeOf(@handle()) == promise->void); - a_promise = @handle(); - } - - //Test to make sure that @handle() works as advertised (issue #1296) - //var our_handle: promise = @handle(); - assert( a_promise == @handle() ); - - result = true; -} - -var await_a_promise: promise = undefined; -var await_final_result: i32 = 0; - -test "coroutine await" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - await_seq('a'); - const p = async<&da.allocator> await_amain() catch unreachable; - await_seq('f'); - resume await_a_promise; - await_seq('i'); - assert(await_final_result == 1234); - assert(std.mem.eql(u8, await_points, "abcdefghi")); -} -async fn await_amain() void { - await_seq('b'); - const p = async await_another() catch unreachable; - await_seq('e'); - await_final_result = await p; - await_seq('h'); -} -async fn await_another() i32 { - await_seq('c'); - suspend { - await_seq('d'); - await_a_promise = @handle(); - } - await_seq('g'); - return 1234; -} - -var await_points = []u8{0} ** "abcdefghi".len; -var await_seq_index: usize = 0; - -fn await_seq(c: u8) void { - await_points[await_seq_index] = c; - await_seq_index += 1; -} - -var early_final_result: i32 = 0; - -test "coroutine await early return" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - early_seq('a'); - const p = async<&da.allocator> early_amain() catch @panic("out of memory"); - early_seq('f'); - assert(early_final_result == 1234); - assert(std.mem.eql(u8, early_points, "abcdef")); -} -async fn early_amain() void { - early_seq('b'); - const p = async early_another() catch @panic("out of memory"); - early_seq('d'); - early_final_result = await p; - early_seq('e'); -} -async fn early_another() i32 { - early_seq('c'); - return 1234; -} - -var early_points = []u8{0} ** "abcdef".len; -var early_seq_index: usize = 0; - -fn early_seq(c: u8) void { - early_points[early_seq_index] = c; - early_seq_index += 1; -} - -test "coro allocation failure" { - var failing_allocator = std.debug.FailingAllocator.init(std.debug.global_allocator, 0); - if (async<&failing_allocator.allocator> asyncFuncThatNeverGetsRun()) { - @panic("expected allocation failure"); - } else |err| switch (err) { - error.OutOfMemory => {}, - } -} -async fn asyncFuncThatNeverGetsRun() void { - @panic("coro frame allocation should fail"); -} - -test "async function with dot syntax" { - const S = struct { - var y: i32 = 1; - async fn foo() void { - y += 1; - suspend; - } - }; - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - const p = try async<&da.allocator> S.foo(); - cancel p; - assert(S.y == 2); -} - -test "async fn pointer in a struct field" { - var data: i32 = 1; - const Foo = struct { - bar: async<*std.mem.Allocator> fn (*i32) void, - }; - var foo = Foo{ .bar = simpleAsyncFn2 }; - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - const p = (async<&da.allocator> foo.bar(&data)) catch unreachable; - assert(data == 2); - cancel p; - assert(data == 4); -} -async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void { - defer y.* += 2; - y.* += 1; - suspend; -} - -test "async fn with inferred error set" { - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - const p = (async<&da.allocator> failing()) catch unreachable; - resume p; - cancel p; -} -async fn failing() !void { - suspend; - return error.Fail; -} - -test "error return trace across suspend points - early return" { - const p = nonFailing(); - resume p; - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - const p2 = try async<&da.allocator> printTrace(p); - cancel p2; -} - -test "error return trace across suspend points - async return" { - const p = nonFailing(); - const p2 = try async printTrace(p); - resume p; - cancel p2; -} - -// TODO https://github.com/ziglang/zig/issues/760 -fn nonFailing() promise->(anyerror!void) { - return async suspendThenFail() catch unreachable; -} -async fn suspendThenFail() anyerror!void { - suspend; - return error.Fail; -} -async fn printTrace(p: promise->(anyerror!void)) void { - (await p) catch |e| { - std.debug.assert(e == error.Fail); - if (@errorReturnTrace()) |trace| { - assert(trace.index == 1); - } else switch (builtin.mode) { - builtin.Mode.Debug, builtin.Mode.ReleaseSafe => @panic("expected return trace"), - builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => {}, - } - }; -} - -test "break from suspend" { - var buf: [500]u8 = undefined; - var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; - var my_result: i32 = 1; - const p = try async testBreakFromSuspend(&my_result); - cancel p; - std.debug.assert(my_result == 2); -} -async fn testBreakFromSuspend(my_result: *i32) void { - suspend { - resume @handle(); - } - my_result.* += 1; - suspend; - my_result.* += 1; -} diff --git a/test/cases/defer.zig b/test/cases/defer.zig deleted file mode 100644 index f9a2b69cd9..0000000000 --- a/test/cases/defer.zig +++ /dev/null @@ -1,78 +0,0 @@ -const assert = @import("std").debug.assert; - -var result: [3]u8 = undefined; -var index: usize = undefined; - -fn runSomeErrorDefers(x: bool) !bool { - index = 0; - defer { - result[index] = 'a'; - index += 1; - } - errdefer { - result[index] = 'b'; - index += 1; - } - defer { - result[index] = 'c'; - index += 1; - } - return if (x) x else error.FalseNotAllowed; -} - -test "mixing normal and error defers" { - assert(runSomeErrorDefers(true) catch unreachable); - assert(result[0] == 'c'); - assert(result[1] == 'a'); - - const ok = runSomeErrorDefers(false) catch |err| x: { - assert(err == error.FalseNotAllowed); - break :x true; - }; - assert(ok); - assert(result[0] == 'c'); - assert(result[1] == 'b'); - assert(result[2] == 'a'); -} - -test "break and continue inside loop inside defer expression" { - testBreakContInDefer(10); - comptime testBreakContInDefer(10); -} - -fn testBreakContInDefer(x: usize) void { - defer { - var i: usize = 0; - while (i < x) : (i += 1) { - if (i < 5) continue; - if (i == 5) break; - } - assert(i == 5); - } -} - -test "defer and labeled break" { - var i = usize(0); - - blk: { - defer i += 1; - break :blk; - } - - assert(i == 1); -} - -test "errdefer does not apply to fn inside fn" { - if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assert(e == error.Bad); -} - -fn testNestedFnErrDefer() anyerror!void { - var a: i32 = 0; - errdefer a += 1; - const S = struct { - fn baz() anyerror { - return error.Bad; - } - }; - return S.baz(); -} diff --git a/test/cases/enum.zig b/test/cases/enum.zig deleted file mode 100644 index 2dd552488c..0000000000 --- a/test/cases/enum.zig +++ /dev/null @@ -1,894 +0,0 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; - -test "enum type" { - const foo1 = Foo{ .One = 13 }; - const foo2 = Foo{ - .Two = Point{ - .x = 1234, - .y = 5678, - }, - }; - const bar = Bar.B; - - assert(bar == Bar.B); - assert(@memberCount(Foo) == 3); - assert(@memberCount(Bar) == 4); - assert(@sizeOf(Foo) == @sizeOf(FooNoVoid)); - assert(@sizeOf(Bar) == 1); -} - -test "enum as return value" { - switch (returnAnInt(13)) { - Foo.One => |value| assert(value == 13), - else => unreachable, - } -} - -const Point = struct { - x: u64, - y: u64, -}; -const Foo = union(enum) { - One: i32, - Two: Point, - Three: void, -}; -const FooNoVoid = union(enum) { - One: i32, - Two: Point, -}; -const Bar = enum { - A, - B, - C, - D, -}; - -fn returnAnInt(x: i32) Foo { - return Foo{ .One = x }; -} - -test "constant enum with payload" { - var empty = AnEnumWithPayload{ .Empty = {} }; - var full = AnEnumWithPayload{ .Full = 13 }; - shouldBeEmpty(empty); - shouldBeNotEmpty(full); -} - -fn shouldBeEmpty(x: AnEnumWithPayload) void { - switch (x) { - AnEnumWithPayload.Empty => {}, - else => unreachable, - } -} - -fn shouldBeNotEmpty(x: AnEnumWithPayload) void { - switch (x) { - AnEnumWithPayload.Empty => unreachable, - else => {}, - } -} - -const AnEnumWithPayload = union(enum) { - Empty: void, - Full: i32, -}; - -const Number = enum { - Zero, - One, - Two, - Three, - Four, -}; - -test "enum to int" { - shouldEqual(Number.Zero, 0); - shouldEqual(Number.One, 1); - shouldEqual(Number.Two, 2); - shouldEqual(Number.Three, 3); - shouldEqual(Number.Four, 4); -} - -fn shouldEqual(n: Number, expected: u3) void { - assert(@enumToInt(n) == expected); -} - -test "int to enum" { - testIntToEnumEval(3); -} -fn testIntToEnumEval(x: i32) void { - assert(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three); -} -const IntToEnumNumber = enum { - Zero, - One, - Two, - Three, - Four, -}; - -test "@tagName" { - assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); - comptime assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); -} - -fn testEnumTagNameBare(n: BareNumber) []const u8 { - return @tagName(n); -} - -const BareNumber = enum { - One, - Two, - Three, -}; - -test "enum alignment" { - comptime { - assert(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); - assert(@alignOf(AlignTestEnum) >= @alignOf(u64)); - } -} - -const AlignTestEnum = union(enum) { - A: [9]u8, - B: u64, -}; - -const ValueCount1 = enum { - I0, -}; -const ValueCount2 = enum { - I0, - I1, -}; -const ValueCount256 = enum { - I0, - I1, - I2, - I3, - I4, - I5, - I6, - I7, - I8, - I9, - I10, - I11, - I12, - I13, - I14, - I15, - I16, - I17, - I18, - I19, - I20, - I21, - I22, - I23, - I24, - I25, - I26, - I27, - I28, - I29, - I30, - I31, - I32, - I33, - I34, - I35, - I36, - I37, - I38, - I39, - I40, - I41, - I42, - I43, - I44, - I45, - I46, - I47, - I48, - I49, - I50, - I51, - I52, - I53, - I54, - I55, - I56, - I57, - I58, - I59, - I60, - I61, - I62, - I63, - I64, - I65, - I66, - I67, - I68, - I69, - I70, - I71, - I72, - I73, - I74, - I75, - I76, - I77, - I78, - I79, - I80, - I81, - I82, - I83, - I84, - I85, - I86, - I87, - I88, - I89, - I90, - I91, - I92, - I93, - I94, - I95, - I96, - I97, - I98, - I99, - I100, - I101, - I102, - I103, - I104, - I105, - I106, - I107, - I108, - I109, - I110, - I111, - I112, - I113, - I114, - I115, - I116, - I117, - I118, - I119, - I120, - I121, - I122, - I123, - I124, - I125, - I126, - I127, - I128, - I129, - I130, - I131, - I132, - I133, - I134, - I135, - I136, - I137, - I138, - I139, - I140, - I141, - I142, - I143, - I144, - I145, - I146, - I147, - I148, - I149, - I150, - I151, - I152, - I153, - I154, - I155, - I156, - I157, - I158, - I159, - I160, - I161, - I162, - I163, - I164, - I165, - I166, - I167, - I168, - I169, - I170, - I171, - I172, - I173, - I174, - I175, - I176, - I177, - I178, - I179, - I180, - I181, - I182, - I183, - I184, - I185, - I186, - I187, - I188, - I189, - I190, - I191, - I192, - I193, - I194, - I195, - I196, - I197, - I198, - I199, - I200, - I201, - I202, - I203, - I204, - I205, - I206, - I207, - I208, - I209, - I210, - I211, - I212, - I213, - I214, - I215, - I216, - I217, - I218, - I219, - I220, - I221, - I222, - I223, - I224, - I225, - I226, - I227, - I228, - I229, - I230, - I231, - I232, - I233, - I234, - I235, - I236, - I237, - I238, - I239, - I240, - I241, - I242, - I243, - I244, - I245, - I246, - I247, - I248, - I249, - I250, - I251, - I252, - I253, - I254, - I255, -}; -const ValueCount257 = enum { - I0, - I1, - I2, - I3, - I4, - I5, - I6, - I7, - I8, - I9, - I10, - I11, - I12, - I13, - I14, - I15, - I16, - I17, - I18, - I19, - I20, - I21, - I22, - I23, - I24, - I25, - I26, - I27, - I28, - I29, - I30, - I31, - I32, - I33, - I34, - I35, - I36, - I37, - I38, - I39, - I40, - I41, - I42, - I43, - I44, - I45, - I46, - I47, - I48, - I49, - I50, - I51, - I52, - I53, - I54, - I55, - I56, - I57, - I58, - I59, - I60, - I61, - I62, - I63, - I64, - I65, - I66, - I67, - I68, - I69, - I70, - I71, - I72, - I73, - I74, - I75, - I76, - I77, - I78, - I79, - I80, - I81, - I82, - I83, - I84, - I85, - I86, - I87, - I88, - I89, - I90, - I91, - I92, - I93, - I94, - I95, - I96, - I97, - I98, - I99, - I100, - I101, - I102, - I103, - I104, - I105, - I106, - I107, - I108, - I109, - I110, - I111, - I112, - I113, - I114, - I115, - I116, - I117, - I118, - I119, - I120, - I121, - I122, - I123, - I124, - I125, - I126, - I127, - I128, - I129, - I130, - I131, - I132, - I133, - I134, - I135, - I136, - I137, - I138, - I139, - I140, - I141, - I142, - I143, - I144, - I145, - I146, - I147, - I148, - I149, - I150, - I151, - I152, - I153, - I154, - I155, - I156, - I157, - I158, - I159, - I160, - I161, - I162, - I163, - I164, - I165, - I166, - I167, - I168, - I169, - I170, - I171, - I172, - I173, - I174, - I175, - I176, - I177, - I178, - I179, - I180, - I181, - I182, - I183, - I184, - I185, - I186, - I187, - I188, - I189, - I190, - I191, - I192, - I193, - I194, - I195, - I196, - I197, - I198, - I199, - I200, - I201, - I202, - I203, - I204, - I205, - I206, - I207, - I208, - I209, - I210, - I211, - I212, - I213, - I214, - I215, - I216, - I217, - I218, - I219, - I220, - I221, - I222, - I223, - I224, - I225, - I226, - I227, - I228, - I229, - I230, - I231, - I232, - I233, - I234, - I235, - I236, - I237, - I238, - I239, - I240, - I241, - I242, - I243, - I244, - I245, - I246, - I247, - I248, - I249, - I250, - I251, - I252, - I253, - I254, - I255, - I256, -}; - -test "enum sizes" { - comptime { - assert(@sizeOf(ValueCount1) == 0); - assert(@sizeOf(ValueCount2) == 1); - assert(@sizeOf(ValueCount256) == 1); - assert(@sizeOf(ValueCount257) == 2); - } -} - -const Small2 = enum(u2) { - One, - Two, -}; -const Small = enum(u2) { - One, - Two, - Three, - Four, -}; - -test "set enum tag type" { - { - var x = Small.One; - x = Small.Two; - comptime assert(@TagType(Small) == u2); - } - { - var x = Small2.One; - x = Small2.Two; - comptime assert(@TagType(Small2) == u2); - } -} - -const A = enum(u3) { - One, - Two, - Three, - Four, - One2, - Two2, - Three2, - Four2, -}; - -const B = enum(u3) { - One3, - Two3, - Three3, - Four3, - One23, - Two23, - Three23, - Four23, -}; - -const C = enum(u2) { - One4, - Two4, - Three4, - Four4, -}; - -const BitFieldOfEnums = packed struct { - a: A, - b: B, - c: C, -}; - -const bit_field_1 = BitFieldOfEnums{ - .a = A.Two, - .b = B.Three3, - .c = C.Four4, -}; - -test "bit field access with enum fields" { - var data = bit_field_1; - assert(getA(&data) == A.Two); - assert(getB(&data) == B.Three3); - assert(getC(&data) == C.Four4); - comptime assert(@sizeOf(BitFieldOfEnums) == 1); - - data.b = B.Four3; - assert(data.b == B.Four3); - - data.a = A.Three; - assert(data.a == A.Three); - assert(data.b == B.Four3); -} - -fn getA(data: *const BitFieldOfEnums) A { - return data.a; -} - -fn getB(data: *const BitFieldOfEnums) B { - return data.b; -} - -fn getC(data: *const BitFieldOfEnums) C { - return data.c; -} - -test "casting enum to its tag type" { - testCastEnumToTagType(Small2.Two); - comptime testCastEnumToTagType(Small2.Two); -} - -fn testCastEnumToTagType(value: Small2) void { - assert(@enumToInt(value) == 1); -} - -const MultipleChoice = enum(u32) { - A = 20, - B = 40, - C = 60, - D = 1000, -}; - -test "enum with specified tag values" { - testEnumWithSpecifiedTagValues(MultipleChoice.C); - comptime testEnumWithSpecifiedTagValues(MultipleChoice.C); -} - -fn testEnumWithSpecifiedTagValues(x: MultipleChoice) void { - assert(@enumToInt(x) == 60); - assert(1234 == switch (x) { - MultipleChoice.A => 1, - MultipleChoice.B => 2, - MultipleChoice.C => u32(1234), - MultipleChoice.D => 4, - }); -} - -const MultipleChoice2 = enum(u32) { - Unspecified1, - A = 20, - Unspecified2, - B = 40, - Unspecified3, - C = 60, - Unspecified4, - D = 1000, - Unspecified5, -}; - -test "enum with specified and unspecified tag values" { - testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D); - comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D); -} - -fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - assert(@enumToInt(x) == 1000); - assert(1234 == switch (x) { - MultipleChoice2.A => 1, - MultipleChoice2.B => 2, - MultipleChoice2.C => 3, - MultipleChoice2.D => u32(1234), - MultipleChoice2.Unspecified1 => 5, - MultipleChoice2.Unspecified2 => 6, - MultipleChoice2.Unspecified3 => 7, - MultipleChoice2.Unspecified4 => 8, - MultipleChoice2.Unspecified5 => 9, - }); -} - -test "cast integer literal to enum" { - assert(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); - assert(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); -} - -const EnumWithOneMember = enum { - Eof, -}; - -fn doALoopThing(id: EnumWithOneMember) void { - while (true) { - if (id == EnumWithOneMember.Eof) { - break; - } - @compileError("above if condition should be comptime"); - } -} - -test "comparison operator on enum with one member is comptime known" { - doALoopThing(EnumWithOneMember.Eof); -} - -const State = enum { - Start, -}; -test "switch on enum with one member is comptime known" { - var state = State.Start; - switch (state) { - State.Start => return, - } - @compileError("analysis should not reach here"); -} - -const EnumWithTagValues = enum(u4) { - A = 1 << 0, - B = 1 << 1, - C = 1 << 2, - D = 1 << 3, -}; -test "enum with tag values don't require parens" { - assert(@enumToInt(EnumWithTagValues.C) == 0b0100); -} - -test "enum with 1 field but explicit tag type should still have the tag type" { - const Enum = enum(u8) { - B = 2, - }; - comptime @import("std").debug.assert(@sizeOf(Enum) == @sizeOf(u8)); -} - -test "empty extern enum with members" { - const E = extern enum { - A, - B, - C, - }; - assert(@sizeOf(E) == @sizeOf(c_int)); -} - -test "aoeu" { - const LocalFoo = enum { - A = 1, - B = 0, - }; - var b = LocalFoo.B; - assert(mem.eql(u8, @tagName(b), "B")); -} diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig deleted file mode 100644 index 088496bd2f..0000000000 --- a/test/cases/enum_with_members.zig +++ /dev/null @@ -1,27 +0,0 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; -const fmt = @import("std").fmt; - -const ET = union(enum) { - SINT: i32, - UINT: u32, - - pub fn print(a: *const ET, buf: []u8) anyerror!usize { - return switch (a.*) { - ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), - ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), - }; - } -}; - -test "enum with members" { - const a = ET{ .SINT = -42 }; - const b = ET{ .UINT = 42 }; - var buf: [20]u8 = undefined; - - assert((a.print(buf[0..]) catch unreachable) == 3); - assert(mem.eql(u8, buf[0..3], "-42")); - - assert((b.print(buf[0..]) catch unreachable) == 2); - assert(mem.eql(u8, buf[0..2], "42")); -} diff --git a/test/cases/error.zig b/test/cases/error.zig deleted file mode 100644 index a731f39021..0000000000 --- a/test/cases/error.zig +++ /dev/null @@ -1,245 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const mem = std.mem; -const builtin = @import("builtin"); - -pub fn foo() anyerror!i32 { - const x = try bar(); - return x + 1; -} - -pub fn bar() anyerror!i32 { - return 13; -} - -pub fn baz() anyerror!i32 { - const y = foo() catch 1234; - return y + 1; -} - -test "error wrapping" { - assert((baz() catch unreachable) == 15); -} - -fn gimmeItBroke() []const u8 { - return @errorName(error.ItBroke); -} - -test "@errorName" { - assert(mem.eql(u8, @errorName(error.AnError), "AnError")); - assert(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); -} - -test "error values" { - const a = @errorToInt(error.err1); - const b = @errorToInt(error.err2); - assert(a != b); -} - -test "redefinition of error values allowed" { - shouldBeNotEqual(error.AnError, error.SecondError); -} -fn shouldBeNotEqual(a: anyerror, b: anyerror) void { - if (a == b) unreachable; -} - -test "error binary operator" { - const a = errBinaryOperatorG(true) catch 3; - const b = errBinaryOperatorG(false) catch 3; - assert(a == 3); - assert(b == 10); -} -fn errBinaryOperatorG(x: bool) anyerror!isize { - return if (x) error.ItBroke else isize(10); -} - -test "unwrap simple value from error" { - const i = unwrapSimpleValueFromErrorDo() catch unreachable; - assert(i == 13); -} -fn unwrapSimpleValueFromErrorDo() anyerror!isize { - return 13; -} - -test "error return in assignment" { - doErrReturnInAssignment() catch unreachable; -} - -fn doErrReturnInAssignment() anyerror!void { - var x: i32 = undefined; - x = try makeANonErr(); -} - -fn makeANonErr() anyerror!i32 { - return 1; -} - -test "error union type " { - testErrorUnionType(); - comptime testErrorUnionType(); -} - -fn testErrorUnionType() void { - const x: anyerror!i32 = 1234; - if (x) |value| assert(value == 1234) else |_| unreachable; - assert(@typeId(@typeOf(x)) == builtin.TypeId.ErrorUnion); - assert(@typeId(@typeOf(x).ErrorSet) == builtin.TypeId.ErrorSet); - assert(@typeOf(x).ErrorSet == anyerror); -} - -test "error set type " { - testErrorSetType(); - comptime testErrorSetType(); -} - -const MyErrSet = error{ - OutOfMemory, - FileNotFound, -}; - -fn testErrorSetType() void { - assert(@memberCount(MyErrSet) == 2); - - const a: MyErrSet!i32 = 5678; - const b: MyErrSet!i32 = MyErrSet.OutOfMemory; - - if (a) |value| assert(value == 5678) else |err| switch (err) { - error.OutOfMemory => unreachable, - error.FileNotFound => unreachable, - } -} - -test "explicit error set cast" { - testExplicitErrorSetCast(Set1.A); - comptime testExplicitErrorSetCast(Set1.A); -} - -const Set1 = error{ - A, - B, -}; -const Set2 = error{ - A, - C, -}; - -fn testExplicitErrorSetCast(set1: Set1) void { - var x = @errSetCast(Set2, set1); - var y = @errSetCast(Set1, x); - assert(y == error.A); -} - -test "comptime test error for empty error set" { - testComptimeTestErrorEmptySet(1234); - comptime testComptimeTestErrorEmptySet(1234); -} - -const EmptyErrorSet = error{}; - -fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void { - if (x) |v| assert(v == 1234) else |err| @compileError("bad"); -} - -test "syntax: optional operator in front of error union operator" { - comptime { - assert(?(anyerror!i32) == ?(anyerror!i32)); - } -} - -test "comptime err to int of error set with only 1 possible value" { - testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); - comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); -} -fn testErrToIntWithOnePossibleValue( - x: error{A}, - comptime value: u32, -) void { - if (@errorToInt(x) != value) { - @compileError("bad"); - } -} - -test "error union peer type resolution" { - testErrorUnionPeerTypeResolution(1); - comptime testErrorUnionPeerTypeResolution(1); -} - -fn testErrorUnionPeerTypeResolution(x: i32) void { - const y = switch (x) { - 1 => bar_1(), - 2 => baz_1(), - else => quux_1(), - }; -} - -fn bar_1() anyerror { - return error.A; -} - -fn baz_1() !i32 { - return error.B; -} - -fn quux_1() !i32 { - return error.C; -} - -test "error: fn returning empty error set can be passed as fn returning any error" { - entry(); - comptime entry(); -} - -fn entry() void { - foo2(bar2); -} - -fn foo2(f: fn () anyerror!void) void { - const x = f(); -} - -fn bar2() (error{}!void) {} - -test "error: Zero sized error set returned with value payload crash" { - _ = foo3(0); - _ = comptime foo3(0); -} - -const Error = error{}; -fn foo3(b: usize) Error!usize { - return b; -} - -test "error: Infer error set from literals" { - _ = nullLiteral("n") catch |err| handleErrors(err); - _ = floatLiteral("n") catch |err| handleErrors(err); - _ = intLiteral("n") catch |err| handleErrors(err); - _ = comptime nullLiteral("n") catch |err| handleErrors(err); - _ = comptime floatLiteral("n") catch |err| handleErrors(err); - _ = comptime intLiteral("n") catch |err| handleErrors(err); -} - -fn handleErrors(err: var) noreturn { - switch (err) { - error.T => {}, - } - - unreachable; -} - -fn nullLiteral(str: []const u8) !?i64 { - if (str[0] == 'n') return null; - - return error.T; -} - -fn floatLiteral(str: []const u8) !?f64 { - if (str[0] == 'n') return 1.0; - - return error.T; -} - -fn intLiteral(str: []const u8) !?i64 { - if (str[0] == 'n') return 1; - - return error.T; -} diff --git a/test/cases/eval.zig b/test/cases/eval.zig deleted file mode 100644 index a9eded151e..0000000000 --- a/test/cases/eval.zig +++ /dev/null @@ -1,782 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const builtin = @import("builtin"); - -test "compile time recursion" { - assert(some_data.len == 21); -} -var some_data: [@intCast(usize, fibonacci(7))]u8 = undefined; -fn fibonacci(x: i32) i32 { - if (x <= 1) return 1; - return fibonacci(x - 1) + fibonacci(x - 2); -} - -fn unwrapAndAddOne(blah: ?i32) i32 { - return blah.? + 1; -} -const should_be_1235 = unwrapAndAddOne(1234); -test "static add one" { - assert(should_be_1235 == 1235); -} - -test "inlined loop" { - comptime var i = 0; - comptime var sum = 0; - inline while (i <= 5) : (i += 1) - sum += i; - assert(sum == 15); -} - -fn gimme1or2(comptime a: bool) i32 { - const x: i32 = 1; - const y: i32 = 2; - comptime var z: i32 = if (a) x else y; - return z; -} -test "inline variable gets result of const if" { - assert(gimme1or2(true) == 1); - assert(gimme1or2(false) == 2); -} - -test "static function evaluation" { - assert(statically_added_number == 3); -} -const statically_added_number = staticAdd(1, 2); -fn staticAdd(a: i32, b: i32) i32 { - return a + b; -} - -test "const expr eval on single expr blocks" { - assert(constExprEvalOnSingleExprBlocksFn(1, true) == 3); -} - -fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 { - const literal = 3; - - const result = if (b) b: { - break :b literal; - } else b: { - break :b x; - }; - - return result; -} - -test "statically initialized list" { - assert(static_point_list[0].x == 1); - assert(static_point_list[0].y == 2); - assert(static_point_list[1].x == 3); - assert(static_point_list[1].y == 4); -} -const Point = struct { - x: i32, - y: i32, -}; -const static_point_list = []Point{ - makePoint(1, 2), - makePoint(3, 4), -}; -fn makePoint(x: i32, y: i32) Point { - return Point{ - .x = x, - .y = y, - }; -} - -test "static eval list init" { - assert(static_vec3.data[2] == 1.0); - assert(vec3(0.0, 0.0, 3.0).data[2] == 3.0); -} -const static_vec3 = vec3(0.0, 0.0, 1.0); -pub const Vec3 = struct { - data: [3]f32, -}; -pub fn vec3(x: f32, y: f32, z: f32) Vec3 { - return Vec3{ .data = []f32{ - x, - y, - z, - } }; -} - -test "constant expressions" { - var array: [array_size]u8 = undefined; - assert(@sizeOf(@typeOf(array)) == 20); -} -const array_size: u8 = 20; - -test "constant struct with negation" { - assert(vertices[0].x == -0.6); -} -const Vertex = struct { - x: f32, - y: f32, - r: f32, - g: f32, - b: f32, -}; -const vertices = []Vertex{ - Vertex{ - .x = -0.6, - .y = -0.4, - .r = 1.0, - .g = 0.0, - .b = 0.0, - }, - Vertex{ - .x = 0.6, - .y = -0.4, - .r = 0.0, - .g = 1.0, - .b = 0.0, - }, - Vertex{ - .x = 0.0, - .y = 0.6, - .r = 0.0, - .g = 0.0, - .b = 1.0, - }, -}; - -test "statically initialized struct" { - st_init_str_foo.x += 1; - assert(st_init_str_foo.x == 14); -} -const StInitStrFoo = struct { - x: i32, - y: bool, -}; -var st_init_str_foo = StInitStrFoo{ - .x = 13, - .y = true, -}; - -test "statically initalized array literal" { - const y: [4]u8 = st_init_arr_lit_x; - assert(y[3] == 4); -} -const st_init_arr_lit_x = []u8{ - 1, - 2, - 3, - 4, -}; - -test "const slice" { - comptime { - const a = "1234567890"; - assert(a.len == 10); - const b = a[1..2]; - assert(b.len == 1); - assert(b[0] == '2'); - } -} - -test "try to trick eval with runtime if" { - assert(testTryToTrickEvalWithRuntimeIf(true) == 10); -} - -fn testTryToTrickEvalWithRuntimeIf(b: bool) usize { - comptime var i: usize = 0; - inline while (i < 10) : (i += 1) { - const result = if (b) false else true; - } - comptime { - return i; - } -} - -fn max(comptime T: type, a: T, b: T) T { - if (T == bool) { - return a or b; - } else if (a > b) { - return a; - } else { - return b; - } -} -fn letsTryToCompareBools(a: bool, b: bool) bool { - return max(bool, a, b); -} -test "inlined block and runtime block phi" { - assert(letsTryToCompareBools(true, true)); - assert(letsTryToCompareBools(true, false)); - assert(letsTryToCompareBools(false, true)); - assert(!letsTryToCompareBools(false, false)); - - comptime { - assert(letsTryToCompareBools(true, true)); - assert(letsTryToCompareBools(true, false)); - assert(letsTryToCompareBools(false, true)); - assert(!letsTryToCompareBools(false, false)); - } -} - -const CmdFn = struct { - name: []const u8, - func: fn (i32) i32, -}; - -const cmd_fns = []CmdFn{ - CmdFn{ - .name = "one", - .func = one, - }, - CmdFn{ - .name = "two", - .func = two, - }, - CmdFn{ - .name = "three", - .func = three, - }, -}; -fn one(value: i32) i32 { - return value + 1; -} -fn two(value: i32) i32 { - return value + 2; -} -fn three(value: i32) i32 { - return value + 3; -} - -fn performFn(comptime prefix_char: u8, start_value: i32) i32 { - var result: i32 = start_value; - comptime var i = 0; - inline while (i < cmd_fns.len) : (i += 1) { - if (cmd_fns[i].name[0] == prefix_char) { - result = cmd_fns[i].func(result); - } - } - return result; -} - -test "comptime iterate over fn ptr list" { - assert(performFn('t', 1) == 6); - assert(performFn('o', 0) == 1); - assert(performFn('w', 99) == 99); -} - -test "eval @setRuntimeSafety at compile-time" { - const result = comptime fnWithSetRuntimeSafety(); - assert(result == 1234); -} - -fn fnWithSetRuntimeSafety() i32 { - @setRuntimeSafety(true); - return 1234; -} - -test "eval @setFloatMode at compile-time" { - const result = comptime fnWithFloatMode(); - assert(result == 1234.0); -} - -fn fnWithFloatMode() f32 { - @setFloatMode(builtin.FloatMode.Strict); - return 1234.0; -} - -const SimpleStruct = struct { - field: i32, - - fn method(self: *const SimpleStruct) i32 { - return self.field + 3; - } -}; - -var simple_struct = SimpleStruct{ .field = 1234 }; - -const bound_fn = simple_struct.method; - -test "call method on bound fn referring to var instance" { - assert(bound_fn() == 1237); -} - -test "ptr to local array argument at comptime" { - comptime { - var bytes: [10]u8 = undefined; - modifySomeBytes(bytes[0..]); - assert(bytes[0] == 'a'); - assert(bytes[9] == 'b'); - } -} - -fn modifySomeBytes(bytes: []u8) void { - bytes[0] = 'a'; - bytes[9] = 'b'; -} - -test "comparisons 0 <= uint and 0 > uint should be comptime" { - testCompTimeUIntComparisons(1234); -} -fn testCompTimeUIntComparisons(x: u32) void { - if (!(0 <= x)) { - @compileError("this condition should be comptime known"); - } - if (0 > x) { - @compileError("this condition should be comptime known"); - } - if (!(x >= 0)) { - @compileError("this condition should be comptime known"); - } - if (x < 0) { - @compileError("this condition should be comptime known"); - } -} - -test "const ptr to variable data changes at runtime" { - assert(foo_ref.name[0] == 'a'); - foo_ref.name = "b"; - assert(foo_ref.name[0] == 'b'); -} - -const Foo = struct { - name: []const u8, -}; - -var foo_contents = Foo{ .name = "a" }; -const foo_ref = &foo_contents; - -test "create global array with for loop" { - assert(global_array[5] == 5 * 5); - assert(global_array[9] == 9 * 9); -} - -const global_array = x: { - var result: [10]usize = undefined; - for (result) |*item, index| { - item.* = index * index; - } - break :x result; -}; - -test "compile-time downcast when the bits fit" { - comptime { - const spartan_count: u16 = 255; - const byte = @intCast(u8, spartan_count); - assert(byte == 255); - } -} - -const hi1 = "hi"; -const hi2 = hi1; -test "const global shares pointer with other same one" { - assertEqualPtrs(&hi1[0], &hi2[0]); - comptime assert(&hi1[0] == &hi2[0]); -} -fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) void { - assert(ptr1 == ptr2); -} - -test "@setEvalBranchQuota" { - comptime { - // 1001 for the loop and then 1 more for the assert fn call - @setEvalBranchQuota(1002); - var i = 0; - var sum = 0; - while (i < 1001) : (i += 1) { - sum += i; - } - assert(sum == 500500); - } -} - -// TODO test "float literal at compile time not lossy" { -// TODO assert(16777216.0 + 1.0 == 16777217.0); -// TODO assert(9007199254740992.0 + 1.0 == 9007199254740993.0); -// TODO } - -test "f32 at compile time is lossy" { - assert(f32(1 << 24) + 1 == 1 << 24); -} - -test "f64 at compile time is lossy" { - assert(f64(1 << 53) + 1 == 1 << 53); -} - -test "f128 at compile time is lossy" { - assert(f128(10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); -} - -// TODO need a better implementation of bigfloat_init_bigint -// assert(f128(1 << 113) == 10384593717069655257060992658440192); - -pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { - return struct { - pub const Node = struct {}; - }; -} - -test "string literal used as comptime slice is memoized" { - const a = "link"; - const b = "link"; - comptime assert(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); - comptime assert(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); -} - -test "comptime slice of undefined pointer of length 0" { - const slice1 = ([*]i32)(undefined)[0..0]; - assert(slice1.len == 0); - const slice2 = ([*]i32)(undefined)[100..100]; - assert(slice2.len == 0); -} - -fn copyWithPartialInline(s: []u32, b: []u8) void { - comptime var i: usize = 0; - inline while (i < 4) : (i += 1) { - s[i] = 0; - s[i] |= u32(b[i * 4 + 0]) << 24; - s[i] |= u32(b[i * 4 + 1]) << 16; - s[i] |= u32(b[i * 4 + 2]) << 8; - s[i] |= u32(b[i * 4 + 3]) << 0; - } -} - -test "binary math operator in partially inlined function" { - var s: [4]u32 = undefined; - var b: [16]u8 = undefined; - - for (b) |*r, i| - r.* = @intCast(u8, i + 1); - - copyWithPartialInline(s[0..], b[0..]); - assert(s[0] == 0x1020304); - assert(s[1] == 0x5060708); - assert(s[2] == 0x90a0b0c); - assert(s[3] == 0xd0e0f10); -} - -test "comptime function with the same args is memoized" { - comptime { - assert(MakeType(i32) == MakeType(i32)); - assert(MakeType(i32) != MakeType(f64)); - } -} - -fn MakeType(comptime T: type) type { - return struct { - field: T, - }; -} - -test "comptime function with mutable pointer is not memoized" { - comptime { - var x: i32 = 1; - const ptr = &x; - increment(ptr); - increment(ptr); - assert(x == 3); - } -} - -fn increment(value: *i32) void { - value.* += 1; -} - -fn generateTable(comptime T: type) [1010]T { - var res: [1010]T = undefined; - var i: usize = 0; - while (i < 1010) : (i += 1) { - res[i] = @intCast(T, i); - } - return res; -} - -fn doesAlotT(comptime T: type, value: usize) T { - @setEvalBranchQuota(5000); - const table = comptime blk: { - break :blk generateTable(T); - }; - return table[value]; -} - -test "@setEvalBranchQuota at same scope as generic function call" { - assert(doesAlotT(u32, 2) == 2); -} - -test "comptime slice of slice preserves comptime var" { - comptime { - var buff: [10]u8 = undefined; - buff[0..][0..][0] = 1; - assert(buff[0..][0..][0] == 1); - } -} - -test "comptime slice of pointer preserves comptime var" { - comptime { - var buff: [10]u8 = undefined; - var a = buff[0..].ptr; - a[0..1][0] = 1; - assert(buff[0..][0..][0] == 1); - } -} - -const SingleFieldStruct = struct { - x: i32, - - fn read_x(self: *const SingleFieldStruct) i32 { - return self.x; - } -}; -test "const ptr to comptime mutable data is not memoized" { - comptime { - var foo = SingleFieldStruct{ .x = 1 }; - assert(foo.read_x() == 1); - foo.x = 2; - assert(foo.read_x() == 2); - } -} - -test "array concat of slices gives slice" { - comptime { - var a: []const u8 = "aoeu"; - var b: []const u8 = "asdf"; - const c = a ++ b; - assert(std.mem.eql(u8, c, "aoeuasdf")); - } -} - -test "comptime shlWithOverflow" { - const ct_shifted: u64 = comptime amt: { - var amt = u64(0); - _ = @shlWithOverflow(u64, ~u64(0), 16, &amt); - break :amt amt; - }; - - const rt_shifted: u64 = amt: { - var amt = u64(0); - _ = @shlWithOverflow(u64, ~u64(0), 16, &amt); - break :amt amt; - }; - - assert(ct_shifted == rt_shifted); -} - -test "runtime 128 bit integer division" { - var a: u128 = 152313999999999991610955792383; - var b: u128 = 10000000000000000000; - var c = a / b; - assert(c == 15231399999); -} - -pub const Info = struct { - version: u8, -}; - -pub const diamond_info = Info{ .version = 0 }; - -test "comptime modification of const struct field" { - comptime { - var res = diamond_info; - res.version = 1; - assert(diamond_info.version == 0); - assert(res.version == 1); - } -} - -test "pointer to type" { - comptime { - var T: type = i32; - assert(T == i32); - var ptr = &T; - assert(@typeOf(ptr) == *type); - ptr.* = f32; - assert(T == f32); - assert(*T == *f32); - } -} - -test "slice of type" { - comptime { - var types_array = []type{ i32, f64, type }; - for (types_array) |T, i| { - switch (i) { - 0 => assert(T == i32), - 1 => assert(T == f64), - 2 => assert(T == type), - else => unreachable, - } - } - for (types_array[0..]) |T, i| { - switch (i) { - 0 => assert(T == i32), - 1 => assert(T == f64), - 2 => assert(T == type), - else => unreachable, - } - } - } -} - -const Wrapper = struct { - T: type, -}; - -fn wrap(comptime T: type) Wrapper { - return Wrapper{ .T = T }; -} - -test "function which returns struct with type field causes implicit comptime" { - const ty = wrap(i32).T; - assert(ty == i32); -} - -test "call method with comptime pass-by-non-copying-value self parameter" { - const S = struct { - a: u8, - - fn b(comptime s: @This()) u8 { - return s.a; - } - }; - - const s = S{ .a = 2 }; - var b = s.b(); - assert(b == 2); -} - -test "@tagName of @typeId" { - const str = @tagName(@typeId(u8)); - assert(std.mem.eql(u8, str, "Int")); -} - -test "setting backward branch quota just before a generic fn call" { - @setEvalBranchQuota(1001); - loopNTimes(1001); -} - -fn loopNTimes(comptime n: usize) void { - comptime var i = 0; - inline while (i < n) : (i += 1) {} -} - -test "variable inside inline loop that has different types on different iterations" { - testVarInsideInlineLoop(true, u32(42)); -} - -fn testVarInsideInlineLoop(args: ...) void { - comptime var i = 0; - inline while (i < args.len) : (i += 1) { - const x = args[i]; - if (i == 0) assert(x); - if (i == 1) assert(x == 42); - } -} - -test "inline for with same type but different values" { - var res: usize = 0; - inline for ([]type{ [2]u8, [1]u8, [2]u8 }) |T| { - var a: T = undefined; - res += a.len; - } - assert(res == 5); -} - -test "refer to the type of a generic function" { - const Func = fn (type) void; - const f: Func = doNothingWithType; - f(i32); -} - -fn doNothingWithType(comptime T: type) void {} - -test "zero extend from u0 to u1" { - var zero_u0: u0 = 0; - var zero_u1: u1 = zero_u0; - assert(zero_u1 == 0); -} - -test "bit shift a u1" { - var x: u1 = 1; - var y = x << 0; - assert(y == 1); -} - -test "@intCast to a u0" { - var x: u8 = 0; - var y: u0 = @intCast(u0, x); - assert(y == 0); -} - -test "@bytesToslice on a packed struct" { - const F = packed struct { - a: u8, - }; - - var b = [1]u8{9}; - var f = @bytesToSlice(F, b); - assert(f[0].a == 9); -} - -test "comptime pointer cast array and then slice" { - const array = []u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; - - const ptrA: [*]const u8 = @ptrCast([*]const u8, &array); - const sliceA: []const u8 = ptrA[0..2]; - - const ptrB: [*]const u8 = &array; - const sliceB: []const u8 = ptrB[0..2]; - - assert(sliceA[1] == 2); - assert(sliceB[1] == 2); -} - -test "slice bounds in comptime concatenation" { - const bs = comptime blk: { - const b = c"11"; - break :blk b[0..1]; - }; - const str = "" ++ bs; - assert(str.len == 1); - assert(std.mem.eql(u8, str, "1")); - - const str2 = bs ++ ""; - assert(str2.len == 1); - assert(std.mem.eql(u8, str2, "1")); -} - -test "comptime bitwise operators" { - comptime { - assert(3 & 1 == 1); - assert(3 & -1 == 3); - assert(-3 & -1 == -3); - assert(3 | -1 == -1); - assert(-3 | -1 == -1); - assert(3 ^ -1 == -4); - assert(-3 ^ -1 == 2); - assert(~i8(-1) == 0); - assert(~i128(-1) == 0); - assert(18446744073709551615 & 18446744073709551611 == 18446744073709551611); - assert(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); - assert(~u128(0) == 0xffffffffffffffffffffffffffffffff); - } -} - -test "*align(1) u16 is the same as *align(1:0:2) u16" { - comptime { - assert(*align(1:0:2) u16 == *align(1) u16); - // TODO add parsing support for this syntax - //assert(*align(:0:2) u16 == *u16); - } -} - -test "array concatenation forces comptime" { - var a = oneItem(3) ++ oneItem(4); - assert(std.mem.eql(i32, a, []i32{3, 4})); -} - -test "array multiplication forces comptime" { - var a = oneItem(3) ** scalar(2); - assert(std.mem.eql(i32, a, []i32{3, 3})); -} - -fn oneItem(x: i32) [1]i32 { - return []i32{x}; -} - -fn scalar(x: u32) u32 { - return x; -} diff --git a/test/cases/field_parent_ptr.zig b/test/cases/field_parent_ptr.zig deleted file mode 100644 index 00d4e0f367..0000000000 --- a/test/cases/field_parent_ptr.zig +++ /dev/null @@ -1,41 +0,0 @@ -const assert = @import("std").debug.assert; - -test "@fieldParentPtr non-first field" { - testParentFieldPtr(&foo.c); - comptime testParentFieldPtr(&foo.c); -} - -test "@fieldParentPtr first field" { - testParentFieldPtrFirst(&foo.a); - comptime testParentFieldPtrFirst(&foo.a); -} - -const Foo = struct { - a: bool, - b: f32, - c: i32, - d: i32, -}; - -const foo = Foo{ - .a = true, - .b = 0.123, - .c = 1234, - .d = -10, -}; - -fn testParentFieldPtr(c: *const i32) void { - assert(c == &foo.c); - - const base = @fieldParentPtr(Foo, "c", c); - assert(base == &foo); - assert(&base.c == c); -} - -fn testParentFieldPtrFirst(a: *const bool) void { - assert(a == &foo.a); - - const base = @fieldParentPtr(Foo, "a", a); - assert(base == &foo); - assert(&base.a == a); -} diff --git a/test/cases/fn.zig b/test/cases/fn.zig deleted file mode 100644 index 8908bd7854..0000000000 --- a/test/cases/fn.zig +++ /dev/null @@ -1,207 +0,0 @@ -const assert = @import("std").debug.assert; - -test "params" { - assert(testParamsAdd(22, 11) == 33); -} -fn testParamsAdd(a: i32, b: i32) i32 { - return a + b; -} - -test "local variables" { - testLocVars(2); -} -fn testLocVars(b: i32) void { - const a: i32 = 1; - if (a + b != 3) unreachable; -} - -test "void parameters" { - voidFun(1, void{}, 2, {}); -} -fn voidFun(a: i32, b: void, c: i32, d: void) void { - const v = b; - const vv: void = if (a == 1) v else {}; - assert(a + c == 3); - return vv; -} - -test "mutable local variables" { - var zero: i32 = 0; - assert(zero == 0); - - var i = i32(0); - while (i != 3) { - i += 1; - } - assert(i == 3); -} - -test "separate block scopes" { - { - const no_conflict: i32 = 5; - assert(no_conflict == 5); - } - - const c = x: { - const no_conflict = i32(10); - break :x no_conflict; - }; - assert(c == 10); -} - -test "call function with empty string" { - acceptsString(""); -} - -fn acceptsString(foo: []u8) void {} - -fn @"weird function name"() i32 { - return 1234; -} -test "weird function name" { - assert(@"weird function name"() == 1234); -} - -test "implicit cast function unreachable return" { - wantsFnWithVoid(fnWithUnreachable); -} - -fn wantsFnWithVoid(f: fn () void) void {} - -fn fnWithUnreachable() noreturn { - unreachable; -} - -test "function pointers" { - const fns = []@typeOf(fn1){ - fn1, - fn2, - fn3, - fn4, - }; - for (fns) |f, i| { - assert(f() == @intCast(u32, i) + 5); - } -} -fn fn1() u32 { - return 5; -} -fn fn2() u32 { - return 6; -} -fn fn3() u32 { - return 7; -} -fn fn4() u32 { - return 8; -} - -test "inline function call" { - assert(@inlineCall(add, 3, 9) == 12); -} - -fn add(a: i32, b: i32) i32 { - return a + b; -} - -test "number literal as an argument" { - numberLiteralArg(3); - comptime numberLiteralArg(3); -} - -fn numberLiteralArg(a: var) void { - assert(a == 3); -} - -test "assign inline fn to const variable" { - const a = inlineFn; - a(); -} - -inline fn inlineFn() void {} - -test "pass by non-copying value" { - assert(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); -} - -const Point = struct { - x: i32, - y: i32, -}; - -fn addPointCoords(pt: Point) i32 { - return pt.x + pt.y; -} - -test "pass by non-copying value through var arg" { - assert(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3); -} - -fn addPointCoordsVar(pt: var) i32 { - comptime assert(@typeOf(pt) == Point); - return pt.x + pt.y; -} - -test "pass by non-copying value as method" { - var pt = Point2{ .x = 1, .y = 2 }; - assert(pt.addPointCoords() == 3); -} - -const Point2 = struct { - x: i32, - y: i32, - - fn addPointCoords(self: Point2) i32 { - return self.x + self.y; - } -}; - -test "pass by non-copying value as method, which is generic" { - var pt = Point3{ .x = 1, .y = 2 }; - assert(pt.addPointCoords(i32) == 3); -} - -const Point3 = struct { - x: i32, - y: i32, - - fn addPointCoords(self: Point3, comptime T: type) i32 { - return self.x + self.y; - } -}; - -test "pass by non-copying value as method, at comptime" { - comptime { - var pt = Point2{ .x = 1, .y = 2 }; - assert(pt.addPointCoords() == 3); - } -} - -fn outer(y: u32) fn (u32) u32 { - const Y = @typeOf(y); - const st = struct { - fn get(z: u32) u32 { - return z + @sizeOf(Y); - } - }; - return st.get; -} - -test "return inner function which references comptime variable of outer function" { - var func = outer(10); - assert(func(3) == 7); -} - -test "extern struct with stdcallcc fn pointer" { - const S = extern struct { - ptr: stdcallcc fn () i32, - - stdcallcc fn foo() i32 { - return 1234; - } - }; - - var s: S = undefined; - s.ptr = S.foo; - assert(s.ptr() == 1234); -} diff --git a/test/cases/fn_in_struct_in_comptime.zig b/test/cases/fn_in_struct_in_comptime.zig deleted file mode 100644 index fabb57e9cb..0000000000 --- a/test/cases/fn_in_struct_in_comptime.zig +++ /dev/null @@ -1,17 +0,0 @@ -const assert = @import("std").debug.assert; - -fn get_foo() fn (*u8) usize { - comptime { - return struct { - fn func(ptr: *u8) usize { - var u = @ptrToInt(ptr); - return u; - } - }.func; - } -} - -test "define a function in an anonymous struct in comptime" { - const foo = get_foo(); - assert(foo(@intToPtr(*u8, 12345)) == 12345); -} diff --git a/test/cases/for.zig b/test/cases/for.zig deleted file mode 100644 index aecd8b9a07..0000000000 --- a/test/cases/for.zig +++ /dev/null @@ -1,106 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const mem = std.mem; - -test "continue in for loop" { - const array = []i32{ - 1, - 2, - 3, - 4, - 5, - }; - var sum: i32 = 0; - for (array) |x| { - sum += x; - if (x < 3) { - continue; - } - break; - } - if (sum != 6) unreachable; -} - -test "for loop with pointer elem var" { - const source = "abcdefg"; - var target: [source.len]u8 = undefined; - mem.copy(u8, target[0..], source); - mangleString(target[0..]); - assert(mem.eql(u8, target, "bcdefgh")); -} -fn mangleString(s: []u8) void { - for (s) |*c| { - c.* += 1; - } -} - -test "basic for loop" { - const expected_result = []u8{ 9, 8, 7, 6, 0, 1, 2, 3 } ** 3; - - var buffer: [expected_result.len]u8 = undefined; - var buf_index: usize = 0; - - const array = []u8{ 9, 8, 7, 6 }; - for (array) |item| { - buffer[buf_index] = item; - buf_index += 1; - } - for (array) |item, index| { - buffer[buf_index] = @intCast(u8, index); - buf_index += 1; - } - const array_ptr = &array; - for (array_ptr) |item| { - buffer[buf_index] = item; - buf_index += 1; - } - for (array_ptr) |item, index| { - buffer[buf_index] = @intCast(u8, index); - buf_index += 1; - } - const unknown_size: []const u8 = array; - for (unknown_size) |item| { - buffer[buf_index] = item; - buf_index += 1; - } - for (unknown_size) |item, index| { - buffer[buf_index] = @intCast(u8, index); - buf_index += 1; - } - - assert(mem.eql(u8, buffer[0..buf_index], expected_result)); -} - -test "break from outer for loop" { - testBreakOuter(); - comptime testBreakOuter(); -} - -fn testBreakOuter() void { - var array = "aoeu"; - var count: usize = 0; - outer: for (array) |_| { - for (array) |_| { - count += 1; - break :outer; - } - } - assert(count == 1); -} - -test "continue outer for loop" { - testContinueOuter(); - comptime testContinueOuter(); -} - -fn testContinueOuter() void { - var array = "aoeu"; - var counter: usize = 0; - outer: for (array) |_| { - for (array) |_| { - counter += 1; - continue :outer; - } - } - assert(counter == array.len); -} diff --git a/test/cases/generics.zig b/test/cases/generics.zig deleted file mode 100644 index 52aa013989..0000000000 --- a/test/cases/generics.zig +++ /dev/null @@ -1,151 +0,0 @@ -const assert = @import("std").debug.assert; - -test "simple generic fn" { - assert(max(i32, 3, -1) == 3); - assert(max(f32, 0.123, 0.456) == 0.456); - assert(add(2, 3) == 5); -} - -fn max(comptime T: type, a: T, b: T) T { - return if (a > b) a else b; -} - -fn add(comptime a: i32, b: i32) i32 { - return (comptime a) + b; -} - -const the_max = max(u32, 1234, 5678); -test "compile time generic eval" { - assert(the_max == 5678); -} - -fn gimmeTheBigOne(a: u32, b: u32) u32 { - return max(u32, a, b); -} - -fn shouldCallSameInstance(a: u32, b: u32) u32 { - return max(u32, a, b); -} - -fn sameButWithFloats(a: f64, b: f64) f64 { - return max(f64, a, b); -} - -test "fn with comptime args" { - assert(gimmeTheBigOne(1234, 5678) == 5678); - assert(shouldCallSameInstance(34, 12) == 34); - assert(sameButWithFloats(0.43, 0.49) == 0.49); -} - -test "var params" { - assert(max_i32(12, 34) == 34); - assert(max_f64(1.2, 3.4) == 3.4); -} - -comptime { - assert(max_i32(12, 34) == 34); - assert(max_f64(1.2, 3.4) == 3.4); -} - -fn max_var(a: var, b: var) @typeOf(a + b) { - return if (a > b) a else b; -} - -fn max_i32(a: i32, b: i32) i32 { - return max_var(a, b); -} - -fn max_f64(a: f64, b: f64) f64 { - return max_var(a, b); -} - -pub fn List(comptime T: type) type { - return SmallList(T, 8); -} - -pub fn SmallList(comptime T: type, comptime STATIC_SIZE: usize) type { - return struct { - items: []T, - length: usize, - prealloc_items: [STATIC_SIZE]T, - }; -} - -test "function with return type type" { - var list: List(i32) = undefined; - var list2: List(i32) = undefined; - list.length = 10; - list2.length = 10; - assert(list.prealloc_items.len == 8); - assert(list2.prealloc_items.len == 8); -} - -test "generic struct" { - var a1 = GenNode(i32){ - .value = 13, - .next = null, - }; - var b1 = GenNode(bool){ - .value = true, - .next = null, - }; - assert(a1.value == 13); - assert(a1.value == a1.getVal()); - assert(b1.getVal()); -} -fn GenNode(comptime T: type) type { - return struct { - value: T, - next: ?*GenNode(T), - fn getVal(n: *const GenNode(T)) T { - return n.value; - } - }; -} - -test "const decls in struct" { - assert(GenericDataThing(3).count_plus_one == 4); -} -fn GenericDataThing(comptime count: isize) type { - return struct { - const count_plus_one = count + 1; - }; -} - -test "use generic param in generic param" { - assert(aGenericFn(i32, 3, 4) == 7); -} -fn aGenericFn(comptime T: type, comptime a: T, b: T) T { - return a + b; -} - -test "generic fn with implicit cast" { - assert(getFirstByte(u8, []u8{13}) == 13); - assert(getFirstByte(u16, []u16{ - 0, - 13, - }) == 0); -} -fn getByte(ptr: ?*const u8) u8 { - return ptr.?.*; -} -fn getFirstByte(comptime T: type, mem: []const T) u8 { - return getByte(@ptrCast(*const u8, &mem[0])); -} - -const foos = []fn (var) bool{ - foo1, - foo2, -}; - -fn foo1(arg: var) bool { - return arg; -} -fn foo2(arg: var) bool { - return !arg; -} - -test "array of generic fns" { - assert(foos[0](true)); - assert(!foos[1](true)); -} diff --git a/test/cases/if.zig b/test/cases/if.zig deleted file mode 100644 index 808936bfa5..0000000000 --- a/test/cases/if.zig +++ /dev/null @@ -1,37 +0,0 @@ -const assert = @import("std").debug.assert; - -test "if statements" { - shouldBeEqual(1, 1); - firstEqlThird(2, 1, 2); -} -fn shouldBeEqual(a: i32, b: i32) void { - if (a != b) { - unreachable; - } else { - return; - } -} -fn firstEqlThird(a: i32, b: i32, c: i32) void { - if (a == b) { - unreachable; - } else if (b == c) { - unreachable; - } else if (a == c) { - return; - } else { - unreachable; - } -} - -test "else if expression" { - assert(elseIfExpressionF(1) == 1); -} -fn elseIfExpressionF(c: u8) u8 { - if (c == 0) { - return 0; - } else if (c == 1) { - return 1; - } else { - return u8(2); - } -} diff --git a/test/cases/import.zig b/test/cases/import.zig deleted file mode 100644 index 6d6d4b0208..0000000000 --- a/test/cases/import.zig +++ /dev/null @@ -1,10 +0,0 @@ -const assert = @import("std").debug.assert; -const a_namespace = @import("import/a_namespace.zig"); - -test "call fn via namespace lookup" { - assert(a_namespace.foo() == 1234); -} - -test "importing the same thing gives the same import" { - assert(@import("std") == @import("std")); -} diff --git a/test/cases/import/a_namespace.zig b/test/cases/import/a_namespace.zig deleted file mode 100644 index 042f1867a5..0000000000 --- a/test/cases/import/a_namespace.zig +++ /dev/null @@ -1,3 +0,0 @@ -pub fn foo() i32 { - return 1234; -} diff --git a/test/cases/incomplete_struct_param_tld.zig b/test/cases/incomplete_struct_param_tld.zig deleted file mode 100644 index f1ac03a292..0000000000 --- a/test/cases/incomplete_struct_param_tld.zig +++ /dev/null @@ -1,30 +0,0 @@ -const assert = @import("std").debug.assert; - -const A = struct { - b: B, -}; - -const B = struct { - c: C, -}; - -const C = struct { - x: i32, - - fn d(c: *const C) i32 { - return c.x; - } -}; - -fn foo(a: A) i32 { - return a.b.c.d(); -} - -test "incomplete struct param top level declaration" { - const a = A{ - .b = B{ - .c = C{ .x = 13 }, - }, - }; - assert(foo(a) == 13); -} diff --git a/test/cases/inttoptr.zig b/test/cases/inttoptr.zig deleted file mode 100644 index ba3cc52f09..0000000000 --- a/test/cases/inttoptr.zig +++ /dev/null @@ -1,27 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const assertOrPanic = std.debug.assertOrPanic; - -test "casting random address to function pointer" { - randomAddressToFunction(); - comptime randomAddressToFunction(); -} - -fn randomAddressToFunction() void { - var addr: usize = 0xdeadbeef; - var ptr = @intToPtr(fn () void, addr); -} - -test "mutate through ptr initialized with constant intToPtr value" { - forceCompilerAnalyzeBranchHardCodedPtrDereference(false); -} - -fn forceCompilerAnalyzeBranchHardCodedPtrDereference(x: bool) void { - const hardCodedP = @intToPtr(*volatile u8, 0xdeadbeef); - if (x) { - hardCodedP.* = hardCodedP.* | 10; - } else { - return; - } -} - diff --git a/test/cases/ir_block_deps.zig b/test/cases/ir_block_deps.zig deleted file mode 100644 index 5c1b18c00e..0000000000 --- a/test/cases/ir_block_deps.zig +++ /dev/null @@ -1,21 +0,0 @@ -const assert = @import("std").debug.assert; - -fn foo(id: u64) !i32 { - return switch (id) { - 1 => getErrInt(), - 2 => { - const size = try getErrInt(); - return try getErrInt(); - }, - else => error.ItBroke, - }; -} - -fn getErrInt() anyerror!i32 { - return 0; -} - -test "ir block deps" { - assert((foo(1) catch unreachable) == 0); - assert((foo(2) catch unreachable) == 0); -} diff --git a/test/cases/math.zig b/test/cases/math.zig deleted file mode 100644 index 7d6b1bd9ac..0000000000 --- a/test/cases/math.zig +++ /dev/null @@ -1,500 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const maxInt = std.math.maxInt; -const minInt = std.math.minInt; - -test "division" { - testDivision(); - comptime testDivision(); -} -fn testDivision() void { - assert(div(u32, 13, 3) == 4); - assert(div(f16, 1.0, 2.0) == 0.5); - assert(div(f32, 1.0, 2.0) == 0.5); - - assert(divExact(u32, 55, 11) == 5); - assert(divExact(i32, -55, 11) == -5); - assert(divExact(f16, 55.0, 11.0) == 5.0); - assert(divExact(f16, -55.0, 11.0) == -5.0); - assert(divExact(f32, 55.0, 11.0) == 5.0); - assert(divExact(f32, -55.0, 11.0) == -5.0); - - assert(divFloor(i32, 5, 3) == 1); - assert(divFloor(i32, -5, 3) == -2); - assert(divFloor(f16, 5.0, 3.0) == 1.0); - assert(divFloor(f16, -5.0, 3.0) == -2.0); - assert(divFloor(f32, 5.0, 3.0) == 1.0); - assert(divFloor(f32, -5.0, 3.0) == -2.0); - assert(divFloor(i32, -0x80000000, -2) == 0x40000000); - assert(divFloor(i32, 0, -0x80000000) == 0); - assert(divFloor(i32, -0x40000001, 0x40000000) == -2); - assert(divFloor(i32, -0x80000000, 1) == -0x80000000); - - assert(divTrunc(i32, 5, 3) == 1); - assert(divTrunc(i32, -5, 3) == -1); - assert(divTrunc(f16, 5.0, 3.0) == 1.0); - assert(divTrunc(f16, -5.0, 3.0) == -1.0); - assert(divTrunc(f32, 5.0, 3.0) == 1.0); - assert(divTrunc(f32, -5.0, 3.0) == -1.0); - assert(divTrunc(f64, 5.0, 3.0) == 1.0); - assert(divTrunc(f64, -5.0, 3.0) == -1.0); - - comptime { - assert( - 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600, - ); - assert( - @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600, - ); - assert( - 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, - ); - assert( - @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, - ); - assert( - @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, - ); - assert( - @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, - ); - assert( - 4126227191251978491697987544882340798050766755606969681711 % 10 == 1, - ); - } -} -fn div(comptime T: type, a: T, b: T) T { - return a / b; -} -fn divExact(comptime T: type, a: T, b: T) T { - return @divExact(a, b); -} -fn divFloor(comptime T: type, a: T, b: T) T { - return @divFloor(a, b); -} -fn divTrunc(comptime T: type, a: T, b: T) T { - return @divTrunc(a, b); -} - -test "@addWithOverflow" { - var result: u8 = undefined; - assert(@addWithOverflow(u8, 250, 100, &result)); - assert(!@addWithOverflow(u8, 100, 150, &result)); - assert(result == 250); -} - -// TODO test mulWithOverflow -// TODO test subWithOverflow - -test "@shlWithOverflow" { - var result: u16 = undefined; - assert(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); - assert(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); - assert(result == 0b1011111111111100); -} - -test "@clz" { - testClz(); - comptime testClz(); -} - -fn testClz() void { - assert(clz(u8(0b00001010)) == 4); - assert(clz(u8(0b10001010)) == 0); - assert(clz(u8(0b00000000)) == 8); - assert(clz(u128(0xffffffffffffffff)) == 64); - assert(clz(u128(0x10000000000000000)) == 63); -} - -fn clz(x: var) usize { - return @clz(x); -} - -test "@ctz" { - testCtz(); - comptime testCtz(); -} - -fn testCtz() void { - assert(ctz(u8(0b10100000)) == 5); - assert(ctz(u8(0b10001010)) == 1); - assert(ctz(u8(0b00000000)) == 8); -} - -fn ctz(x: var) usize { - return @ctz(x); -} - -test "assignment operators" { - var i: u32 = 0; - i += 5; - assert(i == 5); - i -= 2; - assert(i == 3); - i *= 20; - assert(i == 60); - i /= 3; - assert(i == 20); - i %= 11; - assert(i == 9); - i <<= 1; - assert(i == 18); - i >>= 2; - assert(i == 4); - i = 6; - i &= 5; - assert(i == 4); - i ^= 6; - assert(i == 2); - i = 6; - i |= 3; - assert(i == 7); -} - -test "three expr in a row" { - testThreeExprInARow(false, true); - comptime testThreeExprInARow(false, true); -} -fn testThreeExprInARow(f: bool, t: bool) void { - assertFalse(f or f or f); - assertFalse(t and t and f); - assertFalse(1 | 2 | 4 != 7); - assertFalse(3 ^ 6 ^ 8 != 13); - assertFalse(7 & 14 & 28 != 4); - assertFalse(9 << 1 << 2 != 9 << 3); - assertFalse(90 >> 1 >> 2 != 90 >> 3); - assertFalse(100 - 1 + 1000 != 1099); - assertFalse(5 * 4 / 2 % 3 != 1); - assertFalse(i32(i32(5)) != 5); - assertFalse(!!false); - assertFalse(i32(7) != --(i32(7))); -} -fn assertFalse(b: bool) void { - assert(!b); -} - -test "const number literal" { - const one = 1; - const eleven = ten + one; - - assert(eleven == 11); -} -const ten = 10; - -test "unsigned wrapping" { - testUnsignedWrappingEval(maxInt(u32)); - comptime testUnsignedWrappingEval(maxInt(u32)); -} -fn testUnsignedWrappingEval(x: u32) void { - const zero = x +% 1; - assert(zero == 0); - const orig = zero -% 1; - assert(orig == maxInt(u32)); -} - -test "signed wrapping" { - testSignedWrappingEval(maxInt(i32)); - comptime testSignedWrappingEval(maxInt(i32)); -} -fn testSignedWrappingEval(x: i32) void { - const min_val = x +% 1; - assert(min_val == minInt(i32)); - const max_val = min_val -% 1; - assert(max_val == maxInt(i32)); -} - -test "negation wrapping" { - testNegationWrappingEval(minInt(i16)); - comptime testNegationWrappingEval(minInt(i16)); -} -fn testNegationWrappingEval(x: i16) void { - assert(x == -32768); - const neg = -%x; - assert(neg == -32768); -} - -test "unsigned 64-bit division" { - test_u64_div(); - comptime test_u64_div(); -} -fn test_u64_div() void { - const result = divWithResult(1152921504606846976, 34359738365); - assert(result.quotient == 33554432); - assert(result.remainder == 100663296); -} -fn divWithResult(a: u64, b: u64) DivResult { - return DivResult{ - .quotient = a / b, - .remainder = a % b, - }; -} -const DivResult = struct { - quotient: u64, - remainder: u64, -}; - -test "binary not" { - assert(comptime x: { - break :x ~u16(0b1010101010101010) == 0b0101010101010101; - }); - assert(comptime x: { - break :x ~u64(2147483647) == 18446744071562067968; - }); - testBinaryNot(0b1010101010101010); -} - -fn testBinaryNot(x: u16) void { - assert(~x == 0b0101010101010101); -} - -test "small int addition" { - var x: @IntType(false, 2) = 0; - assert(x == 0); - - x += 1; - assert(x == 1); - - x += 1; - assert(x == 2); - - x += 1; - assert(x == 3); - - var result: @typeOf(x) = 3; - assert(@addWithOverflow(@typeOf(x), x, 1, &result)); - - assert(result == 0); -} - -test "float equality" { - const x: f64 = 0.012; - const y: f64 = x + 1.0; - - testFloatEqualityImpl(x, y); - comptime testFloatEqualityImpl(x, y); -} - -fn testFloatEqualityImpl(x: f64, y: f64) void { - const y2 = x + 1.0; - assert(y == y2); -} - -test "allow signed integer division/remainder when values are comptime known and positive or exact" { - assert(5 / 3 == 1); - assert(-5 / -3 == 1); - assert(-6 / 3 == -2); - - assert(5 % 3 == 2); - assert(-6 % 3 == 0); -} - -test "hex float literal parsing" { - comptime assert(0x1.0 == 1.0); -} - -test "quad hex float literal parsing in range" { - const a = 0x1.af23456789bbaaab347645365cdep+5; - const b = 0x1.dedafcff354b6ae9758763545432p-9; - const c = 0x1.2f34dd5f437e849b4baab754cdefp+4534; - const d = 0x1.edcbff8ad76ab5bf46463233214fp-435; -} - -test "quad hex float literal parsing accurate" { - const a: f128 = 0x1.1111222233334444555566667777p+0; - - // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing. - const expected: u128 = 0x3fff1111222233334444555566667777; - assert(@bitCast(u128, a) == expected); -} - -test "hex float literal within range" { - const a = 0x1.0p16383; - const b = 0x0.1p16387; - const c = 0x1.0p-16382; -} - -test "truncating shift left" { - testShlTrunc(maxInt(u16)); - comptime testShlTrunc(maxInt(u16)); -} -fn testShlTrunc(x: u16) void { - const shifted = x << 1; - assert(shifted == 65534); -} - -test "truncating shift right" { - testShrTrunc(maxInt(u16)); - comptime testShrTrunc(maxInt(u16)); -} -fn testShrTrunc(x: u16) void { - const shifted = x >> 1; - assert(shifted == 32767); -} - -test "exact shift left" { - testShlExact(0b00110101); - comptime testShlExact(0b00110101); -} -fn testShlExact(x: u8) void { - const shifted = @shlExact(x, 2); - assert(shifted == 0b11010100); -} - -test "exact shift right" { - testShrExact(0b10110100); - comptime testShrExact(0b10110100); -} -fn testShrExact(x: u8) void { - const shifted = @shrExact(x, 2); - assert(shifted == 0b00101101); -} - -test "comptime_int addition" { - comptime { - assert(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); - assert(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); - } -} - -test "comptime_int multiplication" { - comptime { - assert( - 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567, - ); - assert( - 594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016, - ); - } -} - -test "comptime_int shifting" { - comptime { - assert((u128(1) << 127) == 0x80000000000000000000000000000000); - } -} - -test "comptime_int multi-limb shift and mask" { - comptime { - var a = 0xefffffffa0000001eeeeeeefaaaaaaab; - - assert(u32(a & 0xffffffff) == 0xaaaaaaab); - a >>= 32; - assert(u32(a & 0xffffffff) == 0xeeeeeeef); - a >>= 32; - assert(u32(a & 0xffffffff) == 0xa0000001); - a >>= 32; - assert(u32(a & 0xffffffff) == 0xefffffff); - a >>= 32; - - assert(a == 0); - } -} - -test "comptime_int multi-limb partial shift right" { - comptime { - var a = 0x1ffffffffeeeeeeee; - a >>= 16; - assert(a == 0x1ffffffffeeee); - } -} - -test "xor" { - test_xor(); - comptime test_xor(); -} - -fn test_xor() void { - assert(0xFF ^ 0x00 == 0xFF); - assert(0xF0 ^ 0x0F == 0xFF); - assert(0xFF ^ 0xF0 == 0x0F); - assert(0xFF ^ 0x0F == 0xF0); - assert(0xFF ^ 0xFF == 0x00); -} - -test "comptime_int xor" { - comptime { - assert(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - assert(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - assert(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x0000000000000000FFFFFFFFFFFFFFFF); - assert(0x0000000000000000FFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFF0000000000000000); - assert(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000000000000000000000000000); - assert(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0x00000000FFFFFFFF00000000FFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - assert(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000FFFFFFFF00000000FFFFFFFF); - assert(0x00000000FFFFFFFF00000000FFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFF00000000FFFFFFFF00000000); - } -} - -test "f128" { - test_f128(); - comptime test_f128(); -} - -fn make_f128(x: f128) f128 { - return x; -} - -fn test_f128() void { - assert(@sizeOf(f128) == 16); - assert(make_f128(1.0) == 1.0); - assert(make_f128(1.0) != 1.1); - assert(make_f128(1.0) > 0.9); - assert(make_f128(1.0) >= 0.9); - assert(make_f128(1.0) >= 1.0); - should_not_be_zero(1.0); -} - -fn should_not_be_zero(x: f128) void { - assert(x != 0.0); -} - -test "comptime float rem int" { - comptime { - var x = f32(1) % 2; - assert(x == 1.0); - } -} - -test "remainder division" { - comptime remdiv(f16); - comptime remdiv(f32); - comptime remdiv(f64); - comptime remdiv(f128); - remdiv(f16); - remdiv(f64); - remdiv(f128); -} - -fn remdiv(comptime T: type) void { - assert(T(1) == T(1) % T(2)); - assert(T(1) == T(7) % T(3)); -} - -test "@sqrt" { - testSqrt(f64, 12.0); - comptime testSqrt(f64, 12.0); - testSqrt(f32, 13.0); - comptime testSqrt(f32, 13.0); - testSqrt(f16, 13.0); - comptime testSqrt(f16, 13.0); - - const x = 14.0; - const y = x * x; - const z = @sqrt(@typeOf(y), y); - comptime assert(z == x); -} - -fn testSqrt(comptime T: type, x: T) void { - assert(@sqrt(T, x * x) == x); -} - -test "comptime_int param and return" { - const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702); - assert(a == 137114567242441932203689521744947848950); - - const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768); - assert(b == 985095453608931032642182098849559179469148836107390954364380); -} - -fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int { - return a + b; -} diff --git a/test/cases/merge_error_sets.zig b/test/cases/merge_error_sets.zig deleted file mode 100644 index 147b580232..0000000000 --- a/test/cases/merge_error_sets.zig +++ /dev/null @@ -1,21 +0,0 @@ -const A = error{ - FileNotFound, - NotDir, -}; -const B = error{OutOfMemory}; - -const C = A || B; - -fn foo() C!void { - return error.NotDir; -} - -test "merge error sets" { - if (foo()) { - @panic("unexpected"); - } else |err| switch (err) { - error.OutOfMemory => @panic("unexpected"), - error.FileNotFound => @panic("unexpected"), - error.NotDir => {}, - } -} diff --git a/test/cases/misc.zig b/test/cases/misc.zig deleted file mode 100644 index 1a34d54e9e..0000000000 --- a/test/cases/misc.zig +++ /dev/null @@ -1,681 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const mem = std.mem; -const cstr = std.cstr; -const builtin = @import("builtin"); -const maxInt = std.math.maxInt; - -// normal comment - -/// this is a documentation comment -/// doc comment line 2 -fn emptyFunctionWithComments() void {} - -test "empty function with comments" { - emptyFunctionWithComments(); -} - -comptime { - @export("disabledExternFn", disabledExternFn, builtin.GlobalLinkage.Internal); -} - -extern fn disabledExternFn() void {} - -test "call disabled extern fn" { - disabledExternFn(); -} - -test "@IntType builtin" { - assert(@IntType(true, 8) == i8); - assert(@IntType(true, 16) == i16); - assert(@IntType(true, 32) == i32); - assert(@IntType(true, 64) == i64); - - assert(@IntType(false, 8) == u8); - assert(@IntType(false, 16) == u16); - assert(@IntType(false, 32) == u32); - assert(@IntType(false, 64) == u64); - - assert(i8.bit_count == 8); - assert(i16.bit_count == 16); - assert(i32.bit_count == 32); - assert(i64.bit_count == 64); - - assert(i8.is_signed); - assert(i16.is_signed); - assert(i32.is_signed); - assert(i64.is_signed); - assert(isize.is_signed); - - assert(!u8.is_signed); - assert(!u16.is_signed); - assert(!u32.is_signed); - assert(!u64.is_signed); - assert(!usize.is_signed); -} - -test "floating point primitive bit counts" { - assert(f16.bit_count == 16); - assert(f32.bit_count == 32); - assert(f64.bit_count == 64); -} - -test "short circuit" { - testShortCircuit(false, true); - comptime testShortCircuit(false, true); -} - -fn testShortCircuit(f: bool, t: bool) void { - var hit_1 = f; - var hit_2 = f; - var hit_3 = f; - var hit_4 = f; - - if (t or x: { - assert(f); - break :x f; - }) { - hit_1 = t; - } - if (f or x: { - hit_2 = t; - break :x f; - }) { - assert(f); - } - - if (t and x: { - hit_3 = t; - break :x f; - }) { - assert(f); - } - if (f and x: { - assert(f); - break :x f; - }) { - assert(f); - } else { - hit_4 = t; - } - assert(hit_1); - assert(hit_2); - assert(hit_3); - assert(hit_4); -} - -test "truncate" { - assert(testTruncate(0x10fd) == 0xfd); -} -fn testTruncate(x: u32) u8 { - return @truncate(u8, x); -} - -fn first4KeysOfHomeRow() []const u8 { - return "aoeu"; -} - -test "return string from function" { - assert(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); -} - -const g1: i32 = 1233 + 1; -var g2: i32 = 0; - -test "global variables" { - assert(g2 == 0); - g2 = g1; - assert(g2 == 1234); -} - -test "memcpy and memset intrinsics" { - var foo: [20]u8 = undefined; - var bar: [20]u8 = undefined; - - @memset(foo[0..].ptr, 'A', foo.len); - @memcpy(bar[0..].ptr, foo[0..].ptr, bar.len); - - if (bar[11] != 'A') unreachable; -} - -test "builtin static eval" { - const x: i32 = comptime x: { - break :x 1 + 2 + 3; - }; - assert(x == comptime 6); -} - -test "slicing" { - var array: [20]i32 = undefined; - - array[5] = 1234; - - var slice = array[5..10]; - - if (slice.len != 5) unreachable; - - const ptr = &slice[0]; - if (ptr.* != 1234) unreachable; - - var slice_rest = array[10..]; - if (slice_rest.len != 10) unreachable; -} - -test "constant equal function pointers" { - const alias = emptyFn; - assert(comptime x: { - break :x emptyFn == alias; - }); -} - -fn emptyFn() void {} - -test "hex escape" { - assert(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); -} - -test "string concatenation" { - assert(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); -} - -test "array mult operator" { - assert(mem.eql(u8, "ab" ** 5, "ababababab")); -} - -test "string escapes" { - assert(mem.eql(u8, "\"", "\x22")); - assert(mem.eql(u8, "\'", "\x27")); - assert(mem.eql(u8, "\n", "\x0a")); - assert(mem.eql(u8, "\r", "\x0d")); - assert(mem.eql(u8, "\t", "\x09")); - assert(mem.eql(u8, "\\", "\x5c")); - assert(mem.eql(u8, "\u1234\u0069", "\xe1\x88\xb4\x69")); -} - -test "multiline string" { - const s1 = - \\one - \\two) - \\three - ; - const s2 = "one\ntwo)\nthree"; - assert(mem.eql(u8, s1, s2)); -} - -test "multiline C string" { - const s1 = - c\\one - c\\two) - c\\three - ; - const s2 = c"one\ntwo)\nthree"; - assert(cstr.cmp(s1, s2) == 0); -} - -test "type equality" { - assert(*const u8 != *u8); -} - -const global_a: i32 = 1234; -const global_b: *const i32 = &global_a; -const global_c: *const f32 = @ptrCast(*const f32, global_b); -test "compile time global reinterpret" { - const d = @ptrCast(*const i32, global_c); - assert(d.* == 1234); -} - -test "explicit cast maybe pointers" { - const a: ?*i32 = undefined; - const b: ?*f32 = @ptrCast(?*f32, a); -} - -test "generic malloc free" { - const a = memAlloc(u8, 10) catch unreachable; - memFree(u8, a); -} -var some_mem: [100]u8 = undefined; -fn memAlloc(comptime T: type, n: usize) anyerror![]T { - return @ptrCast([*]T, &some_mem[0])[0..n]; -} -fn memFree(comptime T: type, memory: []T) void {} - -test "cast undefined" { - const array: [100]u8 = undefined; - const slice = ([]const u8)(array); - testCastUndefined(slice); -} -fn testCastUndefined(x: []const u8) void {} - -test "cast small unsigned to larger signed" { - assert(castSmallUnsignedToLargerSigned1(200) == i16(200)); - assert(castSmallUnsignedToLargerSigned2(9999) == i64(9999)); -} -fn castSmallUnsignedToLargerSigned1(x: u8) i16 { - return x; -} -fn castSmallUnsignedToLargerSigned2(x: u16) i64 { - return x; -} - -test "implicit cast after unreachable" { - assert(outer() == 1234); -} -fn inner() i32 { - return 1234; -} -fn outer() i64 { - return inner(); -} - -test "pointer dereferencing" { - var x = i32(3); - const y = &x; - - y.* += 1; - - assert(x == 4); - assert(y.* == 4); -} - -test "call result of if else expression" { - assert(mem.eql(u8, f2(true), "a")); - assert(mem.eql(u8, f2(false), "b")); -} -fn f2(x: bool) []const u8 { - return (if (x) fA else fB)(); -} -fn fA() []const u8 { - return "a"; -} -fn fB() []const u8 { - return "b"; -} - -test "const expression eval handling of variables" { - var x = true; - while (x) { - x = false; - } -} - -test "constant enum initialization with differing sizes" { - test3_1(test3_foo); - test3_2(test3_bar); -} -const Test3Foo = union(enum) { - One: void, - Two: f32, - Three: Test3Point, -}; -const Test3Point = struct { - x: i32, - y: i32, -}; -const test3_foo = Test3Foo{ - .Three = Test3Point{ - .x = 3, - .y = 4, - }, -}; -const test3_bar = Test3Foo{ .Two = 13 }; -fn test3_1(f: Test3Foo) void { - switch (f) { - Test3Foo.Three => |pt| { - assert(pt.x == 3); - assert(pt.y == 4); - }, - else => unreachable, - } -} -fn test3_2(f: Test3Foo) void { - switch (f) { - Test3Foo.Two => |x| { - assert(x == 13); - }, - else => unreachable, - } -} - -test "character literals" { - assert('\'' == single_quote); -} -const single_quote = '\''; - -test "take address of parameter" { - testTakeAddressOfParameter(12.34); -} -fn testTakeAddressOfParameter(f: f32) void { - const f_ptr = &f; - assert(f_ptr.* == 12.34); -} - -test "pointer comparison" { - const a = ([]const u8)("a"); - const b = &a; - assert(ptrEql(b, b)); -} -fn ptrEql(a: *const []const u8, b: *const []const u8) bool { - return a == b; -} - -test "C string concatenation" { - const a = c"OK" ++ c" IT " ++ c"WORKED"; - const b = c"OK IT WORKED"; - - const len = cstr.len(b); - const len_with_null = len + 1; - { - var i: u32 = 0; - while (i < len_with_null) : (i += 1) { - assert(a[i] == b[i]); - } - } - assert(a[len] == 0); - assert(b[len] == 0); -} - -test "cast slice to u8 slice" { - assert(@sizeOf(i32) == 4); - var big_thing_array = []i32{ - 1, - 2, - 3, - 4, - }; - const big_thing_slice: []i32 = big_thing_array[0..]; - const bytes = @sliceToBytes(big_thing_slice); - assert(bytes.len == 4 * 4); - bytes[4] = 0; - bytes[5] = 0; - bytes[6] = 0; - bytes[7] = 0; - assert(big_thing_slice[1] == 0); - const big_thing_again = @bytesToSlice(i32, bytes); - assert(big_thing_again[2] == 3); - big_thing_again[2] = -1; - assert(bytes[8] == maxInt(u8)); - assert(bytes[9] == maxInt(u8)); - assert(bytes[10] == maxInt(u8)); - assert(bytes[11] == maxInt(u8)); -} - -test "pointer to void return type" { - testPointerToVoidReturnType() catch unreachable; -} -fn testPointerToVoidReturnType() anyerror!void { - const a = testPointerToVoidReturnType2(); - return a.*; -} -const test_pointer_to_void_return_type_x = void{}; -fn testPointerToVoidReturnType2() *const void { - return &test_pointer_to_void_return_type_x; -} - -test "non const ptr to aliased type" { - const int = i32; - assert(?*int == ?*i32); -} - -test "array 2D const double ptr" { - const rect_2d_vertexes = [][1]f32{ - []f32{1.0}, - []f32{2.0}, - }; - testArray2DConstDoublePtr(&rect_2d_vertexes[0][0]); -} - -fn testArray2DConstDoublePtr(ptr: *const f32) void { - const ptr2 = @ptrCast([*]const f32, ptr); - assert(ptr2[0] == 1.0); - assert(ptr2[1] == 2.0); -} - -const Tid = builtin.TypeId; -const AStruct = struct { - x: i32, -}; -const AnEnum = enum { - One, - Two, -}; -const AUnionEnum = union(enum) { - One: i32, - Two: void, -}; -const AUnion = union { - One: void, - Two: void, -}; - -test "@typeId" { - comptime { - assert(@typeId(type) == Tid.Type); - assert(@typeId(void) == Tid.Void); - assert(@typeId(bool) == Tid.Bool); - assert(@typeId(noreturn) == Tid.NoReturn); - assert(@typeId(i8) == Tid.Int); - assert(@typeId(u8) == Tid.Int); - assert(@typeId(i64) == Tid.Int); - assert(@typeId(u64) == Tid.Int); - assert(@typeId(f32) == Tid.Float); - assert(@typeId(f64) == Tid.Float); - assert(@typeId(*f32) == Tid.Pointer); - assert(@typeId([2]u8) == Tid.Array); - assert(@typeId(AStruct) == Tid.Struct); - assert(@typeId(@typeOf(1)) == Tid.ComptimeInt); - assert(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); - assert(@typeId(@typeOf(undefined)) == Tid.Undefined); - assert(@typeId(@typeOf(null)) == Tid.Null); - assert(@typeId(?i32) == Tid.Optional); - assert(@typeId(anyerror!i32) == Tid.ErrorUnion); - assert(@typeId(anyerror) == Tid.ErrorSet); - assert(@typeId(AnEnum) == Tid.Enum); - assert(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); - assert(@typeId(AUnionEnum) == Tid.Union); - assert(@typeId(AUnion) == Tid.Union); - assert(@typeId(fn () void) == Tid.Fn); - assert(@typeId(@typeOf(builtin)) == Tid.Namespace); - // TODO bound fn - // TODO arg tuple - // TODO opaque - } -} - -test "@typeName" { - const Struct = struct {}; - const Union = union { - unused: u8, - }; - const Enum = enum { - Unused, - }; - comptime { - assert(mem.eql(u8, @typeName(i64), "i64")); - assert(mem.eql(u8, @typeName(*usize), "*usize")); - // https://github.com/ziglang/zig/issues/675 - assert(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)")); - assert(mem.eql(u8, @typeName(Struct), "Struct")); - assert(mem.eql(u8, @typeName(Union), "Union")); - assert(mem.eql(u8, @typeName(Enum), "Enum")); - } -} - -fn TypeFromFn(comptime T: type) type { - return struct {}; -} - -test "volatile load and store" { - var number: i32 = 1234; - const ptr = (*volatile i32)(&number); - ptr.* += 1; - assert(ptr.* == 1235); -} - -test "slice string literal has type []const u8" { - comptime { - assert(@typeOf("aoeu"[0..]) == []const u8); - const array = []i32{ - 1, - 2, - 3, - 4, - }; - assert(@typeOf(array[0..]) == []const i32); - } -} - -test "global variable initialized to global variable array element" { - assert(global_ptr == &gdt[0]); -} -const GDTEntry = struct { - field: i32, -}; -var gdt = []GDTEntry{ - GDTEntry{ .field = 1 }, - GDTEntry{ .field = 2 }, -}; -var global_ptr = &gdt[0]; - -// can't really run this test but we can make sure it has no compile error -// and generates code -const vram = @intToPtr([*]volatile u8, 0x20000000)[0..0x8000]; -export fn writeToVRam() void { - vram[0] = 'X'; -} - -test "pointer child field" { - assert((*u32).Child == u32); -} - -const OpaqueA = @OpaqueType(); -const OpaqueB = @OpaqueType(); -test "@OpaqueType" { - assert(*OpaqueA != *OpaqueB); - assert(mem.eql(u8, @typeName(OpaqueA), "OpaqueA")); - assert(mem.eql(u8, @typeName(OpaqueB), "OpaqueB")); -} - -test "variable is allowed to be a pointer to an opaque type" { - var x: i32 = 1234; - _ = hereIsAnOpaqueType(@ptrCast(*OpaqueA, &x)); -} -fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA { - var a = ptr; - return a; -} - -test "comptime if inside runtime while which unconditionally breaks" { - testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true); - comptime testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true); -} -fn testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(cond: bool) void { - while (cond) { - if (false) {} - break; - } -} - -test "implicit comptime while" { - while (false) { - @compileError("bad"); - } -} - -test "struct inside function" { - testStructInFn(); - comptime testStructInFn(); -} - -fn testStructInFn() void { - const BlockKind = u32; - - const Block = struct { - kind: BlockKind, - }; - - var block = Block{ .kind = 1234 }; - - block.kind += 1; - - assert(block.kind == 1235); -} - -fn fnThatClosesOverLocalConst() type { - const c = 1; - return struct { - fn g() i32 { - return c; - } - }; -} - -test "function closes over local const" { - const x = fnThatClosesOverLocalConst().g(); - assert(x == 1); -} - -test "cold function" { - thisIsAColdFn(); - comptime thisIsAColdFn(); -} - -fn thisIsAColdFn() void { - @setCold(true); -} - -const PackedStruct = packed struct { - a: u8, - b: u8, -}; -const PackedUnion = packed union { - a: u8, - b: u32, -}; -const PackedEnum = packed enum { - A, - B, -}; - -test "packed struct, enum, union parameters in extern function" { - testPackedStuff(&(PackedStruct{ - .a = 1, - .b = 2, - }), &(PackedUnion{ .a = 1 }), PackedEnum.A); -} - -export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion, c: PackedEnum) void {} - -test "slicing zero length array" { - const s1 = ""[0..]; - const s2 = ([]u32{})[0..]; - assert(s1.len == 0); - assert(s2.len == 0); - assert(mem.eql(u8, s1, "")); - assert(mem.eql(u32, s2, []u32{})); -} - -const addr1 = @ptrCast(*const u8, emptyFn); -test "comptime cast fn to ptr" { - const addr2 = @ptrCast(*const u8, emptyFn); - comptime assert(addr1 == addr2); -} - -test "equality compare fn ptrs" { - var a = emptyFn; - assert(a == a); -} - -test "self reference through fn ptr field" { - const S = struct { - const A = struct { - f: fn (A) u8, - }; - - fn foo(a: A) u8 { - return 12; - } - }; - var a: S.A = undefined; - a.f = S.foo; - assert(a.f(a) == 12); -} diff --git a/test/cases/namespace_depends_on_compile_var/a.zig b/test/cases/namespace_depends_on_compile_var/a.zig deleted file mode 100644 index 5ce0e94f8b..0000000000 --- a/test/cases/namespace_depends_on_compile_var/a.zig +++ /dev/null @@ -1 +0,0 @@ -pub const a_bool = true; diff --git a/test/cases/namespace_depends_on_compile_var/b.zig b/test/cases/namespace_depends_on_compile_var/b.zig deleted file mode 100644 index a12a54b589..0000000000 --- a/test/cases/namespace_depends_on_compile_var/b.zig +++ /dev/null @@ -1 +0,0 @@ -pub const a_bool = false; diff --git a/test/cases/namespace_depends_on_compile_var/index.zig b/test/cases/namespace_depends_on_compile_var/index.zig deleted file mode 100644 index ccc49d9367..0000000000 --- a/test/cases/namespace_depends_on_compile_var/index.zig +++ /dev/null @@ -1,14 +0,0 @@ -const builtin = @import("builtin"); -const assert = @import("std").debug.assert; - -test "namespace depends on compile var" { - if (some_namespace.a_bool) { - assert(some_namespace.a_bool); - } else { - assert(!some_namespace.a_bool); - } -} -const some_namespace = switch (builtin.os) { - builtin.Os.linux => @import("a.zig"), - else => @import("b.zig"), -}; diff --git a/test/cases/new_stack_call.zig b/test/cases/new_stack_call.zig deleted file mode 100644 index 5912550d54..0000000000 --- a/test/cases/new_stack_call.zig +++ /dev/null @@ -1,26 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; - -var new_stack_bytes: [1024]u8 = undefined; - -test "calling a function with a new stack" { - const arg = 1234; - - const a = @newStackCall(new_stack_bytes[0..512], targetFunction, arg); - const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg); - _ = targetFunction(arg); - - assert(arg == 1234); - assert(a < b); -} - -fn targetFunction(x: i32) usize { - assert(x == 1234); - - var local_variable: i32 = 42; - const ptr = &local_variable; - ptr.* += 1; - - assert(local_variable == 43); - return @ptrToInt(ptr); -} diff --git a/test/cases/null.zig b/test/cases/null.zig deleted file mode 100644 index 825db88b1e..0000000000 --- a/test/cases/null.zig +++ /dev/null @@ -1,162 +0,0 @@ -const assert = @import("std").debug.assert; - -test "optional type" { - const x: ?bool = true; - - if (x) |y| { - if (y) { - // OK - } else { - unreachable; - } - } else { - unreachable; - } - - const next_x: ?i32 = null; - - const z = next_x orelse 1234; - - assert(z == 1234); - - const final_x: ?i32 = 13; - - const num = final_x orelse unreachable; - - assert(num == 13); -} - -test "test maybe object and get a pointer to the inner value" { - var maybe_bool: ?bool = true; - - if (maybe_bool) |*b| { - b.* = false; - } - - assert(maybe_bool.? == false); -} - -test "rhs maybe unwrap return" { - const x: ?bool = true; - const y = x orelse return; -} - -test "maybe return" { - maybeReturnImpl(); - comptime maybeReturnImpl(); -} - -fn maybeReturnImpl() void { - assert(foo(1235).?); - if (foo(null) != null) unreachable; - assert(!foo(1234).?); -} - -fn foo(x: ?i32) ?bool { - const value = x orelse return null; - return value > 1234; -} - -test "if var maybe pointer" { - assert(shouldBeAPlus1(Particle{ - .a = 14, - .b = 1, - .c = 1, - .d = 1, - }) == 15); -} -fn shouldBeAPlus1(p: Particle) u64 { - var maybe_particle: ?Particle = p; - if (maybe_particle) |*particle| { - particle.a += 1; - } - if (maybe_particle) |particle| { - return particle.a; - } - return 0; -} -const Particle = struct { - a: u64, - b: u64, - c: u64, - d: u64, -}; - -test "null literal outside function" { - const is_null = here_is_a_null_literal.context == null; - assert(is_null); - - const is_non_null = here_is_a_null_literal.context != null; - assert(!is_non_null); -} -const SillyStruct = struct { - context: ?i32, -}; -const here_is_a_null_literal = SillyStruct{ .context = null }; - -test "test null runtime" { - testTestNullRuntime(null); -} -fn testTestNullRuntime(x: ?i32) void { - assert(x == null); - assert(!(x != null)); -} - -test "optional void" { - optionalVoidImpl(); - comptime optionalVoidImpl(); -} - -fn optionalVoidImpl() void { - assert(bar(null) == null); - assert(bar({}) != null); -} - -fn bar(x: ?void) ?void { - if (x) |_| { - return {}; - } else { - return null; - } -} - -const StructWithOptional = struct { - field: ?i32, -}; - -var struct_with_optional: StructWithOptional = undefined; - -test "unwrap optional which is field of global var" { - struct_with_optional.field = null; - if (struct_with_optional.field) |payload| { - unreachable; - } - struct_with_optional.field = 1234; - if (struct_with_optional.field) |payload| { - assert(payload == 1234); - } else { - unreachable; - } -} - -test "null with default unwrap" { - const x: i32 = null orelse 1; - assert(x == 1); -} - -test "optional types" { - comptime { - const opt_type_struct = StructWithOptionalType{ .t = u8 }; - assert(opt_type_struct.t != null and opt_type_struct.t.? == u8); - } -} - -const StructWithOptionalType = struct { - t: ?type, -}; - -test "optional pointer to 0 bit type null value at runtime" { - const EmptyStruct = struct {}; - var x: ?*EmptyStruct = null; - assert(x == null); -} diff --git a/test/cases/optional.zig b/test/cases/optional.zig deleted file mode 100644 index d43682bbec..0000000000 --- a/test/cases/optional.zig +++ /dev/null @@ -1,30 +0,0 @@ -const assert = @import("std").debug.assert; - -pub const EmptyStruct = struct {}; - -test "optional pointer to size zero struct" { - var e = EmptyStruct{}; - var o: ?*EmptyStruct = &e; - assert(o != null); -} - -test "equality compare nullable pointers" { - testNullPtrsEql(); - comptime testNullPtrsEql(); -} - -fn testNullPtrsEql() void { - var number: i32 = 1234; - - var x: ?*i32 = null; - var y: ?*i32 = null; - assert(x == y); - y = &number; - assert(x != y); - assert(x != &number); - assert(&number != x); - x = &number; - assert(x == y); - assert(x == &number); - assert(&number == x); -} diff --git a/test/cases/pointers.zig b/test/cases/pointers.zig deleted file mode 100644 index 47afb60a2e..0000000000 --- a/test/cases/pointers.zig +++ /dev/null @@ -1,44 +0,0 @@ -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); -} - -test "pointer arithmetic" { - var ptr = c"abcd"; - - assert(ptr[0] == 'a'); - ptr += 1; - assert(ptr[0] == 'b'); - ptr += 1; - assert(ptr[0] == 'c'); - ptr += 1; - assert(ptr[0] == 'd'); - ptr += 1; - assert(ptr[0] == 0); - ptr -= 1; - assert(ptr[0] == 'd'); - ptr -= 1; - assert(ptr[0] == 'c'); - ptr -= 1; - assert(ptr[0] == 'b'); - ptr -= 1; - assert(ptr[0] == 'a'); -} - -test "double pointer parsing" { - comptime assert(PtrOf(PtrOf(i32)) == **i32); -} - -fn PtrOf(comptime T: type) type { - return *T; -} diff --git a/test/cases/popcount.zig b/test/cases/popcount.zig deleted file mode 100644 index 7dc7f28c0e..0000000000 --- a/test/cases/popcount.zig +++ /dev/null @@ -1,24 +0,0 @@ -const assert = @import("std").debug.assert; - -test "@popCount" { - comptime testPopCount(); - testPopCount(); -} - -fn testPopCount() void { - { - var x: u32 = 0xaa; - assert(@popCount(x) == 4); - } - { - var x: u32 = 0xaaaaaaaa; - assert(@popCount(x) == 16); - } - { - var x: i16 = -1; - assert(@popCount(x) == 16); - } - comptime { - assert(@popCount(0b11111111000110001100010000100001000011000011100101010001) == 24); - } -} diff --git a/test/cases/ptrcast.zig b/test/cases/ptrcast.zig deleted file mode 100644 index 54c3dda849..0000000000 --- a/test/cases/ptrcast.zig +++ /dev/null @@ -1,52 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const assertOrPanic = std.debug.assertOrPanic; - -test "reinterpret bytes as integer with nonzero offset" { - testReinterpretBytesAsInteger(); - comptime testReinterpretBytesAsInteger(); -} - -fn testReinterpretBytesAsInteger() void { - const bytes = "\x12\x34\x56\x78\xab"; - const expected = switch (builtin.endian) { - builtin.Endian.Little => 0xab785634, - builtin.Endian.Big => 0x345678ab, - }; - assertOrPanic(@ptrCast(*align(1) const u32, bytes[1..5].ptr).* == expected); -} - -test "reinterpret bytes of an array into an extern struct" { - testReinterpretBytesAsExternStruct(); - comptime testReinterpretBytesAsExternStruct(); -} - -fn testReinterpretBytesAsExternStruct() void { - var bytes align(2) = []u8{ 1, 2, 3, 4, 5, 6 }; - - const S = extern struct { - a: u8, - b: u16, - c: u8, - }; - - var ptr = @ptrCast(*const S, &bytes); - var val = ptr.c; - assertOrPanic(val == 5); -} - -test "reinterpret struct field at comptime" { - const numLittle = comptime Bytes.init(0x12345678); - assertOrPanic(std.mem.eql(u8, []u8{ 0x78, 0x56, 0x34, 0x12 }, numLittle.bytes)); -} - -const Bytes = struct { - bytes: [4]u8, - - pub fn init(v: u32) Bytes { - var res: Bytes = undefined; - @ptrCast(*align(1) u32, &res.bytes).* = v; - - return res; - } -}; diff --git a/test/cases/pub_enum/index.zig b/test/cases/pub_enum/index.zig deleted file mode 100644 index 7fdd07b8a3..0000000000 --- a/test/cases/pub_enum/index.zig +++ /dev/null @@ -1,13 +0,0 @@ -const other = @import("other.zig"); -const assert = @import("std").debug.assert; - -test "pub enum" { - pubEnumTest(other.APubEnum.Two); -} -fn pubEnumTest(foo: other.APubEnum) void { - assert(foo == other.APubEnum.Two); -} - -test "cast with imported symbol" { - assert(other.size_t(42) == 42); -} diff --git a/test/cases/pub_enum/other.zig b/test/cases/pub_enum/other.zig deleted file mode 100644 index c663950383..0000000000 --- a/test/cases/pub_enum/other.zig +++ /dev/null @@ -1,6 +0,0 @@ -pub const APubEnum = enum { - One, - Two, - Three, -}; -pub const size_t = u64; diff --git a/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig b/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig deleted file mode 100644 index 3c94bb0d49..0000000000 --- a/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig +++ /dev/null @@ -1,37 +0,0 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; - -var ok: bool = false; -test "reference a variable in an if after an if in the 2nd switch prong" { - foo(true, Num.Two, false, "aoeu"); - assert(!ok); - foo(false, Num.One, false, "aoeu"); - assert(!ok); - foo(true, Num.One, false, "aoeu"); - assert(ok); -} - -const Num = enum { - One, - Two, -}; - -fn foo(c: bool, k: Num, c2: bool, b: []const u8) void { - switch (k) { - Num.Two => {}, - Num.One => { - if (c) { - const output_path = b; - - if (c2) {} - - a(output_path); - } - }, - } -} - -fn a(x: []const u8) void { - assert(mem.eql(u8, x, "aoeu")); - ok = true; -} diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig deleted file mode 100644 index b9b8aff4e1..0000000000 --- a/test/cases/reflection.zig +++ /dev/null @@ -1,95 +0,0 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; -const reflection = @This(); - -test "reflection: array, pointer, optional, error union type child" { - comptime { - assert(([10]u8).Child == u8); - assert((*u8).Child == u8); - assert((anyerror!u8).Payload == u8); - assert((?u8).Child == u8); - } -} - -test "reflection: function return type, var args, and param types" { - comptime { - assert(@typeOf(dummy).ReturnType == i32); - assert(!@typeOf(dummy).is_var_args); - assert(@typeOf(dummy_varargs).is_var_args); - assert(@typeOf(dummy).arg_count == 3); - assert(@ArgType(@typeOf(dummy), 0) == bool); - assert(@ArgType(@typeOf(dummy), 1) == i32); - assert(@ArgType(@typeOf(dummy), 2) == f32); - } -} - -fn dummy(a: bool, b: i32, c: f32) i32 { - return 1234; -} -fn dummy_varargs(args: ...) void {} - -test "reflection: struct member types and names" { - comptime { - assert(@memberCount(Foo) == 3); - - assert(@memberType(Foo, 0) == i32); - assert(@memberType(Foo, 1) == bool); - assert(@memberType(Foo, 2) == void); - - assert(mem.eql(u8, @memberName(Foo, 0), "one")); - assert(mem.eql(u8, @memberName(Foo, 1), "two")); - assert(mem.eql(u8, @memberName(Foo, 2), "three")); - } -} - -test "reflection: enum member types and names" { - comptime { - assert(@memberCount(Bar) == 4); - - assert(@memberType(Bar, 0) == void); - assert(@memberType(Bar, 1) == i32); - assert(@memberType(Bar, 2) == bool); - assert(@memberType(Bar, 3) == f64); - - assert(mem.eql(u8, @memberName(Bar, 0), "One")); - assert(mem.eql(u8, @memberName(Bar, 1), "Two")); - assert(mem.eql(u8, @memberName(Bar, 2), "Three")); - assert(mem.eql(u8, @memberName(Bar, 3), "Four")); - } -} - -test "reflection: @field" { - var f = Foo{ - .one = 42, - .two = true, - .three = void{}, - }; - - assert(f.one == f.one); - assert(@field(f, "o" ++ "ne") == f.one); - assert(@field(f, "t" ++ "wo") == f.two); - assert(@field(f, "th" ++ "ree") == f.three); - assert(@field(Foo, "const" ++ "ant") == Foo.constant); - assert(@field(Bar, "O" ++ "ne") == Bar.One); - assert(@field(Bar, "T" ++ "wo") == Bar.Two); - assert(@field(Bar, "Th" ++ "ree") == Bar.Three); - assert(@field(Bar, "F" ++ "our") == Bar.Four); - assert(@field(reflection, "dum" ++ "my")(true, 1, 2) == dummy(true, 1, 2)); - @field(f, "o" ++ "ne") = 4; - assert(f.one == 4); -} - -const Foo = struct { - const constant = 52; - - one: i32, - two: bool, - three: void, -}; - -const Bar = union(enum) { - One: void, - Two: i32, - Three: bool, - Four: f64, -}; diff --git a/test/cases/sizeof_and_typeof.zig b/test/cases/sizeof_and_typeof.zig deleted file mode 100644 index 11c6b2f6ba..0000000000 --- a/test/cases/sizeof_and_typeof.zig +++ /dev/null @@ -1,69 +0,0 @@ -const builtin = @import("builtin"); -const assert = @import("std").debug.assert; - -test "@sizeOf and @typeOf" { - const y: @typeOf(x) = 120; - assert(@sizeOf(@typeOf(y)) == 2); -} -const x: u16 = 13; -const z: @typeOf(x) = 19; - -const A = struct { - a: u8, - b: u32, - c: u8, - d: u3, - e: u5, - f: u16, - g: u16, -}; - -const P = packed struct { - a: u8, - b: u32, - c: u8, - d: u3, - e: u5, - f: u16, - g: u16, -}; - -test "@byteOffsetOf" { - // Packed structs have fixed memory layout - assert(@byteOffsetOf(P, "a") == 0); - assert(@byteOffsetOf(P, "b") == 1); - assert(@byteOffsetOf(P, "c") == 5); - assert(@byteOffsetOf(P, "d") == 6); - assert(@byteOffsetOf(P, "e") == 6); - assert(@byteOffsetOf(P, "f") == 7); - assert(@byteOffsetOf(P, "g") == 9); - - // Normal struct fields can be moved/padded - var a: A = undefined; - assert(@ptrToInt(&a.a) - @ptrToInt(&a) == @byteOffsetOf(A, "a")); - assert(@ptrToInt(&a.b) - @ptrToInt(&a) == @byteOffsetOf(A, "b")); - assert(@ptrToInt(&a.c) - @ptrToInt(&a) == @byteOffsetOf(A, "c")); - assert(@ptrToInt(&a.d) - @ptrToInt(&a) == @byteOffsetOf(A, "d")); - assert(@ptrToInt(&a.e) - @ptrToInt(&a) == @byteOffsetOf(A, "e")); - assert(@ptrToInt(&a.f) - @ptrToInt(&a) == @byteOffsetOf(A, "f")); - assert(@ptrToInt(&a.g) - @ptrToInt(&a) == @byteOffsetOf(A, "g")); -} - -test "@bitOffsetOf" { - // Packed structs have fixed memory layout - assert(@bitOffsetOf(P, "a") == 0); - assert(@bitOffsetOf(P, "b") == 8); - assert(@bitOffsetOf(P, "c") == 40); - assert(@bitOffsetOf(P, "d") == 48); - assert(@bitOffsetOf(P, "e") == 51); - assert(@bitOffsetOf(P, "f") == 56); - assert(@bitOffsetOf(P, "g") == 72); - - assert(@byteOffsetOf(A, "a") * 8 == @bitOffsetOf(A, "a")); - assert(@byteOffsetOf(A, "b") * 8 == @bitOffsetOf(A, "b")); - assert(@byteOffsetOf(A, "c") * 8 == @bitOffsetOf(A, "c")); - assert(@byteOffsetOf(A, "d") * 8 == @bitOffsetOf(A, "d")); - assert(@byteOffsetOf(A, "e") * 8 == @bitOffsetOf(A, "e")); - assert(@byteOffsetOf(A, "f") * 8 == @bitOffsetOf(A, "f")); - assert(@byteOffsetOf(A, "g") * 8 == @bitOffsetOf(A, "g")); -} diff --git a/test/cases/slice.zig b/test/cases/slice.zig deleted file mode 100644 index b4b43bdd19..0000000000 --- a/test/cases/slice.zig +++ /dev/null @@ -1,40 +0,0 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; - -const x = @intToPtr([*]i32, 0x1000)[0..0x500]; -const y = x[0x100..]; -test "compile time slice of pointer to hard coded address" { - assert(@ptrToInt(x.ptr) == 0x1000); - assert(x.len == 0x500); - - assert(@ptrToInt(y.ptr) == 0x1100); - assert(y.len == 0x400); -} - -test "slice child property" { - var array: [5]i32 = undefined; - var slice = array[0..]; - assert(@typeOf(slice).Child == i32); -} - -test "runtime safety lets us slice from len..len" { - var an_array = []u8{ - 1, - 2, - 3, - }; - assert(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), "")); -} - -fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { - return a_slice[start..end]; -} - -test "implicitly cast array of size 0 to slice" { - var msg = []u8{}; - assertLenIsZero(msg); -} - -fn assertLenIsZero(msg: []const u8) void { - assert(msg.len == 0); -} diff --git a/test/cases/struct.zig b/test/cases/struct.zig deleted file mode 100644 index bbbd21912c..0000000000 --- a/test/cases/struct.zig +++ /dev/null @@ -1,470 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const builtin = @import("builtin"); -const maxInt = std.math.maxInt; - -const StructWithNoFields = struct { - fn add(a: i32, b: i32) i32 { - return a + b; - } -}; -const empty_global_instance = StructWithNoFields{}; - -test "call struct static method" { - const result = StructWithNoFields.add(3, 4); - assert(result == 7); -} - -test "return empty struct instance" { - _ = returnEmptyStructInstance(); -} -fn returnEmptyStructInstance() StructWithNoFields { - return empty_global_instance; -} - -const should_be_11 = StructWithNoFields.add(5, 6); - -test "invake static method in global scope" { - assert(should_be_11 == 11); -} - -test "void struct fields" { - const foo = VoidStructFieldsFoo{ - .a = void{}, - .b = 1, - .c = void{}, - }; - assert(foo.b == 1); - assert(@sizeOf(VoidStructFieldsFoo) == 4); -} -const VoidStructFieldsFoo = struct { - a: void, - b: i32, - c: void, -}; - -test "structs" { - var foo: StructFoo = undefined; - @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo)); - foo.a += 1; - foo.b = foo.a == 1; - testFoo(foo); - testMutation(&foo); - assert(foo.c == 100); -} -const StructFoo = struct { - a: i32, - b: bool, - c: f32, -}; -fn testFoo(foo: StructFoo) void { - assert(foo.b); -} -fn testMutation(foo: *StructFoo) void { - foo.c = 100; -} - -const Node = struct { - val: Val, - next: *Node, -}; - -const Val = struct { - x: i32, -}; - -test "struct point to self" { - var root: Node = undefined; - root.val.x = 1; - - var node: Node = undefined; - node.next = &root; - node.val.x = 2; - - root.next = &node; - - assert(node.next.next.next.val.x == 1); -} - -test "struct byval assign" { - var foo1: StructFoo = undefined; - var foo2: StructFoo = undefined; - - foo1.a = 1234; - foo2.a = 0; - assert(foo2.a == 0); - foo2 = foo1; - assert(foo2.a == 1234); -} - -fn structInitializer() void { - const val = Val{ .x = 42 }; - assert(val.x == 42); -} - -test "fn call of struct field" { - assert(callStructField(Foo{ .ptr = aFunc }) == 13); -} - -const Foo = struct { - ptr: fn () i32, -}; - -fn aFunc() i32 { - return 13; -} - -fn callStructField(foo: Foo) i32 { - return foo.ptr(); -} - -test "store member function in variable" { - const instance = MemberFnTestFoo{ .x = 1234 }; - const memberFn = MemberFnTestFoo.member; - const result = memberFn(instance); - assert(result == 1234); -} -const MemberFnTestFoo = struct { - x: i32, - fn member(foo: MemberFnTestFoo) i32 { - return foo.x; - } -}; - -test "call member function directly" { - const instance = MemberFnTestFoo{ .x = 1234 }; - const result = MemberFnTestFoo.member(instance); - assert(result == 1234); -} - -test "member functions" { - const r = MemberFnRand{ .seed = 1234 }; - assert(r.getSeed() == 1234); -} -const MemberFnRand = struct { - seed: u32, - pub fn getSeed(r: *const MemberFnRand) u32 { - return r.seed; - } -}; - -test "return struct byval from function" { - const bar = makeBar(1234, 5678); - assert(bar.y == 5678); -} -const Bar = struct { - x: i32, - y: i32, -}; -fn makeBar(x: i32, y: i32) Bar { - return Bar{ - .x = x, - .y = y, - }; -} - -test "empty struct method call" { - const es = EmptyStruct{}; - assert(es.method() == 1234); -} -const EmptyStruct = struct { - fn method(es: *const EmptyStruct) i32 { - return 1234; - } -}; - -test "return empty struct from fn" { - _ = testReturnEmptyStructFromFn(); -} -const EmptyStruct2 = struct {}; -fn testReturnEmptyStructFromFn() EmptyStruct2 { - return EmptyStruct2{}; -} - -test "pass slice of empty struct to fn" { - assert(testPassSliceOfEmptyStructToFn([]EmptyStruct2{EmptyStruct2{}}) == 1); -} -fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { - return slice.len; -} - -const APackedStruct = packed struct { - x: u8, - y: u8, -}; - -test "packed struct" { - var foo = APackedStruct{ - .x = 1, - .y = 2, - }; - foo.y += 1; - const four = foo.x + foo.y; - assert(four == 4); -} - -const BitField1 = packed struct { - a: u3, - b: u3, - c: u2, -}; - -const bit_field_1 = BitField1{ - .a = 1, - .b = 2, - .c = 3, -}; - -test "bit field access" { - var data = bit_field_1; - assert(getA(&data) == 1); - assert(getB(&data) == 2); - assert(getC(&data) == 3); - comptime assert(@sizeOf(BitField1) == 1); - - data.b += 1; - assert(data.b == 3); - - data.a += 1; - assert(data.a == 2); - assert(data.b == 3); -} - -fn getA(data: *const BitField1) u3 { - return data.a; -} - -fn getB(data: *const BitField1) u3 { - return data.b; -} - -fn getC(data: *const BitField1) u2 { - return data.c; -} - -const Foo24Bits = packed struct { - field: u24, -}; -const Foo96Bits = packed struct { - a: u24, - b: u24, - c: u24, - d: u24, -}; - -test "packed struct 24bits" { - comptime { - assert(@sizeOf(Foo24Bits) == 3); - assert(@sizeOf(Foo96Bits) == 12); - } - - var value = Foo96Bits{ - .a = 0, - .b = 0, - .c = 0, - .d = 0, - }; - value.a += 1; - assert(value.a == 1); - assert(value.b == 0); - assert(value.c == 0); - assert(value.d == 0); - - value.b += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 0); - assert(value.d == 0); - - value.c += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 1); - assert(value.d == 0); - - value.d += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 1); - assert(value.d == 1); -} - -const FooArray24Bits = packed struct { - a: u16, - b: [2]Foo24Bits, - c: u16, -}; - -test "packed array 24bits" { - comptime { - assert(@sizeOf([9]Foo24Bits) == 9 * 3); - assert(@sizeOf(FooArray24Bits) == 2 + 2 * 3 + 2); - } - - var bytes = []u8{0} ** (@sizeOf(FooArray24Bits) + 1); - bytes[bytes.len - 1] = 0xaa; - const ptr = &@bytesToSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; - assert(ptr.a == 0); - assert(ptr.b[0].field == 0); - assert(ptr.b[1].field == 0); - assert(ptr.c == 0); - - ptr.a = maxInt(u16); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == 0); - assert(ptr.b[1].field == 0); - assert(ptr.c == 0); - - ptr.b[0].field = maxInt(u24); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == 0); - assert(ptr.c == 0); - - ptr.b[1].field = maxInt(u24); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == maxInt(u24)); - assert(ptr.c == 0); - - ptr.c = maxInt(u16); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == maxInt(u24)); - assert(ptr.c == maxInt(u16)); - - assert(bytes[bytes.len - 1] == 0xaa); -} - -const FooStructAligned = packed struct { - a: u8, - b: u8, -}; - -const FooArrayOfAligned = packed struct { - a: [2]FooStructAligned, -}; - -test "aligned array of packed struct" { - comptime { - assert(@sizeOf(FooStructAligned) == 2); - assert(@sizeOf(FooArrayOfAligned) == 2 * 2); - } - - var bytes = []u8{0xbb} ** @sizeOf(FooArrayOfAligned); - const ptr = &@bytesToSlice(FooArrayOfAligned, bytes[0..bytes.len])[0]; - - assert(ptr.a[0].a == 0xbb); - assert(ptr.a[0].b == 0xbb); - assert(ptr.a[1].a == 0xbb); - assert(ptr.a[1].b == 0xbb); -} - -test "runtime struct initialization of bitfield" { - const s1 = Nibbles{ - .x = x1, - .y = x1, - }; - const s2 = Nibbles{ - .x = @intCast(u4, x2), - .y = @intCast(u4, x2), - }; - - assert(s1.x == x1); - assert(s1.y == x1); - assert(s2.x == @intCast(u4, x2)); - assert(s2.y == @intCast(u4, x2)); -} - -var x1 = u4(1); -var x2 = u8(2); - -const Nibbles = packed struct { - x: u4, - y: u4, -}; - -const Bitfields = packed struct { - f1: u16, - f2: u16, - f3: u8, - f4: u8, - f5: u4, - f6: u4, - f7: u8, -}; - -test "native bit field understands endianness" { - var all: u64 = 0x7765443322221111; - var bytes: [8]u8 = undefined; - @memcpy(bytes[0..].ptr, @ptrCast([*]u8, &all), 8); - var bitfields = @ptrCast(*Bitfields, bytes[0..].ptr).*; - - assert(bitfields.f1 == 0x1111); - assert(bitfields.f2 == 0x2222); - assert(bitfields.f3 == 0x33); - assert(bitfields.f4 == 0x44); - assert(bitfields.f5 == 0x5); - assert(bitfields.f6 == 0x6); - assert(bitfields.f7 == 0x77); -} - -test "align 1 field before self referential align 8 field as slice return type" { - const result = alloc(Expr); - assert(result.len == 0); -} - -const Expr = union(enum) { - Literal: u8, - Question: *Expr, -}; - -fn alloc(comptime T: type) []T { - return []T{}; -} - -test "call method with mutable reference to struct with no fields" { - const S = struct { - fn doC(s: *const @This()) bool { - return true; - } - fn do(s: *@This()) bool { - return true; - } - }; - - var s = S{}; - assert(S.doC(&s)); - assert(s.doC()); - assert(S.do(&s)); - assert(s.do()); -} - -test "implicit cast packed struct field to const ptr" { - const LevelUpMove = packed struct { - move_id: u9, - level: u7, - - fn toInt(value: u7) u7 { - return value; - } - }; - - var lup: LevelUpMove = undefined; - lup.level = 12; - const res = LevelUpMove.toInt(lup.level); - assert(res == 12); -} - -test "pointer to packed struct member in a stack variable" { - const S = packed struct { - a: u2, - b: u2, - }; - - var s = S{ .a = 2, .b = 0 }; - var b_ptr = &s.b; - assert(s.b == 0); - b_ptr.* = 2; - assert(s.b == 2); -} diff --git a/test/cases/struct_contains_null_ptr_itself.zig b/test/cases/struct_contains_null_ptr_itself.zig deleted file mode 100644 index 21175974b3..0000000000 --- a/test/cases/struct_contains_null_ptr_itself.zig +++ /dev/null @@ -1,21 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; - -test "struct contains null pointer which contains original struct" { - var x: ?*NodeLineComment = null; - assert(x == null); -} - -pub const Node = struct { - id: Id, - comment: ?*NodeLineComment, - - pub const Id = enum { - Root, - LineComment, - }; -}; - -pub const NodeLineComment = struct { - base: Node, -}; diff --git a/test/cases/struct_contains_slice_of_itself.zig b/test/cases/struct_contains_slice_of_itself.zig deleted file mode 100644 index aa3075312c..0000000000 --- a/test/cases/struct_contains_slice_of_itself.zig +++ /dev/null @@ -1,85 +0,0 @@ -const assert = @import("std").debug.assert; - -const Node = struct { - payload: i32, - children: []Node, -}; - -const NodeAligned = struct { - payload: i32, - children: []align(@alignOf(NodeAligned)) NodeAligned, -}; - -test "struct contains slice of itself" { - var other_nodes = []Node{ - Node{ - .payload = 31, - .children = []Node{}, - }, - Node{ - .payload = 32, - .children = []Node{}, - }, - }; - var nodes = []Node{ - Node{ - .payload = 1, - .children = []Node{}, - }, - Node{ - .payload = 2, - .children = []Node{}, - }, - Node{ - .payload = 3, - .children = other_nodes[0..], - }, - }; - const root = Node{ - .payload = 1234, - .children = nodes[0..], - }; - assert(root.payload == 1234); - assert(root.children[0].payload == 1); - assert(root.children[1].payload == 2); - assert(root.children[2].payload == 3); - assert(root.children[2].children[0].payload == 31); - assert(root.children[2].children[1].payload == 32); -} - -test "struct contains aligned slice of itself" { - var other_nodes = []NodeAligned{ - NodeAligned{ - .payload = 31, - .children = []NodeAligned{}, - }, - NodeAligned{ - .payload = 32, - .children = []NodeAligned{}, - }, - }; - var nodes = []NodeAligned{ - NodeAligned{ - .payload = 1, - .children = []NodeAligned{}, - }, - NodeAligned{ - .payload = 2, - .children = []NodeAligned{}, - }, - NodeAligned{ - .payload = 3, - .children = other_nodes[0..], - }, - }; - const root = NodeAligned{ - .payload = 1234, - .children = nodes[0..], - }; - assert(root.payload == 1234); - assert(root.children[0].payload == 1); - assert(root.children[1].payload == 2); - assert(root.children[2].payload == 3); - assert(root.children[2].children[0].payload == 31); - assert(root.children[2].children[1].payload == 32); -} diff --git a/test/cases/switch.zig b/test/cases/switch.zig deleted file mode 100644 index 1162fdd4b2..0000000000 --- a/test/cases/switch.zig +++ /dev/null @@ -1,271 +0,0 @@ -const assert = @import("std").debug.assert; - -test "switch with numbers" { - testSwitchWithNumbers(13); -} - -fn testSwitchWithNumbers(x: u32) void { - const result = switch (x) { - 1, 2, 3, 4...8 => false, - 13 => true, - else => false, - }; - assert(result); -} - -test "switch with all ranges" { - assert(testSwitchWithAllRanges(50, 3) == 1); - assert(testSwitchWithAllRanges(101, 0) == 2); - assert(testSwitchWithAllRanges(300, 5) == 3); - assert(testSwitchWithAllRanges(301, 6) == 6); -} - -fn testSwitchWithAllRanges(x: u32, y: u32) u32 { - return switch (x) { - 0...100 => 1, - 101...200 => 2, - 201...300 => 3, - else => y, - }; -} - -test "implicit comptime switch" { - const x = 3 + 4; - const result = switch (x) { - 3 => 10, - 4 => 11, - 5, 6 => 12, - 7, 8 => 13, - else => 14, - }; - - comptime { - assert(result + 1 == 14); - } -} - -test "switch on enum" { - const fruit = Fruit.Orange; - nonConstSwitchOnEnum(fruit); -} -const Fruit = enum { - Apple, - Orange, - Banana, -}; -fn nonConstSwitchOnEnum(fruit: Fruit) void { - switch (fruit) { - Fruit.Apple => unreachable, - Fruit.Orange => {}, - Fruit.Banana => unreachable, - } -} - -test "switch statement" { - nonConstSwitch(SwitchStatmentFoo.C); -} -fn nonConstSwitch(foo: SwitchStatmentFoo) void { - const val = switch (foo) { - SwitchStatmentFoo.A => i32(1), - SwitchStatmentFoo.B => 2, - SwitchStatmentFoo.C => 3, - SwitchStatmentFoo.D => 4, - }; - assert(val == 3); -} -const SwitchStatmentFoo = enum { - A, - B, - C, - D, -}; - -test "switch prong with variable" { - switchProngWithVarFn(SwitchProngWithVarEnum{ .One = 13 }); - switchProngWithVarFn(SwitchProngWithVarEnum{ .Two = 13.0 }); - switchProngWithVarFn(SwitchProngWithVarEnum{ .Meh = {} }); -} -const SwitchProngWithVarEnum = union(enum) { - One: i32, - Two: f32, - Meh: void, -}; -fn switchProngWithVarFn(a: SwitchProngWithVarEnum) void { - switch (a) { - SwitchProngWithVarEnum.One => |x| { - assert(x == 13); - }, - SwitchProngWithVarEnum.Two => |x| { - assert(x == 13.0); - }, - SwitchProngWithVarEnum.Meh => |x| { - const v: void = x; - }, - } -} - -test "switch on enum using pointer capture" { - testSwitchEnumPtrCapture(); - comptime testSwitchEnumPtrCapture(); -} - -fn testSwitchEnumPtrCapture() void { - var value = SwitchProngWithVarEnum{ .One = 1234 }; - switch (value) { - SwitchProngWithVarEnum.One => |*x| x.* += 1, - else => unreachable, - } - switch (value) { - SwitchProngWithVarEnum.One => |x| assert(x == 1235), - else => unreachable, - } -} - -test "switch with multiple expressions" { - const x = switch (returnsFive()) { - 1, 2, 3 => 1, - 4, 5, 6 => 2, - else => i32(3), - }; - assert(x == 2); -} -fn returnsFive() i32 { - return 5; -} - -const Number = union(enum) { - One: u64, - Two: u8, - Three: f32, -}; - -const number = Number{ .Three = 1.23 }; - -fn returnsFalse() bool { - switch (number) { - Number.One => |x| return x > 1234, - Number.Two => |x| return x == 'a', - Number.Three => |x| return x > 12.34, - } -} -test "switch on const enum with var" { - assert(!returnsFalse()); -} - -test "switch on type" { - assert(trueIfBoolFalseOtherwise(bool)); - assert(!trueIfBoolFalseOtherwise(i32)); -} - -fn trueIfBoolFalseOtherwise(comptime T: type) bool { - return switch (T) { - bool => true, - else => false, - }; -} - -test "switch handles all cases of number" { - testSwitchHandleAllCases(); - comptime testSwitchHandleAllCases(); -} - -fn testSwitchHandleAllCases() void { - assert(testSwitchHandleAllCasesExhaustive(0) == 3); - assert(testSwitchHandleAllCasesExhaustive(1) == 2); - assert(testSwitchHandleAllCasesExhaustive(2) == 1); - assert(testSwitchHandleAllCasesExhaustive(3) == 0); - - assert(testSwitchHandleAllCasesRange(100) == 0); - assert(testSwitchHandleAllCasesRange(200) == 1); - assert(testSwitchHandleAllCasesRange(201) == 2); - assert(testSwitchHandleAllCasesRange(202) == 4); - assert(testSwitchHandleAllCasesRange(230) == 3); -} - -fn testSwitchHandleAllCasesExhaustive(x: u2) u2 { - return switch (x) { - 0 => u2(3), - 1 => 2, - 2 => 1, - 3 => 0, - }; -} - -fn testSwitchHandleAllCasesRange(x: u8) u8 { - return switch (x) { - 0...100 => u8(0), - 101...200 => 1, - 201, 203 => 2, - 202 => 4, - 204...255 => 3, - }; -} - -test "switch all prongs unreachable" { - testAllProngsUnreachable(); - comptime testAllProngsUnreachable(); -} - -fn testAllProngsUnreachable() void { - assert(switchWithUnreachable(1) == 2); - assert(switchWithUnreachable(2) == 10); -} - -fn switchWithUnreachable(x: i32) i32 { - while (true) { - switch (x) { - 1 => return 2, - 2 => break, - else => continue, - } - } - return 10; -} - -fn return_a_number() anyerror!i32 { - return 1; -} - -test "capture value of switch with all unreachable prongs" { - const x = return_a_number() catch |err| switch (err) { - else => unreachable, - }; - assert(x == 1); -} - -test "switching on booleans" { - testSwitchOnBools(); - comptime testSwitchOnBools(); -} - -fn testSwitchOnBools() void { - assert(testSwitchOnBoolsTrueAndFalse(true) == false); - assert(testSwitchOnBoolsTrueAndFalse(false) == true); - - assert(testSwitchOnBoolsTrueWithElse(true) == false); - assert(testSwitchOnBoolsTrueWithElse(false) == true); - - assert(testSwitchOnBoolsFalseWithElse(true) == false); - assert(testSwitchOnBoolsFalseWithElse(false) == true); -} - -fn testSwitchOnBoolsTrueAndFalse(x: bool) bool { - return switch (x) { - true => false, - false => true, - }; -} - -fn testSwitchOnBoolsTrueWithElse(x: bool) bool { - return switch (x) { - true => false, - else => true, - }; -} - -fn testSwitchOnBoolsFalseWithElse(x: bool) bool { - return switch (x) { - false => true, - else => false, - }; -} diff --git a/test/cases/switch_prong_err_enum.zig b/test/cases/switch_prong_err_enum.zig deleted file mode 100644 index 89060690fc..0000000000 --- a/test/cases/switch_prong_err_enum.zig +++ /dev/null @@ -1,30 +0,0 @@ -const assert = @import("std").debug.assert; - -var read_count: u64 = 0; - -fn readOnce() anyerror!u64 { - read_count += 1; - return read_count; -} - -const FormValue = union(enum) { - Address: u64, - Other: bool, -}; - -fn doThing(form_id: u64) anyerror!FormValue { - return switch (form_id) { - 17 => FormValue{ .Address = try readOnce() }, - else => error.InvalidDebugInfo, - }; -} - -test "switch prong returns error enum" { - switch (doThing(17) catch unreachable) { - FormValue.Address => |payload| { - assert(payload == 1); - }, - else => unreachable, - } - assert(read_count == 1); -} diff --git a/test/cases/switch_prong_implicit_cast.zig b/test/cases/switch_prong_implicit_cast.zig deleted file mode 100644 index 56d37e290f..0000000000 --- a/test/cases/switch_prong_implicit_cast.zig +++ /dev/null @@ -1,22 +0,0 @@ -const assert = @import("std").debug.assert; - -const FormValue = union(enum) { - One: void, - Two: bool, -}; - -fn foo(id: u64) !FormValue { - return switch (id) { - 2 => FormValue{ .Two = true }, - 1 => FormValue{ .One = {} }, - else => return error.Whatever, - }; -} - -test "switch prong implicit cast" { - const result = switch (foo(2) catch unreachable) { - FormValue.One => false, - FormValue.Two => |x| x, - }; - assert(result); -} diff --git a/test/cases/syntax.zig b/test/cases/syntax.zig deleted file mode 100644 index 0c8c3c5ed3..0000000000 --- a/test/cases/syntax.zig +++ /dev/null @@ -1,59 +0,0 @@ -// Test trailing comma syntax -// zig fmt: off - -const struct_trailing_comma = struct { x: i32, y: i32, }; -const struct_no_comma = struct { x: i32, y: i32 }; -const struct_fn_no_comma = struct { fn m() void {} y: i32 }; - -const enum_no_comma = enum { A, B }; - -fn container_init() void { - const S = struct { x: i32, y: i32 }; - _ = S { .x = 1, .y = 2 }; - _ = S { .x = 1, .y = 2, }; -} - -fn type_expr_return1() if (true) A {} -fn type_expr_return2() for (true) |_| A {} -fn type_expr_return3() while (true) A {} -fn type_expr_return4() comptime A {} - -fn switch_cases(x: i32) void { - switch (x) { - 1,2,3 => {}, - 4,5, => {}, - 6...8, => {}, - else => {}, - } -} - -fn switch_prongs(x: i32) void { - switch (x) { - 0 => {}, - else => {}, - } - switch (x) { - 0 => {}, - else => {} - } -} - -const fn_no_comma = fn(i32, i32)void; -const fn_trailing_comma = fn(i32, i32,)void; - -fn fn_calls() void { - fn add(x: i32, y: i32,) i32 { x + y }; - _ = add(1, 2); - _ = add(1, 2,); -} - -fn asm_lists() void { - if (false) { // Build AST but don't analyze - asm ("not real assembly" - :[a] "x" (x),); - asm ("not real assembly" - :[a] "x" (->i32),:[a] "x" (1),); - asm ("still not real assembly" - :::"a","b",); - } -} diff --git a/test/cases/this.zig b/test/cases/this.zig deleted file mode 100644 index c7be074f36..0000000000 --- a/test/cases/this.zig +++ /dev/null @@ -1,34 +0,0 @@ -const assert = @import("std").debug.assert; - -const module = @This(); - -fn Point(comptime T: type) type { - return struct { - const Self = @This(); - x: T, - y: T, - - fn addOne(self: *Self) void { - self.x += 1; - self.y += 1; - } - }; -} - -fn add(x: i32, y: i32) i32 { - return x + y; -} - -test "this refer to module call private fn" { - assert(module.add(1, 2) == 3); -} - -test "this refer to container" { - var pt = Point(i32){ - .x = 12, - .y = 34, - }; - pt.addOne(); - assert(pt.x == 13); - assert(pt.y == 35); -} diff --git a/test/cases/truncate.zig b/test/cases/truncate.zig deleted file mode 100644 index 02b5085ccd..0000000000 --- a/test/cases/truncate.zig +++ /dev/null @@ -1,8 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; - -test "truncate u0 to larger integer allowed and has comptime known result" { - var x: u0 = 0; - const y = @truncate(u8, x); - comptime assert(y == 0); -} diff --git a/test/cases/try.zig b/test/cases/try.zig deleted file mode 100644 index 450a9af6ac..0000000000 --- a/test/cases/try.zig +++ /dev/null @@ -1,43 +0,0 @@ -const assert = @import("std").debug.assert; - -test "try on error union" { - tryOnErrorUnionImpl(); - comptime tryOnErrorUnionImpl(); -} - -fn tryOnErrorUnionImpl() void { - const x = if (returnsTen()) |val| val + 1 else |err| switch (err) { - error.ItBroke, error.NoMem => 1, - error.CrappedOut => i32(2), - else => unreachable, - }; - assert(x == 11); -} - -fn returnsTen() anyerror!i32 { - return 10; -} - -test "try without vars" { - const result1 = if (failIfTrue(true)) 1 else |_| i32(2); - assert(result1 == 2); - - const result2 = if (failIfTrue(false)) 1 else |_| i32(2); - assert(result2 == 1); -} - -fn failIfTrue(ok: bool) anyerror!void { - if (ok) { - return error.ItBroke; - } else { - return; - } -} - -test "try then not executed with assignment" { - if (failIfTrue(true)) { - unreachable; - } else |err| { - assert(err == error.ItBroke); - } -} diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig deleted file mode 100644 index cec532d5d3..0000000000 --- a/test/cases/type_info.zig +++ /dev/null @@ -1,264 +0,0 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; -const TypeInfo = @import("builtin").TypeInfo; -const TypeId = @import("builtin").TypeId; - -test "type info: tag type, void info" { - testBasic(); - comptime testBasic(); -} - -fn testBasic() void { - assert(@TagType(TypeInfo) == TypeId); - const void_info = @typeInfo(void); - assert(TypeId(void_info) == TypeId.Void); - assert(void_info.Void == {}); -} - -test "type info: integer, floating point type info" { - testIntFloat(); - comptime testIntFloat(); -} - -fn testIntFloat() void { - const u8_info = @typeInfo(u8); - assert(TypeId(u8_info) == TypeId.Int); - assert(!u8_info.Int.is_signed); - assert(u8_info.Int.bits == 8); - - const f64_info = @typeInfo(f64); - assert(TypeId(f64_info) == TypeId.Float); - assert(f64_info.Float.bits == 64); -} - -test "type info: pointer type info" { - testPointer(); - comptime testPointer(); -} - -fn testPointer() void { - const u32_ptr_info = @typeInfo(*u32); - assert(TypeId(u32_ptr_info) == TypeId.Pointer); - assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One); - assert(u32_ptr_info.Pointer.is_const == false); - assert(u32_ptr_info.Pointer.is_volatile == false); - assert(u32_ptr_info.Pointer.alignment == @alignOf(u32)); - assert(u32_ptr_info.Pointer.child == u32); -} - -test "type info: unknown length pointer type info" { - testUnknownLenPtr(); - comptime testUnknownLenPtr(); -} - -fn testUnknownLenPtr() void { - const u32_ptr_info = @typeInfo([*]const volatile f64); - assert(TypeId(u32_ptr_info) == TypeId.Pointer); - assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); - assert(u32_ptr_info.Pointer.is_const == true); - assert(u32_ptr_info.Pointer.is_volatile == true); - assert(u32_ptr_info.Pointer.alignment == @alignOf(f64)); - assert(u32_ptr_info.Pointer.child == f64); -} - -test "type info: slice type info" { - testSlice(); - comptime testSlice(); -} - -fn testSlice() void { - const u32_slice_info = @typeInfo([]u32); - assert(TypeId(u32_slice_info) == TypeId.Pointer); - assert(u32_slice_info.Pointer.size == TypeInfo.Pointer.Size.Slice); - assert(u32_slice_info.Pointer.is_const == false); - assert(u32_slice_info.Pointer.is_volatile == false); - assert(u32_slice_info.Pointer.alignment == 4); - assert(u32_slice_info.Pointer.child == u32); -} - -test "type info: array type info" { - testArray(); - comptime testArray(); -} - -fn testArray() void { - const arr_info = @typeInfo([42]bool); - assert(TypeId(arr_info) == TypeId.Array); - assert(arr_info.Array.len == 42); - assert(arr_info.Array.child == bool); -} - -test "type info: optional type info" { - testOptional(); - comptime testOptional(); -} - -fn testOptional() void { - const null_info = @typeInfo(?void); - assert(TypeId(null_info) == TypeId.Optional); - assert(null_info.Optional.child == void); -} - -test "type info: promise info" { - testPromise(); - comptime testPromise(); -} - -fn testPromise() void { - const null_promise_info = @typeInfo(promise); - assert(TypeId(null_promise_info) == TypeId.Promise); - assert(null_promise_info.Promise.child == null); - - const promise_info = @typeInfo(promise->usize); - assert(TypeId(promise_info) == TypeId.Promise); - assert(promise_info.Promise.child.? == usize); -} - -test "type info: error set, error union info" { - testErrorSet(); - comptime testErrorSet(); -} - -fn testErrorSet() void { - const TestErrorSet = error{ - First, - Second, - Third, - }; - - const error_set_info = @typeInfo(TestErrorSet); - assert(TypeId(error_set_info) == TypeId.ErrorSet); - assert(error_set_info.ErrorSet.errors.len == 3); - assert(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First")); - assert(error_set_info.ErrorSet.errors[2].value == @errorToInt(TestErrorSet.Third)); - - const error_union_info = @typeInfo(TestErrorSet!usize); - assert(TypeId(error_union_info) == TypeId.ErrorUnion); - assert(error_union_info.ErrorUnion.error_set == TestErrorSet); - assert(error_union_info.ErrorUnion.payload == usize); -} - -test "type info: enum info" { - testEnum(); - comptime testEnum(); -} - -fn testEnum() void { - const Os = enum { - Windows, - Macos, - Linux, - FreeBSD, - }; - - const os_info = @typeInfo(Os); - assert(TypeId(os_info) == TypeId.Enum); - assert(os_info.Enum.layout == TypeInfo.ContainerLayout.Auto); - assert(os_info.Enum.fields.len == 4); - assert(mem.eql(u8, os_info.Enum.fields[1].name, "Macos")); - assert(os_info.Enum.fields[3].value == 3); - assert(os_info.Enum.tag_type == u2); - assert(os_info.Enum.defs.len == 0); -} - -test "type info: union info" { - testUnion(); - comptime testUnion(); -} - -fn testUnion() void { - const typeinfo_info = @typeInfo(TypeInfo); - assert(TypeId(typeinfo_info) == TypeId.Union); - assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); - assert(typeinfo_info.Union.tag_type.? == TypeId); - assert(typeinfo_info.Union.fields.len == 24); - assert(typeinfo_info.Union.fields[4].enum_field != null); - assert(typeinfo_info.Union.fields[4].enum_field.?.value == 4); - assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); - assert(typeinfo_info.Union.defs.len == 20); - - const TestNoTagUnion = union { - Foo: void, - Bar: u32, - }; - - const notag_union_info = @typeInfo(TestNoTagUnion); - assert(TypeId(notag_union_info) == TypeId.Union); - assert(notag_union_info.Union.tag_type == null); - assert(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto); - assert(notag_union_info.Union.fields.len == 2); - assert(notag_union_info.Union.fields[0].enum_field == null); - assert(notag_union_info.Union.fields[1].field_type == u32); - - const TestExternUnion = extern union { - foo: *c_void, - }; - - const extern_union_info = @typeInfo(TestExternUnion); - assert(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); - assert(extern_union_info.Union.tag_type == null); - assert(extern_union_info.Union.fields[0].enum_field == null); - assert(extern_union_info.Union.fields[0].field_type == *c_void); -} - -test "type info: struct info" { - testStruct(); - comptime testStruct(); -} - -fn testStruct() void { - const struct_info = @typeInfo(TestStruct); - assert(TypeId(struct_info) == TypeId.Struct); - assert(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); - assert(struct_info.Struct.fields.len == 3); - assert(struct_info.Struct.fields[1].offset == null); - assert(struct_info.Struct.fields[2].field_type == *TestStruct); - assert(struct_info.Struct.defs.len == 2); - assert(struct_info.Struct.defs[0].is_pub); - assert(!struct_info.Struct.defs[0].data.Fn.is_extern); - assert(struct_info.Struct.defs[0].data.Fn.lib_name == null); - assert(struct_info.Struct.defs[0].data.Fn.return_type == void); - assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn (*const TestStruct) void); -} - -const TestStruct = packed struct { - const Self = @This(); - - fieldA: usize, - fieldB: void, - fieldC: *Self, - - pub fn foo(self: *const Self) void {} -}; - -test "type info: function type info" { - testFunction(); - comptime testFunction(); -} - -fn testFunction() void { - const fn_info = @typeInfo(@typeOf(foo)); - assert(TypeId(fn_info) == TypeId.Fn); - assert(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.Unspecified); - assert(fn_info.Fn.is_generic); - assert(fn_info.Fn.args.len == 2); - assert(fn_info.Fn.is_var_args); - assert(fn_info.Fn.return_type == null); - assert(fn_info.Fn.async_allocator_type == null); - - const test_instance: TestStruct = undefined; - const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); - assert(TypeId(bound_fn_info) == TypeId.BoundFn); - assert(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct); -} - -fn foo(comptime a: usize, b: bool, args: ...) usize { - return 0; -} - -test "typeInfo with comptime parameter in struct fn def" { - const S = struct { - pub fn func(comptime x: f32) void {} - }; - comptime var info = @typeInfo(S); -} diff --git a/test/cases/undefined.zig b/test/cases/undefined.zig deleted file mode 100644 index 83c620d211..0000000000 --- a/test/cases/undefined.zig +++ /dev/null @@ -1,68 +0,0 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; - -fn initStaticArray() [10]i32 { - var array: [10]i32 = undefined; - array[0] = 1; - array[4] = 2; - array[7] = 3; - array[9] = 4; - return array; -} -const static_array = initStaticArray(); -test "init static array to undefined" { - assert(static_array[0] == 1); - assert(static_array[4] == 2); - assert(static_array[7] == 3); - assert(static_array[9] == 4); - - comptime { - assert(static_array[0] == 1); - assert(static_array[4] == 2); - assert(static_array[7] == 3); - assert(static_array[9] == 4); - } -} - -const Foo = struct { - x: i32, - - fn setFooXMethod(foo: *Foo) void { - foo.x = 3; - } -}; - -fn setFooX(foo: *Foo) void { - foo.x = 2; -} - -test "assign undefined to struct" { - comptime { - var foo: Foo = undefined; - setFooX(&foo); - assert(foo.x == 2); - } - { - var foo: Foo = undefined; - setFooX(&foo); - assert(foo.x == 2); - } -} - -test "assign undefined to struct with method" { - comptime { - var foo: Foo = undefined; - foo.setFooXMethod(); - assert(foo.x == 3); - } - { - var foo: Foo = undefined; - foo.setFooXMethod(); - assert(foo.x == 3); - } -} - -test "type name of undefined" { - const x = undefined; - assert(mem.eql(u8, @typeName(@typeOf(x)), "(undefined)")); -} diff --git a/test/cases/underscore.zig b/test/cases/underscore.zig deleted file mode 100644 index da1c97659c..0000000000 --- a/test/cases/underscore.zig +++ /dev/null @@ -1,28 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; - -test "ignore lval with underscore" { - _ = false; -} - -test "ignore lval with underscore (for loop)" { - for ([]void{}) |_, i| { - for ([]void{}) |_, j| { - break; - } - break; - } -} - -test "ignore lval with underscore (while loop)" { - while (optionalReturnError()) |_| { - while (optionalReturnError()) |_| { - break; - } else |_| {} - break; - } else |_| {} -} - -fn optionalReturnError() !?u32 { - return error.optionalReturnError; -} diff --git a/test/cases/union.zig b/test/cases/union.zig deleted file mode 100644 index 019a7012da..0000000000 --- a/test/cases/union.zig +++ /dev/null @@ -1,352 +0,0 @@ -const assert = @import("std").debug.assert; - -const Value = union(enum) { - Int: u64, - Array: [9]u8, -}; - -const Agg = struct { - val1: Value, - val2: Value, -}; - -const v1 = Value{ .Int = 1234 }; -const v2 = Value{ .Array = []u8{3} ** 9 }; - -const err = (anyerror!Agg)(Agg{ - .val1 = v1, - .val2 = v2, -}); - -const array = []Value{ - v1, - v2, - v1, - v2, -}; - -test "unions embedded in aggregate types" { - switch (array[1]) { - Value.Array => |arr| assert(arr[4] == 3), - else => unreachable, - } - switch ((err catch unreachable).val1) { - Value.Int => |x| assert(x == 1234), - else => unreachable, - } -} - -const Foo = union { - float: f64, - int: i32, -}; - -test "basic unions" { - var foo = Foo{ .int = 1 }; - assert(foo.int == 1); - foo = Foo{ .float = 12.34 }; - assert(foo.float == 12.34); -} - -test "comptime union field access" { - comptime { - var foo = Foo{ .int = 0 }; - assert(foo.int == 0); - - foo = Foo{ .float = 42.42 }; - assert(foo.float == 42.42); - } -} - -test "init union with runtime value" { - var foo: Foo = undefined; - - setFloat(&foo, 12.34); - assert(foo.float == 12.34); - - setInt(&foo, 42); - assert(foo.int == 42); -} - -fn setFloat(foo: *Foo, x: f64) void { - foo.* = Foo{ .float = x }; -} - -fn setInt(foo: *Foo, x: i32) void { - foo.* = Foo{ .int = x }; -} - -const FooExtern = extern union { - float: f64, - int: i32, -}; - -test "basic extern unions" { - var foo = FooExtern{ .int = 1 }; - assert(foo.int == 1); - foo.float = 12.34; - assert(foo.float == 12.34); -} - -const Letter = enum { - A, - B, - C, -}; -const Payload = union(Letter) { - A: i32, - B: f64, - C: bool, -}; - -test "union with specified enum tag" { - doTest(); - comptime doTest(); -} - -fn doTest() void { - assert(bar(Payload{ .A = 1234 }) == -10); -} - -fn bar(value: Payload) i32 { - assert(Letter(value) == Letter.A); - return switch (value) { - Payload.A => |x| return x - 1244, - Payload.B => |x| if (x == 12.34) i32(20) else 21, - Payload.C => |x| if (x) i32(30) else 31, - }; -} - -const MultipleChoice = union(enum(u32)) { - A = 20, - B = 40, - C = 60, - D = 1000, -}; -test "simple union(enum(u32))" { - var x = MultipleChoice.C; - assert(x == MultipleChoice.C); - assert(@enumToInt(@TagType(MultipleChoice)(x)) == 60); -} - -const MultipleChoice2 = union(enum(u32)) { - Unspecified1: i32, - A: f32 = 20, - Unspecified2: void, - B: bool = 40, - Unspecified3: i32, - C: i8 = 60, - Unspecified4: void, - D: void = 1000, - Unspecified5: i32, -}; - -test "union(enum(u32)) with specified and unspecified tag values" { - comptime assert(@TagType(@TagType(MultipleChoice2)) == u32); - testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); - comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); -} - -fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - assert(@enumToInt(@TagType(MultipleChoice2)(x)) == 60); - assert(1123 == switch (x) { - MultipleChoice2.A => 1, - MultipleChoice2.B => 2, - MultipleChoice2.C => |v| i32(1000) + v, - MultipleChoice2.D => 4, - MultipleChoice2.Unspecified1 => 5, - MultipleChoice2.Unspecified2 => 6, - MultipleChoice2.Unspecified3 => 7, - MultipleChoice2.Unspecified4 => 8, - MultipleChoice2.Unspecified5 => 9, - }); -} - -const ExternPtrOrInt = extern union { - ptr: *u8, - int: u64, -}; -test "extern union size" { - comptime assert(@sizeOf(ExternPtrOrInt) == 8); -} - -const PackedPtrOrInt = packed union { - ptr: *u8, - int: u64, -}; -test "extern union size" { - comptime assert(@sizeOf(PackedPtrOrInt) == 8); -} - -const ZeroBits = union { - OnlyField: void, -}; -test "union with only 1 field which is void should be zero bits" { - comptime assert(@sizeOf(ZeroBits) == 0); -} - -const TheTag = enum { - A, - B, - C, -}; -const TheUnion = union(TheTag) { - A: i32, - B: i32, - C: i32, -}; -test "union field access gives the enum values" { - assert(TheUnion.A == TheTag.A); - assert(TheUnion.B == TheTag.B); - assert(TheUnion.C == TheTag.C); -} - -test "cast union to tag type of union" { - testCastUnionToTagType(TheUnion{ .B = 1234 }); - comptime testCastUnionToTagType(TheUnion{ .B = 1234 }); -} - -fn testCastUnionToTagType(x: TheUnion) void { - assert(TheTag(x) == TheTag.B); -} - -test "cast tag type of union to union" { - var x: Value2 = Letter2.B; - assert(Letter2(x) == Letter2.B); -} -const Letter2 = enum { - A, - B, - C, -}; -const Value2 = union(Letter2) { - A: i32, - B, - C, -}; - -test "implicit cast union to its tag type" { - var x: Value2 = Letter2.B; - assert(x == Letter2.B); - giveMeLetterB(x); -} -fn giveMeLetterB(x: Letter2) void { - assert(x == Value2.B); -} - -pub const PackThis = union(enum) { - Invalid: bool, - StringLiteral: u2, -}; - -test "constant packed union" { - testConstPackedUnion([]PackThis{PackThis{ .StringLiteral = 1 }}); -} - -fn testConstPackedUnion(expected_tokens: []const PackThis) void { - assert(expected_tokens[0].StringLiteral == 1); -} - -test "switch on union with only 1 field" { - var r: PartialInst = undefined; - r = PartialInst.Compiled; - switch (r) { - PartialInst.Compiled => { - var z: PartialInstWithPayload = undefined; - z = PartialInstWithPayload{ .Compiled = 1234 }; - switch (z) { - PartialInstWithPayload.Compiled => |x| { - assert(x == 1234); - return; - }, - } - }, - } - unreachable; -} - -const PartialInst = union(enum) { - Compiled, -}; - -const PartialInstWithPayload = union(enum) { - Compiled: i32, -}; - -test "access a member of tagged union with conflicting enum tag name" { - const Bar = union(enum) { - A: A, - B: B, - - const A = u8; - const B = void; - }; - - comptime assert(Bar.A == u8); -} - -test "tagged union initialization with runtime void" { - assert(testTaggedUnionInit({})); -} - -const TaggedUnionWithAVoid = union(enum) { - A, - B: i32, -}; - -fn testTaggedUnionInit(x: var) bool { - const y = TaggedUnionWithAVoid{ .A = x }; - return @TagType(TaggedUnionWithAVoid)(y) == TaggedUnionWithAVoid.A; -} - -pub const UnionEnumNoPayloads = union(enum) { - A, - B, -}; - -test "tagged union with no payloads" { - const a = UnionEnumNoPayloads{ .B = {} }; - switch (a) { - @TagType(UnionEnumNoPayloads).A => @panic("wrong"), - @TagType(UnionEnumNoPayloads).B => {}, - } -} - -test "union with only 1 field casted to its enum type" { - const Literal = union(enum) { - Number: f64, - Bool: bool, - }; - - const Expr = union(enum) { - Literal: Literal, - }; - - var e = Expr{ .Literal = Literal{ .Bool = true } }; - const Tag = @TagType(Expr); - comptime assert(@TagType(Tag) == comptime_int); - var t = Tag(e); - assert(t == Expr.Literal); -} - -test "union with only 1 field casted to its enum type which has enum value specified" { - const Literal = union(enum) { - Number: f64, - Bool: bool, - }; - - const Tag = enum { - Literal = 33, - }; - - const Expr = union(Tag) { - Literal: Literal, - }; - - var e = Expr{ .Literal = Literal{ .Bool = true } }; - comptime assert(@TagType(Tag) == comptime_int); - var t = Tag(e); - assert(t == Expr.Literal); - assert(@enumToInt(t) == 33); - comptime assert(@enumToInt(t) == 33); -} diff --git a/test/cases/var_args.zig b/test/cases/var_args.zig deleted file mode 100644 index 3eb6e30448..0000000000 --- a/test/cases/var_args.zig +++ /dev/null @@ -1,84 +0,0 @@ -const assert = @import("std").debug.assert; - -fn add(args: ...) i32 { - var sum = i32(0); - { - comptime var i: usize = 0; - inline while (i < args.len) : (i += 1) { - sum += args[i]; - } - } - return sum; -} - -test "add arbitrary args" { - assert(add(i32(1), i32(2), i32(3), i32(4)) == 10); - assert(add(i32(1234)) == 1234); - assert(add() == 0); -} - -fn readFirstVarArg(args: ...) void { - const value = args[0]; -} - -test "send void arg to var args" { - readFirstVarArg({}); -} - -test "pass args directly" { - assert(addSomeStuff(i32(1), i32(2), i32(3), i32(4)) == 10); - assert(addSomeStuff(i32(1234)) == 1234); - assert(addSomeStuff() == 0); -} - -fn addSomeStuff(args: ...) i32 { - return add(args); -} - -test "runtime parameter before var args" { - assert(extraFn(10) == 0); - assert(extraFn(10, false) == 1); - assert(extraFn(10, false, true) == 2); - - // TODO issue #313 - //comptime { - // assert(extraFn(10) == 0); - // assert(extraFn(10, false) == 1); - // assert(extraFn(10, false, true) == 2); - //} -} - -fn extraFn(extra: u32, args: ...) usize { - if (args.len >= 1) { - assert(args[0] == false); - } - if (args.len >= 2) { - assert(args[1] == true); - } - return args.len; -} - -const foos = []fn (...) bool{ - foo1, - foo2, -}; - -fn foo1(args: ...) bool { - return true; -} -fn foo2(args: ...) bool { - return false; -} - -test "array of var args functions" { - assert(foos[0]()); - assert(!foos[1]()); -} - -test "pass zero length array to var args param" { - doNothingWithFirstArg(""); -} - -fn doNothingWithFirstArg(args: ...) void { - const a = args[0]; -} diff --git a/test/cases/void.zig b/test/cases/void.zig deleted file mode 100644 index 7121ac664b..0000000000 --- a/test/cases/void.zig +++ /dev/null @@ -1,30 +0,0 @@ -const assert = @import("std").debug.assert; - -const Foo = struct { - a: void, - b: i32, - c: void, -}; - -test "compare void with void compile time known" { - comptime { - const foo = Foo{ - .a = {}, - .b = 1, - .c = {}, - }; - assert(foo.a == {}); - } -} - -test "iterate over a void slice" { - var j: usize = 0; - for (times(10)) |_, i| { - assert(i == j); - j += 1; - } -} - -fn times(n: usize) []const void { - return ([*]void)(undefined)[0..n]; -} diff --git a/test/cases/while.zig b/test/cases/while.zig deleted file mode 100644 index f774e0ec6b..0000000000 --- a/test/cases/while.zig +++ /dev/null @@ -1,227 +0,0 @@ -const assert = @import("std").debug.assert; - -test "while loop" { - var i: i32 = 0; - while (i < 4) { - i += 1; - } - assert(i == 4); - assert(whileLoop1() == 1); -} -fn whileLoop1() i32 { - return whileLoop2(); -} -fn whileLoop2() i32 { - while (true) { - return 1; - } -} -test "static eval while" { - assert(static_eval_while_number == 1); -} -const static_eval_while_number = staticWhileLoop1(); -fn staticWhileLoop1() i32 { - return whileLoop2(); -} -fn staticWhileLoop2() i32 { - while (true) { - return 1; - } -} - -test "continue and break" { - runContinueAndBreakTest(); - assert(continue_and_break_counter == 8); -} -var continue_and_break_counter: i32 = 0; -fn runContinueAndBreakTest() void { - var i: i32 = 0; - while (true) { - continue_and_break_counter += 2; - i += 1; - if (i < 4) { - continue; - } - break; - } - assert(i == 4); -} - -test "return with implicit cast from while loop" { - returnWithImplicitCastFromWhileLoopTest() catch unreachable; -} -fn returnWithImplicitCastFromWhileLoopTest() anyerror!void { - while (true) { - return; - } -} - -test "while with continue expression" { - var sum: i32 = 0; - { - var i: i32 = 0; - while (i < 10) : (i += 1) { - if (i == 5) continue; - sum += i; - } - } - assert(sum == 40); -} - -test "while with else" { - var sum: i32 = 0; - var i: i32 = 0; - var got_else: i32 = 0; - while (i < 10) : (i += 1) { - sum += 1; - } else { - got_else += 1; - } - assert(sum == 10); - assert(got_else == 1); -} - -test "while with optional as condition" { - numbers_left = 10; - var sum: i32 = 0; - while (getNumberOrNull()) |value| { - sum += value; - } - assert(sum == 45); -} - -test "while with optional as condition with else" { - numbers_left = 10; - var sum: i32 = 0; - var got_else: i32 = 0; - while (getNumberOrNull()) |value| { - sum += value; - assert(got_else == 0); - } else { - got_else += 1; - } - assert(sum == 45); - assert(got_else == 1); -} - -test "while with error union condition" { - numbers_left = 10; - var sum: i32 = 0; - var got_else: i32 = 0; - while (getNumberOrErr()) |value| { - sum += value; - } else |err| { - assert(err == error.OutOfNumbers); - got_else += 1; - } - assert(sum == 45); - assert(got_else == 1); -} - -var numbers_left: i32 = undefined; -fn getNumberOrErr() anyerror!i32 { - return if (numbers_left == 0) error.OutOfNumbers else x: { - numbers_left -= 1; - break :x numbers_left; - }; -} -fn getNumberOrNull() ?i32 { - return if (numbers_left == 0) null else x: { - numbers_left -= 1; - break :x numbers_left; - }; -} - -test "while on optional with else result follow else prong" { - const result = while (returnNull()) |value| { - break value; - } else - i32(2); - assert(result == 2); -} - -test "while on optional with else result follow break prong" { - const result = while (returnOptional(10)) |value| { - break value; - } else - i32(2); - assert(result == 10); -} - -test "while on error union with else result follow else prong" { - const result = while (returnError()) |value| { - break value; - } else |err| - i32(2); - assert(result == 2); -} - -test "while on error union with else result follow break prong" { - const result = while (returnSuccess(10)) |value| { - break value; - } else |err| - i32(2); - assert(result == 10); -} - -test "while on bool with else result follow else prong" { - const result = while (returnFalse()) { - break i32(10); - } else - i32(2); - assert(result == 2); -} - -test "while on bool with else result follow break prong" { - const result = while (returnTrue()) { - break i32(10); - } else - i32(2); - assert(result == 10); -} - -test "break from outer while loop" { - testBreakOuter(); - comptime testBreakOuter(); -} - -fn testBreakOuter() void { - outer: while (true) { - while (true) { - break :outer; - } - } -} - -test "continue outer while loop" { - testContinueOuter(); - comptime testContinueOuter(); -} - -fn testContinueOuter() void { - var i: usize = 0; - outer: while (i < 10) : (i += 1) { - while (true) { - continue :outer; - } - } -} - -fn returnNull() ?i32 { - return null; -} -fn returnOptional(x: i32) ?i32 { - return x; -} -fn returnError() anyerror!i32 { - return error.YouWantedAnError; -} -fn returnSuccess(x: i32) anyerror!i32 { - return x; -} -fn returnFalse() bool { - return false; -} -fn returnTrue() bool { - return true; -} diff --git a/test/cases/widening.zig b/test/cases/widening.zig deleted file mode 100644 index cf6ab4ca0f..0000000000 --- a/test/cases/widening.zig +++ /dev/null @@ -1,27 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const mem = std.mem; - -test "integer widening" { - var a: u8 = 250; - var b: u16 = a; - var c: u32 = b; - var d: u64 = c; - var e: u64 = d; - var f: u128 = e; - assert(f == a); -} - -test "implicit unsigned integer to signed integer" { - var a: u8 = 250; - var b: i16 = a; - assert(b == 250); -} - -test "float widening" { - var a: f16 = 12.34; - var b: f32 = a; - var c: f64 = b; - var d: f128 = c; - assert(d == a); -} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 0754f223e8..bc1ef660c3 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3220,7 +3220,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return 2; \\} , - ".tmp_source.zig:2:15: error: unable to infer expression type", + ".tmp_source.zig:2:15: error: values of type 'comptime_int' must be comptime known", ); cases.add( @@ -3566,7 +3566,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(a)); } , - ".tmp_source.zig:2:11: error: expected type, found 'i32'", + ".tmp_source.zig:2:11: error: expected type 'type', found 'i32'", ); cases.add( diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig new file mode 100644 index 0000000000..e545a4c418 --- /dev/null +++ b/test/stage1/behavior.zig @@ -0,0 +1,80 @@ +comptime { + _ = @import("behavior/align.zig"); + _ = @import("behavior/alignof.zig"); + _ = @import("behavior/array.zig"); + _ = @import("behavior/asm.zig"); + _ = @import("behavior/atomics.zig"); + _ = @import("behavior/bit_shifting.zig"); + _ = @import("behavior/bitcast.zig"); + _ = @import("behavior/bitreverse.zig"); + _ = @import("behavior/bool.zig"); + _ = @import("behavior/bswap.zig"); + _ = @import("behavior/bugs/1076.zig"); + _ = @import("behavior/bugs/1111.zig"); + _ = @import("behavior/bugs/1277.zig"); + _ = @import("behavior/bugs/1322.zig"); + _ = @import("behavior/bugs/1381.zig"); + _ = @import("behavior/bugs/1421.zig"); + _ = @import("behavior/bugs/1442.zig"); + _ = @import("behavior/bugs/1486.zig"); + _ = @import("behavior/bugs/394.zig"); + _ = @import("behavior/bugs/655.zig"); + _ = @import("behavior/bugs/656.zig"); + _ = @import("behavior/bugs/726.zig"); + _ = @import("behavior/bugs/828.zig"); + _ = @import("behavior/bugs/920.zig"); + _ = @import("behavior/byval_arg_var.zig"); + _ = @import("behavior/cancel.zig"); + _ = @import("behavior/cast.zig"); + _ = @import("behavior/const_slice_child.zig"); + _ = @import("behavior/coroutine_await_struct.zig"); + _ = @import("behavior/coroutines.zig"); + _ = @import("behavior/defer.zig"); + _ = @import("behavior/enum.zig"); + _ = @import("behavior/enum_with_members.zig"); + _ = @import("behavior/error.zig"); + _ = @import("behavior/eval.zig"); + _ = @import("behavior/field_parent_ptr.zig"); + _ = @import("behavior/fn.zig"); + _ = @import("behavior/fn_in_struct_in_comptime.zig"); + _ = @import("behavior/for.zig"); + _ = @import("behavior/generics.zig"); + _ = @import("behavior/if.zig"); + _ = @import("behavior/import.zig"); + _ = @import("behavior/incomplete_struct_param_tld.zig"); + _ = @import("behavior/inttoptr.zig"); + _ = @import("behavior/ir_block_deps.zig"); + _ = @import("behavior/math.zig"); + _ = @import("behavior/merge_error_sets.zig"); + _ = @import("behavior/misc.zig"); + _ = @import("behavior/namespace_depends_on_compile_var/index.zig"); + _ = @import("behavior/new_stack_call.zig"); + _ = @import("behavior/null.zig"); + _ = @import("behavior/optional.zig"); + _ = @import("behavior/pointers.zig"); + _ = @import("behavior/popcount.zig"); + _ = @import("behavior/ptrcast.zig"); + _ = @import("behavior/pub_enum/index.zig"); + _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); + _ = @import("behavior/reflection.zig"); + _ = @import("behavior/sizeof_and_typeof.zig"); + _ = @import("behavior/slice.zig"); + _ = @import("behavior/struct.zig"); + _ = @import("behavior/struct_contains_null_ptr_itself.zig"); + _ = @import("behavior/struct_contains_slice_of_itself.zig"); + _ = @import("behavior/switch.zig"); + _ = @import("behavior/switch_prong_err_enum.zig"); + _ = @import("behavior/switch_prong_implicit_cast.zig"); + _ = @import("behavior/syntax.zig"); + _ = @import("behavior/this.zig"); + _ = @import("behavior/truncate.zig"); + _ = @import("behavior/try.zig"); + _ = @import("behavior/type_info.zig"); + _ = @import("behavior/undefined.zig"); + _ = @import("behavior/underscore.zig"); + _ = @import("behavior/union.zig"); + _ = @import("behavior/var_args.zig"); + _ = @import("behavior/void.zig"); + _ = @import("behavior/while.zig"); + _ = @import("behavior/widening.zig"); +} diff --git a/test/stage1/behavior/align.zig b/test/stage1/behavior/align.zig new file mode 100644 index 0000000000..aa7a93ad84 --- /dev/null +++ b/test/stage1/behavior/align.zig @@ -0,0 +1,230 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const builtin = @import("builtin"); + +var foo: u8 align(4) = 100; + +test "global variable alignment" { + assertOrPanic(@typeOf(&foo).alignment == 4); + assertOrPanic(@typeOf(&foo) == *align(4) u8); + const slice = (*[1]u8)(&foo)[0..]; + assertOrPanic(@typeOf(slice) == []align(4) u8); +} + +fn derp() align(@sizeOf(usize) * 2) i32 { + return 1234; +} +fn noop1() align(1) void {} +fn noop4() align(4) void {} + +test "function alignment" { + assertOrPanic(derp() == 1234); + assertOrPanic(@typeOf(noop1) == fn () align(1) void); + assertOrPanic(@typeOf(noop4) == fn () align(4) void); + noop1(); + noop4(); +} + +var baz: packed struct { + a: u32, + b: u32, +} = undefined; + +test "packed struct alignment" { + assertOrPanic(@typeOf(&baz.b) == *align(1) u32); +} + +const blah: packed struct { + a: u3, + b: u3, + c: u2, +} = undefined; + +test "bit field alignment" { + assertOrPanic(@typeOf(&blah.b) == *align(1:3:1) const u3); +} + +test "default alignment allows unspecified in type syntax" { + assertOrPanic(*u32 == *align(@alignOf(u32)) u32); +} + +test "implicitly decreasing pointer alignment" { + const a: u32 align(4) = 3; + const b: u32 align(8) = 4; + assertOrPanic(addUnaligned(&a, &b) == 7); +} + +fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 { + return a.* + b.*; +} + +test "implicitly decreasing slice alignment" { + const a: u32 align(4) = 3; + const b: u32 align(8) = 4; + assertOrPanic(addUnalignedSlice((*[1]u32)(&a)[0..], (*[1]u32)(&b)[0..]) == 7); +} +fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 { + return a[0] + b[0]; +} + +test "specifying alignment allows pointer cast" { + testBytesAlign(0x33); +} +fn testBytesAlign(b: u8) void { + var bytes align(4) = []u8{ + b, + b, + b, + b, + }; + const ptr = @ptrCast(*u32, &bytes[0]); + assertOrPanic(ptr.* == 0x33333333); +} + +test "specifying alignment allows slice cast" { + testBytesAlignSlice(0x33); +} +fn testBytesAlignSlice(b: u8) void { + var bytes align(4) = []u8{ + b, + b, + b, + b, + }; + const slice: []u32 = @bytesToSlice(u32, bytes[0..]); + assertOrPanic(slice[0] == 0x33333333); +} + +test "@alignCast pointers" { + var x: u32 align(4) = 1; + expectsOnly1(&x); + assertOrPanic(x == 2); +} +fn expectsOnly1(x: *align(1) u32) void { + expects4(@alignCast(4, x)); +} +fn expects4(x: *align(4) u32) void { + x.* += 1; +} + +test "@alignCast slices" { + var array align(4) = []u32{ + 1, + 1, + }; + const slice = array[0..]; + sliceExpectsOnly1(slice); + assertOrPanic(slice[0] == 2); +} +fn sliceExpectsOnly1(slice: []align(1) u32) void { + sliceExpects4(@alignCast(4, slice)); +} +fn sliceExpects4(slice: []align(4) u32) void { + slice[0] += 1; +} + +test "implicitly decreasing fn alignment" { + testImplicitlyDecreaseFnAlign(alignedSmall, 1234); + testImplicitlyDecreaseFnAlign(alignedBig, 5678); +} + +fn testImplicitlyDecreaseFnAlign(ptr: fn () align(1) i32, answer: i32) void { + assertOrPanic(ptr() == answer); +} + +fn alignedSmall() align(8) i32 { + return 1234; +} +fn alignedBig() align(16) i32 { + return 5678; +} + +test "@alignCast functions" { + assertOrPanic(fnExpectsOnly1(simple4) == 0x19); +} +fn fnExpectsOnly1(ptr: fn () align(1) i32) i32 { + return fnExpects4(@alignCast(4, ptr)); +} +fn fnExpects4(ptr: fn () align(4) i32) i32 { + return ptr(); +} +fn simple4() align(4) i32 { + return 0x19; +} + +test "generic function with align param" { + assertOrPanic(whyWouldYouEverDoThis(1) == 0x1); + assertOrPanic(whyWouldYouEverDoThis(4) == 0x1); + assertOrPanic(whyWouldYouEverDoThis(8) == 0x1); +} + +fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { + return 0x1; +} + +test "@ptrCast preserves alignment of bigger source" { + var x: u32 align(16) = 1234; + const ptr = @ptrCast(*u8, &x); + assertOrPanic(@typeOf(ptr) == *align(16) u8); +} + +test "runtime known array index has best alignment possible" { + // take full advantage of over-alignment + var array align(4) = []u8{ 1, 2, 3, 4 }; + assertOrPanic(@typeOf(&array[0]) == *align(4) u8); + assertOrPanic(@typeOf(&array[1]) == *u8); + assertOrPanic(@typeOf(&array[2]) == *align(2) u8); + assertOrPanic(@typeOf(&array[3]) == *u8); + + // because align is too small but we still figure out to use 2 + var bigger align(2) = []u64{ 1, 2, 3, 4 }; + assertOrPanic(@typeOf(&bigger[0]) == *align(2) u64); + assertOrPanic(@typeOf(&bigger[1]) == *align(2) u64); + assertOrPanic(@typeOf(&bigger[2]) == *align(2) u64); + assertOrPanic(@typeOf(&bigger[3]) == *align(2) u64); + + // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2 + var smaller align(2) = []u32{ 1, 2, 3, 4 }; + comptime assertOrPanic(@typeOf(smaller[0..]) == []align(2) u32); + comptime assertOrPanic(@typeOf(smaller[0..].ptr) == [*]align(2) u32); + testIndex(smaller[0..].ptr, 0, *align(2) u32); + testIndex(smaller[0..].ptr, 1, *align(2) u32); + testIndex(smaller[0..].ptr, 2, *align(2) u32); + testIndex(smaller[0..].ptr, 3, *align(2) u32); + + // has to use ABI alignment because index known at runtime only + testIndex2(array[0..].ptr, 0, *u8); + testIndex2(array[0..].ptr, 1, *u8); + testIndex2(array[0..].ptr, 2, *u8); + testIndex2(array[0..].ptr, 3, *u8); +} +fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) void { + comptime assertOrPanic(@typeOf(&smaller[index]) == T); +} +fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) void { + comptime assertOrPanic(@typeOf(&ptr[index]) == T); +} + +test "alignstack" { + assertOrPanic(fnWithAlignedStack() == 1234); +} + +fn fnWithAlignedStack() i32 { + @setAlignStack(256); + return 1234; +} + +test "alignment of structs" { + assertOrPanic(@alignOf(struct { + a: i32, + b: *i32, + }) == @alignOf(usize)); +} + +test "alignment of extern() void" { + var runtime_nothing = nothing; + const casted1 = @ptrCast(*const u8, runtime_nothing); + const casted2 = @ptrCast(extern fn () void, casted1); + casted2(); +} + +extern fn nothing() void {} diff --git a/test/stage1/behavior/alignof.zig b/test/stage1/behavior/alignof.zig new file mode 100644 index 0000000000..98c805908b --- /dev/null +++ b/test/stage1/behavior/alignof.zig @@ -0,0 +1,18 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const builtin = @import("builtin"); +const maxInt = std.math.maxInt; + +const Foo = struct { + x: u32, + y: u32, + z: u32, +}; + +test "@alignOf(T) before referencing T" { + comptime assertOrPanic(@alignOf(Foo) != maxInt(usize)); + if (builtin.arch == builtin.Arch.x86_64) { + comptime assertOrPanic(@alignOf(Foo) == 4); + } +} + diff --git a/test/stage1/behavior/array.zig b/test/stage1/behavior/array.zig new file mode 100644 index 0000000000..1183305209 --- /dev/null +++ b/test/stage1/behavior/array.zig @@ -0,0 +1,270 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const mem = @import("std").mem; + +test "arrays" { + var array: [5]u32 = undefined; + + var i: u32 = 0; + while (i < 5) { + array[i] = i + 1; + i = array[i]; + } + + i = 0; + var accumulator = u32(0); + while (i < 5) { + accumulator += array[i]; + + i += 1; + } + + assertOrPanic(accumulator == 15); + assertOrPanic(getArrayLen(array) == 5); +} +fn getArrayLen(a: []const u32) usize { + return a.len; +} + +test "void arrays" { + var array: [4]void = undefined; + array[0] = void{}; + array[1] = array[2]; + assertOrPanic(@sizeOf(@typeOf(array)) == 0); + assertOrPanic(array.len == 4); +} + +test "array literal" { + const hex_mult = []u16{ + 4096, + 256, + 16, + 1, + }; + + assertOrPanic(hex_mult.len == 4); + assertOrPanic(hex_mult[1] == 256); +} + +test "array dot len const expr" { + assertOrPanic(comptime x: { + break :x some_array.len == 4; + }); +} + +const ArrayDotLenConstExpr = struct { + y: [some_array.len]u8, +}; +const some_array = []u8{ + 0, + 1, + 2, + 3, +}; + +test "nested arrays" { + const array_of_strings = [][]const u8{ + "hello", + "this", + "is", + "my", + "thing", + }; + for (array_of_strings) |s, i| { + if (i == 0) assertOrPanic(mem.eql(u8, s, "hello")); + if (i == 1) assertOrPanic(mem.eql(u8, s, "this")); + if (i == 2) assertOrPanic(mem.eql(u8, s, "is")); + if (i == 3) assertOrPanic(mem.eql(u8, s, "my")); + if (i == 4) assertOrPanic(mem.eql(u8, s, "thing")); + } +} + +var s_array: [8]Sub = undefined; +const Sub = struct { + b: u8, +}; +const Str = struct { + a: []Sub, +}; +test "set global var array via slice embedded in struct" { + var s = Str{ .a = s_array[0..] }; + + s.a[0].b = 1; + s.a[1].b = 2; + s.a[2].b = 3; + + assertOrPanic(s_array[0].b == 1); + assertOrPanic(s_array[1].b == 2); + assertOrPanic(s_array[2].b == 3); +} + +test "array literal with specified size" { + var array = [2]u8{ + 1, + 2, + }; + assertOrPanic(array[0] == 1); + assertOrPanic(array[1] == 2); +} + +test "array child property" { + var x: [5]i32 = undefined; + assertOrPanic(@typeOf(x).Child == i32); +} + +test "array len property" { + var x: [5]i32 = undefined; + assertOrPanic(@typeOf(x).len == 5); +} + +test "array len field" { + var arr = [4]u8{ 0, 0, 0, 0 }; + var ptr = &arr; + assertOrPanic(arr.len == 4); + comptime assertOrPanic(arr.len == 4); + assertOrPanic(ptr.len == 4); + comptime assertOrPanic(ptr.len == 4); +} + +test "single-item pointer to array indexing and slicing" { + testSingleItemPtrArrayIndexSlice(); + comptime testSingleItemPtrArrayIndexSlice(); +} + +fn testSingleItemPtrArrayIndexSlice() void { + var array = "aaaa"; + doSomeMangling(&array); + assertOrPanic(mem.eql(u8, "azya", array)); +} + +fn doSomeMangling(array: *[4]u8) void { + array[1] = 'z'; + array[2..3][0] = 'y'; +} + +test "implicit cast single-item pointer" { + testImplicitCastSingleItemPtr(); + comptime testImplicitCastSingleItemPtr(); +} + +fn testImplicitCastSingleItemPtr() void { + var byte: u8 = 100; + const slice = (*[1]u8)(&byte)[0..]; + slice[0] += 1; + assertOrPanic(byte == 101); +} + +fn testArrayByValAtComptime(b: [2]u8) u8 { + return b[0]; +} + +test "comptime evalutating function that takes array by value" { + const arr = []u8{ 0, 1 }; + _ = comptime testArrayByValAtComptime(arr); + _ = comptime testArrayByValAtComptime(arr); +} + +test "implicit comptime in array type size" { + var arr: [plusOne(10)]bool = undefined; + assertOrPanic(arr.len == 11); +} + +fn plusOne(x: u32) u32 { + return x + 1; +} + +test "array literal as argument to function" { + const S = struct { + fn entry(two: i32) void { + foo([]i32{ + 1, + 2, + 3, + }); + foo([]i32{ + 1, + two, + 3, + }); + foo2(true, []i32{ + 1, + 2, + 3, + }); + foo2(true, []i32{ + 1, + two, + 3, + }); + } + fn foo(x: []const i32) void { + assertOrPanic(x[0] == 1); + assertOrPanic(x[1] == 2); + assertOrPanic(x[2] == 3); + } + fn foo2(trash: bool, x: []const i32) void { + assertOrPanic(trash); + assertOrPanic(x[0] == 1); + assertOrPanic(x[1] == 2); + assertOrPanic(x[2] == 3); + } + }; + S.entry(2); + comptime S.entry(2); +} + +test "double nested array to const slice cast in array literal" { + const S = struct { + fn entry(two: i32) void { + const cases = [][]const []const i32{ + [][]const i32{[]i32{1}}, + [][]const i32{[]i32{ 2, 3 }}, + [][]const i32{ + []i32{4}, + []i32{ 5, 6, 7 }, + }, + }; + check(cases); + + const cases2 = [][]const i32{ + []i32{1}, + []i32{ two, 3 }, + }; + assertOrPanic(cases2.len == 2); + assertOrPanic(cases2[0].len == 1); + assertOrPanic(cases2[0][0] == 1); + assertOrPanic(cases2[1].len == 2); + assertOrPanic(cases2[1][0] == 2); + assertOrPanic(cases2[1][1] == 3); + + const cases3 = [][]const []const i32{ + [][]const i32{[]i32{1}}, + [][]const i32{[]i32{ two, 3 }}, + [][]const i32{ + []i32{4}, + []i32{ 5, 6, 7 }, + }, + }; + check(cases3); + } + + fn check(cases: []const []const []const i32) void { + assertOrPanic(cases.len == 3); + assertOrPanic(cases[0].len == 1); + assertOrPanic(cases[0][0].len == 1); + assertOrPanic(cases[0][0][0] == 1); + assertOrPanic(cases[1].len == 1); + assertOrPanic(cases[1][0].len == 2); + assertOrPanic(cases[1][0][0] == 2); + assertOrPanic(cases[1][0][1] == 3); + assertOrPanic(cases[2].len == 2); + assertOrPanic(cases[2][0].len == 1); + assertOrPanic(cases[2][0][0] == 4); + assertOrPanic(cases[2][1].len == 3); + assertOrPanic(cases[2][1][0] == 5); + assertOrPanic(cases[2][1][1] == 6); + assertOrPanic(cases[2][1][2] == 7); + } + }; + S.entry(2); + comptime S.entry(2); +} diff --git a/test/stage1/behavior/asm.zig b/test/stage1/behavior/asm.zig new file mode 100644 index 0000000000..48701c5836 --- /dev/null +++ b/test/stage1/behavior/asm.zig @@ -0,0 +1,92 @@ +const config = @import("builtin"); +const assertOrPanic = @import("std").debug.assertOrPanic; + +comptime { + if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { + asm volatile ( + \\.globl aoeu; + \\.type aoeu, @function; + \\.set aoeu, derp; + ); + } +} + +test "module level assembly" { + if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { + assertOrPanic(aoeu() == 1234); + } +} + +test "output constraint modifiers" { + // This is only testing compilation. + var a: u32 = 3; + asm volatile ("" + : [_] "=m,r" (a) + : + : "" + ); + asm volatile ("" + : [_] "=r,m" (a) + : + : "" + ); +} + +test "alternative constraints" { + // Make sure we allow commas as a separator for alternative constraints. + var a: u32 = 3; + asm volatile ("" + : [_] "=r,m" (a) + : [_] "r,m" (a) + : "" + ); +} + +test "sized integer/float in asm input" { + asm volatile ("" + : + : [_] "m" (usize(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (i15(-3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (u3(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (i3(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (u121(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (i121(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (f32(3.17)) + : "" + ); + asm volatile ("" + : + : [_] "m" (f64(3.17)) + : "" + ); +} + +extern fn aoeu() i32; + +export fn derp() i32 { + return 1234; +} diff --git a/test/stage1/behavior/atomics.zig b/test/stage1/behavior/atomics.zig new file mode 100644 index 0000000000..fa3c5f29a6 --- /dev/null +++ b/test/stage1/behavior/atomics.zig @@ -0,0 +1,71 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const builtin = @import("builtin"); +const AtomicRmwOp = builtin.AtomicRmwOp; +const AtomicOrder = builtin.AtomicOrder; + +test "cmpxchg" { + var x: i32 = 1234; + if (@cmpxchgWeak(i32, &x, 99, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assertOrPanic(x1 == 1234); + } else { + @panic("cmpxchg should have failed"); + } + + while (@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assertOrPanic(x1 == 1234); + } + assertOrPanic(x == 5678); + + assertOrPanic(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + assertOrPanic(x == 42); +} + +test "fence" { + var x: i32 = 1234; + @fence(AtomicOrder.SeqCst); + x = 5678; +} + +test "atomicrmw and atomicload" { + var data: u8 = 200; + testAtomicRmw(&data); + assertOrPanic(data == 42); + testAtomicLoad(&data); +} + +fn testAtomicRmw(ptr: *u8) void { + const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst); + assertOrPanic(prev_value == 200); + comptime { + var x: i32 = 1234; + const y: i32 = 12345; + assertOrPanic(@atomicLoad(i32, &x, AtomicOrder.SeqCst) == 1234); + assertOrPanic(@atomicLoad(i32, &y, AtomicOrder.SeqCst) == 12345); + } +} + +fn testAtomicLoad(ptr: *u8) void { + const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst); + assertOrPanic(x == 42); +} + +test "cmpxchg with ptr" { + var data1: i32 = 1234; + var data2: i32 = 5678; + var data3: i32 = 9101; + var x: *i32 = &data1; + if (@cmpxchgWeak(*i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assertOrPanic(x1 == &data1); + } else { + @panic("cmpxchg should have failed"); + } + + while (@cmpxchgWeak(*i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assertOrPanic(x1 == &data1); + } + assertOrPanic(x == &data3); + + assertOrPanic(@cmpxchgStrong(*i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + assertOrPanic(x == &data2); +} diff --git a/test/stage1/behavior/bit_shifting.zig b/test/stage1/behavior/bit_shifting.zig new file mode 100644 index 0000000000..3290688358 --- /dev/null +++ b/test/stage1/behavior/bit_shifting.zig @@ -0,0 +1,88 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; + +fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, comptime V: type) type { + assertOrPanic(Key == @IntType(false, Key.bit_count)); + assertOrPanic(Key.bit_count >= mask_bit_count); + const ShardKey = @IntType(false, mask_bit_count); + const shift_amount = Key.bit_count - ShardKey.bit_count; + return struct { + const Self = @This(); + shards: [1 << ShardKey.bit_count]?*Node, + + pub fn create() Self { + return Self{ .shards = []?*Node{null} ** (1 << ShardKey.bit_count) }; + } + + fn getShardKey(key: Key) ShardKey { + // https://github.com/ziglang/zig/issues/1544 + // this special case is needed because you can't u32 >> 32. + if (ShardKey == u0) return 0; + + // this can be u1 >> u0 + const shard_key = key >> shift_amount; + + // TODO: https://github.com/ziglang/zig/issues/1544 + // This cast could be implicit if we teach the compiler that + // u32 >> 30 -> u2 + return @intCast(ShardKey, shard_key); + } + + pub fn put(self: *Self, node: *Node) void { + const shard_key = Self.getShardKey(node.key); + node.next = self.shards[shard_key]; + self.shards[shard_key] = node; + } + + pub fn get(self: *Self, key: Key) ?*Node { + const shard_key = Self.getShardKey(key); + var maybe_node = self.shards[shard_key]; + while (maybe_node) |node| : (maybe_node = node.next) { + if (node.key == key) return node; + } + return null; + } + + pub const Node = struct { + key: Key, + value: V, + next: ?*Node, + + pub fn init(self: *Node, key: Key, value: V) void { + self.key = key; + self.value = value; + self.next = null; + } + }; + }; +} + +test "sharded table" { + // realistic 16-way sharding + testShardedTable(u32, 4, 8); + + testShardedTable(u5, 0, 32); // ShardKey == u0 + testShardedTable(u5, 2, 32); + testShardedTable(u5, 5, 32); + + testShardedTable(u1, 0, 2); + testShardedTable(u1, 1, 2); // this does u1 >> u0 + + testShardedTable(u0, 0, 1); +} +fn testShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, comptime node_count: comptime_int) void { + const Table = ShardedTable(Key, mask_bit_count, void); + + var table = Table.create(); + var node_buffer: [node_count]Table.Node = undefined; + for (node_buffer) |*node, i| { + const key = @intCast(Key, i); + assertOrPanic(table.get(key) == null); + node.init(key, {}); + table.put(node); + } + + for (node_buffer) |*node, i| { + assertOrPanic(table.get(@intCast(Key, i)) == node); + } +} diff --git a/test/stage1/behavior/bitcast.zig b/test/stage1/behavior/bitcast.zig new file mode 100644 index 0000000000..19030255e4 --- /dev/null +++ b/test/stage1/behavior/bitcast.zig @@ -0,0 +1,36 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const maxInt = std.math.maxInt; + +test "@bitCast i32 -> u32" { + testBitCast_i32_u32(); + comptime testBitCast_i32_u32(); +} + +fn testBitCast_i32_u32() void { + assertOrPanic(conv(-1) == maxInt(u32)); + assertOrPanic(conv2(maxInt(u32)) == -1); +} + +fn conv(x: i32) u32 { + return @bitCast(u32, x); +} +fn conv2(x: u32) i32 { + return @bitCast(i32, x); +} + +test "@bitCast extern enum to its integer type" { + const SOCK = extern enum { + A, + B, + + fn testBitCastExternEnum() void { + var SOCK_DGRAM = @This().B; + var sock_dgram = @bitCast(c_int, SOCK_DGRAM); + assertOrPanic(sock_dgram == 1); + } + }; + + SOCK.testBitCastExternEnum(); + comptime SOCK.testBitCastExternEnum(); +} diff --git a/test/stage1/behavior/bitreverse.zig b/test/stage1/behavior/bitreverse.zig new file mode 100644 index 0000000000..97787ace84 --- /dev/null +++ b/test/stage1/behavior/bitreverse.zig @@ -0,0 +1,81 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const minInt = std.math.minInt; + +test "@bitreverse" { + comptime testBitReverse(); + testBitReverse(); +} + +fn testBitReverse() void { + // using comptime_ints, unsigned + assertOrPanic(@bitreverse(u0, 0) == 0); + assertOrPanic(@bitreverse(u5, 0x12) == 0x9); + assertOrPanic(@bitreverse(u8, 0x12) == 0x48); + assertOrPanic(@bitreverse(u16, 0x1234) == 0x2c48); + assertOrPanic(@bitreverse(u24, 0x123456) == 0x6a2c48); + assertOrPanic(@bitreverse(u32, 0x12345678) == 0x1e6a2c48); + assertOrPanic(@bitreverse(u40, 0x123456789a) == 0x591e6a2c48); + assertOrPanic(@bitreverse(u48, 0x123456789abc) == 0x3d591e6a2c48); + assertOrPanic(@bitreverse(u56, 0x123456789abcde) == 0x7b3d591e6a2c48); + assertOrPanic(@bitreverse(u64, 0x123456789abcdef1) == 0x8f7b3d591e6a2c48); + assertOrPanic(@bitreverse(u128, 0x123456789abcdef11121314151617181) == 0x818e868a828c84888f7b3d591e6a2c48); + + // using runtime uints, unsigned + var num0: u0 = 0; + assertOrPanic(@bitreverse(u0, num0) == 0); + var num5: u5 = 0x12; + assertOrPanic(@bitreverse(u5, num5) == 0x9); + var num8: u8 = 0x12; + assertOrPanic(@bitreverse(u8, num8) == 0x48); + var num16: u16 = 0x1234; + assertOrPanic(@bitreverse(u16, num16) == 0x2c48); + var num24: u24 = 0x123456; + assertOrPanic(@bitreverse(u24, num24) == 0x6a2c48); + var num32: u32 = 0x12345678; + assertOrPanic(@bitreverse(u32, num32) == 0x1e6a2c48); + var num40: u40 = 0x123456789a; + assertOrPanic(@bitreverse(u40, num40) == 0x591e6a2c48); + var num48: u48 = 0x123456789abc; + assertOrPanic(@bitreverse(u48, num48) == 0x3d591e6a2c48); + var num56: u56 = 0x123456789abcde; + assertOrPanic(@bitreverse(u56, num56) == 0x7b3d591e6a2c48); + var num64: u64 = 0x123456789abcdef1; + assertOrPanic(@bitreverse(u64, num64) == 0x8f7b3d591e6a2c48); + var num128: u128 = 0x123456789abcdef11121314151617181; + assertOrPanic(@bitreverse(u128, num128) == 0x818e868a828c84888f7b3d591e6a2c48); + + // using comptime_ints, signed, positive + assertOrPanic(@bitreverse(i0, 0) == 0); + assertOrPanic(@bitreverse(i8, @bitCast(i8, u8(0x92))) == @bitCast(i8, u8(0x49))); + assertOrPanic(@bitreverse(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16(0x2c48))); + assertOrPanic(@bitreverse(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24(0x6a2c48))); + assertOrPanic(@bitreverse(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32(0x1e6a2c48))); + assertOrPanic(@bitreverse(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40(0x591e6a2c48))); + assertOrPanic(@bitreverse(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48(0x3d591e6a2c48))); + assertOrPanic(@bitreverse(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56(0x7b3d591e6a2c48))); + assertOrPanic(@bitreverse(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64, u64(0x8f7b3d591e6a2c48))); + assertOrPanic(@bitreverse(i128, @bitCast(i128, u128(0x123456789abcdef11121314151617181))) == @bitCast(i128, u128(0x818e868a828c84888f7b3d591e6a2c48))); + + // using comptime_ints, signed, negative. Compare to runtime ints returned from llvm. + var neg5: i5 = minInt(i5) + 1; + assertOrPanic(@bitreverse(i5, minInt(i5) + 1) == @bitreverse(i5, neg5)); + var neg8: i8 = -18; + assertOrPanic(@bitreverse(i8, -18) == @bitreverse(i8, neg8)); + var neg16: i16 = -32694; + assertOrPanic(@bitreverse(i16, -32694) == @bitreverse(i16, neg16)); + var neg24: i24 = -6773785; + assertOrPanic(@bitreverse(i24, -6773785) == @bitreverse(i24, neg24)); + var neg32: i32 = -16773785; + assertOrPanic(@bitreverse(i32, -16773785) == @bitreverse(i32, neg32)); + var neg40: i40 = minInt(i40) + 12345; + assertOrPanic(@bitreverse(i40, minInt(i40) + 12345) == @bitreverse(i40, neg40)); + var neg48: i48 = minInt(i48) + 12345; + assertOrPanic(@bitreverse(i48, minInt(i48) + 12345) == @bitreverse(i48, neg48)); + var neg56: i56 = minInt(i56) + 12345; + assertOrPanic(@bitreverse(i56, minInt(i56) + 12345) == @bitreverse(i56, neg56)); + var neg64: i64 = minInt(i64) + 12345; + assertOrPanic(@bitreverse(i64, minInt(i64) + 12345) == @bitreverse(i64, neg64)); + var neg128: i128 = minInt(i128) + 12345; + assertOrPanic(@bitreverse(i128, minInt(i128) + 12345) == @bitreverse(i128, neg128)); +} diff --git a/test/stage1/behavior/bool.zig b/test/stage1/behavior/bool.zig new file mode 100644 index 0000000000..2d7241526f --- /dev/null +++ b/test/stage1/behavior/bool.zig @@ -0,0 +1,35 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "bool literals" { + assertOrPanic(true); + assertOrPanic(!false); +} + +test "cast bool to int" { + const t = true; + const f = false; + assertOrPanic(@boolToInt(t) == u32(1)); + assertOrPanic(@boolToInt(f) == u32(0)); + nonConstCastBoolToInt(t, f); +} + +fn nonConstCastBoolToInt(t: bool, f: bool) void { + assertOrPanic(@boolToInt(t) == u32(1)); + assertOrPanic(@boolToInt(f) == u32(0)); +} + +test "bool cmp" { + assertOrPanic(testBoolCmp(true, false) == false); +} +fn testBoolCmp(a: bool, b: bool) bool { + return a == b; +} + +const global_f = false; +const global_t = true; +const not_global_f = !global_f; +const not_global_t = !global_t; +test "compile time bool not" { + assertOrPanic(not_global_f); + assertOrPanic(!not_global_t); +} diff --git a/test/stage1/behavior/bswap.zig b/test/stage1/behavior/bswap.zig new file mode 100644 index 0000000000..8084538e03 --- /dev/null +++ b/test/stage1/behavior/bswap.zig @@ -0,0 +1,32 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; + +test "@bswap" { + comptime testByteSwap(); + testByteSwap(); +} + +fn testByteSwap() void { + assertOrPanic(@bswap(u0, 0) == 0); + assertOrPanic(@bswap(u8, 0x12) == 0x12); + assertOrPanic(@bswap(u16, 0x1234) == 0x3412); + assertOrPanic(@bswap(u24, 0x123456) == 0x563412); + assertOrPanic(@bswap(u32, 0x12345678) == 0x78563412); + assertOrPanic(@bswap(u40, 0x123456789a) == 0x9a78563412); + assertOrPanic(@bswap(u48, 0x123456789abc) == 0xbc9a78563412); + assertOrPanic(@bswap(u56, 0x123456789abcde) == 0xdebc9a78563412); + assertOrPanic(@bswap(u64, 0x123456789abcdef1) == 0xf1debc9a78563412); + assertOrPanic(@bswap(u128, 0x123456789abcdef11121314151617181) == 0x8171615141312111f1debc9a78563412); + + assertOrPanic(@bswap(i0, 0) == 0); + assertOrPanic(@bswap(i8, -50) == -50); + assertOrPanic(@bswap(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16(0x3412))); + assertOrPanic(@bswap(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24(0x563412))); + assertOrPanic(@bswap(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32(0x78563412))); + assertOrPanic(@bswap(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40(0x9a78563412))); + assertOrPanic(@bswap(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48(0xbc9a78563412))); + assertOrPanic(@bswap(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56(0xdebc9a78563412))); + assertOrPanic(@bswap(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64, u64(0xf1debc9a78563412))); + assertOrPanic(@bswap(i128, @bitCast(i128, u128(0x123456789abcdef11121314151617181))) == + @bitCast(i128, u128(0x8171615141312111f1debc9a78563412))); +} diff --git a/test/stage1/behavior/bugs/1076.zig b/test/stage1/behavior/bugs/1076.zig new file mode 100644 index 0000000000..69a7e70f7d --- /dev/null +++ b/test/stage1/behavior/bugs/1076.zig @@ -0,0 +1,16 @@ +const std = @import("std"); +const mem = std.mem; +const assertOrPanic = std.debug.assertOrPanic; + +test "comptime code should not modify constant data" { + testCastPtrOfArrayToSliceAndPtr(); + comptime testCastPtrOfArrayToSliceAndPtr(); +} + +fn testCastPtrOfArrayToSliceAndPtr() void { + var array = "aoeu"; + const x: [*]u8 = &array; + x[0] += 1; + assertOrPanic(mem.eql(u8, array[0..], "boeu")); +} + diff --git a/test/stage1/behavior/bugs/1111.zig b/test/stage1/behavior/bugs/1111.zig new file mode 100644 index 0000000000..f62107f9a3 --- /dev/null +++ b/test/stage1/behavior/bugs/1111.zig @@ -0,0 +1,12 @@ +const Foo = extern enum { + Bar = -1, +}; + +test "issue 1111 fixed" { + const v = Foo.Bar; + + switch (v) { + Foo.Bar => return, + else => return, + } +} diff --git a/test/stage1/behavior/bugs/1277.zig b/test/stage1/behavior/bugs/1277.zig new file mode 100644 index 0000000000..a83e7653e2 --- /dev/null +++ b/test/stage1/behavior/bugs/1277.zig @@ -0,0 +1,15 @@ +const std = @import("std"); + +const S = struct { + f: ?fn () i32, +}; + +const s = S{ .f = f }; + +fn f() i32 { + return 1234; +} + +test "don't emit an LLVM global for a const function when it's in an optional in a struct" { + std.debug.assertOrPanic(s.f.?() == 1234); +} diff --git a/test/stage1/behavior/bugs/1322.zig b/test/stage1/behavior/bugs/1322.zig new file mode 100644 index 0000000000..2e67f4473f --- /dev/null +++ b/test/stage1/behavior/bugs/1322.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +const B = union(enum) { + c: C, + None, +}; + +const A = struct { + b: B, +}; + +const C = struct {}; + +test "tagged union with all void fields but a meaningful tag" { + var a: A = A{ .b = B{ .c = C{} } }; + std.debug.assertOrPanic(@TagType(B)(a.b) == @TagType(B).c); + a = A{ .b = B.None }; + std.debug.assertOrPanic(@TagType(B)(a.b) == @TagType(B).None); +} diff --git a/test/stage1/behavior/bugs/1381.zig b/test/stage1/behavior/bugs/1381.zig new file mode 100644 index 0000000000..2d452da156 --- /dev/null +++ b/test/stage1/behavior/bugs/1381.zig @@ -0,0 +1,21 @@ +const std = @import("std"); + +const B = union(enum) { + D: u8, + E: u16, +}; + +const A = union(enum) { + B: B, + C: u8, +}; + +test "union that needs padding bytes inside an array" { + var as = []A{ + A{ .B = B{ .D = 1 } }, + A{ .B = B{ .D = 1 } }, + }; + + const a = as[0].B; + std.debug.assertOrPanic(a.D == 1); +} diff --git a/test/stage1/behavior/bugs/1421.zig b/test/stage1/behavior/bugs/1421.zig new file mode 100644 index 0000000000..fbc932781a --- /dev/null +++ b/test/stage1/behavior/bugs/1421.zig @@ -0,0 +1,14 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const assertOrPanic = std.debug.assertOrPanic; + +const S = struct { + fn method() builtin.TypeInfo { + return @typeInfo(S); + } +}; + +test "functions with return type required to be comptime are generic" { + const ti = S.method(); + assertOrPanic(builtin.TypeId(ti) == builtin.TypeId.Struct); +} diff --git a/test/stage1/behavior/bugs/1442.zig b/test/stage1/behavior/bugs/1442.zig new file mode 100644 index 0000000000..e9dfd5d2ce --- /dev/null +++ b/test/stage1/behavior/bugs/1442.zig @@ -0,0 +1,11 @@ +const std = @import("std"); + +const Union = union(enum) { + Text: []const u8, + Color: u32, +}; + +test "const error union field alignment" { + var union_or_err: anyerror!Union = Union{ .Color = 1234 }; + std.debug.assertOrPanic((union_or_err catch unreachable).Color == 1234); +} diff --git a/test/stage1/behavior/bugs/1486.zig b/test/stage1/behavior/bugs/1486.zig new file mode 100644 index 0000000000..0483e3828c --- /dev/null +++ b/test/stage1/behavior/bugs/1486.zig @@ -0,0 +1,11 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +const ptr = &global; +var global: u64 = 123; + +test "constant pointer to global variable causes runtime load" { + global = 1234; + assertOrPanic(&global == ptr); + assertOrPanic(ptr.* == 1234); +} + diff --git a/test/stage1/behavior/bugs/394.zig b/test/stage1/behavior/bugs/394.zig new file mode 100644 index 0000000000..766ad9e157 --- /dev/null +++ b/test/stage1/behavior/bugs/394.zig @@ -0,0 +1,18 @@ +const E = union(enum) { + A: [9]u8, + B: u64, +}; +const S = struct { + x: u8, + y: E, +}; + +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "bug 394 fixed" { + const x = S{ + .x = 3, + .y = E{ .B = 1 }, + }; + assertOrPanic(x.x == 3); +} diff --git a/test/stage1/behavior/bugs/655.zig b/test/stage1/behavior/bugs/655.zig new file mode 100644 index 0000000000..67ba6a231f --- /dev/null +++ b/test/stage1/behavior/bugs/655.zig @@ -0,0 +1,12 @@ +const std = @import("std"); +const other_file = @import("655_other_file.zig"); + +test "function with *const parameter with type dereferenced by namespace" { + const x: other_file.Integer = 1234; + comptime std.debug.assertOrPanic(@typeOf(&x) == *const other_file.Integer); + foo(&x); +} + +fn foo(x: *const other_file.Integer) void { + std.debug.assertOrPanic(x.* == 1234); +} diff --git a/test/stage1/behavior/bugs/655_other_file.zig b/test/stage1/behavior/bugs/655_other_file.zig new file mode 100644 index 0000000000..df1df44955 --- /dev/null +++ b/test/stage1/behavior/bugs/655_other_file.zig @@ -0,0 +1 @@ +pub const Integer = u32; diff --git a/test/stage1/behavior/bugs/656.zig b/test/stage1/behavior/bugs/656.zig new file mode 100644 index 0000000000..cb37fe67fe --- /dev/null +++ b/test/stage1/behavior/bugs/656.zig @@ -0,0 +1,31 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +const PrefixOp = union(enum) { + Return, + AddrOf: Value, +}; + +const Value = struct { + align_expr: ?u32, +}; + +test "optional if after an if in a switch prong of a switch with 2 prongs in an else" { + foo(false, true); +} + +fn foo(a: bool, b: bool) void { + var prefix_op = PrefixOp{ + .AddrOf = Value{ .align_expr = 1234 }, + }; + if (a) {} else { + switch (prefix_op) { + PrefixOp.AddrOf => |addr_of_info| { + if (b) {} + if (addr_of_info.align_expr) |align_expr| { + assertOrPanic(align_expr == 1234); + } + }, + PrefixOp.Return => {}, + } + } +} diff --git a/test/stage1/behavior/bugs/726.zig b/test/stage1/behavior/bugs/726.zig new file mode 100644 index 0000000000..ce20480c63 --- /dev/null +++ b/test/stage1/behavior/bugs/726.zig @@ -0,0 +1,16 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "@ptrCast from const to nullable" { + const c: u8 = 4; + var x: ?*const u8 = @ptrCast(?*const u8, &c); + assertOrPanic(x.?.* == 4); +} + +test "@ptrCast from var in empty struct to nullable" { + const container = struct { + var c: u8 = 4; + }; + var x: ?*const u8 = @ptrCast(?*const u8, &container.c); + assertOrPanic(x.?.* == 4); +} + diff --git a/test/stage1/behavior/bugs/828.zig b/test/stage1/behavior/bugs/828.zig new file mode 100644 index 0000000000..50ae0fd279 --- /dev/null +++ b/test/stage1/behavior/bugs/828.zig @@ -0,0 +1,33 @@ +const CountBy = struct { + a: usize, + + const One = CountBy{ .a = 1 }; + + pub fn counter(self: *const CountBy) Counter { + return Counter{ .i = 0 }; + } +}; + +const Counter = struct { + i: usize, + + pub fn count(self: *Counter) bool { + self.i += 1; + return self.i <= 10; + } +}; + +fn constCount(comptime cb: *const CountBy, comptime unused: u32) void { + comptime { + var cnt = cb.counter(); + if (cnt.i != 0) @compileError("Counter instance reused!"); + while (cnt.count()) {} + } +} + +test "comptime struct return should not return the same instance" { + //the first parameter must be passed by reference to trigger the bug + //a second parameter is required to trigger the bug + const ValA = constCount(&CountBy.One, 12); + const ValB = constCount(&CountBy.One, 15); +} diff --git a/test/stage1/behavior/bugs/920.zig b/test/stage1/behavior/bugs/920.zig new file mode 100644 index 0000000000..e29c5c4acf --- /dev/null +++ b/test/stage1/behavior/bugs/920.zig @@ -0,0 +1,65 @@ +const std = @import("std"); +const math = std.math; +const Random = std.rand.Random; + +const ZigTable = struct { + r: f64, + x: [257]f64, + f: [257]f64, + + pdf: fn (f64) f64, + is_symmetric: bool, + zero_case: fn (*Random, f64) f64, +}; + +fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn (f64) f64, comptime f_inv: fn (f64) f64, comptime zero_case: fn (*Random, f64) f64) ZigTable { + var tables: ZigTable = undefined; + + tables.is_symmetric = is_symmetric; + tables.r = r; + tables.pdf = f; + tables.zero_case = zero_case; + + tables.x[0] = v / f(r); + tables.x[1] = r; + + for (tables.x[2..256]) |*entry, i| { + const last = tables.x[2 + i - 1]; + entry.* = f_inv(v / last + f(last)); + } + tables.x[256] = 0; + + for (tables.f[0..]) |*entry, i| { + entry.* = f(tables.x[i]); + } + + return tables; +} + +const norm_r = 3.6541528853610088; +const norm_v = 0.00492867323399; + +fn norm_f(x: f64) f64 { + return math.exp(-x * x / 2.0); +} +fn norm_f_inv(y: f64) f64 { + return math.sqrt(-2.0 * math.ln(y)); +} +fn norm_zero_case(random: *Random, u: f64) f64 { + return 0.0; +} + +const NormalDist = blk: { + @setEvalBranchQuota(30000); + break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case); +}; + +test "bug 920 fixed" { + const NormalDist1 = blk: { + break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case); + }; + + for (NormalDist1.f) |_, i| { + std.debug.assertOrPanic(NormalDist1.f[i] == NormalDist.f[i]); + } +} diff --git a/test/stage1/behavior/byval_arg_var.zig b/test/stage1/behavior/byval_arg_var.zig new file mode 100644 index 0000000000..14ee212ce0 --- /dev/null +++ b/test/stage1/behavior/byval_arg_var.zig @@ -0,0 +1,27 @@ +const std = @import("std"); + +var result: []const u8 = "wrong"; + +test "pass string literal byvalue to a generic var param" { + start(); + blowUpStack(10); + + std.debug.assertOrPanic(std.mem.eql(u8, result, "string literal")); +} + +fn start() void { + foo("string literal"); +} + +fn foo(x: var) void { + bar(x); +} + +fn bar(x: var) void { + result = x; +} + +fn blowUpStack(x: u32) void { + if (x == 0) return; + blowUpStack(x - 1); +} diff --git a/test/stage1/behavior/cancel.zig b/test/stage1/behavior/cancel.zig new file mode 100644 index 0000000000..863da4bdb8 --- /dev/null +++ b/test/stage1/behavior/cancel.zig @@ -0,0 +1,92 @@ +const std = @import("std"); + +var defer_f1: bool = false; +var defer_f2: bool = false; +var defer_f3: bool = false; + +test "cancel forwards" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const p = async<&da.allocator> f1() catch unreachable; + cancel p; + std.debug.assertOrPanic(defer_f1); + std.debug.assertOrPanic(defer_f2); + std.debug.assertOrPanic(defer_f3); +} + +async fn f1() void { + defer { + defer_f1 = true; + } + await (async f2() catch unreachable); +} + +async fn f2() void { + defer { + defer_f2 = true; + } + await (async f3() catch unreachable); +} + +async fn f3() void { + defer { + defer_f3 = true; + } + suspend; +} + +var defer_b1: bool = false; +var defer_b2: bool = false; +var defer_b3: bool = false; +var defer_b4: bool = false; + +test "cancel backwards" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const p = async<&da.allocator> b1() catch unreachable; + cancel p; + std.debug.assertOrPanic(defer_b1); + std.debug.assertOrPanic(defer_b2); + std.debug.assertOrPanic(defer_b3); + std.debug.assertOrPanic(defer_b4); +} + +async fn b1() void { + defer { + defer_b1 = true; + } + await (async b2() catch unreachable); +} + +var b4_handle: promise = undefined; + +async fn b2() void { + const b3_handle = async b3() catch unreachable; + resume b4_handle; + cancel b4_handle; + defer { + defer_b2 = true; + } + const value = await b3_handle; + @panic("unreachable"); +} + +async fn b3() i32 { + defer { + defer_b3 = true; + } + await (async b4() catch unreachable); + return 1234; +} + +async fn b4() void { + defer { + defer_b4 = true; + } + suspend { + b4_handle = @handle(); + } + suspend; +} diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig new file mode 100644 index 0000000000..61ddcd8135 --- /dev/null +++ b/test/stage1/behavior/cast.zig @@ -0,0 +1,473 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const mem = std.mem; +const maxInt = std.math.maxInt; + +test "int to ptr cast" { + const x = usize(13); + const y = @intToPtr(*u8, x); + const z = @ptrToInt(y); + assertOrPanic(z == 13); +} + +test "integer literal to pointer cast" { + const vga_mem = @intToPtr(*u16, 0xB8000); + assertOrPanic(@ptrToInt(vga_mem) == 0xB8000); +} + +test "pointer reinterpret const float to int" { + const float: f64 = 5.99999999999994648725e-01; + const float_ptr = &float; + const int_ptr = @ptrCast(*const i32, float_ptr); + const int_val = int_ptr.*; + assertOrPanic(int_val == 858993411); +} + +test "implicitly cast indirect pointer to maybe-indirect pointer" { + const S = struct { + const Self = @This(); + x: u8, + fn constConst(p: *const *const Self) u8 { + return p.*.x; + } + fn maybeConstConst(p: ?*const *const Self) u8 { + return p.?.*.x; + } + fn constConstConst(p: *const *const *const Self) u8 { + return p.*.*.x; + } + fn maybeConstConstConst(p: ?*const *const *const Self) u8 { + return p.?.*.*.x; + } + }; + const s = S{ .x = 42 }; + const p = &s; + const q = &p; + const r = &q; + assertOrPanic(42 == S.constConst(q)); + assertOrPanic(42 == S.maybeConstConst(q)); + assertOrPanic(42 == S.constConstConst(r)); + assertOrPanic(42 == S.maybeConstConstConst(r)); +} + +test "explicit cast from integer to error type" { + testCastIntToErr(error.ItBroke); + comptime testCastIntToErr(error.ItBroke); +} +fn testCastIntToErr(err: anyerror) void { + const x = @errorToInt(err); + const y = @intToError(x); + assertOrPanic(error.ItBroke == y); +} + +test "peer resolve arrays of different size to const slice" { + assertOrPanic(mem.eql(u8, boolToStr(true), "true")); + assertOrPanic(mem.eql(u8, boolToStr(false), "false")); + comptime assertOrPanic(mem.eql(u8, boolToStr(true), "true")); + comptime assertOrPanic(mem.eql(u8, boolToStr(false), "false")); +} +fn boolToStr(b: bool) []const u8 { + return if (b) "true" else "false"; +} + +test "peer resolve array and const slice" { + testPeerResolveArrayConstSlice(true); + comptime testPeerResolveArrayConstSlice(true); +} +fn testPeerResolveArrayConstSlice(b: bool) void { + const value1 = if (b) "aoeu" else ([]const u8)("zz"); + const value2 = if (b) ([]const u8)("zz") else "aoeu"; + assertOrPanic(mem.eql(u8, value1, "aoeu")); + assertOrPanic(mem.eql(u8, value2, "zz")); +} + +test "implicitly cast from T to anyerror!?T" { + castToOptionalTypeError(1); + comptime castToOptionalTypeError(1); +} + +const A = struct { + a: i32, +}; +fn castToOptionalTypeError(z: i32) void { + const x = i32(1); + const y: anyerror!?i32 = x; + assertOrPanic((try y).? == 1); + + const f = z; + const g: anyerror!?i32 = f; + + const a = A{ .a = z }; + const b: anyerror!?A = a; + assertOrPanic((b catch unreachable).?.a == 1); +} + +test "implicitly cast from int to anyerror!?T" { + implicitIntLitToOptional(); + comptime implicitIntLitToOptional(); +} +fn implicitIntLitToOptional() void { + const f: ?i32 = 1; + const g: anyerror!?i32 = 1; +} + +test "return null from fn() anyerror!?&T" { + const a = returnNullFromOptionalTypeErrorRef(); + const b = returnNullLitFromOptionalTypeErrorRef(); + assertOrPanic((try a) == null and (try b) == null); +} +fn returnNullFromOptionalTypeErrorRef() anyerror!?*A { + const a: ?*A = null; + return a; +} +fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A { + return null; +} + +test "peer type resolution: ?T and T" { + assertOrPanic(peerTypeTAndOptionalT(true, false).? == 0); + assertOrPanic(peerTypeTAndOptionalT(false, false).? == 3); + comptime { + assertOrPanic(peerTypeTAndOptionalT(true, false).? == 0); + assertOrPanic(peerTypeTAndOptionalT(false, false).? == 3); + } +} +fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { + if (c) { + return if (b) null else usize(0); + } + + return usize(3); +} + +test "peer type resolution: [0]u8 and []const u8" { + assertOrPanic(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); + assertOrPanic(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); + comptime { + assertOrPanic(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); + assertOrPanic(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); + } +} +fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { + if (a) { + return []const u8{}; + } + + return slice[0..1]; +} + +test "implicitly cast from [N]T to ?[]const T" { + assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); + comptime assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); +} + +fn castToOptionalSlice() ?[]const u8 { + return "hi"; +} + +test "implicitly cast from [0]T to anyerror![]T" { + testCastZeroArrayToErrSliceMut(); + comptime testCastZeroArrayToErrSliceMut(); +} + +fn testCastZeroArrayToErrSliceMut() void { + assertOrPanic((gimmeErrOrSlice() catch unreachable).len == 0); +} + +fn gimmeErrOrSlice() anyerror![]u8 { + return []u8{}; +} + +test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" { + { + var data = "hi"; + const slice = data[0..]; + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); + } + comptime { + var data = "hi"; + const slice = data[0..]; + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); + } +} +fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 { + if (a) { + return []u8{}; + } + + return slice[0..1]; +} + +test "resolve undefined with integer" { + testResolveUndefWithInt(true, 1234); + comptime testResolveUndefWithInt(true, 1234); +} +fn testResolveUndefWithInt(b: bool, x: i32) void { + const value = if (b) x else undefined; + if (b) { + assertOrPanic(value == x); + } +} + +test "implicit cast from &const [N]T to []const T" { + testCastConstArrayRefToConstSlice(); + comptime testCastConstArrayRefToConstSlice(); +} + +fn testCastConstArrayRefToConstSlice() void { + const blah = "aoeu"; + const const_array_ref = &blah; + assertOrPanic(@typeOf(const_array_ref) == *const [4]u8); + const slice: []const u8 = const_array_ref; + assertOrPanic(mem.eql(u8, slice, "aoeu")); +} + +test "peer type resolution: error and [N]T" { + // TODO: implicit error!T to error!U where T can implicitly cast to U + //assertOrPanic(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); + //comptime assertOrPanic(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); + assertOrPanic(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); + comptime assertOrPanic(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); +} + +//fn testPeerErrorAndArray(x: u8) error![]const u8 { +// return switch (x) { +// 0x00 => "OK", +// else => error.BadValue, +// }; +//} +fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 { + return switch (x) { + 0x00 => "OK", + 0x01 => "OKK", + else => error.BadValue, + }; +} + +test "@floatToInt" { + testFloatToInts(); + comptime testFloatToInts(); +} + +fn testFloatToInts() void { + const x = i32(1e4); + assertOrPanic(x == 10000); + const y = @floatToInt(i32, f32(1e4)); + assertOrPanic(y == 10000); + expectFloatToInt(f16, 255.1, u8, 255); + expectFloatToInt(f16, 127.2, i8, 127); + expectFloatToInt(f16, -128.2, i8, -128); + expectFloatToInt(f32, 255.1, u8, 255); + expectFloatToInt(f32, 127.2, i8, 127); + expectFloatToInt(f32, -128.2, i8, -128); + expectFloatToInt(comptime_int, 1234, i16, 1234); +} + +fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) void { + assertOrPanic(@floatToInt(I, f) == i); +} + +test "cast u128 to f128 and back" { + comptime testCast128(); + testCast128(); +} + +fn testCast128() void { + assertOrPanic(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); +} + +fn cast128Int(x: f128) u128 { + return @bitCast(u128, x); +} + +fn cast128Float(x: u128) f128 { + return @bitCast(f128, x); +} + +test "const slice widen cast" { + const bytes align(4) = []u8{ + 0x12, + 0x12, + 0x12, + 0x12, + }; + + const u32_value = @bytesToSlice(u32, bytes[0..])[0]; + assertOrPanic(u32_value == 0x12121212); + + assertOrPanic(@bitCast(u32, bytes) == 0x12121212); +} + +test "single-item pointer of array to slice and to unknown length pointer" { + testCastPtrOfArrayToSliceAndPtr(); + comptime testCastPtrOfArrayToSliceAndPtr(); +} + +fn testCastPtrOfArrayToSliceAndPtr() void { + var array = "aoeu"; + const x: [*]u8 = &array; + x[0] += 1; + assertOrPanic(mem.eql(u8, array[0..], "boeu")); + const y: []u8 = &array; + y[0] += 1; + assertOrPanic(mem.eql(u8, array[0..], "coeu")); +} + +test "cast *[1][*]const u8 to [*]const ?[*]const u8" { + const window_name = [1][*]const u8{c"window name"}; + const x: [*]const ?[*]const u8 = &window_name; + assertOrPanic(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); +} + +test "@intCast comptime_int" { + const result = @intCast(i32, 1234); + assertOrPanic(@typeOf(result) == i32); + assertOrPanic(result == 1234); +} + +test "@floatCast comptime_int and comptime_float" { + { + const result = @floatCast(f16, 1234); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); + } + { + const result = @floatCast(f16, 1234.0); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); + } + { + const result = @floatCast(f32, 1234); + assertOrPanic(@typeOf(result) == f32); + assertOrPanic(result == 1234.0); + } + { + const result = @floatCast(f32, 1234.0); + assertOrPanic(@typeOf(result) == f32); + assertOrPanic(result == 1234.0); + } +} + +test "comptime_int @intToFloat" { + { + const result = @intToFloat(f16, 1234); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); + } + { + const result = @intToFloat(f32, 1234); + assertOrPanic(@typeOf(result) == f32); + assertOrPanic(result == 1234.0); + } +} + +test "@bytesToSlice keeps pointer alignment" { + var bytes = []u8{ 0x01, 0x02, 0x03, 0x04 }; + const numbers = @bytesToSlice(u32, bytes[0..]); + comptime assertOrPanic(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); +} + +test "@intCast i32 to u7" { + var x: u128 = maxInt(u128); + var y: i32 = 120; + var z = x >> @intCast(u7, y); + assertOrPanic(z == 0xff); +} + +test "implicit cast undefined to optional" { + assertOrPanic(MakeType(void).getNull() == null); + assertOrPanic(MakeType(void).getNonNull() != null); +} + +fn MakeType(comptime T: type) type { + return struct { + fn getNull() ?T { + return null; + } + + fn getNonNull() ?T { + return T(undefined); + } + }; +} + +test "implicit cast from *[N]T to ?[*]T" { + var x: ?[*]u16 = null; + var y: [4]u16 = [4]u16{ 0, 1, 2, 3 }; + + x = &y; + assertOrPanic(std.mem.eql(u16, x.?[0..4], y[0..4])); + x.?[0] = 8; + y[3] = 6; + assertOrPanic(std.mem.eql(u16, x.?[0..4], y[0..4])); +} + +test "implicit cast from *T to ?*c_void" { + var a: u8 = 1; + incrementVoidPtrValue(&a); + std.debug.assertOrPanic(a == 2); +} + +fn incrementVoidPtrValue(value: ?*c_void) void { + @ptrCast(*u8, value.?).* += 1; +} + +test "implicit cast from [*]T to ?*c_void" { + var a = []u8{ 3, 2, 1 }; + incrementVoidPtrArray(a[0..].ptr, 3); + assertOrPanic(std.mem.eql(u8, a, []u8{ 4, 3, 2 })); +} + +fn incrementVoidPtrArray(array: ?*c_void, len: usize) void { + var n: usize = 0; + while (n < len) : (n += 1) { + @ptrCast([*]u8, array.?)[n] += 1; + } +} + +test "*usize to *void" { + var i = usize(0); + var v = @ptrCast(*void, &i); + v.* = {}; +} + +test "compile time int to ptr of function" { + foobar(FUNCTION_CONSTANT); +} + +pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize)); +pub const PFN_void = extern fn (*c_void) void; + +fn foobar(func: PFN_void) void { + std.debug.assertOrPanic(@ptrToInt(func) == maxInt(usize)); +} + +test "implicit ptr to *c_void" { + var a: u32 = 1; + var ptr: *c_void = &a; + var b: *u32 = @ptrCast(*u32, ptr); + assertOrPanic(b.* == 1); + var ptr2: ?*c_void = &a; + var c: *u32 = @ptrCast(*u32, ptr2.?); + assertOrPanic(c.* == 1); +} + +test "@intCast to comptime_int" { + assertOrPanic(@intCast(comptime_int, 0) == 0); +} + +test "implicit cast comptime numbers to any type when the value fits" { + const a: u64 = 255; + var b: u8 = a; + assertOrPanic(b == 255); +} + +test "@intToEnum passed a comptime_int to an enum with one item" { + const E = enum { + A, + }; + const x = @intToEnum(E, 0); + assertOrPanic(x == E.A); +} diff --git a/test/stage1/behavior/const_slice_child.zig b/test/stage1/behavior/const_slice_child.zig new file mode 100644 index 0000000000..5b9b70a558 --- /dev/null +++ b/test/stage1/behavior/const_slice_child.zig @@ -0,0 +1,45 @@ +const debug = @import("std").debug; +const assertOrPanic = debug.assertOrPanic; + +var argv: [*]const [*]const u8 = undefined; + +test "const slice child" { + const strs = ([][*]const u8){ + c"one", + c"two", + c"three", + }; + // TODO this should implicitly cast + argv = @ptrCast([*]const [*]const u8, &strs); + bar(strs.len); +} + +fn foo(args: [][]const u8) void { + assertOrPanic(args.len == 3); + assertOrPanic(streql(args[0], "one")); + assertOrPanic(streql(args[1], "two")); + assertOrPanic(streql(args[2], "three")); +} + +fn bar(argc: usize) void { + const args = debug.global_allocator.alloc([]const u8, argc) catch unreachable; + for (args) |_, i| { + const ptr = argv[i]; + args[i] = ptr[0..strlen(ptr)]; + } + foo(args); +} + +fn strlen(ptr: [*]const u8) usize { + var count: usize = 0; + while (ptr[count] != 0) : (count += 1) {} + return count; +} + +fn streql(a: []const u8, b: []const u8) bool { + if (a.len != b.len) return false; + for (a) |item, index| { + if (b[index] != item) return false; + } + return true; +} diff --git a/test/stage1/behavior/coroutine_await_struct.zig b/test/stage1/behavior/coroutine_await_struct.zig new file mode 100644 index 0000000000..6ca2a301ec --- /dev/null +++ b/test/stage1/behavior/coroutine_await_struct.zig @@ -0,0 +1,47 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const assertOrPanic = std.debug.assertOrPanic; + +const Foo = struct { + x: i32, +}; + +var await_a_promise: promise = undefined; +var await_final_result = Foo{ .x = 0 }; + +test "coroutine await struct" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + await_seq('a'); + const p = async<&da.allocator> await_amain() catch unreachable; + await_seq('f'); + resume await_a_promise; + await_seq('i'); + assertOrPanic(await_final_result.x == 1234); + assertOrPanic(std.mem.eql(u8, await_points, "abcdefghi")); +} +async fn await_amain() void { + await_seq('b'); + const p = async await_another() catch unreachable; + await_seq('e'); + await_final_result = await p; + await_seq('h'); +} +async fn await_another() Foo { + await_seq('c'); + suspend { + await_seq('d'); + await_a_promise = @handle(); + } + await_seq('g'); + return Foo{ .x = 1234 }; +} + +var await_points = []u8{0} ** "abcdefghi".len; +var await_seq_index: usize = 0; + +fn await_seq(c: u8) void { + await_points[await_seq_index] = c; + await_seq_index += 1; +} diff --git a/test/stage1/behavior/coroutines.zig b/test/stage1/behavior/coroutines.zig new file mode 100644 index 0000000000..a2327c5060 --- /dev/null +++ b/test/stage1/behavior/coroutines.zig @@ -0,0 +1,258 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const assertOrPanic = std.debug.assertOrPanic; + +var x: i32 = 1; + +test "create a coroutine and cancel it" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const p = try async<&da.allocator> simpleAsyncFn(); + comptime assertOrPanic(@typeOf(p) == promise->void); + cancel p; + assertOrPanic(x == 2); +} +async fn simpleAsyncFn() void { + x += 1; + suspend; + x += 1; +} + +test "coroutine suspend, resume, cancel" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + seq('a'); + const p = try async<&da.allocator> testAsyncSeq(); + seq('c'); + resume p; + seq('f'); + cancel p; + seq('g'); + + assertOrPanic(std.mem.eql(u8, points, "abcdefg")); +} +async fn testAsyncSeq() void { + defer seq('e'); + + seq('b'); + suspend; + seq('d'); +} +var points = []u8{0} ** "abcdefg".len; +var index: usize = 0; + +fn seq(c: u8) void { + points[index] = c; + index += 1; +} + +test "coroutine suspend with block" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const p = try async<&da.allocator> testSuspendBlock(); + std.debug.assertOrPanic(!result); + resume a_promise; + std.debug.assertOrPanic(result); + cancel p; +} + +var a_promise: promise = undefined; +var result = false; +async fn testSuspendBlock() void { + suspend { + comptime assertOrPanic(@typeOf(@handle()) == promise->void); + a_promise = @handle(); + } + + //Test to make sure that @handle() works as advertised (issue #1296) + //var our_handle: promise = @handle(); + assertOrPanic(a_promise == @handle()); + + result = true; +} + +var await_a_promise: promise = undefined; +var await_final_result: i32 = 0; + +test "coroutine await" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + await_seq('a'); + const p = async<&da.allocator> await_amain() catch unreachable; + await_seq('f'); + resume await_a_promise; + await_seq('i'); + assertOrPanic(await_final_result == 1234); + assertOrPanic(std.mem.eql(u8, await_points, "abcdefghi")); +} +async fn await_amain() void { + await_seq('b'); + const p = async await_another() catch unreachable; + await_seq('e'); + await_final_result = await p; + await_seq('h'); +} +async fn await_another() i32 { + await_seq('c'); + suspend { + await_seq('d'); + await_a_promise = @handle(); + } + await_seq('g'); + return 1234; +} + +var await_points = []u8{0} ** "abcdefghi".len; +var await_seq_index: usize = 0; + +fn await_seq(c: u8) void { + await_points[await_seq_index] = c; + await_seq_index += 1; +} + +var early_final_result: i32 = 0; + +test "coroutine await early return" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + early_seq('a'); + const p = async<&da.allocator> early_amain() catch @panic("out of memory"); + early_seq('f'); + assertOrPanic(early_final_result == 1234); + assertOrPanic(std.mem.eql(u8, early_points, "abcdef")); +} +async fn early_amain() void { + early_seq('b'); + const p = async early_another() catch @panic("out of memory"); + early_seq('d'); + early_final_result = await p; + early_seq('e'); +} +async fn early_another() i32 { + early_seq('c'); + return 1234; +} + +var early_points = []u8{0} ** "abcdef".len; +var early_seq_index: usize = 0; + +fn early_seq(c: u8) void { + early_points[early_seq_index] = c; + early_seq_index += 1; +} + +test "coro allocation failure" { + var failing_allocator = std.debug.FailingAllocator.init(std.debug.global_allocator, 0); + if (async<&failing_allocator.allocator> asyncFuncThatNeverGetsRun()) { + @panic("expected allocation failure"); + } else |err| switch (err) { + error.OutOfMemory => {}, + } +} +async fn asyncFuncThatNeverGetsRun() void { + @panic("coro frame allocation should fail"); +} + +test "async function with dot syntax" { + const S = struct { + var y: i32 = 1; + async fn foo() void { + y += 1; + suspend; + } + }; + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + const p = try async<&da.allocator> S.foo(); + cancel p; + assertOrPanic(S.y == 2); +} + +test "async fn pointer in a struct field" { + var data: i32 = 1; + const Foo = struct { + bar: async<*std.mem.Allocator> fn (*i32) void, + }; + var foo = Foo{ .bar = simpleAsyncFn2 }; + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + const p = (async<&da.allocator> foo.bar(&data)) catch unreachable; + assertOrPanic(data == 2); + cancel p; + assertOrPanic(data == 4); +} +async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void { + defer y.* += 2; + y.* += 1; + suspend; +} + +test "async fn with inferred error set" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + const p = (async<&da.allocator> failing()) catch unreachable; + resume p; + cancel p; +} + +async fn failing() !void { + suspend; + return error.Fail; +} + +test "error return trace across suspend points - early return" { + const p = nonFailing(); + resume p; + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + const p2 = try async<&da.allocator> printTrace(p); + cancel p2; +} + +test "error return trace across suspend points - async return" { + const p = nonFailing(); + const p2 = try async printTrace(p); + resume p; + cancel p2; +} + +fn nonFailing() (promise->anyerror!void) { + return async suspendThenFail() catch unreachable; +} +async fn suspendThenFail() anyerror!void { + suspend; + return error.Fail; +} +async fn printTrace(p: promise->(anyerror!void)) void { + (await p) catch |e| { + std.debug.assertOrPanic(e == error.Fail); + if (@errorReturnTrace()) |trace| { + assertOrPanic(trace.index == 1); + } else switch (builtin.mode) { + builtin.Mode.Debug, builtin.Mode.ReleaseSafe => @panic("expected return trace"), + builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => {}, + } + }; +} + +test "break from suspend" { + var buf: [500]u8 = undefined; + var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; + var my_result: i32 = 1; + const p = try async testBreakFromSuspend(&my_result); + cancel p; + std.debug.assertOrPanic(my_result == 2); +} +async fn testBreakFromSuspend(my_result: *i32) void { + suspend { + resume @handle(); + } + my_result.* += 1; + suspend; + my_result.* += 1; +} diff --git a/test/stage1/behavior/defer.zig b/test/stage1/behavior/defer.zig new file mode 100644 index 0000000000..6c6c60311e --- /dev/null +++ b/test/stage1/behavior/defer.zig @@ -0,0 +1,78 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +var result: [3]u8 = undefined; +var index: usize = undefined; + +fn runSomeErrorDefers(x: bool) !bool { + index = 0; + defer { + result[index] = 'a'; + index += 1; + } + errdefer { + result[index] = 'b'; + index += 1; + } + defer { + result[index] = 'c'; + index += 1; + } + return if (x) x else error.FalseNotAllowed; +} + +test "mixing normal and error defers" { + assertOrPanic(runSomeErrorDefers(true) catch unreachable); + assertOrPanic(result[0] == 'c'); + assertOrPanic(result[1] == 'a'); + + const ok = runSomeErrorDefers(false) catch |err| x: { + assertOrPanic(err == error.FalseNotAllowed); + break :x true; + }; + assertOrPanic(ok); + assertOrPanic(result[0] == 'c'); + assertOrPanic(result[1] == 'b'); + assertOrPanic(result[2] == 'a'); +} + +test "break and continue inside loop inside defer expression" { + testBreakContInDefer(10); + comptime testBreakContInDefer(10); +} + +fn testBreakContInDefer(x: usize) void { + defer { + var i: usize = 0; + while (i < x) : (i += 1) { + if (i < 5) continue; + if (i == 5) break; + } + assertOrPanic(i == 5); + } +} + +test "defer and labeled break" { + var i = usize(0); + + blk: { + defer i += 1; + break :blk; + } + + assertOrPanic(i == 1); +} + +test "errdefer does not apply to fn inside fn" { + if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assertOrPanic(e == error.Bad); +} + +fn testNestedFnErrDefer() anyerror!void { + var a: i32 = 0; + errdefer a += 1; + const S = struct { + fn baz() anyerror { + return error.Bad; + } + }; + return S.baz(); +} diff --git a/test/stage1/behavior/enum.zig b/test/stage1/behavior/enum.zig new file mode 100644 index 0000000000..9de138ef78 --- /dev/null +++ b/test/stage1/behavior/enum.zig @@ -0,0 +1,894 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const mem = @import("std").mem; + +test "enum type" { + const foo1 = Foo{ .One = 13 }; + const foo2 = Foo{ + .Two = Point{ + .x = 1234, + .y = 5678, + }, + }; + const bar = Bar.B; + + assertOrPanic(bar == Bar.B); + assertOrPanic(@memberCount(Foo) == 3); + assertOrPanic(@memberCount(Bar) == 4); + assertOrPanic(@sizeOf(Foo) == @sizeOf(FooNoVoid)); + assertOrPanic(@sizeOf(Bar) == 1); +} + +test "enum as return value" { + switch (returnAnInt(13)) { + Foo.One => |value| assertOrPanic(value == 13), + else => unreachable, + } +} + +const Point = struct { + x: u64, + y: u64, +}; +const Foo = union(enum) { + One: i32, + Two: Point, + Three: void, +}; +const FooNoVoid = union(enum) { + One: i32, + Two: Point, +}; +const Bar = enum { + A, + B, + C, + D, +}; + +fn returnAnInt(x: i32) Foo { + return Foo{ .One = x }; +} + +test "constant enum with payload" { + var empty = AnEnumWithPayload{ .Empty = {} }; + var full = AnEnumWithPayload{ .Full = 13 }; + shouldBeEmpty(empty); + shouldBeNotEmpty(full); +} + +fn shouldBeEmpty(x: AnEnumWithPayload) void { + switch (x) { + AnEnumWithPayload.Empty => {}, + else => unreachable, + } +} + +fn shouldBeNotEmpty(x: AnEnumWithPayload) void { + switch (x) { + AnEnumWithPayload.Empty => unreachable, + else => {}, + } +} + +const AnEnumWithPayload = union(enum) { + Empty: void, + Full: i32, +}; + +const Number = enum { + Zero, + One, + Two, + Three, + Four, +}; + +test "enum to int" { + shouldEqual(Number.Zero, 0); + shouldEqual(Number.One, 1); + shouldEqual(Number.Two, 2); + shouldEqual(Number.Three, 3); + shouldEqual(Number.Four, 4); +} + +fn shouldEqual(n: Number, expected: u3) void { + assertOrPanic(@enumToInt(n) == expected); +} + +test "int to enum" { + testIntToEnumEval(3); +} +fn testIntToEnumEval(x: i32) void { + assertOrPanic(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three); +} +const IntToEnumNumber = enum { + Zero, + One, + Two, + Three, + Four, +}; + +test "@tagName" { + assertOrPanic(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); + comptime assertOrPanic(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); +} + +fn testEnumTagNameBare(n: BareNumber) []const u8 { + return @tagName(n); +} + +const BareNumber = enum { + One, + Two, + Three, +}; + +test "enum alignment" { + comptime { + assertOrPanic(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); + assertOrPanic(@alignOf(AlignTestEnum) >= @alignOf(u64)); + } +} + +const AlignTestEnum = union(enum) { + A: [9]u8, + B: u64, +}; + +const ValueCount1 = enum { + I0, +}; +const ValueCount2 = enum { + I0, + I1, +}; +const ValueCount256 = enum { + I0, + I1, + I2, + I3, + I4, + I5, + I6, + I7, + I8, + I9, + I10, + I11, + I12, + I13, + I14, + I15, + I16, + I17, + I18, + I19, + I20, + I21, + I22, + I23, + I24, + I25, + I26, + I27, + I28, + I29, + I30, + I31, + I32, + I33, + I34, + I35, + I36, + I37, + I38, + I39, + I40, + I41, + I42, + I43, + I44, + I45, + I46, + I47, + I48, + I49, + I50, + I51, + I52, + I53, + I54, + I55, + I56, + I57, + I58, + I59, + I60, + I61, + I62, + I63, + I64, + I65, + I66, + I67, + I68, + I69, + I70, + I71, + I72, + I73, + I74, + I75, + I76, + I77, + I78, + I79, + I80, + I81, + I82, + I83, + I84, + I85, + I86, + I87, + I88, + I89, + I90, + I91, + I92, + I93, + I94, + I95, + I96, + I97, + I98, + I99, + I100, + I101, + I102, + I103, + I104, + I105, + I106, + I107, + I108, + I109, + I110, + I111, + I112, + I113, + I114, + I115, + I116, + I117, + I118, + I119, + I120, + I121, + I122, + I123, + I124, + I125, + I126, + I127, + I128, + I129, + I130, + I131, + I132, + I133, + I134, + I135, + I136, + I137, + I138, + I139, + I140, + I141, + I142, + I143, + I144, + I145, + I146, + I147, + I148, + I149, + I150, + I151, + I152, + I153, + I154, + I155, + I156, + I157, + I158, + I159, + I160, + I161, + I162, + I163, + I164, + I165, + I166, + I167, + I168, + I169, + I170, + I171, + I172, + I173, + I174, + I175, + I176, + I177, + I178, + I179, + I180, + I181, + I182, + I183, + I184, + I185, + I186, + I187, + I188, + I189, + I190, + I191, + I192, + I193, + I194, + I195, + I196, + I197, + I198, + I199, + I200, + I201, + I202, + I203, + I204, + I205, + I206, + I207, + I208, + I209, + I210, + I211, + I212, + I213, + I214, + I215, + I216, + I217, + I218, + I219, + I220, + I221, + I222, + I223, + I224, + I225, + I226, + I227, + I228, + I229, + I230, + I231, + I232, + I233, + I234, + I235, + I236, + I237, + I238, + I239, + I240, + I241, + I242, + I243, + I244, + I245, + I246, + I247, + I248, + I249, + I250, + I251, + I252, + I253, + I254, + I255, +}; +const ValueCount257 = enum { + I0, + I1, + I2, + I3, + I4, + I5, + I6, + I7, + I8, + I9, + I10, + I11, + I12, + I13, + I14, + I15, + I16, + I17, + I18, + I19, + I20, + I21, + I22, + I23, + I24, + I25, + I26, + I27, + I28, + I29, + I30, + I31, + I32, + I33, + I34, + I35, + I36, + I37, + I38, + I39, + I40, + I41, + I42, + I43, + I44, + I45, + I46, + I47, + I48, + I49, + I50, + I51, + I52, + I53, + I54, + I55, + I56, + I57, + I58, + I59, + I60, + I61, + I62, + I63, + I64, + I65, + I66, + I67, + I68, + I69, + I70, + I71, + I72, + I73, + I74, + I75, + I76, + I77, + I78, + I79, + I80, + I81, + I82, + I83, + I84, + I85, + I86, + I87, + I88, + I89, + I90, + I91, + I92, + I93, + I94, + I95, + I96, + I97, + I98, + I99, + I100, + I101, + I102, + I103, + I104, + I105, + I106, + I107, + I108, + I109, + I110, + I111, + I112, + I113, + I114, + I115, + I116, + I117, + I118, + I119, + I120, + I121, + I122, + I123, + I124, + I125, + I126, + I127, + I128, + I129, + I130, + I131, + I132, + I133, + I134, + I135, + I136, + I137, + I138, + I139, + I140, + I141, + I142, + I143, + I144, + I145, + I146, + I147, + I148, + I149, + I150, + I151, + I152, + I153, + I154, + I155, + I156, + I157, + I158, + I159, + I160, + I161, + I162, + I163, + I164, + I165, + I166, + I167, + I168, + I169, + I170, + I171, + I172, + I173, + I174, + I175, + I176, + I177, + I178, + I179, + I180, + I181, + I182, + I183, + I184, + I185, + I186, + I187, + I188, + I189, + I190, + I191, + I192, + I193, + I194, + I195, + I196, + I197, + I198, + I199, + I200, + I201, + I202, + I203, + I204, + I205, + I206, + I207, + I208, + I209, + I210, + I211, + I212, + I213, + I214, + I215, + I216, + I217, + I218, + I219, + I220, + I221, + I222, + I223, + I224, + I225, + I226, + I227, + I228, + I229, + I230, + I231, + I232, + I233, + I234, + I235, + I236, + I237, + I238, + I239, + I240, + I241, + I242, + I243, + I244, + I245, + I246, + I247, + I248, + I249, + I250, + I251, + I252, + I253, + I254, + I255, + I256, +}; + +test "enum sizes" { + comptime { + assertOrPanic(@sizeOf(ValueCount1) == 0); + assertOrPanic(@sizeOf(ValueCount2) == 1); + assertOrPanic(@sizeOf(ValueCount256) == 1); + assertOrPanic(@sizeOf(ValueCount257) == 2); + } +} + +const Small2 = enum(u2) { + One, + Two, +}; +const Small = enum(u2) { + One, + Two, + Three, + Four, +}; + +test "set enum tag type" { + { + var x = Small.One; + x = Small.Two; + comptime assertOrPanic(@TagType(Small) == u2); + } + { + var x = Small2.One; + x = Small2.Two; + comptime assertOrPanic(@TagType(Small2) == u2); + } +} + +const A = enum(u3) { + One, + Two, + Three, + Four, + One2, + Two2, + Three2, + Four2, +}; + +const B = enum(u3) { + One3, + Two3, + Three3, + Four3, + One23, + Two23, + Three23, + Four23, +}; + +const C = enum(u2) { + One4, + Two4, + Three4, + Four4, +}; + +const BitFieldOfEnums = packed struct { + a: A, + b: B, + c: C, +}; + +const bit_field_1 = BitFieldOfEnums{ + .a = A.Two, + .b = B.Three3, + .c = C.Four4, +}; + +test "bit field access with enum fields" { + var data = bit_field_1; + assertOrPanic(getA(&data) == A.Two); + assertOrPanic(getB(&data) == B.Three3); + assertOrPanic(getC(&data) == C.Four4); + comptime assertOrPanic(@sizeOf(BitFieldOfEnums) == 1); + + data.b = B.Four3; + assertOrPanic(data.b == B.Four3); + + data.a = A.Three; + assertOrPanic(data.a == A.Three); + assertOrPanic(data.b == B.Four3); +} + +fn getA(data: *const BitFieldOfEnums) A { + return data.a; +} + +fn getB(data: *const BitFieldOfEnums) B { + return data.b; +} + +fn getC(data: *const BitFieldOfEnums) C { + return data.c; +} + +test "casting enum to its tag type" { + testCastEnumToTagType(Small2.Two); + comptime testCastEnumToTagType(Small2.Two); +} + +fn testCastEnumToTagType(value: Small2) void { + assertOrPanic(@enumToInt(value) == 1); +} + +const MultipleChoice = enum(u32) { + A = 20, + B = 40, + C = 60, + D = 1000, +}; + +test "enum with specified tag values" { + testEnumWithSpecifiedTagValues(MultipleChoice.C); + comptime testEnumWithSpecifiedTagValues(MultipleChoice.C); +} + +fn testEnumWithSpecifiedTagValues(x: MultipleChoice) void { + assertOrPanic(@enumToInt(x) == 60); + assertOrPanic(1234 == switch (x) { + MultipleChoice.A => 1, + MultipleChoice.B => 2, + MultipleChoice.C => u32(1234), + MultipleChoice.D => 4, + }); +} + +const MultipleChoice2 = enum(u32) { + Unspecified1, + A = 20, + Unspecified2, + B = 40, + Unspecified3, + C = 60, + Unspecified4, + D = 1000, + Unspecified5, +}; + +test "enum with specified and unspecified tag values" { + testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D); + comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D); +} + +fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { + assertOrPanic(@enumToInt(x) == 1000); + assertOrPanic(1234 == switch (x) { + MultipleChoice2.A => 1, + MultipleChoice2.B => 2, + MultipleChoice2.C => 3, + MultipleChoice2.D => u32(1234), + MultipleChoice2.Unspecified1 => 5, + MultipleChoice2.Unspecified2 => 6, + MultipleChoice2.Unspecified3 => 7, + MultipleChoice2.Unspecified4 => 8, + MultipleChoice2.Unspecified5 => 9, + }); +} + +test "cast integer literal to enum" { + assertOrPanic(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); + assertOrPanic(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); +} + +const EnumWithOneMember = enum { + Eof, +}; + +fn doALoopThing(id: EnumWithOneMember) void { + while (true) { + if (id == EnumWithOneMember.Eof) { + break; + } + @compileError("above if condition should be comptime"); + } +} + +test "comparison operator on enum with one member is comptime known" { + doALoopThing(EnumWithOneMember.Eof); +} + +const State = enum { + Start, +}; +test "switch on enum with one member is comptime known" { + var state = State.Start; + switch (state) { + State.Start => return, + } + @compileError("analysis should not reach here"); +} + +const EnumWithTagValues = enum(u4) { + A = 1 << 0, + B = 1 << 1, + C = 1 << 2, + D = 1 << 3, +}; +test "enum with tag values don't require parens" { + assertOrPanic(@enumToInt(EnumWithTagValues.C) == 0b0100); +} + +test "enum with 1 field but explicit tag type should still have the tag type" { + const Enum = enum(u8) { + B = 2, + }; + comptime @import("std").debug.assertOrPanic(@sizeOf(Enum) == @sizeOf(u8)); +} + +test "empty extern enum with members" { + const E = extern enum { + A, + B, + C, + }; + assertOrPanic(@sizeOf(E) == @sizeOf(c_int)); +} + +test "tag name with assigned enum values" { + const LocalFoo = enum { + A = 1, + B = 0, + }; + var b = LocalFoo.B; + assertOrPanic(mem.eql(u8, @tagName(b), "B")); +} diff --git a/test/stage1/behavior/enum_with_members.zig b/test/stage1/behavior/enum_with_members.zig new file mode 100644 index 0000000000..49af1ceae7 --- /dev/null +++ b/test/stage1/behavior/enum_with_members.zig @@ -0,0 +1,27 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const mem = @import("std").mem; +const fmt = @import("std").fmt; + +const ET = union(enum) { + SINT: i32, + UINT: u32, + + pub fn print(a: *const ET, buf: []u8) anyerror!usize { + return switch (a.*) { + ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), + ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), + }; + } +}; + +test "enum with members" { + const a = ET{ .SINT = -42 }; + const b = ET{ .UINT = 42 }; + var buf: [20]u8 = undefined; + + assertOrPanic((a.print(buf[0..]) catch unreachable) == 3); + assertOrPanic(mem.eql(u8, buf[0..3], "-42")); + + assertOrPanic((b.print(buf[0..]) catch unreachable) == 2); + assertOrPanic(mem.eql(u8, buf[0..2], "42")); +} diff --git a/test/stage1/behavior/error.zig b/test/stage1/behavior/error.zig new file mode 100644 index 0000000000..c7e38712bc --- /dev/null +++ b/test/stage1/behavior/error.zig @@ -0,0 +1,332 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const assertError = std.debug.assertError; +const mem = std.mem; +const builtin = @import("builtin"); + +pub fn foo() anyerror!i32 { + const x = try bar(); + return x + 1; +} + +pub fn bar() anyerror!i32 { + return 13; +} + +pub fn baz() anyerror!i32 { + const y = foo() catch 1234; + return y + 1; +} + +test "error wrapping" { + assertOrPanic((baz() catch unreachable) == 15); +} + +fn gimmeItBroke() []const u8 { + return @errorName(error.ItBroke); +} + +test "@errorName" { + assertOrPanic(mem.eql(u8, @errorName(error.AnError), "AnError")); + assertOrPanic(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); +} + +test "error values" { + const a = @errorToInt(error.err1); + const b = @errorToInt(error.err2); + assertOrPanic(a != b); +} + +test "redefinition of error values allowed" { + shouldBeNotEqual(error.AnError, error.SecondError); +} +fn shouldBeNotEqual(a: anyerror, b: anyerror) void { + if (a == b) unreachable; +} + +test "error binary operator" { + const a = errBinaryOperatorG(true) catch 3; + const b = errBinaryOperatorG(false) catch 3; + assertOrPanic(a == 3); + assertOrPanic(b == 10); +} +fn errBinaryOperatorG(x: bool) anyerror!isize { + return if (x) error.ItBroke else isize(10); +} + +test "unwrap simple value from error" { + const i = unwrapSimpleValueFromErrorDo() catch unreachable; + assertOrPanic(i == 13); +} +fn unwrapSimpleValueFromErrorDo() anyerror!isize { + return 13; +} + +test "error return in assignment" { + doErrReturnInAssignment() catch unreachable; +} + +fn doErrReturnInAssignment() anyerror!void { + var x: i32 = undefined; + x = try makeANonErr(); +} + +fn makeANonErr() anyerror!i32 { + return 1; +} + +test "error union type " { + testErrorUnionType(); + comptime testErrorUnionType(); +} + +fn testErrorUnionType() void { + const x: anyerror!i32 = 1234; + if (x) |value| assertOrPanic(value == 1234) else |_| unreachable; + assertOrPanic(@typeId(@typeOf(x)) == builtin.TypeId.ErrorUnion); + assertOrPanic(@typeId(@typeOf(x).ErrorSet) == builtin.TypeId.ErrorSet); + assertOrPanic(@typeOf(x).ErrorSet == anyerror); +} + +test "error set type" { + testErrorSetType(); + comptime testErrorSetType(); +} + +const MyErrSet = error{ + OutOfMemory, + FileNotFound, +}; + +fn testErrorSetType() void { + assertOrPanic(@memberCount(MyErrSet) == 2); + + const a: MyErrSet!i32 = 5678; + const b: MyErrSet!i32 = MyErrSet.OutOfMemory; + + if (a) |value| assertOrPanic(value == 5678) else |err| switch (err) { + error.OutOfMemory => unreachable, + error.FileNotFound => unreachable, + } +} + +test "explicit error set cast" { + testExplicitErrorSetCast(Set1.A); + comptime testExplicitErrorSetCast(Set1.A); +} + +const Set1 = error{ + A, + B, +}; +const Set2 = error{ + A, + C, +}; + +fn testExplicitErrorSetCast(set1: Set1) void { + var x = @errSetCast(Set2, set1); + var y = @errSetCast(Set1, x); + assertOrPanic(y == error.A); +} + +test "comptime test error for empty error set" { + testComptimeTestErrorEmptySet(1234); + comptime testComptimeTestErrorEmptySet(1234); +} + +const EmptyErrorSet = error{}; + +fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void { + if (x) |v| assertOrPanic(v == 1234) else |err| @compileError("bad"); +} + +test "syntax: optional operator in front of error union operator" { + comptime { + assertOrPanic(?(anyerror!i32) == ?(anyerror!i32)); + } +} + +test "comptime err to int of error set with only 1 possible value" { + testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); + comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); +} +fn testErrToIntWithOnePossibleValue( + x: error{A}, + comptime value: u32, +) void { + if (@errorToInt(x) != value) { + @compileError("bad"); + } +} + +test "error union peer type resolution" { + testErrorUnionPeerTypeResolution(1); +} + +fn testErrorUnionPeerTypeResolution(x: i32) void { + const y = switch (x) { + 1 => bar_1(), + 2 => baz_1(), + else => quux_1(), + }; + if (y) |_| { + @panic("expected error"); + } else |e| { + assertOrPanic(e == error.A); + } +} + +fn bar_1() anyerror { + return error.A; +} + +fn baz_1() !i32 { + return error.B; +} + +fn quux_1() !i32 { + return error.C; +} + +test "error: fn returning empty error set can be passed as fn returning any error" { + entry(); + comptime entry(); +} + +fn entry() void { + foo2(bar2); +} + +fn foo2(f: fn () anyerror!void) void { + const x = f(); +} + +fn bar2() (error{}!void) {} + +test "error: Zero sized error set returned with value payload crash" { + _ = foo3(0); + _ = comptime foo3(0); +} + +const Error = error{}; +fn foo3(b: usize) Error!usize { + return b; +} + +test "error: Infer error set from literals" { + _ = nullLiteral("n") catch |err| handleErrors(err); + _ = floatLiteral("n") catch |err| handleErrors(err); + _ = intLiteral("n") catch |err| handleErrors(err); + _ = comptime nullLiteral("n") catch |err| handleErrors(err); + _ = comptime floatLiteral("n") catch |err| handleErrors(err); + _ = comptime intLiteral("n") catch |err| handleErrors(err); +} + +fn handleErrors(err: var) noreturn { + switch (err) { + error.T => {}, + } + + unreachable; +} + +fn nullLiteral(str: []const u8) !?i64 { + if (str[0] == 'n') return null; + + return error.T; +} + +fn floatLiteral(str: []const u8) !?f64 { + if (str[0] == 'n') return 1.0; + + return error.T; +} + +fn intLiteral(str: []const u8) !?i64 { + if (str[0] == 'n') return 1; + + return error.T; +} + +test "nested error union function call in optional unwrap" { + const S = struct { + const Foo = struct { + a: i32, + }; + + fn errorable() !i32 { + var x: Foo = (try getFoo()) orelse return error.Other; + return x.a; + } + + fn errorable2() !i32 { + var x: Foo = (try getFoo2()) orelse return error.Other; + return x.a; + } + + fn errorable3() !i32 { + var x: Foo = (try getFoo3()) orelse return error.Other; + return x.a; + } + + fn getFoo() anyerror!?Foo { + return Foo{ .a = 1234 }; + } + + fn getFoo2() anyerror!?Foo { + return error.Failure; + } + + fn getFoo3() anyerror!?Foo { + return null; + } + }; + assertOrPanic((try S.errorable()) == 1234); + assertError(S.errorable2(), error.Failure); + assertError(S.errorable3(), error.Other); + comptime { + assertOrPanic((try S.errorable()) == 1234); + assertError(S.errorable2(), error.Failure); + assertError(S.errorable3(), error.Other); + } +} + +test "widen cast integer payload of error union function call" { + const S = struct { + fn errorable() !u64 { + var x = u64(try number()); + return x; + } + + fn number() anyerror!u32 { + return 1234; + } + }; + assertOrPanic((try S.errorable()) == 1234); +} + +test "return function call to error set from error union function" { + const S = struct { + fn errorable() anyerror!i32 { + return fail(); + } + + fn fail() anyerror { + return error.Failure; + } + }; + assertError(S.errorable(), error.Failure); + comptime assertError(S.errorable(), error.Failure); +} + +test "optional error set is the same size as error set" { + comptime assertOrPanic(@sizeOf(?anyerror) == @sizeOf(anyerror)); + const S = struct { + fn returnsOptErrSet() ?anyerror { + return null; + } + }; + assertOrPanic(S.returnsOptErrSet() == null); + comptime assertOrPanic(S.returnsOptErrSet() == null); +} diff --git a/test/stage1/behavior/eval.zig b/test/stage1/behavior/eval.zig new file mode 100644 index 0000000000..2d8494eb0b --- /dev/null +++ b/test/stage1/behavior/eval.zig @@ -0,0 +1,784 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const builtin = @import("builtin"); + +test "compile time recursion" { + assertOrPanic(some_data.len == 21); +} +var some_data: [@intCast(usize, fibonacci(7))]u8 = undefined; +fn fibonacci(x: i32) i32 { + if (x <= 1) return 1; + return fibonacci(x - 1) + fibonacci(x - 2); +} + +fn unwrapAndAddOne(blah: ?i32) i32 { + return blah.? + 1; +} +const should_be_1235 = unwrapAndAddOne(1234); +test "static add one" { + assertOrPanic(should_be_1235 == 1235); +} + +test "inlined loop" { + comptime var i = 0; + comptime var sum = 0; + inline while (i <= 5) : (i += 1) + sum += i; + assertOrPanic(sum == 15); +} + +fn gimme1or2(comptime a: bool) i32 { + const x: i32 = 1; + const y: i32 = 2; + comptime var z: i32 = if (a) x else y; + return z; +} +test "inline variable gets result of const if" { + assertOrPanic(gimme1or2(true) == 1); + assertOrPanic(gimme1or2(false) == 2); +} + +test "static function evaluation" { + assertOrPanic(statically_added_number == 3); +} +const statically_added_number = staticAdd(1, 2); +fn staticAdd(a: i32, b: i32) i32 { + return a + b; +} + +test "const expr eval on single expr blocks" { + assertOrPanic(constExprEvalOnSingleExprBlocksFn(1, true) == 3); + comptime assertOrPanic(constExprEvalOnSingleExprBlocksFn(1, true) == 3); +} + +fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 { + const literal = 3; + + const result = if (b) b: { + break :b literal; + } else b: { + break :b x; + }; + + return result; +} + +test "statically initialized list" { + assertOrPanic(static_point_list[0].x == 1); + assertOrPanic(static_point_list[0].y == 2); + assertOrPanic(static_point_list[1].x == 3); + assertOrPanic(static_point_list[1].y == 4); +} +const Point = struct { + x: i32, + y: i32, +}; +const static_point_list = []Point{ + makePoint(1, 2), + makePoint(3, 4), +}; +fn makePoint(x: i32, y: i32) Point { + return Point{ + .x = x, + .y = y, + }; +} + +test "static eval list init" { + assertOrPanic(static_vec3.data[2] == 1.0); + assertOrPanic(vec3(0.0, 0.0, 3.0).data[2] == 3.0); +} +const static_vec3 = vec3(0.0, 0.0, 1.0); +pub const Vec3 = struct { + data: [3]f32, +}; +pub fn vec3(x: f32, y: f32, z: f32) Vec3 { + return Vec3{ .data = []f32{ + x, + y, + z, + } }; +} + +test "constant expressions" { + var array: [array_size]u8 = undefined; + assertOrPanic(@sizeOf(@typeOf(array)) == 20); +} +const array_size: u8 = 20; + +test "constant struct with negation" { + assertOrPanic(vertices[0].x == -0.6); +} +const Vertex = struct { + x: f32, + y: f32, + r: f32, + g: f32, + b: f32, +}; +const vertices = []Vertex{ + Vertex{ + .x = -0.6, + .y = -0.4, + .r = 1.0, + .g = 0.0, + .b = 0.0, + }, + Vertex{ + .x = 0.6, + .y = -0.4, + .r = 0.0, + .g = 1.0, + .b = 0.0, + }, + Vertex{ + .x = 0.0, + .y = 0.6, + .r = 0.0, + .g = 0.0, + .b = 1.0, + }, +}; + +test "statically initialized struct" { + st_init_str_foo.x += 1; + assertOrPanic(st_init_str_foo.x == 14); +} +const StInitStrFoo = struct { + x: i32, + y: bool, +}; +var st_init_str_foo = StInitStrFoo{ + .x = 13, + .y = true, +}; + +test "statically initalized array literal" { + const y: [4]u8 = st_init_arr_lit_x; + assertOrPanic(y[3] == 4); +} +const st_init_arr_lit_x = []u8{ + 1, + 2, + 3, + 4, +}; + +test "const slice" { + comptime { + const a = "1234567890"; + assertOrPanic(a.len == 10); + const b = a[1..2]; + assertOrPanic(b.len == 1); + assertOrPanic(b[0] == '2'); + } +} + +test "try to trick eval with runtime if" { + assertOrPanic(testTryToTrickEvalWithRuntimeIf(true) == 10); +} + +fn testTryToTrickEvalWithRuntimeIf(b: bool) usize { + comptime var i: usize = 0; + inline while (i < 10) : (i += 1) { + const result = if (b) false else true; + } + comptime { + return i; + } +} + +fn max(comptime T: type, a: T, b: T) T { + if (T == bool) { + return a or b; + } else if (a > b) { + return a; + } else { + return b; + } +} +fn letsTryToCompareBools(a: bool, b: bool) bool { + return max(bool, a, b); +} +test "inlined block and runtime block phi" { + assertOrPanic(letsTryToCompareBools(true, true)); + assertOrPanic(letsTryToCompareBools(true, false)); + assertOrPanic(letsTryToCompareBools(false, true)); + assertOrPanic(!letsTryToCompareBools(false, false)); + + comptime { + assertOrPanic(letsTryToCompareBools(true, true)); + assertOrPanic(letsTryToCompareBools(true, false)); + assertOrPanic(letsTryToCompareBools(false, true)); + assertOrPanic(!letsTryToCompareBools(false, false)); + } +} + +const CmdFn = struct { + name: []const u8, + func: fn (i32) i32, +}; + +const cmd_fns = []CmdFn{ + CmdFn{ + .name = "one", + .func = one, + }, + CmdFn{ + .name = "two", + .func = two, + }, + CmdFn{ + .name = "three", + .func = three, + }, +}; +fn one(value: i32) i32 { + return value + 1; +} +fn two(value: i32) i32 { + return value + 2; +} +fn three(value: i32) i32 { + return value + 3; +} + +fn performFn(comptime prefix_char: u8, start_value: i32) i32 { + var result: i32 = start_value; + comptime var i = 0; + inline while (i < cmd_fns.len) : (i += 1) { + if (cmd_fns[i].name[0] == prefix_char) { + result = cmd_fns[i].func(result); + } + } + return result; +} + +test "comptime iterate over fn ptr list" { + assertOrPanic(performFn('t', 1) == 6); + assertOrPanic(performFn('o', 0) == 1); + assertOrPanic(performFn('w', 99) == 99); +} + +test "eval @setRuntimeSafety at compile-time" { + const result = comptime fnWithSetRuntimeSafety(); + assertOrPanic(result == 1234); +} + +fn fnWithSetRuntimeSafety() i32 { + @setRuntimeSafety(true); + return 1234; +} + +test "eval @setFloatMode at compile-time" { + const result = comptime fnWithFloatMode(); + assertOrPanic(result == 1234.0); +} + +fn fnWithFloatMode() f32 { + @setFloatMode(builtin.FloatMode.Strict); + return 1234.0; +} + +const SimpleStruct = struct { + field: i32, + + fn method(self: *const SimpleStruct) i32 { + return self.field + 3; + } +}; + +var simple_struct = SimpleStruct{ .field = 1234 }; + +const bound_fn = simple_struct.method; + +test "call method on bound fn referring to var instance" { + assertOrPanic(bound_fn() == 1237); +} + +test "ptr to local array argument at comptime" { + comptime { + var bytes: [10]u8 = undefined; + modifySomeBytes(bytes[0..]); + assertOrPanic(bytes[0] == 'a'); + assertOrPanic(bytes[9] == 'b'); + } +} + +fn modifySomeBytes(bytes: []u8) void { + bytes[0] = 'a'; + bytes[9] = 'b'; +} + +test "comparisons 0 <= uint and 0 > uint should be comptime" { + testCompTimeUIntComparisons(1234); +} +fn testCompTimeUIntComparisons(x: u32) void { + if (!(0 <= x)) { + @compileError("this condition should be comptime known"); + } + if (0 > x) { + @compileError("this condition should be comptime known"); + } + if (!(x >= 0)) { + @compileError("this condition should be comptime known"); + } + if (x < 0) { + @compileError("this condition should be comptime known"); + } +} + +test "const ptr to variable data changes at runtime" { + assertOrPanic(foo_ref.name[0] == 'a'); + foo_ref.name = "b"; + assertOrPanic(foo_ref.name[0] == 'b'); +} + +const Foo = struct { + name: []const u8, +}; + +var foo_contents = Foo{ .name = "a" }; +const foo_ref = &foo_contents; + +test "create global array with for loop" { + assertOrPanic(global_array[5] == 5 * 5); + assertOrPanic(global_array[9] == 9 * 9); +} + +const global_array = x: { + var result: [10]usize = undefined; + for (result) |*item, index| { + item.* = index * index; + } + break :x result; +}; + +test "compile-time downcast when the bits fit" { + comptime { + const spartan_count: u16 = 255; + const byte = @intCast(u8, spartan_count); + assertOrPanic(byte == 255); + } +} + +const hi1 = "hi"; +const hi2 = hi1; +test "const global shares pointer with other same one" { + assertEqualPtrs(&hi1[0], &hi2[0]); + comptime assertOrPanic(&hi1[0] == &hi2[0]); +} +fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) void { + assertOrPanic(ptr1 == ptr2); +} + +test "@setEvalBranchQuota" { + comptime { + // 1001 for the loop and then 1 more for the assertOrPanic fn call + @setEvalBranchQuota(1002); + var i = 0; + var sum = 0; + while (i < 1001) : (i += 1) { + sum += i; + } + assertOrPanic(sum == 500500); + } +} + +// TODO test "float literal at compile time not lossy" { +// TODO assertOrPanic(16777216.0 + 1.0 == 16777217.0); +// TODO assertOrPanic(9007199254740992.0 + 1.0 == 9007199254740993.0); +// TODO } + +test "f32 at compile time is lossy" { + assertOrPanic(f32(1 << 24) + 1 == 1 << 24); +} + +test "f64 at compile time is lossy" { + assertOrPanic(f64(1 << 53) + 1 == 1 << 53); +} + +test "f128 at compile time is lossy" { + assertOrPanic(f128(10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); +} + +comptime { + assertOrPanic(f128(1 << 113) == 10384593717069655257060992658440192); +} + +pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { + return struct { + pub const Node = struct {}; + }; +} + +test "string literal used as comptime slice is memoized" { + const a = "link"; + const b = "link"; + comptime assertOrPanic(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); + comptime assertOrPanic(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); +} + +test "comptime slice of undefined pointer of length 0" { + const slice1 = ([*]i32)(undefined)[0..0]; + assertOrPanic(slice1.len == 0); + const slice2 = ([*]i32)(undefined)[100..100]; + assertOrPanic(slice2.len == 0); +} + +fn copyWithPartialInline(s: []u32, b: []u8) void { + comptime var i: usize = 0; + inline while (i < 4) : (i += 1) { + s[i] = 0; + s[i] |= u32(b[i * 4 + 0]) << 24; + s[i] |= u32(b[i * 4 + 1]) << 16; + s[i] |= u32(b[i * 4 + 2]) << 8; + s[i] |= u32(b[i * 4 + 3]) << 0; + } +} + +test "binary math operator in partially inlined function" { + var s: [4]u32 = undefined; + var b: [16]u8 = undefined; + + for (b) |*r, i| + r.* = @intCast(u8, i + 1); + + copyWithPartialInline(s[0..], b[0..]); + assertOrPanic(s[0] == 0x1020304); + assertOrPanic(s[1] == 0x5060708); + assertOrPanic(s[2] == 0x90a0b0c); + assertOrPanic(s[3] == 0xd0e0f10); +} + +test "comptime function with the same args is memoized" { + comptime { + assertOrPanic(MakeType(i32) == MakeType(i32)); + assertOrPanic(MakeType(i32) != MakeType(f64)); + } +} + +fn MakeType(comptime T: type) type { + return struct { + field: T, + }; +} + +test "comptime function with mutable pointer is not memoized" { + comptime { + var x: i32 = 1; + const ptr = &x; + increment(ptr); + increment(ptr); + assertOrPanic(x == 3); + } +} + +fn increment(value: *i32) void { + value.* += 1; +} + +fn generateTable(comptime T: type) [1010]T { + var res: [1010]T = undefined; + var i: usize = 0; + while (i < 1010) : (i += 1) { + res[i] = @intCast(T, i); + } + return res; +} + +fn doesAlotT(comptime T: type, value: usize) T { + @setEvalBranchQuota(5000); + const table = comptime blk: { + break :blk generateTable(T); + }; + return table[value]; +} + +test "@setEvalBranchQuota at same scope as generic function call" { + assertOrPanic(doesAlotT(u32, 2) == 2); +} + +test "comptime slice of slice preserves comptime var" { + comptime { + var buff: [10]u8 = undefined; + buff[0..][0..][0] = 1; + assertOrPanic(buff[0..][0..][0] == 1); + } +} + +test "comptime slice of pointer preserves comptime var" { + comptime { + var buff: [10]u8 = undefined; + var a = buff[0..].ptr; + a[0..1][0] = 1; + assertOrPanic(buff[0..][0..][0] == 1); + } +} + +const SingleFieldStruct = struct { + x: i32, + + fn read_x(self: *const SingleFieldStruct) i32 { + return self.x; + } +}; +test "const ptr to comptime mutable data is not memoized" { + comptime { + var foo = SingleFieldStruct{ .x = 1 }; + assertOrPanic(foo.read_x() == 1); + foo.x = 2; + assertOrPanic(foo.read_x() == 2); + } +} + +test "array concat of slices gives slice" { + comptime { + var a: []const u8 = "aoeu"; + var b: []const u8 = "asdf"; + const c = a ++ b; + assertOrPanic(std.mem.eql(u8, c, "aoeuasdf")); + } +} + +test "comptime shlWithOverflow" { + const ct_shifted: u64 = comptime amt: { + var amt = u64(0); + _ = @shlWithOverflow(u64, ~u64(0), 16, &amt); + break :amt amt; + }; + + const rt_shifted: u64 = amt: { + var amt = u64(0); + _ = @shlWithOverflow(u64, ~u64(0), 16, &amt); + break :amt amt; + }; + + assertOrPanic(ct_shifted == rt_shifted); +} + +test "runtime 128 bit integer division" { + var a: u128 = 152313999999999991610955792383; + var b: u128 = 10000000000000000000; + var c = a / b; + assertOrPanic(c == 15231399999); +} + +pub const Info = struct { + version: u8, +}; + +pub const diamond_info = Info{ .version = 0 }; + +test "comptime modification of const struct field" { + comptime { + var res = diamond_info; + res.version = 1; + assertOrPanic(diamond_info.version == 0); + assertOrPanic(res.version == 1); + } +} + +test "pointer to type" { + comptime { + var T: type = i32; + assertOrPanic(T == i32); + var ptr = &T; + assertOrPanic(@typeOf(ptr) == *type); + ptr.* = f32; + assertOrPanic(T == f32); + assertOrPanic(*T == *f32); + } +} + +test "slice of type" { + comptime { + var types_array = []type{ i32, f64, type }; + for (types_array) |T, i| { + switch (i) { + 0 => assertOrPanic(T == i32), + 1 => assertOrPanic(T == f64), + 2 => assertOrPanic(T == type), + else => unreachable, + } + } + for (types_array[0..]) |T, i| { + switch (i) { + 0 => assertOrPanic(T == i32), + 1 => assertOrPanic(T == f64), + 2 => assertOrPanic(T == type), + else => unreachable, + } + } + } +} + +const Wrapper = struct { + T: type, +}; + +fn wrap(comptime T: type) Wrapper { + return Wrapper{ .T = T }; +} + +test "function which returns struct with type field causes implicit comptime" { + const ty = wrap(i32).T; + assertOrPanic(ty == i32); +} + +test "call method with comptime pass-by-non-copying-value self parameter" { + const S = struct { + a: u8, + + fn b(comptime s: @This()) u8 { + return s.a; + } + }; + + const s = S{ .a = 2 }; + var b = s.b(); + assertOrPanic(b == 2); +} + +test "@tagName of @typeId" { + const str = @tagName(@typeId(u8)); + assertOrPanic(std.mem.eql(u8, str, "Int")); +} + +test "setting backward branch quota just before a generic fn call" { + @setEvalBranchQuota(1001); + loopNTimes(1001); +} + +fn loopNTimes(comptime n: usize) void { + comptime var i = 0; + inline while (i < n) : (i += 1) {} +} + +test "variable inside inline loop that has different types on different iterations" { + testVarInsideInlineLoop(true, u32(42)); +} + +fn testVarInsideInlineLoop(args: ...) void { + comptime var i = 0; + inline while (i < args.len) : (i += 1) { + const x = args[i]; + if (i == 0) assertOrPanic(x); + if (i == 1) assertOrPanic(x == 42); + } +} + +test "inline for with same type but different values" { + var res: usize = 0; + inline for ([]type{ [2]u8, [1]u8, [2]u8 }) |T| { + var a: T = undefined; + res += a.len; + } + assertOrPanic(res == 5); +} + +test "refer to the type of a generic function" { + const Func = fn (type) void; + const f: Func = doNothingWithType; + f(i32); +} + +fn doNothingWithType(comptime T: type) void {} + +test "zero extend from u0 to u1" { + var zero_u0: u0 = 0; + var zero_u1: u1 = zero_u0; + assertOrPanic(zero_u1 == 0); +} + +test "bit shift a u1" { + var x: u1 = 1; + var y = x << 0; + assertOrPanic(y == 1); +} + +test "@intCast to a u0" { + var x: u8 = 0; + var y: u0 = @intCast(u0, x); + assertOrPanic(y == 0); +} + +test "@bytesToslice on a packed struct" { + const F = packed struct { + a: u8, + }; + + var b = [1]u8{9}; + var f = @bytesToSlice(F, b); + assertOrPanic(f[0].a == 9); +} + +test "comptime pointer cast array and then slice" { + const array = []u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; + + const ptrA: [*]const u8 = @ptrCast([*]const u8, &array); + const sliceA: []const u8 = ptrA[0..2]; + + const ptrB: [*]const u8 = &array; + const sliceB: []const u8 = ptrB[0..2]; + + assertOrPanic(sliceA[1] == 2); + assertOrPanic(sliceB[1] == 2); +} + +test "slice bounds in comptime concatenation" { + const bs = comptime blk: { + const b = c"11"; + break :blk b[0..1]; + }; + const str = "" ++ bs; + assertOrPanic(str.len == 1); + assertOrPanic(std.mem.eql(u8, str, "1")); + + const str2 = bs ++ ""; + assertOrPanic(str2.len == 1); + assertOrPanic(std.mem.eql(u8, str2, "1")); +} + +test "comptime bitwise operators" { + comptime { + assertOrPanic(3 & 1 == 1); + assertOrPanic(3 & -1 == 3); + assertOrPanic(-3 & -1 == -3); + assertOrPanic(3 | -1 == -1); + assertOrPanic(-3 | -1 == -1); + assertOrPanic(3 ^ -1 == -4); + assertOrPanic(-3 ^ -1 == 2); + assertOrPanic(~i8(-1) == 0); + assertOrPanic(~i128(-1) == 0); + assertOrPanic(18446744073709551615 & 18446744073709551611 == 18446744073709551611); + assertOrPanic(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); + assertOrPanic(~u128(0) == 0xffffffffffffffffffffffffffffffff); + } +} + +test "*align(1) u16 is the same as *align(1:0:2) u16" { + comptime { + assertOrPanic(*align(1:0:2) u16 == *align(1) u16); + // TODO add parsing support for this syntax + //assertOrPanic(*align(:0:2) u16 == *u16); + } +} + +test "array concatenation forces comptime" { + var a = oneItem(3) ++ oneItem(4); + assertOrPanic(std.mem.eql(i32, a, []i32{ 3, 4 })); +} + +test "array multiplication forces comptime" { + var a = oneItem(3) ** scalar(2); + assertOrPanic(std.mem.eql(i32, a, []i32{ 3, 3 })); +} + +fn oneItem(x: i32) [1]i32 { + return []i32{x}; +} + +fn scalar(x: u32) u32 { + return x; +} diff --git a/test/stage1/behavior/field_parent_ptr.zig b/test/stage1/behavior/field_parent_ptr.zig new file mode 100644 index 0000000000..ed2487c020 --- /dev/null +++ b/test/stage1/behavior/field_parent_ptr.zig @@ -0,0 +1,41 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "@fieldParentPtr non-first field" { + testParentFieldPtr(&foo.c); + comptime testParentFieldPtr(&foo.c); +} + +test "@fieldParentPtr first field" { + testParentFieldPtrFirst(&foo.a); + comptime testParentFieldPtrFirst(&foo.a); +} + +const Foo = struct { + a: bool, + b: f32, + c: i32, + d: i32, +}; + +const foo = Foo{ + .a = true, + .b = 0.123, + .c = 1234, + .d = -10, +}; + +fn testParentFieldPtr(c: *const i32) void { + assertOrPanic(c == &foo.c); + + const base = @fieldParentPtr(Foo, "c", c); + assertOrPanic(base == &foo); + assertOrPanic(&base.c == c); +} + +fn testParentFieldPtrFirst(a: *const bool) void { + assertOrPanic(a == &foo.a); + + const base = @fieldParentPtr(Foo, "a", a); + assertOrPanic(base == &foo); + assertOrPanic(&base.a == a); +} diff --git a/test/stage1/behavior/fn.zig b/test/stage1/behavior/fn.zig new file mode 100644 index 0000000000..3011bc41d0 --- /dev/null +++ b/test/stage1/behavior/fn.zig @@ -0,0 +1,208 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "params" { + assertOrPanic(testParamsAdd(22, 11) == 33); +} +fn testParamsAdd(a: i32, b: i32) i32 { + return a + b; +} + +test "local variables" { + testLocVars(2); +} +fn testLocVars(b: i32) void { + const a: i32 = 1; + if (a + b != 3) unreachable; +} + +test "void parameters" { + voidFun(1, void{}, 2, {}); +} +fn voidFun(a: i32, b: void, c: i32, d: void) void { + const v = b; + const vv: void = if (a == 1) v else {}; + assertOrPanic(a + c == 3); + return vv; +} + +test "mutable local variables" { + var zero: i32 = 0; + assertOrPanic(zero == 0); + + var i = i32(0); + while (i != 3) { + i += 1; + } + assertOrPanic(i == 3); +} + +test "separate block scopes" { + { + const no_conflict: i32 = 5; + assertOrPanic(no_conflict == 5); + } + + const c = x: { + const no_conflict = i32(10); + break :x no_conflict; + }; + assertOrPanic(c == 10); +} + +test "call function with empty string" { + acceptsString(""); +} + +fn acceptsString(foo: []u8) void {} + +fn @"weird function name"() i32 { + return 1234; +} +test "weird function name" { + assertOrPanic(@"weird function name"() == 1234); +} + +test "implicit cast function unreachable return" { + wantsFnWithVoid(fnWithUnreachable); +} + +fn wantsFnWithVoid(f: fn () void) void {} + +fn fnWithUnreachable() noreturn { + unreachable; +} + +test "function pointers" { + const fns = []@typeOf(fn1){ + fn1, + fn2, + fn3, + fn4, + }; + for (fns) |f, i| { + assertOrPanic(f() == @intCast(u32, i) + 5); + } +} +fn fn1() u32 { + return 5; +} +fn fn2() u32 { + return 6; +} +fn fn3() u32 { + return 7; +} +fn fn4() u32 { + return 8; +} + +test "inline function call" { + assertOrPanic(@inlineCall(add, 3, 9) == 12); +} + +fn add(a: i32, b: i32) i32 { + return a + b; +} + +test "number literal as an argument" { + numberLiteralArg(3); + comptime numberLiteralArg(3); +} + +fn numberLiteralArg(a: var) void { + assertOrPanic(a == 3); +} + +test "assign inline fn to const variable" { + const a = inlineFn; + a(); +} + +inline fn inlineFn() void {} + +test "pass by non-copying value" { + assertOrPanic(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); +} + +const Point = struct { + x: i32, + y: i32, +}; + +fn addPointCoords(pt: Point) i32 { + return pt.x + pt.y; +} + +test "pass by non-copying value through var arg" { + assertOrPanic(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3); +} + +fn addPointCoordsVar(pt: var) i32 { + comptime assertOrPanic(@typeOf(pt) == Point); + return pt.x + pt.y; +} + +test "pass by non-copying value as method" { + var pt = Point2{ .x = 1, .y = 2 }; + assertOrPanic(pt.addPointCoords() == 3); +} + +const Point2 = struct { + x: i32, + y: i32, + + fn addPointCoords(self: Point2) i32 { + return self.x + self.y; + } +}; + +test "pass by non-copying value as method, which is generic" { + var pt = Point3{ .x = 1, .y = 2 }; + assertOrPanic(pt.addPointCoords(i32) == 3); +} + +const Point3 = struct { + x: i32, + y: i32, + + fn addPointCoords(self: Point3, comptime T: type) i32 { + return self.x + self.y; + } +}; + +test "pass by non-copying value as method, at comptime" { + comptime { + var pt = Point2{ .x = 1, .y = 2 }; + assertOrPanic(pt.addPointCoords() == 3); + } +} + +fn outer(y: u32) fn (u32) u32 { + const Y = @typeOf(y); + const st = struct { + fn get(z: u32) u32 { + return z + @sizeOf(Y); + } + }; + return st.get; +} + +test "return inner function which references comptime variable of outer function" { + var func = outer(10); + assertOrPanic(func(3) == 7); +} + +test "extern struct with stdcallcc fn pointer" { + const S = extern struct { + ptr: stdcallcc fn () i32, + + stdcallcc fn foo() i32 { + return 1234; + } + }; + + var s: S = undefined; + s.ptr = S.foo; + assertOrPanic(s.ptr() == 1234); +} + diff --git a/test/stage1/behavior/fn_in_struct_in_comptime.zig b/test/stage1/behavior/fn_in_struct_in_comptime.zig new file mode 100644 index 0000000000..0af076d40a --- /dev/null +++ b/test/stage1/behavior/fn_in_struct_in_comptime.zig @@ -0,0 +1,17 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +fn get_foo() fn (*u8) usize { + comptime { + return struct { + fn func(ptr: *u8) usize { + var u = @ptrToInt(ptr); + return u; + } + }.func; + } +} + +test "define a function in an anonymous struct in comptime" { + const foo = get_foo(); + assertOrPanic(foo(@intToPtr(*u8, 12345)) == 12345); +} diff --git a/test/stage1/behavior/for.zig b/test/stage1/behavior/for.zig new file mode 100644 index 0000000000..b6d1ef24c4 --- /dev/null +++ b/test/stage1/behavior/for.zig @@ -0,0 +1,106 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const mem = std.mem; + +test "continue in for loop" { + const array = []i32{ + 1, + 2, + 3, + 4, + 5, + }; + var sum: i32 = 0; + for (array) |x| { + sum += x; + if (x < 3) { + continue; + } + break; + } + if (sum != 6) unreachable; +} + +test "for loop with pointer elem var" { + const source = "abcdefg"; + var target: [source.len]u8 = undefined; + mem.copy(u8, target[0..], source); + mangleString(target[0..]); + assertOrPanic(mem.eql(u8, target, "bcdefgh")); +} +fn mangleString(s: []u8) void { + for (s) |*c| { + c.* += 1; + } +} + +test "basic for loop" { + const expected_result = []u8{ 9, 8, 7, 6, 0, 1, 2, 3 } ** 3; + + var buffer: [expected_result.len]u8 = undefined; + var buf_index: usize = 0; + + const array = []u8{ 9, 8, 7, 6 }; + for (array) |item| { + buffer[buf_index] = item; + buf_index += 1; + } + for (array) |item, index| { + buffer[buf_index] = @intCast(u8, index); + buf_index += 1; + } + const array_ptr = &array; + for (array_ptr) |item| { + buffer[buf_index] = item; + buf_index += 1; + } + for (array_ptr) |item, index| { + buffer[buf_index] = @intCast(u8, index); + buf_index += 1; + } + const unknown_size: []const u8 = array; + for (unknown_size) |item| { + buffer[buf_index] = item; + buf_index += 1; + } + for (unknown_size) |item, index| { + buffer[buf_index] = @intCast(u8, index); + buf_index += 1; + } + + assertOrPanic(mem.eql(u8, buffer[0..buf_index], expected_result)); +} + +test "break from outer for loop" { + testBreakOuter(); + comptime testBreakOuter(); +} + +fn testBreakOuter() void { + var array = "aoeu"; + var count: usize = 0; + outer: for (array) |_| { + for (array) |_| { + count += 1; + break :outer; + } + } + assertOrPanic(count == 1); +} + +test "continue outer for loop" { + testContinueOuter(); + comptime testContinueOuter(); +} + +fn testContinueOuter() void { + var array = "aoeu"; + var counter: usize = 0; + outer: for (array) |_| { + for (array) |_| { + counter += 1; + continue :outer; + } + } + assertOrPanic(counter == array.len); +} diff --git a/test/stage1/behavior/generics.zig b/test/stage1/behavior/generics.zig new file mode 100644 index 0000000000..a0928634a7 --- /dev/null +++ b/test/stage1/behavior/generics.zig @@ -0,0 +1,151 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "simple generic fn" { + assertOrPanic(max(i32, 3, -1) == 3); + assertOrPanic(max(f32, 0.123, 0.456) == 0.456); + assertOrPanic(add(2, 3) == 5); +} + +fn max(comptime T: type, a: T, b: T) T { + return if (a > b) a else b; +} + +fn add(comptime a: i32, b: i32) i32 { + return (comptime a) + b; +} + +const the_max = max(u32, 1234, 5678); +test "compile time generic eval" { + assertOrPanic(the_max == 5678); +} + +fn gimmeTheBigOne(a: u32, b: u32) u32 { + return max(u32, a, b); +} + +fn shouldCallSameInstance(a: u32, b: u32) u32 { + return max(u32, a, b); +} + +fn sameButWithFloats(a: f64, b: f64) f64 { + return max(f64, a, b); +} + +test "fn with comptime args" { + assertOrPanic(gimmeTheBigOne(1234, 5678) == 5678); + assertOrPanic(shouldCallSameInstance(34, 12) == 34); + assertOrPanic(sameButWithFloats(0.43, 0.49) == 0.49); +} + +test "var params" { + assertOrPanic(max_i32(12, 34) == 34); + assertOrPanic(max_f64(1.2, 3.4) == 3.4); +} + +comptime { + assertOrPanic(max_i32(12, 34) == 34); + assertOrPanic(max_f64(1.2, 3.4) == 3.4); +} + +fn max_var(a: var, b: var) @typeOf(a + b) { + return if (a > b) a else b; +} + +fn max_i32(a: i32, b: i32) i32 { + return max_var(a, b); +} + +fn max_f64(a: f64, b: f64) f64 { + return max_var(a, b); +} + +pub fn List(comptime T: type) type { + return SmallList(T, 8); +} + +pub fn SmallList(comptime T: type, comptime STATIC_SIZE: usize) type { + return struct { + items: []T, + length: usize, + prealloc_items: [STATIC_SIZE]T, + }; +} + +test "function with return type type" { + var list: List(i32) = undefined; + var list2: List(i32) = undefined; + list.length = 10; + list2.length = 10; + assertOrPanic(list.prealloc_items.len == 8); + assertOrPanic(list2.prealloc_items.len == 8); +} + +test "generic struct" { + var a1 = GenNode(i32){ + .value = 13, + .next = null, + }; + var b1 = GenNode(bool){ + .value = true, + .next = null, + }; + assertOrPanic(a1.value == 13); + assertOrPanic(a1.value == a1.getVal()); + assertOrPanic(b1.getVal()); +} +fn GenNode(comptime T: type) type { + return struct { + value: T, + next: ?*GenNode(T), + fn getVal(n: *const GenNode(T)) T { + return n.value; + } + }; +} + +test "const decls in struct" { + assertOrPanic(GenericDataThing(3).count_plus_one == 4); +} +fn GenericDataThing(comptime count: isize) type { + return struct { + const count_plus_one = count + 1; + }; +} + +test "use generic param in generic param" { + assertOrPanic(aGenericFn(i32, 3, 4) == 7); +} +fn aGenericFn(comptime T: type, comptime a: T, b: T) T { + return a + b; +} + +test "generic fn with implicit cast" { + assertOrPanic(getFirstByte(u8, []u8{13}) == 13); + assertOrPanic(getFirstByte(u16, []u16{ + 0, + 13, + }) == 0); +} +fn getByte(ptr: ?*const u8) u8 { + return ptr.?.*; +} +fn getFirstByte(comptime T: type, mem: []const T) u8 { + return getByte(@ptrCast(*const u8, &mem[0])); +} + +const foos = []fn (var) bool{ + foo1, + foo2, +}; + +fn foo1(arg: var) bool { + return arg; +} +fn foo2(arg: var) bool { + return !arg; +} + +test "array of generic fns" { + assertOrPanic(foos[0](true)); + assertOrPanic(!foos[1](true)); +} diff --git a/test/stage1/behavior/if.zig b/test/stage1/behavior/if.zig new file mode 100644 index 0000000000..58d1b8fd73 --- /dev/null +++ b/test/stage1/behavior/if.zig @@ -0,0 +1,37 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "if statements" { + shouldBeEqual(1, 1); + firstEqlThird(2, 1, 2); +} +fn shouldBeEqual(a: i32, b: i32) void { + if (a != b) { + unreachable; + } else { + return; + } +} +fn firstEqlThird(a: i32, b: i32, c: i32) void { + if (a == b) { + unreachable; + } else if (b == c) { + unreachable; + } else if (a == c) { + return; + } else { + unreachable; + } +} + +test "else if expression" { + assertOrPanic(elseIfExpressionF(1) == 1); +} +fn elseIfExpressionF(c: u8) u8 { + if (c == 0) { + return 0; + } else if (c == 1) { + return 1; + } else { + return u8(2); + } +} diff --git a/test/stage1/behavior/import.zig b/test/stage1/behavior/import.zig new file mode 100644 index 0000000000..736e4c219d --- /dev/null +++ b/test/stage1/behavior/import.zig @@ -0,0 +1,10 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const a_namespace = @import("import/a_namespace.zig"); + +test "call fn via namespace lookup" { + assertOrPanic(a_namespace.foo() == 1234); +} + +test "importing the same thing gives the same import" { + assertOrPanic(@import("std") == @import("std")); +} diff --git a/test/stage1/behavior/import/a_namespace.zig b/test/stage1/behavior/import/a_namespace.zig new file mode 100644 index 0000000000..042f1867a5 --- /dev/null +++ b/test/stage1/behavior/import/a_namespace.zig @@ -0,0 +1,3 @@ +pub fn foo() i32 { + return 1234; +} diff --git a/test/stage1/behavior/incomplete_struct_param_tld.zig b/test/stage1/behavior/incomplete_struct_param_tld.zig new file mode 100644 index 0000000000..d062311b2e --- /dev/null +++ b/test/stage1/behavior/incomplete_struct_param_tld.zig @@ -0,0 +1,30 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +const A = struct { + b: B, +}; + +const B = struct { + c: C, +}; + +const C = struct { + x: i32, + + fn d(c: *const C) i32 { + return c.x; + } +}; + +fn foo(a: A) i32 { + return a.b.c.d(); +} + +test "incomplete struct param top level declaration" { + const a = A{ + .b = B{ + .c = C{ .x = 13 }, + }, + }; + assertOrPanic(foo(a) == 13); +} diff --git a/test/stage1/behavior/inttoptr.zig b/test/stage1/behavior/inttoptr.zig new file mode 100644 index 0000000000..bf657fc86a --- /dev/null +++ b/test/stage1/behavior/inttoptr.zig @@ -0,0 +1,26 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; + +test "casting random address to function pointer" { + randomAddressToFunction(); + comptime randomAddressToFunction(); +} + +fn randomAddressToFunction() void { + var addr: usize = 0xdeadbeef; + var ptr = @intToPtr(fn () void, addr); +} + +test "mutate through ptr initialized with constant intToPtr value" { + forceCompilerAnalyzeBranchHardCodedPtrDereference(false); +} + +fn forceCompilerAnalyzeBranchHardCodedPtrDereference(x: bool) void { + const hardCodedP = @intToPtr(*volatile u8, 0xdeadbeef); + if (x) { + hardCodedP.* = hardCodedP.* | 10; + } else { + return; + } +} diff --git a/test/stage1/behavior/ir_block_deps.zig b/test/stage1/behavior/ir_block_deps.zig new file mode 100644 index 0000000000..bc61e11df7 --- /dev/null +++ b/test/stage1/behavior/ir_block_deps.zig @@ -0,0 +1,21 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +fn foo(id: u64) !i32 { + return switch (id) { + 1 => getErrInt(), + 2 => { + const size = try getErrInt(); + return try getErrInt(); + }, + else => error.ItBroke, + }; +} + +fn getErrInt() anyerror!i32 { + return 0; +} + +test "ir block deps" { + assertOrPanic((foo(1) catch unreachable) == 0); + assertOrPanic((foo(2) catch unreachable) == 0); +} diff --git a/test/stage1/behavior/math.zig b/test/stage1/behavior/math.zig new file mode 100644 index 0000000000..9d6a5a4997 --- /dev/null +++ b/test/stage1/behavior/math.zig @@ -0,0 +1,500 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const maxInt = std.math.maxInt; +const minInt = std.math.minInt; + +test "division" { + testDivision(); + comptime testDivision(); +} +fn testDivision() void { + assertOrPanic(div(u32, 13, 3) == 4); + assertOrPanic(div(f16, 1.0, 2.0) == 0.5); + assertOrPanic(div(f32, 1.0, 2.0) == 0.5); + + assertOrPanic(divExact(u32, 55, 11) == 5); + assertOrPanic(divExact(i32, -55, 11) == -5); + assertOrPanic(divExact(f16, 55.0, 11.0) == 5.0); + assertOrPanic(divExact(f16, -55.0, 11.0) == -5.0); + assertOrPanic(divExact(f32, 55.0, 11.0) == 5.0); + assertOrPanic(divExact(f32, -55.0, 11.0) == -5.0); + + assertOrPanic(divFloor(i32, 5, 3) == 1); + assertOrPanic(divFloor(i32, -5, 3) == -2); + assertOrPanic(divFloor(f16, 5.0, 3.0) == 1.0); + assertOrPanic(divFloor(f16, -5.0, 3.0) == -2.0); + assertOrPanic(divFloor(f32, 5.0, 3.0) == 1.0); + assertOrPanic(divFloor(f32, -5.0, 3.0) == -2.0); + assertOrPanic(divFloor(i32, -0x80000000, -2) == 0x40000000); + assertOrPanic(divFloor(i32, 0, -0x80000000) == 0); + assertOrPanic(divFloor(i32, -0x40000001, 0x40000000) == -2); + assertOrPanic(divFloor(i32, -0x80000000, 1) == -0x80000000); + + assertOrPanic(divTrunc(i32, 5, 3) == 1); + assertOrPanic(divTrunc(i32, -5, 3) == -1); + assertOrPanic(divTrunc(f16, 5.0, 3.0) == 1.0); + assertOrPanic(divTrunc(f16, -5.0, 3.0) == -1.0); + assertOrPanic(divTrunc(f32, 5.0, 3.0) == 1.0); + assertOrPanic(divTrunc(f32, -5.0, 3.0) == -1.0); + assertOrPanic(divTrunc(f64, 5.0, 3.0) == 1.0); + assertOrPanic(divTrunc(f64, -5.0, 3.0) == -1.0); + + comptime { + assertOrPanic( + 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600, + ); + assertOrPanic( + @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600, + ); + assertOrPanic( + 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, + ); + assertOrPanic( + @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, + ); + assertOrPanic( + @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, + ); + assertOrPanic( + @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, + ); + assertOrPanic( + 4126227191251978491697987544882340798050766755606969681711 % 10 == 1, + ); + } +} +fn div(comptime T: type, a: T, b: T) T { + return a / b; +} +fn divExact(comptime T: type, a: T, b: T) T { + return @divExact(a, b); +} +fn divFloor(comptime T: type, a: T, b: T) T { + return @divFloor(a, b); +} +fn divTrunc(comptime T: type, a: T, b: T) T { + return @divTrunc(a, b); +} + +test "@addWithOverflow" { + var result: u8 = undefined; + assertOrPanic(@addWithOverflow(u8, 250, 100, &result)); + assertOrPanic(!@addWithOverflow(u8, 100, 150, &result)); + assertOrPanic(result == 250); +} + +// TODO test mulWithOverflow +// TODO test subWithOverflow + +test "@shlWithOverflow" { + var result: u16 = undefined; + assertOrPanic(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); + assertOrPanic(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); + assertOrPanic(result == 0b1011111111111100); +} + +test "@clz" { + testClz(); + comptime testClz(); +} + +fn testClz() void { + assertOrPanic(clz(u8(0b00001010)) == 4); + assertOrPanic(clz(u8(0b10001010)) == 0); + assertOrPanic(clz(u8(0b00000000)) == 8); + assertOrPanic(clz(u128(0xffffffffffffffff)) == 64); + assertOrPanic(clz(u128(0x10000000000000000)) == 63); +} + +fn clz(x: var) usize { + return @clz(x); +} + +test "@ctz" { + testCtz(); + comptime testCtz(); +} + +fn testCtz() void { + assertOrPanic(ctz(u8(0b10100000)) == 5); + assertOrPanic(ctz(u8(0b10001010)) == 1); + assertOrPanic(ctz(u8(0b00000000)) == 8); +} + +fn ctz(x: var) usize { + return @ctz(x); +} + +test "assignment operators" { + var i: u32 = 0; + i += 5; + assertOrPanic(i == 5); + i -= 2; + assertOrPanic(i == 3); + i *= 20; + assertOrPanic(i == 60); + i /= 3; + assertOrPanic(i == 20); + i %= 11; + assertOrPanic(i == 9); + i <<= 1; + assertOrPanic(i == 18); + i >>= 2; + assertOrPanic(i == 4); + i = 6; + i &= 5; + assertOrPanic(i == 4); + i ^= 6; + assertOrPanic(i == 2); + i = 6; + i |= 3; + assertOrPanic(i == 7); +} + +test "three expr in a row" { + testThreeExprInARow(false, true); + comptime testThreeExprInARow(false, true); +} +fn testThreeExprInARow(f: bool, t: bool) void { + assertFalse(f or f or f); + assertFalse(t and t and f); + assertFalse(1 | 2 | 4 != 7); + assertFalse(3 ^ 6 ^ 8 != 13); + assertFalse(7 & 14 & 28 != 4); + assertFalse(9 << 1 << 2 != 9 << 3); + assertFalse(90 >> 1 >> 2 != 90 >> 3); + assertFalse(100 - 1 + 1000 != 1099); + assertFalse(5 * 4 / 2 % 3 != 1); + assertFalse(i32(i32(5)) != 5); + assertFalse(!!false); + assertFalse(i32(7) != --(i32(7))); +} +fn assertFalse(b: bool) void { + assertOrPanic(!b); +} + +test "const number literal" { + const one = 1; + const eleven = ten + one; + + assertOrPanic(eleven == 11); +} +const ten = 10; + +test "unsigned wrapping" { + testUnsignedWrappingEval(maxInt(u32)); + comptime testUnsignedWrappingEval(maxInt(u32)); +} +fn testUnsignedWrappingEval(x: u32) void { + const zero = x +% 1; + assertOrPanic(zero == 0); + const orig = zero -% 1; + assertOrPanic(orig == maxInt(u32)); +} + +test "signed wrapping" { + testSignedWrappingEval(maxInt(i32)); + comptime testSignedWrappingEval(maxInt(i32)); +} +fn testSignedWrappingEval(x: i32) void { + const min_val = x +% 1; + assertOrPanic(min_val == minInt(i32)); + const max_val = min_val -% 1; + assertOrPanic(max_val == maxInt(i32)); +} + +test "negation wrapping" { + testNegationWrappingEval(minInt(i16)); + comptime testNegationWrappingEval(minInt(i16)); +} +fn testNegationWrappingEval(x: i16) void { + assertOrPanic(x == -32768); + const neg = -%x; + assertOrPanic(neg == -32768); +} + +test "unsigned 64-bit division" { + test_u64_div(); + comptime test_u64_div(); +} +fn test_u64_div() void { + const result = divWithResult(1152921504606846976, 34359738365); + assertOrPanic(result.quotient == 33554432); + assertOrPanic(result.remainder == 100663296); +} +fn divWithResult(a: u64, b: u64) DivResult { + return DivResult{ + .quotient = a / b, + .remainder = a % b, + }; +} +const DivResult = struct { + quotient: u64, + remainder: u64, +}; + +test "binary not" { + assertOrPanic(comptime x: { + break :x ~u16(0b1010101010101010) == 0b0101010101010101; + }); + assertOrPanic(comptime x: { + break :x ~u64(2147483647) == 18446744071562067968; + }); + testBinaryNot(0b1010101010101010); +} + +fn testBinaryNot(x: u16) void { + assertOrPanic(~x == 0b0101010101010101); +} + +test "small int addition" { + var x: @IntType(false, 2) = 0; + assertOrPanic(x == 0); + + x += 1; + assertOrPanic(x == 1); + + x += 1; + assertOrPanic(x == 2); + + x += 1; + assertOrPanic(x == 3); + + var result: @typeOf(x) = 3; + assertOrPanic(@addWithOverflow(@typeOf(x), x, 1, &result)); + + assertOrPanic(result == 0); +} + +test "float equality" { + const x: f64 = 0.012; + const y: f64 = x + 1.0; + + testFloatEqualityImpl(x, y); + comptime testFloatEqualityImpl(x, y); +} + +fn testFloatEqualityImpl(x: f64, y: f64) void { + const y2 = x + 1.0; + assertOrPanic(y == y2); +} + +test "allow signed integer division/remainder when values are comptime known and positive or exact" { + assertOrPanic(5 / 3 == 1); + assertOrPanic(-5 / -3 == 1); + assertOrPanic(-6 / 3 == -2); + + assertOrPanic(5 % 3 == 2); + assertOrPanic(-6 % 3 == 0); +} + +test "hex float literal parsing" { + comptime assertOrPanic(0x1.0 == 1.0); +} + +test "quad hex float literal parsing in range" { + const a = 0x1.af23456789bbaaab347645365cdep+5; + const b = 0x1.dedafcff354b6ae9758763545432p-9; + const c = 0x1.2f34dd5f437e849b4baab754cdefp+4534; + const d = 0x1.edcbff8ad76ab5bf46463233214fp-435; +} + +test "quad hex float literal parsing accurate" { + const a: f128 = 0x1.1111222233334444555566667777p+0; + + // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing. + const expected: u128 = 0x3fff1111222233334444555566667777; + assertOrPanic(@bitCast(u128, a) == expected); +} + +test "hex float literal within range" { + const a = 0x1.0p16383; + const b = 0x0.1p16387; + const c = 0x1.0p-16382; +} + +test "truncating shift left" { + testShlTrunc(maxInt(u16)); + comptime testShlTrunc(maxInt(u16)); +} +fn testShlTrunc(x: u16) void { + const shifted = x << 1; + assertOrPanic(shifted == 65534); +} + +test "truncating shift right" { + testShrTrunc(maxInt(u16)); + comptime testShrTrunc(maxInt(u16)); +} +fn testShrTrunc(x: u16) void { + const shifted = x >> 1; + assertOrPanic(shifted == 32767); +} + +test "exact shift left" { + testShlExact(0b00110101); + comptime testShlExact(0b00110101); +} +fn testShlExact(x: u8) void { + const shifted = @shlExact(x, 2); + assertOrPanic(shifted == 0b11010100); +} + +test "exact shift right" { + testShrExact(0b10110100); + comptime testShrExact(0b10110100); +} +fn testShrExact(x: u8) void { + const shifted = @shrExact(x, 2); + assertOrPanic(shifted == 0b00101101); +} + +test "comptime_int addition" { + comptime { + assertOrPanic(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); + assertOrPanic(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); + } +} + +test "comptime_int multiplication" { + comptime { + assertOrPanic( + 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567, + ); + assertOrPanic( + 594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016, + ); + } +} + +test "comptime_int shifting" { + comptime { + assertOrPanic((u128(1) << 127) == 0x80000000000000000000000000000000); + } +} + +test "comptime_int multi-limb shift and mask" { + comptime { + var a = 0xefffffffa0000001eeeeeeefaaaaaaab; + + assertOrPanic(u32(a & 0xffffffff) == 0xaaaaaaab); + a >>= 32; + assertOrPanic(u32(a & 0xffffffff) == 0xeeeeeeef); + a >>= 32; + assertOrPanic(u32(a & 0xffffffff) == 0xa0000001); + a >>= 32; + assertOrPanic(u32(a & 0xffffffff) == 0xefffffff); + a >>= 32; + + assertOrPanic(a == 0); + } +} + +test "comptime_int multi-limb partial shift right" { + comptime { + var a = 0x1ffffffffeeeeeeee; + a >>= 16; + assertOrPanic(a == 0x1ffffffffeeee); + } +} + +test "xor" { + test_xor(); + comptime test_xor(); +} + +fn test_xor() void { + assertOrPanic(0xFF ^ 0x00 == 0xFF); + assertOrPanic(0xF0 ^ 0x0F == 0xFF); + assertOrPanic(0xFF ^ 0xF0 == 0x0F); + assertOrPanic(0xFF ^ 0x0F == 0xF0); + assertOrPanic(0xFF ^ 0xFF == 0x00); +} + +test "comptime_int xor" { + comptime { + assertOrPanic(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + assertOrPanic(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + assertOrPanic(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x0000000000000000FFFFFFFFFFFFFFFF); + assertOrPanic(0x0000000000000000FFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFF0000000000000000); + assertOrPanic(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000000000000000000000000000); + assertOrPanic(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0x00000000FFFFFFFF00000000FFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + assertOrPanic(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000FFFFFFFF00000000FFFFFFFF); + assertOrPanic(0x00000000FFFFFFFF00000000FFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFF00000000FFFFFFFF00000000); + } +} + +test "f128" { + test_f128(); + comptime test_f128(); +} + +fn make_f128(x: f128) f128 { + return x; +} + +fn test_f128() void { + assertOrPanic(@sizeOf(f128) == 16); + assertOrPanic(make_f128(1.0) == 1.0); + assertOrPanic(make_f128(1.0) != 1.1); + assertOrPanic(make_f128(1.0) > 0.9); + assertOrPanic(make_f128(1.0) >= 0.9); + assertOrPanic(make_f128(1.0) >= 1.0); + should_not_be_zero(1.0); +} + +fn should_not_be_zero(x: f128) void { + assertOrPanic(x != 0.0); +} + +test "comptime float rem int" { + comptime { + var x = f32(1) % 2; + assertOrPanic(x == 1.0); + } +} + +test "remainder division" { + comptime remdiv(f16); + comptime remdiv(f32); + comptime remdiv(f64); + comptime remdiv(f128); + remdiv(f16); + remdiv(f64); + remdiv(f128); +} + +fn remdiv(comptime T: type) void { + assertOrPanic(T(1) == T(1) % T(2)); + assertOrPanic(T(1) == T(7) % T(3)); +} + +test "@sqrt" { + testSqrt(f64, 12.0); + comptime testSqrt(f64, 12.0); + testSqrt(f32, 13.0); + comptime testSqrt(f32, 13.0); + testSqrt(f16, 13.0); + comptime testSqrt(f16, 13.0); + + const x = 14.0; + const y = x * x; + const z = @sqrt(@typeOf(y), y); + comptime assertOrPanic(z == x); +} + +fn testSqrt(comptime T: type, x: T) void { + assertOrPanic(@sqrt(T, x * x) == x); +} + +test "comptime_int param and return" { + const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702); + assertOrPanic(a == 137114567242441932203689521744947848950); + + const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768); + assertOrPanic(b == 985095453608931032642182098849559179469148836107390954364380); +} + +fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int { + return a + b; +} diff --git a/test/stage1/behavior/merge_error_sets.zig b/test/stage1/behavior/merge_error_sets.zig new file mode 100644 index 0000000000..147b580232 --- /dev/null +++ b/test/stage1/behavior/merge_error_sets.zig @@ -0,0 +1,21 @@ +const A = error{ + FileNotFound, + NotDir, +}; +const B = error{OutOfMemory}; + +const C = A || B; + +fn foo() C!void { + return error.NotDir; +} + +test "merge error sets" { + if (foo()) { + @panic("unexpected"); + } else |err| switch (err) { + error.OutOfMemory => @panic("unexpected"), + error.FileNotFound => @panic("unexpected"), + error.NotDir => {}, + } +} diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig new file mode 100644 index 0000000000..8d2555dddd --- /dev/null +++ b/test/stage1/behavior/misc.zig @@ -0,0 +1,687 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const mem = std.mem; +const cstr = std.cstr; +const builtin = @import("builtin"); +const maxInt = std.math.maxInt; + +// normal comment + +/// this is a documentation comment +/// doc comment line 2 +fn emptyFunctionWithComments() void {} + +test "empty function with comments" { + emptyFunctionWithComments(); +} + +comptime { + @export("disabledExternFn", disabledExternFn, builtin.GlobalLinkage.Internal); +} + +extern fn disabledExternFn() void {} + +test "call disabled extern fn" { + disabledExternFn(); +} + +test "@IntType builtin" { + assertOrPanic(@IntType(true, 8) == i8); + assertOrPanic(@IntType(true, 16) == i16); + assertOrPanic(@IntType(true, 32) == i32); + assertOrPanic(@IntType(true, 64) == i64); + + assertOrPanic(@IntType(false, 8) == u8); + assertOrPanic(@IntType(false, 16) == u16); + assertOrPanic(@IntType(false, 32) == u32); + assertOrPanic(@IntType(false, 64) == u64); + + assertOrPanic(i8.bit_count == 8); + assertOrPanic(i16.bit_count == 16); + assertOrPanic(i32.bit_count == 32); + assertOrPanic(i64.bit_count == 64); + + assertOrPanic(i8.is_signed); + assertOrPanic(i16.is_signed); + assertOrPanic(i32.is_signed); + assertOrPanic(i64.is_signed); + assertOrPanic(isize.is_signed); + + assertOrPanic(!u8.is_signed); + assertOrPanic(!u16.is_signed); + assertOrPanic(!u32.is_signed); + assertOrPanic(!u64.is_signed); + assertOrPanic(!usize.is_signed); +} + +test "floating point primitive bit counts" { + assertOrPanic(f16.bit_count == 16); + assertOrPanic(f32.bit_count == 32); + assertOrPanic(f64.bit_count == 64); +} + +test "short circuit" { + testShortCircuit(false, true); + comptime testShortCircuit(false, true); +} + +fn testShortCircuit(f: bool, t: bool) void { + var hit_1 = f; + var hit_2 = f; + var hit_3 = f; + var hit_4 = f; + + if (t or x: { + assertOrPanic(f); + break :x f; + }) { + hit_1 = t; + } + if (f or x: { + hit_2 = t; + break :x f; + }) { + assertOrPanic(f); + } + + if (t and x: { + hit_3 = t; + break :x f; + }) { + assertOrPanic(f); + } + if (f and x: { + assertOrPanic(f); + break :x f; + }) { + assertOrPanic(f); + } else { + hit_4 = t; + } + assertOrPanic(hit_1); + assertOrPanic(hit_2); + assertOrPanic(hit_3); + assertOrPanic(hit_4); +} + +test "truncate" { + assertOrPanic(testTruncate(0x10fd) == 0xfd); +} +fn testTruncate(x: u32) u8 { + return @truncate(u8, x); +} + +fn first4KeysOfHomeRow() []const u8 { + return "aoeu"; +} + +test "return string from function" { + assertOrPanic(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); +} + +const g1: i32 = 1233 + 1; +var g2: i32 = 0; + +test "global variables" { + assertOrPanic(g2 == 0); + g2 = g1; + assertOrPanic(g2 == 1234); +} + +test "memcpy and memset intrinsics" { + var foo: [20]u8 = undefined; + var bar: [20]u8 = undefined; + + @memset(foo[0..].ptr, 'A', foo.len); + @memcpy(bar[0..].ptr, foo[0..].ptr, bar.len); + + if (bar[11] != 'A') unreachable; +} + +test "builtin static eval" { + const x: i32 = comptime x: { + break :x 1 + 2 + 3; + }; + assertOrPanic(x == comptime 6); +} + +test "slicing" { + var array: [20]i32 = undefined; + + array[5] = 1234; + + var slice = array[5..10]; + + if (slice.len != 5) unreachable; + + const ptr = &slice[0]; + if (ptr.* != 1234) unreachable; + + var slice_rest = array[10..]; + if (slice_rest.len != 10) unreachable; +} + +test "constant equal function pointers" { + const alias = emptyFn; + assertOrPanic(comptime x: { + break :x emptyFn == alias; + }); +} + +fn emptyFn() void {} + +test "hex escape" { + assertOrPanic(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); +} + +test "string concatenation" { + assertOrPanic(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); +} + +test "array mult operator" { + assertOrPanic(mem.eql(u8, "ab" ** 5, "ababababab")); +} + +test "string escapes" { + assertOrPanic(mem.eql(u8, "\"", "\x22")); + assertOrPanic(mem.eql(u8, "\'", "\x27")); + assertOrPanic(mem.eql(u8, "\n", "\x0a")); + assertOrPanic(mem.eql(u8, "\r", "\x0d")); + assertOrPanic(mem.eql(u8, "\t", "\x09")); + assertOrPanic(mem.eql(u8, "\\", "\x5c")); + assertOrPanic(mem.eql(u8, "\u1234\u0069", "\xe1\x88\xb4\x69")); +} + +test "multiline string" { + const s1 = + \\one + \\two) + \\three + ; + const s2 = "one\ntwo)\nthree"; + assertOrPanic(mem.eql(u8, s1, s2)); +} + +test "multiline C string" { + const s1 = + c\\one + c\\two) + c\\three + ; + const s2 = c"one\ntwo)\nthree"; + assertOrPanic(cstr.cmp(s1, s2) == 0); +} + +test "type equality" { + assertOrPanic(*const u8 != *u8); +} + +const global_a: i32 = 1234; +const global_b: *const i32 = &global_a; +const global_c: *const f32 = @ptrCast(*const f32, global_b); +test "compile time global reinterpret" { + const d = @ptrCast(*const i32, global_c); + assertOrPanic(d.* == 1234); +} + +test "explicit cast maybe pointers" { + const a: ?*i32 = undefined; + const b: ?*f32 = @ptrCast(?*f32, a); +} + +test "generic malloc free" { + const a = memAlloc(u8, 10) catch unreachable; + memFree(u8, a); +} +var some_mem: [100]u8 = undefined; +fn memAlloc(comptime T: type, n: usize) anyerror![]T { + return @ptrCast([*]T, &some_mem[0])[0..n]; +} +fn memFree(comptime T: type, memory: []T) void {} + +test "cast undefined" { + const array: [100]u8 = undefined; + const slice = ([]const u8)(array); + testCastUndefined(slice); +} +fn testCastUndefined(x: []const u8) void {} + +test "cast small unsigned to larger signed" { + assertOrPanic(castSmallUnsignedToLargerSigned1(200) == i16(200)); + assertOrPanic(castSmallUnsignedToLargerSigned2(9999) == i64(9999)); +} +fn castSmallUnsignedToLargerSigned1(x: u8) i16 { + return x; +} +fn castSmallUnsignedToLargerSigned2(x: u16) i64 { + return x; +} + +test "implicit cast after unreachable" { + assertOrPanic(outer() == 1234); +} +fn inner() i32 { + return 1234; +} +fn outer() i64 { + return inner(); +} + +test "pointer dereferencing" { + var x = i32(3); + const y = &x; + + y.* += 1; + + assertOrPanic(x == 4); + assertOrPanic(y.* == 4); +} + +test "call result of if else expression" { + assertOrPanic(mem.eql(u8, f2(true), "a")); + assertOrPanic(mem.eql(u8, f2(false), "b")); +} +fn f2(x: bool) []const u8 { + return (if (x) fA else fB)(); +} +fn fA() []const u8 { + return "a"; +} +fn fB() []const u8 { + return "b"; +} + +test "const expression eval handling of variables" { + var x = true; + while (x) { + x = false; + } +} + +test "constant enum initialization with differing sizes" { + test3_1(test3_foo); + test3_2(test3_bar); +} +const Test3Foo = union(enum) { + One: void, + Two: f32, + Three: Test3Point, +}; +const Test3Point = struct { + x: i32, + y: i32, +}; +const test3_foo = Test3Foo{ + .Three = Test3Point{ + .x = 3, + .y = 4, + }, +}; +const test3_bar = Test3Foo{ .Two = 13 }; +fn test3_1(f: Test3Foo) void { + switch (f) { + Test3Foo.Three => |pt| { + assertOrPanic(pt.x == 3); + assertOrPanic(pt.y == 4); + }, + else => unreachable, + } +} +fn test3_2(f: Test3Foo) void { + switch (f) { + Test3Foo.Two => |x| { + assertOrPanic(x == 13); + }, + else => unreachable, + } +} + +test "character literals" { + assertOrPanic('\'' == single_quote); +} +const single_quote = '\''; + +test "take address of parameter" { + testTakeAddressOfParameter(12.34); +} +fn testTakeAddressOfParameter(f: f32) void { + const f_ptr = &f; + assertOrPanic(f_ptr.* == 12.34); +} + +test "pointer comparison" { + const a = ([]const u8)("a"); + const b = &a; + assertOrPanic(ptrEql(b, b)); +} +fn ptrEql(a: *const []const u8, b: *const []const u8) bool { + return a == b; +} + +test "C string concatenation" { + const a = c"OK" ++ c" IT " ++ c"WORKED"; + const b = c"OK IT WORKED"; + + const len = cstr.len(b); + const len_with_null = len + 1; + { + var i: u32 = 0; + while (i < len_with_null) : (i += 1) { + assertOrPanic(a[i] == b[i]); + } + } + assertOrPanic(a[len] == 0); + assertOrPanic(b[len] == 0); +} + +test "cast slice to u8 slice" { + assertOrPanic(@sizeOf(i32) == 4); + var big_thing_array = []i32{ 1, 2, 3, 4 }; + const big_thing_slice: []i32 = big_thing_array[0..]; + const bytes = @sliceToBytes(big_thing_slice); + assertOrPanic(bytes.len == 4 * 4); + bytes[4] = 0; + bytes[5] = 0; + bytes[6] = 0; + bytes[7] = 0; + assertOrPanic(big_thing_slice[1] == 0); + const big_thing_again = @bytesToSlice(i32, bytes); + assertOrPanic(big_thing_again[2] == 3); + big_thing_again[2] = -1; + assertOrPanic(bytes[8] == maxInt(u8)); + assertOrPanic(bytes[9] == maxInt(u8)); + assertOrPanic(bytes[10] == maxInt(u8)); + assertOrPanic(bytes[11] == maxInt(u8)); +} + +test "pointer to void return type" { + testPointerToVoidReturnType() catch unreachable; +} +fn testPointerToVoidReturnType() anyerror!void { + const a = testPointerToVoidReturnType2(); + return a.*; +} +const test_pointer_to_void_return_type_x = void{}; +fn testPointerToVoidReturnType2() *const void { + return &test_pointer_to_void_return_type_x; +} + +test "non const ptr to aliased type" { + const int = i32; + assertOrPanic(?*int == ?*i32); +} + +test "array 2D const double ptr" { + const rect_2d_vertexes = [][1]f32{ + []f32{1.0}, + []f32{2.0}, + }; + testArray2DConstDoublePtr(&rect_2d_vertexes[0][0]); +} + +fn testArray2DConstDoublePtr(ptr: *const f32) void { + const ptr2 = @ptrCast([*]const f32, ptr); + assertOrPanic(ptr2[0] == 1.0); + assertOrPanic(ptr2[1] == 2.0); +} + +const Tid = builtin.TypeId; +const AStruct = struct { + x: i32, +}; +const AnEnum = enum { + One, + Two, +}; +const AUnionEnum = union(enum) { + One: i32, + Two: void, +}; +const AUnion = union { + One: void, + Two: void, +}; + +test "@typeId" { + comptime { + assertOrPanic(@typeId(type) == Tid.Type); + assertOrPanic(@typeId(void) == Tid.Void); + assertOrPanic(@typeId(bool) == Tid.Bool); + assertOrPanic(@typeId(noreturn) == Tid.NoReturn); + assertOrPanic(@typeId(i8) == Tid.Int); + assertOrPanic(@typeId(u8) == Tid.Int); + assertOrPanic(@typeId(i64) == Tid.Int); + assertOrPanic(@typeId(u64) == Tid.Int); + assertOrPanic(@typeId(f32) == Tid.Float); + assertOrPanic(@typeId(f64) == Tid.Float); + assertOrPanic(@typeId(*f32) == Tid.Pointer); + assertOrPanic(@typeId([2]u8) == Tid.Array); + assertOrPanic(@typeId(AStruct) == Tid.Struct); + assertOrPanic(@typeId(@typeOf(1)) == Tid.ComptimeInt); + assertOrPanic(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); + assertOrPanic(@typeId(@typeOf(undefined)) == Tid.Undefined); + assertOrPanic(@typeId(@typeOf(null)) == Tid.Null); + assertOrPanic(@typeId(?i32) == Tid.Optional); + assertOrPanic(@typeId(anyerror!i32) == Tid.ErrorUnion); + assertOrPanic(@typeId(anyerror) == Tid.ErrorSet); + assertOrPanic(@typeId(AnEnum) == Tid.Enum); + assertOrPanic(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); + assertOrPanic(@typeId(AUnionEnum) == Tid.Union); + assertOrPanic(@typeId(AUnion) == Tid.Union); + assertOrPanic(@typeId(fn () void) == Tid.Fn); + assertOrPanic(@typeId(@typeOf(builtin)) == Tid.Namespace); + // TODO bound fn + // TODO arg tuple + // TODO opaque + } +} + +test "@typeName" { + const Struct = struct {}; + const Union = union { + unused: u8, + }; + const Enum = enum { + Unused, + }; + comptime { + assertOrPanic(mem.eql(u8, @typeName(i64), "i64")); + assertOrPanic(mem.eql(u8, @typeName(*usize), "*usize")); + // https://github.com/ziglang/zig/issues/675 + assertOrPanic(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)")); + assertOrPanic(mem.eql(u8, @typeName(Struct), "Struct")); + assertOrPanic(mem.eql(u8, @typeName(Union), "Union")); + assertOrPanic(mem.eql(u8, @typeName(Enum), "Enum")); + } +} + +fn TypeFromFn(comptime T: type) type { + return struct {}; +} + +test "double implicit cast in same expression" { + var x = i32(u16(nine())); + assertOrPanic(x == 9); +} +fn nine() u8 { + return 9; +} + +test "global variable initialized to global variable array element" { + assertOrPanic(global_ptr == &gdt[0]); +} +const GDTEntry = struct { + field: i32, +}; +var gdt = []GDTEntry{ + GDTEntry{ .field = 1 }, + GDTEntry{ .field = 2 }, +}; +var global_ptr = &gdt[0]; + +// can't really run this test but we can make sure it has no compile error +// and generates code +const vram = @intToPtr([*]volatile u8, 0x20000000)[0..0x8000]; +export fn writeToVRam() void { + vram[0] = 'X'; +} + +const OpaqueA = @OpaqueType(); +const OpaqueB = @OpaqueType(); +test "@OpaqueType" { + assertOrPanic(*OpaqueA != *OpaqueB); + assertOrPanic(mem.eql(u8, @typeName(OpaqueA), "OpaqueA")); + assertOrPanic(mem.eql(u8, @typeName(OpaqueB), "OpaqueB")); +} + +test "variable is allowed to be a pointer to an opaque type" { + var x: i32 = 1234; + _ = hereIsAnOpaqueType(@ptrCast(*OpaqueA, &x)); +} +fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA { + var a = ptr; + return a; +} + +test "comptime if inside runtime while which unconditionally breaks" { + testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true); + comptime testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true); +} +fn testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(cond: bool) void { + while (cond) { + if (false) {} + break; + } +} + +test "implicit comptime while" { + while (false) { + @compileError("bad"); + } +} + +fn fnThatClosesOverLocalConst() type { + const c = 1; + return struct { + fn g() i32 { + return c; + } + }; +} + +test "function closes over local const" { + const x = fnThatClosesOverLocalConst().g(); + assertOrPanic(x == 1); +} + +test "cold function" { + thisIsAColdFn(); + comptime thisIsAColdFn(); +} + +fn thisIsAColdFn() void { + @setCold(true); +} + +const PackedStruct = packed struct { + a: u8, + b: u8, +}; +const PackedUnion = packed union { + a: u8, + b: u32, +}; +const PackedEnum = packed enum { + A, + B, +}; + +test "packed struct, enum, union parameters in extern function" { + testPackedStuff(&(PackedStruct{ + .a = 1, + .b = 2, + }), &(PackedUnion{ .a = 1 }), PackedEnum.A); +} + +export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion, c: PackedEnum) void {} + +test "slicing zero length array" { + const s1 = ""[0..]; + const s2 = ([]u32{})[0..]; + assertOrPanic(s1.len == 0); + assertOrPanic(s2.len == 0); + assertOrPanic(mem.eql(u8, s1, "")); + assertOrPanic(mem.eql(u32, s2, []u32{})); +} + +const addr1 = @ptrCast(*const u8, emptyFn); +test "comptime cast fn to ptr" { + const addr2 = @ptrCast(*const u8, emptyFn); + comptime assertOrPanic(addr1 == addr2); +} + +test "equality compare fn ptrs" { + var a = emptyFn; + assertOrPanic(a == a); +} + +test "self reference through fn ptr field" { + const S = struct { + const A = struct { + f: fn (A) u8, + }; + + fn foo(a: A) u8 { + return 12; + } + }; + var a: S.A = undefined; + a.f = S.foo; + assertOrPanic(a.f(a) == 12); +} + +test "volatile load and store" { + var number: i32 = 1234; + const ptr = (*volatile i32)(&number); + ptr.* += 1; + assertOrPanic(ptr.* == 1235); +} + +test "slice string literal has type []const u8" { + comptime { + assertOrPanic(@typeOf("aoeu"[0..]) == []const u8); + const array = []i32{ 1, 2, 3, 4 }; + assertOrPanic(@typeOf(array[0..]) == []const i32); + } +} + +test "pointer child field" { + assertOrPanic((*u32).Child == u32); +} + +test "struct inside function" { + testStructInFn(); + comptime testStructInFn(); +} + +fn testStructInFn() void { + const BlockKind = u32; + + const Block = struct { + kind: BlockKind, + }; + + var block = Block{ .kind = 1234 }; + + block.kind += 1; + + assertOrPanic(block.kind == 1235); +} + +test "fn call returning scalar optional in equality expression" { + assertOrPanic(getNull() == null); +} + +fn getNull() ?*i32 { + return null; +} diff --git a/test/stage1/behavior/namespace_depends_on_compile_var/a.zig b/test/stage1/behavior/namespace_depends_on_compile_var/a.zig new file mode 100644 index 0000000000..5ce0e94f8b --- /dev/null +++ b/test/stage1/behavior/namespace_depends_on_compile_var/a.zig @@ -0,0 +1 @@ +pub const a_bool = true; diff --git a/test/stage1/behavior/namespace_depends_on_compile_var/b.zig b/test/stage1/behavior/namespace_depends_on_compile_var/b.zig new file mode 100644 index 0000000000..a12a54b589 --- /dev/null +++ b/test/stage1/behavior/namespace_depends_on_compile_var/b.zig @@ -0,0 +1 @@ +pub const a_bool = false; diff --git a/test/stage1/behavior/namespace_depends_on_compile_var/index.zig b/test/stage1/behavior/namespace_depends_on_compile_var/index.zig new file mode 100644 index 0000000000..fe3e0cc020 --- /dev/null +++ b/test/stage1/behavior/namespace_depends_on_compile_var/index.zig @@ -0,0 +1,14 @@ +const builtin = @import("builtin"); +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "namespace depends on compile var" { + if (some_namespace.a_bool) { + assertOrPanic(some_namespace.a_bool); + } else { + assertOrPanic(!some_namespace.a_bool); + } +} +const some_namespace = switch (builtin.os) { + builtin.Os.linux => @import("a.zig"), + else => @import("b.zig"), +}; diff --git a/test/stage1/behavior/new_stack_call.zig b/test/stage1/behavior/new_stack_call.zig new file mode 100644 index 0000000000..b9ae2d27cd --- /dev/null +++ b/test/stage1/behavior/new_stack_call.zig @@ -0,0 +1,26 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; + +var new_stack_bytes: [1024]u8 = undefined; + +test "calling a function with a new stack" { + const arg = 1234; + + const a = @newStackCall(new_stack_bytes[0..512], targetFunction, arg); + const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg); + _ = targetFunction(arg); + + assertOrPanic(arg == 1234); + assertOrPanic(a < b); +} + +fn targetFunction(x: i32) usize { + assertOrPanic(x == 1234); + + var local_variable: i32 = 42; + const ptr = &local_variable; + ptr.* += 1; + + assertOrPanic(local_variable == 43); + return @ptrToInt(ptr); +} diff --git a/test/stage1/behavior/null.zig b/test/stage1/behavior/null.zig new file mode 100644 index 0000000000..e2f86a05ba --- /dev/null +++ b/test/stage1/behavior/null.zig @@ -0,0 +1,162 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "optional type" { + const x: ?bool = true; + + if (x) |y| { + if (y) { + // OK + } else { + unreachable; + } + } else { + unreachable; + } + + const next_x: ?i32 = null; + + const z = next_x orelse 1234; + + assertOrPanic(z == 1234); + + const final_x: ?i32 = 13; + + const num = final_x orelse unreachable; + + assertOrPanic(num == 13); +} + +test "test maybe object and get a pointer to the inner value" { + var maybe_bool: ?bool = true; + + if (maybe_bool) |*b| { + b.* = false; + } + + assertOrPanic(maybe_bool.? == false); +} + +test "rhs maybe unwrap return" { + const x: ?bool = true; + const y = x orelse return; +} + +test "maybe return" { + maybeReturnImpl(); + comptime maybeReturnImpl(); +} + +fn maybeReturnImpl() void { + assertOrPanic(foo(1235).?); + if (foo(null) != null) unreachable; + assertOrPanic(!foo(1234).?); +} + +fn foo(x: ?i32) ?bool { + const value = x orelse return null; + return value > 1234; +} + +test "if var maybe pointer" { + assertOrPanic(shouldBeAPlus1(Particle{ + .a = 14, + .b = 1, + .c = 1, + .d = 1, + }) == 15); +} +fn shouldBeAPlus1(p: Particle) u64 { + var maybe_particle: ?Particle = p; + if (maybe_particle) |*particle| { + particle.a += 1; + } + if (maybe_particle) |particle| { + return particle.a; + } + return 0; +} +const Particle = struct { + a: u64, + b: u64, + c: u64, + d: u64, +}; + +test "null literal outside function" { + const is_null = here_is_a_null_literal.context == null; + assertOrPanic(is_null); + + const is_non_null = here_is_a_null_literal.context != null; + assertOrPanic(!is_non_null); +} +const SillyStruct = struct { + context: ?i32, +}; +const here_is_a_null_literal = SillyStruct{ .context = null }; + +test "test null runtime" { + testTestNullRuntime(null); +} +fn testTestNullRuntime(x: ?i32) void { + assertOrPanic(x == null); + assertOrPanic(!(x != null)); +} + +test "optional void" { + optionalVoidImpl(); + comptime optionalVoidImpl(); +} + +fn optionalVoidImpl() void { + assertOrPanic(bar(null) == null); + assertOrPanic(bar({}) != null); +} + +fn bar(x: ?void) ?void { + if (x) |_| { + return {}; + } else { + return null; + } +} + +const StructWithOptional = struct { + field: ?i32, +}; + +var struct_with_optional: StructWithOptional = undefined; + +test "unwrap optional which is field of global var" { + struct_with_optional.field = null; + if (struct_with_optional.field) |payload| { + unreachable; + } + struct_with_optional.field = 1234; + if (struct_with_optional.field) |payload| { + assertOrPanic(payload == 1234); + } else { + unreachable; + } +} + +test "null with default unwrap" { + const x: i32 = null orelse 1; + assertOrPanic(x == 1); +} + +test "optional types" { + comptime { + const opt_type_struct = StructWithOptionalType{ .t = u8 }; + assertOrPanic(opt_type_struct.t != null and opt_type_struct.t.? == u8); + } +} + +const StructWithOptionalType = struct { + t: ?type, +}; + +test "optional pointer to 0 bit type null value at runtime" { + const EmptyStruct = struct {}; + var x: ?*EmptyStruct = null; + assertOrPanic(x == null); +} diff --git a/test/stage1/behavior/optional.zig b/test/stage1/behavior/optional.zig new file mode 100644 index 0000000000..14692cb1ea --- /dev/null +++ b/test/stage1/behavior/optional.zig @@ -0,0 +1,81 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +pub const EmptyStruct = struct {}; + +test "optional pointer to size zero struct" { + var e = EmptyStruct{}; + var o: ?*EmptyStruct = &e; + assertOrPanic(o != null); +} + +test "equality compare nullable pointers" { + testNullPtrsEql(); + comptime testNullPtrsEql(); +} + +fn testNullPtrsEql() void { + var number: i32 = 1234; + + var x: ?*i32 = null; + var y: ?*i32 = null; + assertOrPanic(x == y); + y = &number; + assertOrPanic(x != y); + assertOrPanic(x != &number); + assertOrPanic(&number != x); + x = &number; + assertOrPanic(x == y); + assertOrPanic(x == &number); + assertOrPanic(&number == x); +} + +test "address of unwrap optional" { + const S = struct { + const Foo = struct { + a: i32, + }; + + var global: ?Foo = null; + + pub fn getFoo() anyerror!*Foo { + return &global.?; + } + }; + S.global = S.Foo{ .a = 1234 }; + const foo = S.getFoo() catch unreachable; + assertOrPanic(foo.a == 1234); +} + +test "passing an optional integer as a parameter" { + const S = struct { + fn entry() bool { + var x: i32 = 1234; + return foo(x); + } + + fn foo(x: ?i32) bool { + return x.? == 1234; + } + }; + assertOrPanic(S.entry()); + comptime assertOrPanic(S.entry()); +} + +test "unwrap function call with optional pointer return value" { + const S = struct { + fn entry() void { + assertOrPanic(foo().?.* == 1234); + assertOrPanic(bar() == null); + } + const global: i32 = 1234; + fn foo() ?*const i32 { + return &global; + } + fn bar() ?*i32 { + return null; + } + }; + S.entry(); + // TODO https://github.com/ziglang/zig/issues/1901 + //comptime S.entry(); +} diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig new file mode 100644 index 0000000000..1142d89ab5 --- /dev/null +++ b/test/stage1/behavior/pointers.zig @@ -0,0 +1,44 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; + +test "dereference pointer" { + comptime testDerefPtr(); + testDerefPtr(); +} + +fn testDerefPtr() void { + var x: i32 = 1234; + var y = &x; + y.* += 1; + assertOrPanic(x == 1235); +} + +test "pointer arithmetic" { + var ptr = c"abcd"; + + assertOrPanic(ptr[0] == 'a'); + ptr += 1; + assertOrPanic(ptr[0] == 'b'); + ptr += 1; + assertOrPanic(ptr[0] == 'c'); + ptr += 1; + assertOrPanic(ptr[0] == 'd'); + ptr += 1; + assertOrPanic(ptr[0] == 0); + ptr -= 1; + assertOrPanic(ptr[0] == 'd'); + ptr -= 1; + assertOrPanic(ptr[0] == 'c'); + ptr -= 1; + assertOrPanic(ptr[0] == 'b'); + ptr -= 1; + assertOrPanic(ptr[0] == 'a'); +} + +test "double pointer parsing" { + comptime assertOrPanic(PtrOf(PtrOf(i32)) == **i32); +} + +fn PtrOf(comptime T: type) type { + return *T; +} diff --git a/test/stage1/behavior/popcount.zig b/test/stage1/behavior/popcount.zig new file mode 100644 index 0000000000..f7f8bb523b --- /dev/null +++ b/test/stage1/behavior/popcount.zig @@ -0,0 +1,25 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "@popCount" { + comptime testPopCount(); + testPopCount(); +} + +fn testPopCount() void { + { + var x: u32 = 0xaa; + assertOrPanic(@popCount(x) == 4); + } + { + var x: u32 = 0xaaaaaaaa; + assertOrPanic(@popCount(x) == 16); + } + { + var x: i16 = -1; + assertOrPanic(@popCount(x) == 16); + } + comptime { + assertOrPanic(@popCount(0b11111111000110001100010000100001000011000011100101010001) == 24); + } +} + diff --git a/test/stage1/behavior/ptrcast.zig b/test/stage1/behavior/ptrcast.zig new file mode 100644 index 0000000000..54c3dda849 --- /dev/null +++ b/test/stage1/behavior/ptrcast.zig @@ -0,0 +1,52 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; + +test "reinterpret bytes as integer with nonzero offset" { + testReinterpretBytesAsInteger(); + comptime testReinterpretBytesAsInteger(); +} + +fn testReinterpretBytesAsInteger() void { + const bytes = "\x12\x34\x56\x78\xab"; + const expected = switch (builtin.endian) { + builtin.Endian.Little => 0xab785634, + builtin.Endian.Big => 0x345678ab, + }; + assertOrPanic(@ptrCast(*align(1) const u32, bytes[1..5].ptr).* == expected); +} + +test "reinterpret bytes of an array into an extern struct" { + testReinterpretBytesAsExternStruct(); + comptime testReinterpretBytesAsExternStruct(); +} + +fn testReinterpretBytesAsExternStruct() void { + var bytes align(2) = []u8{ 1, 2, 3, 4, 5, 6 }; + + const S = extern struct { + a: u8, + b: u16, + c: u8, + }; + + var ptr = @ptrCast(*const S, &bytes); + var val = ptr.c; + assertOrPanic(val == 5); +} + +test "reinterpret struct field at comptime" { + const numLittle = comptime Bytes.init(0x12345678); + assertOrPanic(std.mem.eql(u8, []u8{ 0x78, 0x56, 0x34, 0x12 }, numLittle.bytes)); +} + +const Bytes = struct { + bytes: [4]u8, + + pub fn init(v: u32) Bytes { + var res: Bytes = undefined; + @ptrCast(*align(1) u32, &res.bytes).* = v; + + return res; + } +}; diff --git a/test/stage1/behavior/pub_enum/index.zig b/test/stage1/behavior/pub_enum/index.zig new file mode 100644 index 0000000000..181113f6bf --- /dev/null +++ b/test/stage1/behavior/pub_enum/index.zig @@ -0,0 +1,13 @@ +const other = @import("other.zig"); +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "pub enum" { + pubEnumTest(other.APubEnum.Two); +} +fn pubEnumTest(foo: other.APubEnum) void { + assertOrPanic(foo == other.APubEnum.Two); +} + +test "cast with imported symbol" { + assertOrPanic(other.size_t(42) == 42); +} diff --git a/test/stage1/behavior/pub_enum/other.zig b/test/stage1/behavior/pub_enum/other.zig new file mode 100644 index 0000000000..c663950383 --- /dev/null +++ b/test/stage1/behavior/pub_enum/other.zig @@ -0,0 +1,6 @@ +pub const APubEnum = enum { + One, + Two, + Three, +}; +pub const size_t = u64; diff --git a/test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig b/test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig new file mode 100644 index 0000000000..acbe6b2459 --- /dev/null +++ b/test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig @@ -0,0 +1,37 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const mem = @import("std").mem; + +var ok: bool = false; +test "reference a variable in an if after an if in the 2nd switch prong" { + foo(true, Num.Two, false, "aoeu"); + assertOrPanic(!ok); + foo(false, Num.One, false, "aoeu"); + assertOrPanic(!ok); + foo(true, Num.One, false, "aoeu"); + assertOrPanic(ok); +} + +const Num = enum { + One, + Two, +}; + +fn foo(c: bool, k: Num, c2: bool, b: []const u8) void { + switch (k) { + Num.Two => {}, + Num.One => { + if (c) { + const output_path = b; + + if (c2) {} + + a(output_path); + } + }, + } +} + +fn a(x: []const u8) void { + assertOrPanic(mem.eql(u8, x, "aoeu")); + ok = true; +} diff --git a/test/stage1/behavior/reflection.zig b/test/stage1/behavior/reflection.zig new file mode 100644 index 0000000000..f4c142e0f7 --- /dev/null +++ b/test/stage1/behavior/reflection.zig @@ -0,0 +1,96 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const mem = @import("std").mem; +const reflection = @This(); + +test "reflection: array, pointer, optional, error union type child" { + comptime { + assertOrPanic(([10]u8).Child == u8); + assertOrPanic((*u8).Child == u8); + assertOrPanic((anyerror!u8).Payload == u8); + assertOrPanic((?u8).Child == u8); + } +} + +test "reflection: function return type, var args, and param types" { + comptime { + assertOrPanic(@typeOf(dummy).ReturnType == i32); + assertOrPanic(!@typeOf(dummy).is_var_args); + assertOrPanic(@typeOf(dummy_varargs).is_var_args); + assertOrPanic(@typeOf(dummy).arg_count == 3); + assertOrPanic(@ArgType(@typeOf(dummy), 0) == bool); + assertOrPanic(@ArgType(@typeOf(dummy), 1) == i32); + assertOrPanic(@ArgType(@typeOf(dummy), 2) == f32); + } +} + +fn dummy(a: bool, b: i32, c: f32) i32 { + return 1234; +} +fn dummy_varargs(args: ...) void {} + +test "reflection: struct member types and names" { + comptime { + assertOrPanic(@memberCount(Foo) == 3); + + assertOrPanic(@memberType(Foo, 0) == i32); + assertOrPanic(@memberType(Foo, 1) == bool); + assertOrPanic(@memberType(Foo, 2) == void); + + assertOrPanic(mem.eql(u8, @memberName(Foo, 0), "one")); + assertOrPanic(mem.eql(u8, @memberName(Foo, 1), "two")); + assertOrPanic(mem.eql(u8, @memberName(Foo, 2), "three")); + } +} + +test "reflection: enum member types and names" { + comptime { + assertOrPanic(@memberCount(Bar) == 4); + + assertOrPanic(@memberType(Bar, 0) == void); + assertOrPanic(@memberType(Bar, 1) == i32); + assertOrPanic(@memberType(Bar, 2) == bool); + assertOrPanic(@memberType(Bar, 3) == f64); + + assertOrPanic(mem.eql(u8, @memberName(Bar, 0), "One")); + assertOrPanic(mem.eql(u8, @memberName(Bar, 1), "Two")); + assertOrPanic(mem.eql(u8, @memberName(Bar, 2), "Three")); + assertOrPanic(mem.eql(u8, @memberName(Bar, 3), "Four")); + } +} + +test "reflection: @field" { + var f = Foo{ + .one = 42, + .two = true, + .three = void{}, + }; + + assertOrPanic(f.one == f.one); + assertOrPanic(@field(f, "o" ++ "ne") == f.one); + assertOrPanic(@field(f, "t" ++ "wo") == f.two); + assertOrPanic(@field(f, "th" ++ "ree") == f.three); + assertOrPanic(@field(Foo, "const" ++ "ant") == Foo.constant); + assertOrPanic(@field(Bar, "O" ++ "ne") == Bar.One); + assertOrPanic(@field(Bar, "T" ++ "wo") == Bar.Two); + assertOrPanic(@field(Bar, "Th" ++ "ree") == Bar.Three); + assertOrPanic(@field(Bar, "F" ++ "our") == Bar.Four); + assertOrPanic(@field(reflection, "dum" ++ "my")(true, 1, 2) == dummy(true, 1, 2)); + @field(f, "o" ++ "ne") = 4; + assertOrPanic(f.one == 4); +} + +const Foo = struct { + const constant = 52; + + one: i32, + two: bool, + three: void, +}; + +const Bar = union(enum) { + One: void, + Two: i32, + Three: bool, + Four: f64, +}; + diff --git a/test/stage1/behavior/sizeof_and_typeof.zig b/test/stage1/behavior/sizeof_and_typeof.zig new file mode 100644 index 0000000000..ddaea4c242 --- /dev/null +++ b/test/stage1/behavior/sizeof_and_typeof.zig @@ -0,0 +1,69 @@ +const builtin = @import("builtin"); +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "@sizeOf and @typeOf" { + const y: @typeOf(x) = 120; + assertOrPanic(@sizeOf(@typeOf(y)) == 2); +} +const x: u16 = 13; +const z: @typeOf(x) = 19; + +const A = struct { + a: u8, + b: u32, + c: u8, + d: u3, + e: u5, + f: u16, + g: u16, +}; + +const P = packed struct { + a: u8, + b: u32, + c: u8, + d: u3, + e: u5, + f: u16, + g: u16, +}; + +test "@byteOffsetOf" { + // Packed structs have fixed memory layout + assertOrPanic(@byteOffsetOf(P, "a") == 0); + assertOrPanic(@byteOffsetOf(P, "b") == 1); + assertOrPanic(@byteOffsetOf(P, "c") == 5); + assertOrPanic(@byteOffsetOf(P, "d") == 6); + assertOrPanic(@byteOffsetOf(P, "e") == 6); + assertOrPanic(@byteOffsetOf(P, "f") == 7); + assertOrPanic(@byteOffsetOf(P, "g") == 9); + + // Normal struct fields can be moved/padded + var a: A = undefined; + assertOrPanic(@ptrToInt(&a.a) - @ptrToInt(&a) == @byteOffsetOf(A, "a")); + assertOrPanic(@ptrToInt(&a.b) - @ptrToInt(&a) == @byteOffsetOf(A, "b")); + assertOrPanic(@ptrToInt(&a.c) - @ptrToInt(&a) == @byteOffsetOf(A, "c")); + assertOrPanic(@ptrToInt(&a.d) - @ptrToInt(&a) == @byteOffsetOf(A, "d")); + assertOrPanic(@ptrToInt(&a.e) - @ptrToInt(&a) == @byteOffsetOf(A, "e")); + assertOrPanic(@ptrToInt(&a.f) - @ptrToInt(&a) == @byteOffsetOf(A, "f")); + assertOrPanic(@ptrToInt(&a.g) - @ptrToInt(&a) == @byteOffsetOf(A, "g")); +} + +test "@bitOffsetOf" { + // Packed structs have fixed memory layout + assertOrPanic(@bitOffsetOf(P, "a") == 0); + assertOrPanic(@bitOffsetOf(P, "b") == 8); + assertOrPanic(@bitOffsetOf(P, "c") == 40); + assertOrPanic(@bitOffsetOf(P, "d") == 48); + assertOrPanic(@bitOffsetOf(P, "e") == 51); + assertOrPanic(@bitOffsetOf(P, "f") == 56); + assertOrPanic(@bitOffsetOf(P, "g") == 72); + + assertOrPanic(@byteOffsetOf(A, "a") * 8 == @bitOffsetOf(A, "a")); + assertOrPanic(@byteOffsetOf(A, "b") * 8 == @bitOffsetOf(A, "b")); + assertOrPanic(@byteOffsetOf(A, "c") * 8 == @bitOffsetOf(A, "c")); + assertOrPanic(@byteOffsetOf(A, "d") * 8 == @bitOffsetOf(A, "d")); + assertOrPanic(@byteOffsetOf(A, "e") * 8 == @bitOffsetOf(A, "e")); + assertOrPanic(@byteOffsetOf(A, "f") * 8 == @bitOffsetOf(A, "f")); + assertOrPanic(@byteOffsetOf(A, "g") * 8 == @bitOffsetOf(A, "g")); +} diff --git a/test/stage1/behavior/slice.zig b/test/stage1/behavior/slice.zig new file mode 100644 index 0000000000..cc29e43485 --- /dev/null +++ b/test/stage1/behavior/slice.zig @@ -0,0 +1,40 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const mem = @import("std").mem; + +const x = @intToPtr([*]i32, 0x1000)[0..0x500]; +const y = x[0x100..]; +test "compile time slice of pointer to hard coded address" { + assertOrPanic(@ptrToInt(x.ptr) == 0x1000); + assertOrPanic(x.len == 0x500); + + assertOrPanic(@ptrToInt(y.ptr) == 0x1100); + assertOrPanic(y.len == 0x400); +} + +test "slice child property" { + var array: [5]i32 = undefined; + var slice = array[0..]; + assertOrPanic(@typeOf(slice).Child == i32); +} + +test "runtime safety lets us slice from len..len" { + var an_array = []u8{ + 1, + 2, + 3, + }; + assertOrPanic(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), "")); +} + +fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { + return a_slice[start..end]; +} + +test "implicitly cast array of size 0 to slice" { + var msg = []u8{}; + assertLenIsZero(msg); +} + +fn assertLenIsZero(msg: []const u8) void { + assertOrPanic(msg.len == 0); +} diff --git a/test/stage1/behavior/struct.zig b/test/stage1/behavior/struct.zig new file mode 100644 index 0000000000..92ae2baa15 --- /dev/null +++ b/test/stage1/behavior/struct.zig @@ -0,0 +1,470 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const builtin = @import("builtin"); +const maxInt = std.math.maxInt; + +const StructWithNoFields = struct { + fn add(a: i32, b: i32) i32 { + return a + b; + } +}; +const empty_global_instance = StructWithNoFields{}; + +test "call struct static method" { + const result = StructWithNoFields.add(3, 4); + assertOrPanic(result == 7); +} + +test "return empty struct instance" { + _ = returnEmptyStructInstance(); +} +fn returnEmptyStructInstance() StructWithNoFields { + return empty_global_instance; +} + +const should_be_11 = StructWithNoFields.add(5, 6); + +test "invoke static method in global scope" { + assertOrPanic(should_be_11 == 11); +} + +test "void struct fields" { + const foo = VoidStructFieldsFoo{ + .a = void{}, + .b = 1, + .c = void{}, + }; + assertOrPanic(foo.b == 1); + assertOrPanic(@sizeOf(VoidStructFieldsFoo) == 4); +} +const VoidStructFieldsFoo = struct { + a: void, + b: i32, + c: void, +}; + +test "structs" { + var foo: StructFoo = undefined; + @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo)); + foo.a += 1; + foo.b = foo.a == 1; + testFoo(foo); + testMutation(&foo); + assertOrPanic(foo.c == 100); +} +const StructFoo = struct { + a: i32, + b: bool, + c: f32, +}; +fn testFoo(foo: StructFoo) void { + assertOrPanic(foo.b); +} +fn testMutation(foo: *StructFoo) void { + foo.c = 100; +} + +const Node = struct { + val: Val, + next: *Node, +}; + +const Val = struct { + x: i32, +}; + +test "struct point to self" { + var root: Node = undefined; + root.val.x = 1; + + var node: Node = undefined; + node.next = &root; + node.val.x = 2; + + root.next = &node; + + assertOrPanic(node.next.next.next.val.x == 1); +} + +test "struct byval assign" { + var foo1: StructFoo = undefined; + var foo2: StructFoo = undefined; + + foo1.a = 1234; + foo2.a = 0; + assertOrPanic(foo2.a == 0); + foo2 = foo1; + assertOrPanic(foo2.a == 1234); +} + +fn structInitializer() void { + const val = Val{ .x = 42 }; + assertOrPanic(val.x == 42); +} + +test "fn call of struct field" { + assertOrPanic(callStructField(Foo{ .ptr = aFunc }) == 13); +} + +const Foo = struct { + ptr: fn () i32, +}; + +fn aFunc() i32 { + return 13; +} + +fn callStructField(foo: Foo) i32 { + return foo.ptr(); +} + +test "store member function in variable" { + const instance = MemberFnTestFoo{ .x = 1234 }; + const memberFn = MemberFnTestFoo.member; + const result = memberFn(instance); + assertOrPanic(result == 1234); +} +const MemberFnTestFoo = struct { + x: i32, + fn member(foo: MemberFnTestFoo) i32 { + return foo.x; + } +}; + +test "call member function directly" { + const instance = MemberFnTestFoo{ .x = 1234 }; + const result = MemberFnTestFoo.member(instance); + assertOrPanic(result == 1234); +} + +test "member functions" { + const r = MemberFnRand{ .seed = 1234 }; + assertOrPanic(r.getSeed() == 1234); +} +const MemberFnRand = struct { + seed: u32, + pub fn getSeed(r: *const MemberFnRand) u32 { + return r.seed; + } +}; + +test "return struct byval from function" { + const bar = makeBar(1234, 5678); + assertOrPanic(bar.y == 5678); +} +const Bar = struct { + x: i32, + y: i32, +}; +fn makeBar(x: i32, y: i32) Bar { + return Bar{ + .x = x, + .y = y, + }; +} + +test "empty struct method call" { + const es = EmptyStruct{}; + assertOrPanic(es.method() == 1234); +} +const EmptyStruct = struct { + fn method(es: *const EmptyStruct) i32 { + return 1234; + } +}; + +test "return empty struct from fn" { + _ = testReturnEmptyStructFromFn(); +} +const EmptyStruct2 = struct {}; +fn testReturnEmptyStructFromFn() EmptyStruct2 { + return EmptyStruct2{}; +} + +test "pass slice of empty struct to fn" { + assertOrPanic(testPassSliceOfEmptyStructToFn([]EmptyStruct2{EmptyStruct2{}}) == 1); +} +fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { + return slice.len; +} + +const APackedStruct = packed struct { + x: u8, + y: u8, +}; + +test "packed struct" { + var foo = APackedStruct{ + .x = 1, + .y = 2, + }; + foo.y += 1; + const four = foo.x + foo.y; + assertOrPanic(four == 4); +} + +const BitField1 = packed struct { + a: u3, + b: u3, + c: u2, +}; + +const bit_field_1 = BitField1{ + .a = 1, + .b = 2, + .c = 3, +}; + +test "bit field access" { + var data = bit_field_1; + assertOrPanic(getA(&data) == 1); + assertOrPanic(getB(&data) == 2); + assertOrPanic(getC(&data) == 3); + comptime assertOrPanic(@sizeOf(BitField1) == 1); + + data.b += 1; + assertOrPanic(data.b == 3); + + data.a += 1; + assertOrPanic(data.a == 2); + assertOrPanic(data.b == 3); +} + +fn getA(data: *const BitField1) u3 { + return data.a; +} + +fn getB(data: *const BitField1) u3 { + return data.b; +} + +fn getC(data: *const BitField1) u2 { + return data.c; +} + +const Foo24Bits = packed struct { + field: u24, +}; +const Foo96Bits = packed struct { + a: u24, + b: u24, + c: u24, + d: u24, +}; + +test "packed struct 24bits" { + comptime { + assertOrPanic(@sizeOf(Foo24Bits) == 3); + assertOrPanic(@sizeOf(Foo96Bits) == 12); + } + + var value = Foo96Bits{ + .a = 0, + .b = 0, + .c = 0, + .d = 0, + }; + value.a += 1; + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 0); + assertOrPanic(value.c == 0); + assertOrPanic(value.d == 0); + + value.b += 1; + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 0); + assertOrPanic(value.d == 0); + + value.c += 1; + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 1); + assertOrPanic(value.d == 0); + + value.d += 1; + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 1); + assertOrPanic(value.d == 1); +} + +const FooArray24Bits = packed struct { + a: u16, + b: [2]Foo24Bits, + c: u16, +}; + +test "packed array 24bits" { + comptime { + assertOrPanic(@sizeOf([9]Foo24Bits) == 9 * 3); + assertOrPanic(@sizeOf(FooArray24Bits) == 2 + 2 * 3 + 2); + } + + var bytes = []u8{0} ** (@sizeOf(FooArray24Bits) + 1); + bytes[bytes.len - 1] = 0xaa; + const ptr = &@bytesToSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; + assertOrPanic(ptr.a == 0); + assertOrPanic(ptr.b[0].field == 0); + assertOrPanic(ptr.b[1].field == 0); + assertOrPanic(ptr.c == 0); + + ptr.a = maxInt(u16); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == 0); + assertOrPanic(ptr.b[1].field == 0); + assertOrPanic(ptr.c == 0); + + ptr.b[0].field = maxInt(u24); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == maxInt(u24)); + assertOrPanic(ptr.b[1].field == 0); + assertOrPanic(ptr.c == 0); + + ptr.b[1].field = maxInt(u24); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == maxInt(u24)); + assertOrPanic(ptr.b[1].field == maxInt(u24)); + assertOrPanic(ptr.c == 0); + + ptr.c = maxInt(u16); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == maxInt(u24)); + assertOrPanic(ptr.b[1].field == maxInt(u24)); + assertOrPanic(ptr.c == maxInt(u16)); + + assertOrPanic(bytes[bytes.len - 1] == 0xaa); +} + +const FooStructAligned = packed struct { + a: u8, + b: u8, +}; + +const FooArrayOfAligned = packed struct { + a: [2]FooStructAligned, +}; + +test "aligned array of packed struct" { + comptime { + assertOrPanic(@sizeOf(FooStructAligned) == 2); + assertOrPanic(@sizeOf(FooArrayOfAligned) == 2 * 2); + } + + var bytes = []u8{0xbb} ** @sizeOf(FooArrayOfAligned); + const ptr = &@bytesToSlice(FooArrayOfAligned, bytes[0..bytes.len])[0]; + + assertOrPanic(ptr.a[0].a == 0xbb); + assertOrPanic(ptr.a[0].b == 0xbb); + assertOrPanic(ptr.a[1].a == 0xbb); + assertOrPanic(ptr.a[1].b == 0xbb); +} + +test "runtime struct initialization of bitfield" { + const s1 = Nibbles{ + .x = x1, + .y = x1, + }; + const s2 = Nibbles{ + .x = @intCast(u4, x2), + .y = @intCast(u4, x2), + }; + + assertOrPanic(s1.x == x1); + assertOrPanic(s1.y == x1); + assertOrPanic(s2.x == @intCast(u4, x2)); + assertOrPanic(s2.y == @intCast(u4, x2)); +} + +var x1 = u4(1); +var x2 = u8(2); + +const Nibbles = packed struct { + x: u4, + y: u4, +}; + +const Bitfields = packed struct { + f1: u16, + f2: u16, + f3: u8, + f4: u8, + f5: u4, + f6: u4, + f7: u8, +}; + +test "native bit field understands endianness" { + var all: u64 = 0x7765443322221111; + var bytes: [8]u8 = undefined; + @memcpy(bytes[0..].ptr, @ptrCast([*]u8, &all), 8); + var bitfields = @ptrCast(*Bitfields, bytes[0..].ptr).*; + + assertOrPanic(bitfields.f1 == 0x1111); + assertOrPanic(bitfields.f2 == 0x2222); + assertOrPanic(bitfields.f3 == 0x33); + assertOrPanic(bitfields.f4 == 0x44); + assertOrPanic(bitfields.f5 == 0x5); + assertOrPanic(bitfields.f6 == 0x6); + assertOrPanic(bitfields.f7 == 0x77); +} + +test "align 1 field before self referential align 8 field as slice return type" { + const result = alloc(Expr); + assertOrPanic(result.len == 0); +} + +const Expr = union(enum) { + Literal: u8, + Question: *Expr, +}; + +fn alloc(comptime T: type) []T { + return []T{}; +} + +test "call method with mutable reference to struct with no fields" { + const S = struct { + fn doC(s: *const @This()) bool { + return true; + } + fn do(s: *@This()) bool { + return true; + } + }; + + var s = S{}; + assertOrPanic(S.doC(&s)); + assertOrPanic(s.doC()); + assertOrPanic(S.do(&s)); + assertOrPanic(s.do()); +} + +test "implicit cast packed struct field to const ptr" { + const LevelUpMove = packed struct { + move_id: u9, + level: u7, + + fn toInt(value: u7) u7 { + return value; + } + }; + + var lup: LevelUpMove = undefined; + lup.level = 12; + const res = LevelUpMove.toInt(lup.level); + assertOrPanic(res == 12); +} + +test "pointer to packed struct member in a stack variable" { + const S = packed struct { + a: u2, + b: u2, + }; + + var s = S{ .a = 2, .b = 0 }; + var b_ptr = &s.b; + assertOrPanic(s.b == 0); + b_ptr.* = 2; + assertOrPanic(s.b == 2); +} diff --git a/test/stage1/behavior/struct_contains_null_ptr_itself.zig b/test/stage1/behavior/struct_contains_null_ptr_itself.zig new file mode 100644 index 0000000000..4cc479f31c --- /dev/null +++ b/test/stage1/behavior/struct_contains_null_ptr_itself.zig @@ -0,0 +1,21 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; + +test "struct contains null pointer which contains original struct" { + var x: ?*NodeLineComment = null; + assertOrPanic(x == null); +} + +pub const Node = struct { + id: Id, + comment: ?*NodeLineComment, + + pub const Id = enum { + Root, + LineComment, + }; +}; + +pub const NodeLineComment = struct { + base: Node, +}; diff --git a/test/stage1/behavior/struct_contains_slice_of_itself.zig b/test/stage1/behavior/struct_contains_slice_of_itself.zig new file mode 100644 index 0000000000..15780a7c44 --- /dev/null +++ b/test/stage1/behavior/struct_contains_slice_of_itself.zig @@ -0,0 +1,85 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +const Node = struct { + payload: i32, + children: []Node, +}; + +const NodeAligned = struct { + payload: i32, + children: []align(@alignOf(NodeAligned)) NodeAligned, +}; + +test "struct contains slice of itself" { + var other_nodes = []Node{ + Node{ + .payload = 31, + .children = []Node{}, + }, + Node{ + .payload = 32, + .children = []Node{}, + }, + }; + var nodes = []Node{ + Node{ + .payload = 1, + .children = []Node{}, + }, + Node{ + .payload = 2, + .children = []Node{}, + }, + Node{ + .payload = 3, + .children = other_nodes[0..], + }, + }; + const root = Node{ + .payload = 1234, + .children = nodes[0..], + }; + assertOrPanic(root.payload == 1234); + assertOrPanic(root.children[0].payload == 1); + assertOrPanic(root.children[1].payload == 2); + assertOrPanic(root.children[2].payload == 3); + assertOrPanic(root.children[2].children[0].payload == 31); + assertOrPanic(root.children[2].children[1].payload == 32); +} + +test "struct contains aligned slice of itself" { + var other_nodes = []NodeAligned{ + NodeAligned{ + .payload = 31, + .children = []NodeAligned{}, + }, + NodeAligned{ + .payload = 32, + .children = []NodeAligned{}, + }, + }; + var nodes = []NodeAligned{ + NodeAligned{ + .payload = 1, + .children = []NodeAligned{}, + }, + NodeAligned{ + .payload = 2, + .children = []NodeAligned{}, + }, + NodeAligned{ + .payload = 3, + .children = other_nodes[0..], + }, + }; + const root = NodeAligned{ + .payload = 1234, + .children = nodes[0..], + }; + assertOrPanic(root.payload == 1234); + assertOrPanic(root.children[0].payload == 1); + assertOrPanic(root.children[1].payload == 2); + assertOrPanic(root.children[2].payload == 3); + assertOrPanic(root.children[2].children[0].payload == 31); + assertOrPanic(root.children[2].children[1].payload == 32); +} diff --git a/test/stage1/behavior/switch.zig b/test/stage1/behavior/switch.zig new file mode 100644 index 0000000000..4ac971397e --- /dev/null +++ b/test/stage1/behavior/switch.zig @@ -0,0 +1,271 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "switch with numbers" { + testSwitchWithNumbers(13); +} + +fn testSwitchWithNumbers(x: u32) void { + const result = switch (x) { + 1, 2, 3, 4...8 => false, + 13 => true, + else => false, + }; + assertOrPanic(result); +} + +test "switch with all ranges" { + assertOrPanic(testSwitchWithAllRanges(50, 3) == 1); + assertOrPanic(testSwitchWithAllRanges(101, 0) == 2); + assertOrPanic(testSwitchWithAllRanges(300, 5) == 3); + assertOrPanic(testSwitchWithAllRanges(301, 6) == 6); +} + +fn testSwitchWithAllRanges(x: u32, y: u32) u32 { + return switch (x) { + 0...100 => 1, + 101...200 => 2, + 201...300 => 3, + else => y, + }; +} + +test "implicit comptime switch" { + const x = 3 + 4; + const result = switch (x) { + 3 => 10, + 4 => 11, + 5, 6 => 12, + 7, 8 => 13, + else => 14, + }; + + comptime { + assertOrPanic(result + 1 == 14); + } +} + +test "switch on enum" { + const fruit = Fruit.Orange; + nonConstSwitchOnEnum(fruit); +} +const Fruit = enum { + Apple, + Orange, + Banana, +}; +fn nonConstSwitchOnEnum(fruit: Fruit) void { + switch (fruit) { + Fruit.Apple => unreachable, + Fruit.Orange => {}, + Fruit.Banana => unreachable, + } +} + +test "switch statement" { + nonConstSwitch(SwitchStatmentFoo.C); +} +fn nonConstSwitch(foo: SwitchStatmentFoo) void { + const val = switch (foo) { + SwitchStatmentFoo.A => i32(1), + SwitchStatmentFoo.B => 2, + SwitchStatmentFoo.C => 3, + SwitchStatmentFoo.D => 4, + }; + assertOrPanic(val == 3); +} +const SwitchStatmentFoo = enum { + A, + B, + C, + D, +}; + +test "switch prong with variable" { + switchProngWithVarFn(SwitchProngWithVarEnum{ .One = 13 }); + switchProngWithVarFn(SwitchProngWithVarEnum{ .Two = 13.0 }); + switchProngWithVarFn(SwitchProngWithVarEnum{ .Meh = {} }); +} +const SwitchProngWithVarEnum = union(enum) { + One: i32, + Two: f32, + Meh: void, +}; +fn switchProngWithVarFn(a: SwitchProngWithVarEnum) void { + switch (a) { + SwitchProngWithVarEnum.One => |x| { + assertOrPanic(x == 13); + }, + SwitchProngWithVarEnum.Two => |x| { + assertOrPanic(x == 13.0); + }, + SwitchProngWithVarEnum.Meh => |x| { + const v: void = x; + }, + } +} + +test "switch on enum using pointer capture" { + testSwitchEnumPtrCapture(); + comptime testSwitchEnumPtrCapture(); +} + +fn testSwitchEnumPtrCapture() void { + var value = SwitchProngWithVarEnum{ .One = 1234 }; + switch (value) { + SwitchProngWithVarEnum.One => |*x| x.* += 1, + else => unreachable, + } + switch (value) { + SwitchProngWithVarEnum.One => |x| assertOrPanic(x == 1235), + else => unreachable, + } +} + +test "switch with multiple expressions" { + const x = switch (returnsFive()) { + 1, 2, 3 => 1, + 4, 5, 6 => 2, + else => i32(3), + }; + assertOrPanic(x == 2); +} +fn returnsFive() i32 { + return 5; +} + +const Number = union(enum) { + One: u64, + Two: u8, + Three: f32, +}; + +const number = Number{ .Three = 1.23 }; + +fn returnsFalse() bool { + switch (number) { + Number.One => |x| return x > 1234, + Number.Two => |x| return x == 'a', + Number.Three => |x| return x > 12.34, + } +} +test "switch on const enum with var" { + assertOrPanic(!returnsFalse()); +} + +test "switch on type" { + assertOrPanic(trueIfBoolFalseOtherwise(bool)); + assertOrPanic(!trueIfBoolFalseOtherwise(i32)); +} + +fn trueIfBoolFalseOtherwise(comptime T: type) bool { + return switch (T) { + bool => true, + else => false, + }; +} + +test "switch handles all cases of number" { + testSwitchHandleAllCases(); + comptime testSwitchHandleAllCases(); +} + +fn testSwitchHandleAllCases() void { + assertOrPanic(testSwitchHandleAllCasesExhaustive(0) == 3); + assertOrPanic(testSwitchHandleAllCasesExhaustive(1) == 2); + assertOrPanic(testSwitchHandleAllCasesExhaustive(2) == 1); + assertOrPanic(testSwitchHandleAllCasesExhaustive(3) == 0); + + assertOrPanic(testSwitchHandleAllCasesRange(100) == 0); + assertOrPanic(testSwitchHandleAllCasesRange(200) == 1); + assertOrPanic(testSwitchHandleAllCasesRange(201) == 2); + assertOrPanic(testSwitchHandleAllCasesRange(202) == 4); + assertOrPanic(testSwitchHandleAllCasesRange(230) == 3); +} + +fn testSwitchHandleAllCasesExhaustive(x: u2) u2 { + return switch (x) { + 0 => u2(3), + 1 => 2, + 2 => 1, + 3 => 0, + }; +} + +fn testSwitchHandleAllCasesRange(x: u8) u8 { + return switch (x) { + 0...100 => u8(0), + 101...200 => 1, + 201, 203 => 2, + 202 => 4, + 204...255 => 3, + }; +} + +test "switch all prongs unreachable" { + testAllProngsUnreachable(); + comptime testAllProngsUnreachable(); +} + +fn testAllProngsUnreachable() void { + assertOrPanic(switchWithUnreachable(1) == 2); + assertOrPanic(switchWithUnreachable(2) == 10); +} + +fn switchWithUnreachable(x: i32) i32 { + while (true) { + switch (x) { + 1 => return 2, + 2 => break, + else => continue, + } + } + return 10; +} + +fn return_a_number() anyerror!i32 { + return 1; +} + +test "capture value of switch with all unreachable prongs" { + const x = return_a_number() catch |err| switch (err) { + else => unreachable, + }; + assertOrPanic(x == 1); +} + +test "switching on booleans" { + testSwitchOnBools(); + comptime testSwitchOnBools(); +} + +fn testSwitchOnBools() void { + assertOrPanic(testSwitchOnBoolsTrueAndFalse(true) == false); + assertOrPanic(testSwitchOnBoolsTrueAndFalse(false) == true); + + assertOrPanic(testSwitchOnBoolsTrueWithElse(true) == false); + assertOrPanic(testSwitchOnBoolsTrueWithElse(false) == true); + + assertOrPanic(testSwitchOnBoolsFalseWithElse(true) == false); + assertOrPanic(testSwitchOnBoolsFalseWithElse(false) == true); +} + +fn testSwitchOnBoolsTrueAndFalse(x: bool) bool { + return switch (x) { + true => false, + false => true, + }; +} + +fn testSwitchOnBoolsTrueWithElse(x: bool) bool { + return switch (x) { + true => false, + else => true, + }; +} + +fn testSwitchOnBoolsFalseWithElse(x: bool) bool { + return switch (x) { + false => true, + else => false, + }; +} diff --git a/test/stage1/behavior/switch_prong_err_enum.zig b/test/stage1/behavior/switch_prong_err_enum.zig new file mode 100644 index 0000000000..6ac1919f0d --- /dev/null +++ b/test/stage1/behavior/switch_prong_err_enum.zig @@ -0,0 +1,30 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +var read_count: u64 = 0; + +fn readOnce() anyerror!u64 { + read_count += 1; + return read_count; +} + +const FormValue = union(enum) { + Address: u64, + Other: bool, +}; + +fn doThing(form_id: u64) anyerror!FormValue { + return switch (form_id) { + 17 => FormValue{ .Address = try readOnce() }, + else => error.InvalidDebugInfo, + }; +} + +test "switch prong returns error enum" { + switch (doThing(17) catch unreachable) { + FormValue.Address => |payload| { + assertOrPanic(payload == 1); + }, + else => unreachable, + } + assertOrPanic(read_count == 1); +} diff --git a/test/stage1/behavior/switch_prong_implicit_cast.zig b/test/stage1/behavior/switch_prong_implicit_cast.zig new file mode 100644 index 0000000000..4ca031e2e1 --- /dev/null +++ b/test/stage1/behavior/switch_prong_implicit_cast.zig @@ -0,0 +1,22 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +const FormValue = union(enum) { + One: void, + Two: bool, +}; + +fn foo(id: u64) !FormValue { + return switch (id) { + 2 => FormValue{ .Two = true }, + 1 => FormValue{ .One = {} }, + else => return error.Whatever, + }; +} + +test "switch prong implicit cast" { + const result = switch (foo(2) catch unreachable) { + FormValue.One => false, + FormValue.Two => |x| x, + }; + assertOrPanic(result); +} diff --git a/test/stage1/behavior/syntax.zig b/test/stage1/behavior/syntax.zig new file mode 100644 index 0000000000..451e396142 --- /dev/null +++ b/test/stage1/behavior/syntax.zig @@ -0,0 +1,60 @@ +// Test trailing comma syntax +// zig fmt: off + +const struct_trailing_comma = struct { x: i32, y: i32, }; +const struct_no_comma = struct { x: i32, y: i32 }; +const struct_fn_no_comma = struct { fn m() void {} y: i32 }; + +const enum_no_comma = enum { A, B }; + +fn container_init() void { + const S = struct { x: i32, y: i32 }; + _ = S { .x = 1, .y = 2 }; + _ = S { .x = 1, .y = 2, }; +} + +fn type_expr_return1() if (true) A {} +fn type_expr_return2() for (true) |_| A {} +fn type_expr_return3() while (true) A {} +fn type_expr_return4() comptime A {} + +fn switch_cases(x: i32) void { + switch (x) { + 1,2,3 => {}, + 4,5, => {}, + 6...8, => {}, + else => {}, + } +} + +fn switch_prongs(x: i32) void { + switch (x) { + 0 => {}, + else => {}, + } + switch (x) { + 0 => {}, + else => {} + } +} + +const fn_no_comma = fn(i32, i32)void; +const fn_trailing_comma = fn(i32, i32,)void; + +fn fn_calls() void { + fn add(x: i32, y: i32,) i32 { x + y }; + _ = add(1, 2); + _ = add(1, 2,); +} + +fn asm_lists() void { + if (false) { // Build AST but don't analyze + asm ("not real assembly" + :[a] "x" (x),); + asm ("not real assembly" + :[a] "x" (->i32),:[a] "x" (1),); + asm ("still not real assembly" + :::"a","b",); + } +} + diff --git a/test/stage1/behavior/this.zig b/test/stage1/behavior/this.zig new file mode 100644 index 0000000000..0e3a7a03ae --- /dev/null +++ b/test/stage1/behavior/this.zig @@ -0,0 +1,35 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +const module = @This(); + +fn Point(comptime T: type) type { + return struct { + const Self = @This(); + x: T, + y: T, + + fn addOne(self: *Self) void { + self.x += 1; + self.y += 1; + } + }; +} + +fn add(x: i32, y: i32) i32 { + return x + y; +} + +test "this refer to module call private fn" { + assertOrPanic(module.add(1, 2) == 3); +} + +test "this refer to container" { + var pt = Point(i32){ + .x = 12, + .y = 34, + }; + pt.addOne(); + assertOrPanic(pt.x == 13); + assertOrPanic(pt.y == 35); +} + diff --git a/test/stage1/behavior/truncate.zig b/test/stage1/behavior/truncate.zig new file mode 100644 index 0000000000..b7904bc7fb --- /dev/null +++ b/test/stage1/behavior/truncate.zig @@ -0,0 +1,8 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; + +test "truncate u0 to larger integer allowed and has comptime known result" { + var x: u0 = 0; + const y = @truncate(u8, x); + comptime assertOrPanic(y == 0); +} diff --git a/test/stage1/behavior/try.zig b/test/stage1/behavior/try.zig new file mode 100644 index 0000000000..ed48875eb4 --- /dev/null +++ b/test/stage1/behavior/try.zig @@ -0,0 +1,43 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "try on error union" { + tryOnErrorUnionImpl(); + comptime tryOnErrorUnionImpl(); +} + +fn tryOnErrorUnionImpl() void { + const x = if (returnsTen()) |val| val + 1 else |err| switch (err) { + error.ItBroke, error.NoMem => 1, + error.CrappedOut => i32(2), + else => unreachable, + }; + assertOrPanic(x == 11); +} + +fn returnsTen() anyerror!i32 { + return 10; +} + +test "try without vars" { + const result1 = if (failIfTrue(true)) 1 else |_| i32(2); + assertOrPanic(result1 == 2); + + const result2 = if (failIfTrue(false)) 1 else |_| i32(2); + assertOrPanic(result2 == 1); +} + +fn failIfTrue(ok: bool) anyerror!void { + if (ok) { + return error.ItBroke; + } else { + return; + } +} + +test "try then not executed with assignment" { + if (failIfTrue(true)) { + unreachable; + } else |err| { + assertOrPanic(err == error.ItBroke); + } +} diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig new file mode 100644 index 0000000000..5ad80e06e1 --- /dev/null +++ b/test/stage1/behavior/type_info.zig @@ -0,0 +1,264 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const mem = @import("std").mem; +const TypeInfo = @import("builtin").TypeInfo; +const TypeId = @import("builtin").TypeId; + +test "type info: tag type, void info" { + testBasic(); + comptime testBasic(); +} + +fn testBasic() void { + assertOrPanic(@TagType(TypeInfo) == TypeId); + const void_info = @typeInfo(void); + assertOrPanic(TypeId(void_info) == TypeId.Void); + assertOrPanic(void_info.Void == {}); +} + +test "type info: integer, floating point type info" { + testIntFloat(); + comptime testIntFloat(); +} + +fn testIntFloat() void { + const u8_info = @typeInfo(u8); + assertOrPanic(TypeId(u8_info) == TypeId.Int); + assertOrPanic(!u8_info.Int.is_signed); + assertOrPanic(u8_info.Int.bits == 8); + + const f64_info = @typeInfo(f64); + assertOrPanic(TypeId(f64_info) == TypeId.Float); + assertOrPanic(f64_info.Float.bits == 64); +} + +test "type info: pointer type info" { + testPointer(); + comptime testPointer(); +} + +fn testPointer() void { + const u32_ptr_info = @typeInfo(*u32); + assertOrPanic(TypeId(u32_ptr_info) == TypeId.Pointer); + assertOrPanic(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One); + assertOrPanic(u32_ptr_info.Pointer.is_const == false); + assertOrPanic(u32_ptr_info.Pointer.is_volatile == false); + assertOrPanic(u32_ptr_info.Pointer.alignment == @alignOf(u32)); + assertOrPanic(u32_ptr_info.Pointer.child == u32); +} + +test "type info: unknown length pointer type info" { + testUnknownLenPtr(); + comptime testUnknownLenPtr(); +} + +fn testUnknownLenPtr() void { + const u32_ptr_info = @typeInfo([*]const volatile f64); + assertOrPanic(TypeId(u32_ptr_info) == TypeId.Pointer); + assertOrPanic(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); + assertOrPanic(u32_ptr_info.Pointer.is_const == true); + assertOrPanic(u32_ptr_info.Pointer.is_volatile == true); + assertOrPanic(u32_ptr_info.Pointer.alignment == @alignOf(f64)); + assertOrPanic(u32_ptr_info.Pointer.child == f64); +} + +test "type info: slice type info" { + testSlice(); + comptime testSlice(); +} + +fn testSlice() void { + const u32_slice_info = @typeInfo([]u32); + assertOrPanic(TypeId(u32_slice_info) == TypeId.Pointer); + assertOrPanic(u32_slice_info.Pointer.size == TypeInfo.Pointer.Size.Slice); + assertOrPanic(u32_slice_info.Pointer.is_const == false); + assertOrPanic(u32_slice_info.Pointer.is_volatile == false); + assertOrPanic(u32_slice_info.Pointer.alignment == 4); + assertOrPanic(u32_slice_info.Pointer.child == u32); +} + +test "type info: array type info" { + testArray(); + comptime testArray(); +} + +fn testArray() void { + const arr_info = @typeInfo([42]bool); + assertOrPanic(TypeId(arr_info) == TypeId.Array); + assertOrPanic(arr_info.Array.len == 42); + assertOrPanic(arr_info.Array.child == bool); +} + +test "type info: optional type info" { + testOptional(); + comptime testOptional(); +} + +fn testOptional() void { + const null_info = @typeInfo(?void); + assertOrPanic(TypeId(null_info) == TypeId.Optional); + assertOrPanic(null_info.Optional.child == void); +} + +test "type info: promise info" { + testPromise(); + comptime testPromise(); +} + +fn testPromise() void { + const null_promise_info = @typeInfo(promise); + assertOrPanic(TypeId(null_promise_info) == TypeId.Promise); + assertOrPanic(null_promise_info.Promise.child == null); + + const promise_info = @typeInfo(promise->usize); + assertOrPanic(TypeId(promise_info) == TypeId.Promise); + assertOrPanic(promise_info.Promise.child.? == usize); +} + +test "type info: error set, error union info" { + testErrorSet(); + comptime testErrorSet(); +} + +fn testErrorSet() void { + const TestErrorSet = error{ + First, + Second, + Third, + }; + + const error_set_info = @typeInfo(TestErrorSet); + assertOrPanic(TypeId(error_set_info) == TypeId.ErrorSet); + assertOrPanic(error_set_info.ErrorSet.errors.len == 3); + assertOrPanic(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First")); + assertOrPanic(error_set_info.ErrorSet.errors[2].value == @errorToInt(TestErrorSet.Third)); + + const error_union_info = @typeInfo(TestErrorSet!usize); + assertOrPanic(TypeId(error_union_info) == TypeId.ErrorUnion); + assertOrPanic(error_union_info.ErrorUnion.error_set == TestErrorSet); + assertOrPanic(error_union_info.ErrorUnion.payload == usize); +} + +test "type info: enum info" { + testEnum(); + comptime testEnum(); +} + +fn testEnum() void { + const Os = enum { + Windows, + Macos, + Linux, + FreeBSD, + }; + + const os_info = @typeInfo(Os); + assertOrPanic(TypeId(os_info) == TypeId.Enum); + assertOrPanic(os_info.Enum.layout == TypeInfo.ContainerLayout.Auto); + assertOrPanic(os_info.Enum.fields.len == 4); + assertOrPanic(mem.eql(u8, os_info.Enum.fields[1].name, "Macos")); + assertOrPanic(os_info.Enum.fields[3].value == 3); + assertOrPanic(os_info.Enum.tag_type == u2); + assertOrPanic(os_info.Enum.defs.len == 0); +} + +test "type info: union info" { + testUnion(); + comptime testUnion(); +} + +fn testUnion() void { + const typeinfo_info = @typeInfo(TypeInfo); + assertOrPanic(TypeId(typeinfo_info) == TypeId.Union); + assertOrPanic(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); + assertOrPanic(typeinfo_info.Union.tag_type.? == TypeId); + assertOrPanic(typeinfo_info.Union.fields.len == 24); + assertOrPanic(typeinfo_info.Union.fields[4].enum_field != null); + assertOrPanic(typeinfo_info.Union.fields[4].enum_field.?.value == 4); + assertOrPanic(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); + assertOrPanic(typeinfo_info.Union.defs.len == 20); + + const TestNoTagUnion = union { + Foo: void, + Bar: u32, + }; + + const notag_union_info = @typeInfo(TestNoTagUnion); + assertOrPanic(TypeId(notag_union_info) == TypeId.Union); + assertOrPanic(notag_union_info.Union.tag_type == null); + assertOrPanic(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto); + assertOrPanic(notag_union_info.Union.fields.len == 2); + assertOrPanic(notag_union_info.Union.fields[0].enum_field == null); + assertOrPanic(notag_union_info.Union.fields[1].field_type == u32); + + const TestExternUnion = extern union { + foo: *c_void, + }; + + const extern_union_info = @typeInfo(TestExternUnion); + assertOrPanic(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); + assertOrPanic(extern_union_info.Union.tag_type == null); + assertOrPanic(extern_union_info.Union.fields[0].enum_field == null); + assertOrPanic(extern_union_info.Union.fields[0].field_type == *c_void); +} + +test "type info: struct info" { + testStruct(); + comptime testStruct(); +} + +fn testStruct() void { + const struct_info = @typeInfo(TestStruct); + assertOrPanic(TypeId(struct_info) == TypeId.Struct); + assertOrPanic(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); + assertOrPanic(struct_info.Struct.fields.len == 3); + assertOrPanic(struct_info.Struct.fields[1].offset == null); + assertOrPanic(struct_info.Struct.fields[2].field_type == *TestStruct); + assertOrPanic(struct_info.Struct.defs.len == 2); + assertOrPanic(struct_info.Struct.defs[0].is_pub); + assertOrPanic(!struct_info.Struct.defs[0].data.Fn.is_extern); + assertOrPanic(struct_info.Struct.defs[0].data.Fn.lib_name == null); + assertOrPanic(struct_info.Struct.defs[0].data.Fn.return_type == void); + assertOrPanic(struct_info.Struct.defs[0].data.Fn.fn_type == fn (*const TestStruct) void); +} + +const TestStruct = packed struct { + const Self = @This(); + + fieldA: usize, + fieldB: void, + fieldC: *Self, + + pub fn foo(self: *const Self) void {} +}; + +test "type info: function type info" { + testFunction(); + comptime testFunction(); +} + +fn testFunction() void { + const fn_info = @typeInfo(@typeOf(foo)); + assertOrPanic(TypeId(fn_info) == TypeId.Fn); + assertOrPanic(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.Unspecified); + assertOrPanic(fn_info.Fn.is_generic); + assertOrPanic(fn_info.Fn.args.len == 2); + assertOrPanic(fn_info.Fn.is_var_args); + assertOrPanic(fn_info.Fn.return_type == null); + assertOrPanic(fn_info.Fn.async_allocator_type == null); + + const test_instance: TestStruct = undefined; + const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); + assertOrPanic(TypeId(bound_fn_info) == TypeId.BoundFn); + assertOrPanic(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct); +} + +fn foo(comptime a: usize, b: bool, args: ...) usize { + return 0; +} + +test "typeInfo with comptime parameter in struct fn def" { + const S = struct { + pub fn func(comptime x: f32) void {} + }; + comptime var info = @typeInfo(S); +} diff --git a/test/stage1/behavior/undefined.zig b/test/stage1/behavior/undefined.zig new file mode 100644 index 0000000000..333e217d49 --- /dev/null +++ b/test/stage1/behavior/undefined.zig @@ -0,0 +1,69 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const mem = @import("std").mem; + +fn initStaticArray() [10]i32 { + var array: [10]i32 = undefined; + array[0] = 1; + array[4] = 2; + array[7] = 3; + array[9] = 4; + return array; +} +const static_array = initStaticArray(); +test "init static array to undefined" { + assertOrPanic(static_array[0] == 1); + assertOrPanic(static_array[4] == 2); + assertOrPanic(static_array[7] == 3); + assertOrPanic(static_array[9] == 4); + + comptime { + assertOrPanic(static_array[0] == 1); + assertOrPanic(static_array[4] == 2); + assertOrPanic(static_array[7] == 3); + assertOrPanic(static_array[9] == 4); + } +} + +const Foo = struct { + x: i32, + + fn setFooXMethod(foo: *Foo) void { + foo.x = 3; + } +}; + +fn setFooX(foo: *Foo) void { + foo.x = 2; +} + +test "assign undefined to struct" { + comptime { + var foo: Foo = undefined; + setFooX(&foo); + assertOrPanic(foo.x == 2); + } + { + var foo: Foo = undefined; + setFooX(&foo); + assertOrPanic(foo.x == 2); + } +} + +test "assign undefined to struct with method" { + comptime { + var foo: Foo = undefined; + foo.setFooXMethod(); + assertOrPanic(foo.x == 3); + } + { + var foo: Foo = undefined; + foo.setFooXMethod(); + assertOrPanic(foo.x == 3); + } +} + +test "type name of undefined" { + const x = undefined; + assertOrPanic(mem.eql(u8, @typeName(@typeOf(x)), "(undefined)")); +} + diff --git a/test/stage1/behavior/underscore.zig b/test/stage1/behavior/underscore.zig new file mode 100644 index 0000000000..7443319336 --- /dev/null +++ b/test/stage1/behavior/underscore.zig @@ -0,0 +1,28 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; + +test "ignore lval with underscore" { + _ = false; +} + +test "ignore lval with underscore (for loop)" { + for ([]void{}) |_, i| { + for ([]void{}) |_, j| { + break; + } + break; + } +} + +test "ignore lval with underscore (while loop)" { + while (optionalReturnError()) |_| { + while (optionalReturnError()) |_| { + break; + } else |_| {} + break; + } else |_| {} +} + +fn optionalReturnError() !?u32 { + return error.optionalReturnError; +} diff --git a/test/stage1/behavior/union.zig b/test/stage1/behavior/union.zig new file mode 100644 index 0000000000..c8e8feb11e --- /dev/null +++ b/test/stage1/behavior/union.zig @@ -0,0 +1,352 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +const Value = union(enum) { + Int: u64, + Array: [9]u8, +}; + +const Agg = struct { + val1: Value, + val2: Value, +}; + +const v1 = Value{ .Int = 1234 }; +const v2 = Value{ .Array = []u8{3} ** 9 }; + +const err = (anyerror!Agg)(Agg{ + .val1 = v1, + .val2 = v2, +}); + +const array = []Value{ + v1, + v2, + v1, + v2, +}; + +test "unions embedded in aggregate types" { + switch (array[1]) { + Value.Array => |arr| assertOrPanic(arr[4] == 3), + else => unreachable, + } + switch ((err catch unreachable).val1) { + Value.Int => |x| assertOrPanic(x == 1234), + else => unreachable, + } +} + +const Foo = union { + float: f64, + int: i32, +}; + +test "basic unions" { + var foo = Foo{ .int = 1 }; + assertOrPanic(foo.int == 1); + foo = Foo{ .float = 12.34 }; + assertOrPanic(foo.float == 12.34); +} + +test "comptime union field access" { + comptime { + var foo = Foo{ .int = 0 }; + assertOrPanic(foo.int == 0); + + foo = Foo{ .float = 42.42 }; + assertOrPanic(foo.float == 42.42); + } +} + +test "init union with runtime value" { + var foo: Foo = undefined; + + setFloat(&foo, 12.34); + assertOrPanic(foo.float == 12.34); + + setInt(&foo, 42); + assertOrPanic(foo.int == 42); +} + +fn setFloat(foo: *Foo, x: f64) void { + foo.* = Foo{ .float = x }; +} + +fn setInt(foo: *Foo, x: i32) void { + foo.* = Foo{ .int = x }; +} + +const FooExtern = extern union { + float: f64, + int: i32, +}; + +test "basic extern unions" { + var foo = FooExtern{ .int = 1 }; + assertOrPanic(foo.int == 1); + foo.float = 12.34; + assertOrPanic(foo.float == 12.34); +} + +const Letter = enum { + A, + B, + C, +}; +const Payload = union(Letter) { + A: i32, + B: f64, + C: bool, +}; + +test "union with specified enum tag" { + doTest(); + comptime doTest(); +} + +fn doTest() void { + assertOrPanic(bar(Payload{ .A = 1234 }) == -10); +} + +fn bar(value: Payload) i32 { + assertOrPanic(Letter(value) == Letter.A); + return switch (value) { + Payload.A => |x| return x - 1244, + Payload.B => |x| if (x == 12.34) i32(20) else 21, + Payload.C => |x| if (x) i32(30) else 31, + }; +} + +const MultipleChoice = union(enum(u32)) { + A = 20, + B = 40, + C = 60, + D = 1000, +}; +test "simple union(enum(u32))" { + var x = MultipleChoice.C; + assertOrPanic(x == MultipleChoice.C); + assertOrPanic(@enumToInt(@TagType(MultipleChoice)(x)) == 60); +} + +const MultipleChoice2 = union(enum(u32)) { + Unspecified1: i32, + A: f32 = 20, + Unspecified2: void, + B: bool = 40, + Unspecified3: i32, + C: i8 = 60, + Unspecified4: void, + D: void = 1000, + Unspecified5: i32, +}; + +test "union(enum(u32)) with specified and unspecified tag values" { + comptime assertOrPanic(@TagType(@TagType(MultipleChoice2)) == u32); + testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); + comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); +} + +fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { + assertOrPanic(@enumToInt(@TagType(MultipleChoice2)(x)) == 60); + assertOrPanic(1123 == switch (x) { + MultipleChoice2.A => 1, + MultipleChoice2.B => 2, + MultipleChoice2.C => |v| i32(1000) + v, + MultipleChoice2.D => 4, + MultipleChoice2.Unspecified1 => 5, + MultipleChoice2.Unspecified2 => 6, + MultipleChoice2.Unspecified3 => 7, + MultipleChoice2.Unspecified4 => 8, + MultipleChoice2.Unspecified5 => 9, + }); +} + +const ExternPtrOrInt = extern union { + ptr: *u8, + int: u64, +}; +test "extern union size" { + comptime assertOrPanic(@sizeOf(ExternPtrOrInt) == 8); +} + +const PackedPtrOrInt = packed union { + ptr: *u8, + int: u64, +}; +test "extern union size" { + comptime assertOrPanic(@sizeOf(PackedPtrOrInt) == 8); +} + +const ZeroBits = union { + OnlyField: void, +}; +test "union with only 1 field which is void should be zero bits" { + comptime assertOrPanic(@sizeOf(ZeroBits) == 0); +} + +const TheTag = enum { + A, + B, + C, +}; +const TheUnion = union(TheTag) { + A: i32, + B: i32, + C: i32, +}; +test "union field access gives the enum values" { + assertOrPanic(TheUnion.A == TheTag.A); + assertOrPanic(TheUnion.B == TheTag.B); + assertOrPanic(TheUnion.C == TheTag.C); +} + +test "cast union to tag type of union" { + testCastUnionToTagType(TheUnion{ .B = 1234 }); + comptime testCastUnionToTagType(TheUnion{ .B = 1234 }); +} + +fn testCastUnionToTagType(x: TheUnion) void { + assertOrPanic(TheTag(x) == TheTag.B); +} + +test "cast tag type of union to union" { + var x: Value2 = Letter2.B; + assertOrPanic(Letter2(x) == Letter2.B); +} +const Letter2 = enum { + A, + B, + C, +}; +const Value2 = union(Letter2) { + A: i32, + B, + C, +}; + +test "implicit cast union to its tag type" { + var x: Value2 = Letter2.B; + assertOrPanic(x == Letter2.B); + giveMeLetterB(x); +} +fn giveMeLetterB(x: Letter2) void { + assertOrPanic(x == Value2.B); +} + +pub const PackThis = union(enum) { + Invalid: bool, + StringLiteral: u2, +}; + +test "constant packed union" { + testConstPackedUnion([]PackThis{PackThis{ .StringLiteral = 1 }}); +} + +fn testConstPackedUnion(expected_tokens: []const PackThis) void { + assertOrPanic(expected_tokens[0].StringLiteral == 1); +} + +test "switch on union with only 1 field" { + var r: PartialInst = undefined; + r = PartialInst.Compiled; + switch (r) { + PartialInst.Compiled => { + var z: PartialInstWithPayload = undefined; + z = PartialInstWithPayload{ .Compiled = 1234 }; + switch (z) { + PartialInstWithPayload.Compiled => |x| { + assertOrPanic(x == 1234); + return; + }, + } + }, + } + unreachable; +} + +const PartialInst = union(enum) { + Compiled, +}; + +const PartialInstWithPayload = union(enum) { + Compiled: i32, +}; + +test "access a member of tagged union with conflicting enum tag name" { + const Bar = union(enum) { + A: A, + B: B, + + const A = u8; + const B = void; + }; + + comptime assertOrPanic(Bar.A == u8); +} + +test "tagged union initialization with runtime void" { + assertOrPanic(testTaggedUnionInit({})); +} + +const TaggedUnionWithAVoid = union(enum) { + A, + B: i32, +}; + +fn testTaggedUnionInit(x: var) bool { + const y = TaggedUnionWithAVoid{ .A = x }; + return @TagType(TaggedUnionWithAVoid)(y) == TaggedUnionWithAVoid.A; +} + +pub const UnionEnumNoPayloads = union(enum) { + A, + B, +}; + +test "tagged union with no payloads" { + const a = UnionEnumNoPayloads{ .B = {} }; + switch (a) { + @TagType(UnionEnumNoPayloads).A => @panic("wrong"), + @TagType(UnionEnumNoPayloads).B => {}, + } +} + +test "union with only 1 field casted to its enum type" { + const Literal = union(enum) { + Number: f64, + Bool: bool, + }; + + const Expr = union(enum) { + Literal: Literal, + }; + + var e = Expr{ .Literal = Literal{ .Bool = true } }; + const Tag = @TagType(Expr); + comptime assertOrPanic(@TagType(Tag) == comptime_int); + var t = Tag(e); + assertOrPanic(t == Expr.Literal); +} + +test "union with only 1 field casted to its enum type which has enum value specified" { + const Literal = union(enum) { + Number: f64, + Bool: bool, + }; + + const Tag = enum { + Literal = 33, + }; + + const Expr = union(Tag) { + Literal: Literal, + }; + + var e = Expr{ .Literal = Literal{ .Bool = true } }; + comptime assertOrPanic(@TagType(Tag) == comptime_int); + var t = Tag(e); + assertOrPanic(t == Expr.Literal); + assertOrPanic(@enumToInt(t) == 33); + comptime assertOrPanic(@enumToInt(t) == 33); +} diff --git a/test/stage1/behavior/var_args.zig b/test/stage1/behavior/var_args.zig new file mode 100644 index 0000000000..1f782a3bb3 --- /dev/null +++ b/test/stage1/behavior/var_args.zig @@ -0,0 +1,84 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +fn add(args: ...) i32 { + var sum = i32(0); + { + comptime var i: usize = 0; + inline while (i < args.len) : (i += 1) { + sum += args[i]; + } + } + return sum; +} + +test "add arbitrary args" { + assertOrPanic(add(i32(1), i32(2), i32(3), i32(4)) == 10); + assertOrPanic(add(i32(1234)) == 1234); + assertOrPanic(add() == 0); +} + +fn readFirstVarArg(args: ...) void { + const value = args[0]; +} + +test "send void arg to var args" { + readFirstVarArg({}); +} + +test "pass args directly" { + assertOrPanic(addSomeStuff(i32(1), i32(2), i32(3), i32(4)) == 10); + assertOrPanic(addSomeStuff(i32(1234)) == 1234); + assertOrPanic(addSomeStuff() == 0); +} + +fn addSomeStuff(args: ...) i32 { + return add(args); +} + +test "runtime parameter before var args" { + assertOrPanic(extraFn(10) == 0); + assertOrPanic(extraFn(10, false) == 1); + assertOrPanic(extraFn(10, false, true) == 2); + + // TODO issue #313 + //comptime { + // assertOrPanic(extraFn(10) == 0); + // assertOrPanic(extraFn(10, false) == 1); + // assertOrPanic(extraFn(10, false, true) == 2); + //} +} + +fn extraFn(extra: u32, args: ...) usize { + if (args.len >= 1) { + assertOrPanic(args[0] == false); + } + if (args.len >= 2) { + assertOrPanic(args[1] == true); + } + return args.len; +} + +const foos = []fn (...) bool{ + foo1, + foo2, +}; + +fn foo1(args: ...) bool { + return true; +} +fn foo2(args: ...) bool { + return false; +} + +test "array of var args functions" { + assertOrPanic(foos[0]()); + assertOrPanic(!foos[1]()); +} + +test "pass zero length array to var args param" { + doNothingWithFirstArg(""); +} + +fn doNothingWithFirstArg(args: ...) void { + const a = args[0]; +} diff --git a/test/stage1/behavior/void.zig b/test/stage1/behavior/void.zig new file mode 100644 index 0000000000..431d3f4eb1 --- /dev/null +++ b/test/stage1/behavior/void.zig @@ -0,0 +1,35 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +const Foo = struct { + a: void, + b: i32, + c: void, +}; + +test "compare void with void compile time known" { + comptime { + const foo = Foo{ + .a = {}, + .b = 1, + .c = {}, + }; + assertOrPanic(foo.a == {}); + } +} + +test "iterate over a void slice" { + var j: usize = 0; + for (times(10)) |_, i| { + assertOrPanic(i == j); + j += 1; + } +} + +fn times(n: usize) []const void { + return ([*]void)(undefined)[0..n]; +} + +test "void optional" { + var x: ?void = {}; + assertOrPanic(x != null); +} diff --git a/test/stage1/behavior/while.zig b/test/stage1/behavior/while.zig new file mode 100644 index 0000000000..579b4e4db8 --- /dev/null +++ b/test/stage1/behavior/while.zig @@ -0,0 +1,228 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "while loop" { + var i: i32 = 0; + while (i < 4) { + i += 1; + } + assertOrPanic(i == 4); + assertOrPanic(whileLoop1() == 1); +} +fn whileLoop1() i32 { + return whileLoop2(); +} +fn whileLoop2() i32 { + while (true) { + return 1; + } +} + +test "static eval while" { + assertOrPanic(static_eval_while_number == 1); +} +const static_eval_while_number = staticWhileLoop1(); +fn staticWhileLoop1() i32 { + return whileLoop2(); +} +fn staticWhileLoop2() i32 { + while (true) { + return 1; + } +} + +test "continue and break" { + runContinueAndBreakTest(); + assertOrPanic(continue_and_break_counter == 8); +} +var continue_and_break_counter: i32 = 0; +fn runContinueAndBreakTest() void { + var i: i32 = 0; + while (true) { + continue_and_break_counter += 2; + i += 1; + if (i < 4) { + continue; + } + break; + } + assertOrPanic(i == 4); +} + +test "return with implicit cast from while loop" { + returnWithImplicitCastFromWhileLoopTest() catch unreachable; +} +fn returnWithImplicitCastFromWhileLoopTest() anyerror!void { + while (true) { + return; + } +} + +test "while with continue expression" { + var sum: i32 = 0; + { + var i: i32 = 0; + while (i < 10) : (i += 1) { + if (i == 5) continue; + sum += i; + } + } + assertOrPanic(sum == 40); +} + +test "while with else" { + var sum: i32 = 0; + var i: i32 = 0; + var got_else: i32 = 0; + while (i < 10) : (i += 1) { + sum += 1; + } else { + got_else += 1; + } + assertOrPanic(sum == 10); + assertOrPanic(got_else == 1); +} + +test "while with optional as condition" { + numbers_left = 10; + var sum: i32 = 0; + while (getNumberOrNull()) |value| { + sum += value; + } + assertOrPanic(sum == 45); +} + +test "while with optional as condition with else" { + numbers_left = 10; + var sum: i32 = 0; + var got_else: i32 = 0; + while (getNumberOrNull()) |value| { + sum += value; + assertOrPanic(got_else == 0); + } else { + got_else += 1; + } + assertOrPanic(sum == 45); + assertOrPanic(got_else == 1); +} + +test "while with error union condition" { + numbers_left = 10; + var sum: i32 = 0; + var got_else: i32 = 0; + while (getNumberOrErr()) |value| { + sum += value; + } else |err| { + assertOrPanic(err == error.OutOfNumbers); + got_else += 1; + } + assertOrPanic(sum == 45); + assertOrPanic(got_else == 1); +} + +var numbers_left: i32 = undefined; +fn getNumberOrErr() anyerror!i32 { + return if (numbers_left == 0) error.OutOfNumbers else x: { + numbers_left -= 1; + break :x numbers_left; + }; +} +fn getNumberOrNull() ?i32 { + return if (numbers_left == 0) null else x: { + numbers_left -= 1; + break :x numbers_left; + }; +} + +test "while on optional with else result follow else prong" { + const result = while (returnNull()) |value| { + break value; + } else + i32(2); + assertOrPanic(result == 2); +} + +test "while on optional with else result follow break prong" { + const result = while (returnOptional(10)) |value| { + break value; + } else + i32(2); + assertOrPanic(result == 10); +} + +test "while on error union with else result follow else prong" { + const result = while (returnError()) |value| { + break value; + } else |err| + i32(2); + assertOrPanic(result == 2); +} + +test "while on error union with else result follow break prong" { + const result = while (returnSuccess(10)) |value| { + break value; + } else |err| + i32(2); + assertOrPanic(result == 10); +} + +test "while on bool with else result follow else prong" { + const result = while (returnFalse()) { + break i32(10); + } else + i32(2); + assertOrPanic(result == 2); +} + +test "while on bool with else result follow break prong" { + const result = while (returnTrue()) { + break i32(10); + } else + i32(2); + assertOrPanic(result == 10); +} + +test "break from outer while loop" { + testBreakOuter(); + comptime testBreakOuter(); +} + +fn testBreakOuter() void { + outer: while (true) { + while (true) { + break :outer; + } + } +} + +test "continue outer while loop" { + testContinueOuter(); + comptime testContinueOuter(); +} + +fn testContinueOuter() void { + var i: usize = 0; + outer: while (i < 10) : (i += 1) { + while (true) { + continue :outer; + } + } +} + +fn returnNull() ?i32 { + return null; +} +fn returnOptional(x: i32) ?i32 { + return x; +} +fn returnError() anyerror!i32 { + return error.YouWantedAnError; +} +fn returnSuccess(x: i32) anyerror!i32 { + return x; +} +fn returnFalse() bool { + return false; +} +fn returnTrue() bool { + return true; +} diff --git a/test/stage1/behavior/widening.zig b/test/stage1/behavior/widening.zig new file mode 100644 index 0000000000..7577868aff --- /dev/null +++ b/test/stage1/behavior/widening.zig @@ -0,0 +1,28 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const mem = std.mem; + +test "integer widening" { + var a: u8 = 250; + var b: u16 = a; + var c: u32 = b; + var d: u64 = c; + var e: u64 = d; + var f: u128 = e; + assertOrPanic(f == a); +} + +test "implicit unsigned integer to signed integer" { + var a: u8 = 250; + var b: i16 = a; + assertOrPanic(b == 250); +} + +test "float widening" { + var a: f16 = 12.34; + var b: f32 = a; + var c: f64 = b; + var d: f128 = c; + assertOrPanic(d == a); +} + -- cgit v1.2.3 From b1775ca168e0bcfba6753346c5226881da49c6c4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 6 Feb 2019 13:48:04 -0500 Subject: thread local storage working for linux x86_64 --- CMakeLists.txt | 1 + src/all_types.hpp | 13 +++-- src/analyze.cpp | 69 +++++++++++++++--------- src/analyze.hpp | 1 + src/ast_render.cpp | 7 ++- src/codegen.cpp | 7 +++ src/ir.cpp | 4 ++ src/parser.cpp | 22 +++++--- src/tokenizer.cpp | 2 + src/tokenizer.hpp | 1 + std/debug/index.zig | 1 - std/heap.zig | 7 +-- std/index.zig | 3 +- std/mem.zig | 20 +++++++ std/os/index.zig | 119 +++++++++++++++++++++++------------------- std/os/startup.zig | 26 +++++++++ std/os/test.zig | 16 ++++++ std/special/bootstrap.zig | 63 ++++++++++++++++++++-- test/compile_errors.zig | 19 +++++++ test/stage1/behavior/misc.zig | 8 +++ 20 files changed, 306 insertions(+), 103 deletions(-) create mode 100644 std/os/startup.zig (limited to 'src/parser.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 4dd6a1dcfa..a093bfbfd9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -587,6 +587,7 @@ set(ZIG_STD_FILES "os/linux/vdso.zig" "os/linux/x86_64.zig" "os/path.zig" + "os/startup.zig" "os/time.zig" "os/uefi.zig" "os/windows/advapi32.zig" diff --git a/src/all_types.hpp b/src/all_types.hpp index c4c9e13cfb..5af4e71157 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -544,12 +544,7 @@ struct AstNodeDefer { }; struct AstNodeVariableDeclaration { - VisibMod visib_mod; Buf *symbol; - bool is_const; - bool is_comptime; - bool is_export; - bool is_extern; // one or both of type and expr will be non null AstNode *type; AstNode *expr; @@ -559,6 +554,13 @@ struct AstNodeVariableDeclaration { AstNode *align_expr; // populated if the "section(S)" is present AstNode *section_expr; + Token *threadlocal_tok; + + VisibMod visib_mod; + bool is_const; + bool is_comptime; + bool is_export; + bool is_extern; }; struct AstNodeTestDecl { @@ -1873,6 +1875,7 @@ struct ZigVar { bool shadowable; bool src_is_const; bool gen_is_const; + bool is_thread_local; }; struct ErrorTableEntry { diff --git a/src/analyze.cpp b/src/analyze.cpp index ff961a7044..96f1faaaa9 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -28,28 +28,10 @@ static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type); static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry); -ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) { - if (node->owner->c_import_node != nullptr) { - // if this happens, then translate_c generated code that - // failed semantic analysis, which isn't supposed to happen - ErrorMsg *err = add_node_error(g, node->owner->c_import_node, - buf_sprintf("compiler bug: @cImport generated invalid zig code")); - - add_error_note(g, err, node, msg); - - g->errors.append(err); - return err; - } - - ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column, - node->owner->source_code, node->owner->line_offsets, msg); - - g->errors.append(err); - return err; -} - -ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) { - if (node->owner->c_import_node != nullptr) { +static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ImportTableEntry *owner, Token *token, + Buf *msg) +{ + if (owner->c_import_node != nullptr) { // if this happens, then translate_c generated code that // failed semantic analysis, which isn't supposed to happen @@ -64,13 +46,46 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m return note; } - ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column, - node->owner->source_code, node->owner->line_offsets, msg); + ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column, + owner->source_code, owner->line_offsets, msg); err_msg_add_note(parent_msg, err); return err; } +ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg) { + if (owner->c_import_node != nullptr) { + // if this happens, then translate_c generated code that + // failed semantic analysis, which isn't supposed to happen + ErrorMsg *err = add_node_error(g, owner->c_import_node, + buf_sprintf("compiler bug: @cImport generated invalid zig code")); + + add_error_note_token(g, err, owner, token, msg); + + g->errors.append(err); + return err; + } + ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column, + owner->source_code, owner->line_offsets, msg); + + g->errors.append(err); + return err; +} + +ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) { + Token fake_token; + fake_token.start_line = node->line; + fake_token.start_column = node->column; + return add_token_error(g, node->owner, &fake_token, msg); +} + +ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) { + Token fake_token; + fake_token.start_line = node->line; + fake_token.start_column = node->column; + return add_error_note_token(g, parent_msg, node->owner, &fake_token, msg); +} + ZigType *new_type_table_entry(ZigTypeId id) { ZigType *entry = allocate(1); entry->id = id; @@ -3668,6 +3683,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { bool is_const = var_decl->is_const; bool is_extern = var_decl->is_extern; bool is_export = var_decl->is_export; + bool is_thread_local = var_decl->threadlocal_tok != nullptr; ZigType *explicit_type = nullptr; if (var_decl->type) { @@ -3727,6 +3743,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol, is_const, init_val, &tld_var->base, type); tld_var->var->linkage = linkage; + tld_var->var->is_thread_local = is_thread_local; if (implicit_type != nullptr && type_is_invalid(implicit_type)) { tld_var->var->var_type = g->builtin_types.entry_invalid; @@ -3747,6 +3764,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { } } + if (is_thread_local && is_const) { + add_node_error(g, source_node, buf_sprintf("threadlocal variable cannot be constant")); + } + g->global_vars.append(tld_var); } diff --git a/src/analyze.hpp b/src/analyze.hpp index f558fa44b0..9773782510 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -12,6 +12,7 @@ void semantic_analyze(CodeGen *g); ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg); +ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg); ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg); ZigType *new_type_table_entry(ZigTypeId id); ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 994ba5f5b1..34a7faa2a5 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -132,6 +132,10 @@ static const char *const_or_var_string(bool is_const) { return is_const ? "const" : "var"; } +static const char *thread_local_string(Token *tok) { + return (tok == nullptr) ? "" : "threadlocal "; +} + const char *container_string(ContainerKind kind) { switch (kind) { case ContainerKindEnum: return "enum"; @@ -554,8 +558,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { { const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod); const char *extern_str = extern_string(node->data.variable_declaration.is_extern); + const char *thread_local_str = thread_local_string(node->data.variable_declaration.threadlocal_tok); const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const); - fprintf(ar->f, "%s%s%s ", pub_str, extern_str, const_or_var); + fprintf(ar->f, "%s%s%s%s ", pub_str, extern_str, thread_local_str, const_or_var); print_symbol(ar, node->data.variable_declaration.symbol); if (node->data.variable_declaration.type) { diff --git a/src/codegen.cpp b/src/codegen.cpp index de2222afb7..d8fc077efc 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6445,6 +6445,9 @@ static void do_code_gen(CodeGen *g) { maybe_import_dll(g, global_value, GlobalLinkageIdStrong); LLVMSetAlignment(global_value, var->align_bytes); LLVMSetGlobalConstant(global_value, var->gen_is_const); + if (var->is_thread_local && !g->is_single_threaded) { + LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel); + } } } else { bool exported = (var->linkage == VarLinkageExport); @@ -6470,6 +6473,9 @@ static void do_code_gen(CodeGen *g) { } LLVMSetGlobalConstant(global_value, var->gen_is_const); + if (var->is_thread_local && !g->is_single_threaded) { + LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel); + } } var->value_ref = global_value; @@ -7520,6 +7526,7 @@ static Error define_builtin_compile_vars(CodeGen *g) { g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename); g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); + g->std_package->package_table.put(buf_create_from_str("std"), g->std_package); g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents); scan_import(g, g->compile_var_import); diff --git a/src/ir.cpp b/src/ir.cpp index 3cbbdc8103..02b2b12230 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5204,6 +5204,10 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod add_node_error(irb->codegen, variable_declaration->section_expr, buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol))); } + if (variable_declaration->threadlocal_tok != nullptr) { + add_token_error(irb->codegen, node->owner, variable_declaration->threadlocal_tok, + buf_sprintf("function-local variable '%s' cannot be threadlocal", buf_ptr(variable_declaration->symbol))); + } // Temporarily set the name of the IrExecutable to the VariableDeclaration // so that the struct or enum from the init expression inherits the name. diff --git a/src/parser.cpp b/src/parser.cpp index 81bd469d1c..1c1af87c51 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -844,12 +844,17 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) { // VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON static AstNode *ast_parse_var_decl(ParseContext *pc) { - Token *first = eat_token_if(pc, TokenIdKeywordConst); - if (first == nullptr) - first = eat_token_if(pc, TokenIdKeywordVar); - if (first == nullptr) - return nullptr; - + Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal); + Token *mut_kw = eat_token_if(pc, TokenIdKeywordConst); + if (mut_kw == nullptr) + mut_kw = eat_token_if(pc, TokenIdKeywordVar); + if (mut_kw == nullptr) { + if (thread_local_kw == nullptr) { + return nullptr; + } else { + ast_invalid_token_error(pc, peek_token(pc)); + } + } Token *identifier = expect_token(pc, TokenIdSymbol); AstNode *type_expr = nullptr; if (eat_token_if(pc, TokenIdColon) != nullptr) @@ -863,8 +868,9 @@ static AstNode *ast_parse_var_decl(ParseContext *pc) { expect_token(pc, TokenIdSemicolon); - AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, first); - res->data.variable_declaration.is_const = first->id == TokenIdKeywordConst; + AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, mut_kw); + res->data.variable_declaration.threadlocal_tok = thread_local_kw; + res->data.variable_declaration.is_const = mut_kw->id == TokenIdKeywordConst; res->data.variable_declaration.symbol = token_buf(identifier); res->data.variable_declaration.type = type_expr; res->data.variable_declaration.align_expr = align_expr; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index d43bfabf6d..3acd605748 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -146,6 +146,7 @@ static const struct ZigKeyword zig_keywords[] = { {"suspend", TokenIdKeywordSuspend}, {"switch", TokenIdKeywordSwitch}, {"test", TokenIdKeywordTest}, + {"threadlocal", TokenIdKeywordThreadLocal}, {"true", TokenIdKeywordTrue}, {"try", TokenIdKeywordTry}, {"undefined", TokenIdKeywordUndefined}, @@ -1586,6 +1587,7 @@ const char * token_name(TokenId id) { case TokenIdKeywordStruct: return "struct"; case TokenIdKeywordSwitch: return "switch"; case TokenIdKeywordTest: return "test"; + case TokenIdKeywordThreadLocal: return "threadlocal"; case TokenIdKeywordTrue: return "true"; case TokenIdKeywordTry: return "try"; case TokenIdKeywordUndefined: return "undefined"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 1574e95571..17f36699b3 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -88,6 +88,7 @@ enum TokenId { TokenIdKeywordSuspend, TokenIdKeywordSwitch, TokenIdKeywordTest, + TokenIdKeywordThreadLocal, TokenIdKeywordTrue, TokenIdKeywordTry, TokenIdKeywordUndefined, diff --git a/std/debug/index.zig b/std/debug/index.zig index 838bd0c166..b4ef849509 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -37,7 +37,6 @@ const Module = struct { var stderr_file: os.File = undefined; var stderr_file_out_stream: os.File.OutStream = undefined; -/// TODO multithreaded awareness var stderr_stream: ?*io.OutStream(os.File.WriteError) = null; var stderr_mutex = std.Mutex.init(); pub fn warn(comptime fmt: []const u8, args: ...) void { diff --git a/std/heap.zig b/std/heap.zig index fd2ce1e965..1403f8e831 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -106,9 +106,7 @@ pub const DirectAllocator = struct { }; const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory; const root_addr = @ptrToInt(ptr); - const rem = @rem(root_addr, alignment); - const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); - const adjusted_addr = root_addr + march_forward_bytes; + const adjusted_addr = mem.alignForward(root_addr, alignment); const record_addr = adjusted_addr + n; @intToPtr(*align(1) usize, record_addr).* = root_addr; return @intToPtr([*]u8, adjusted_addr)[0..n]; @@ -126,8 +124,7 @@ pub const DirectAllocator = struct { const base_addr = @ptrToInt(old_mem.ptr); const old_addr_end = base_addr + old_mem.len; const new_addr_end = base_addr + new_size; - const rem = @rem(new_addr_end, os.page_size); - const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem); + const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size); if (old_addr_end > new_addr_end_rounded) { _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded); } diff --git a/std/index.zig b/std/index.zig index 80d1e46bb6..ef3988460f 100644 --- a/std/index.zig +++ b/std/index.zig @@ -33,8 +33,8 @@ pub const io = @import("io.zig"); pub const json = @import("json.zig"); pub const macho = @import("macho.zig"); pub const math = @import("math/index.zig"); -pub const meta = @import("meta/index.zig"); pub const mem = @import("mem.zig"); +pub const meta = @import("meta/index.zig"); pub const net = @import("net.zig"); pub const os = @import("os/index.zig"); pub const pdb = @import("pdb.zig"); @@ -45,6 +45,7 @@ pub const unicode = @import("unicode.zig"); pub const zig = @import("zig/index.zig"); pub const lazyInit = @import("lazy_init.zig").lazyInit; +pub const startup = @import("os/startup.zig"); test "std" { // run tests from these diff --git a/std/mem.zig b/std/mem.zig index 26ae4ef089..178a5f6c6f 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -1366,3 +1366,23 @@ test "std.mem.subArrayPtr" { sub2[1] = 'X'; debug.assert(std.mem.eql(u8, a2, "abcXef")); } + +/// Round an address up to the nearest aligned address +pub fn alignForward(addr: usize, alignment: usize) usize { + return (addr + alignment - 1) & ~(alignment - 1); +} + +test "std.mem.alignForward" { + debug.assertOrPanic(alignForward(1, 1) == 1); + debug.assertOrPanic(alignForward(2, 1) == 2); + debug.assertOrPanic(alignForward(1, 2) == 2); + debug.assertOrPanic(alignForward(2, 2) == 2); + debug.assertOrPanic(alignForward(3, 2) == 4); + debug.assertOrPanic(alignForward(4, 2) == 4); + debug.assertOrPanic(alignForward(7, 8) == 8); + debug.assertOrPanic(alignForward(8, 8) == 8); + debug.assertOrPanic(alignForward(9, 8) == 16); + debug.assertOrPanic(alignForward(15, 8) == 16); + debug.assertOrPanic(alignForward(16, 8) == 16); + debug.assertOrPanic(alignForward(17, 8) == 24); +} diff --git a/std/os/index.zig b/std/os/index.zig index 451c0a3436..68b3409757 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -8,6 +8,9 @@ const is_posix = switch (builtin.os) { }; const os = @This(); +// See the comment in startup.zig for why this does not use the `std` global above. +const startup = @import("std").startup; + test "std.os" { _ = @import("child_process.zig"); _ = @import("darwin.zig"); @@ -667,14 +670,11 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError { } } -pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null; -pub var posix_environ_raw: [][*]u8 = undefined; - /// See std.elf for the constants. pub fn linuxGetAuxVal(index: usize) usize { if (builtin.link_libc) { return usize(std.c.getauxval(index)); - } else if (linux_elf_aux_maybe) |auxv| { + } else if (startup.linux_elf_aux_maybe) |auxv| { var i: usize = 0; while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) { if (auxv[i].a_type == index) @@ -692,12 +692,7 @@ pub fn getBaseAddress() usize { return base; } const phdr = linuxGetAuxVal(std.elf.AT_PHDR); - const ElfHeader = switch (@sizeOf(usize)) { - 4 => std.elf.Elf32_Ehdr, - 8 => std.elf.Elf64_Ehdr, - else => @compileError("Unsupported architecture"), - }; - return phdr - @sizeOf(ElfHeader); + return phdr - @sizeOf(std.elf.Ehdr); }, builtin.Os.macosx, builtin.Os.freebsd => return @ptrToInt(&std.c._mh_execute_header), builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)), @@ -739,7 +734,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { try result.setMove(key, value); } } else { - for (posix_environ_raw) |ptr| { + for (startup.posix_environ_raw) |ptr| { var line_i: usize = 0; while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} const key = ptr[0..line_i]; @@ -761,7 +756,7 @@ test "os.getEnvMap" { /// TODO make this go through libc when we have it pub fn getEnvPosix(key: []const u8) ?[]const u8 { - for (posix_environ_raw) |ptr| { + for (startup.posix_environ_raw) |ptr| { var line_i: usize = 0; while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} const this_key = ptr[0..line_i]; @@ -1942,14 +1937,14 @@ pub const ArgIteratorPosix = struct { pub fn init() ArgIteratorPosix { return ArgIteratorPosix{ .index = 0, - .count = raw.len, + .count = startup.posix_argv_raw.len, }; } pub fn next(self: *ArgIteratorPosix) ?[]const u8 { if (self.index == self.count) return null; - const s = raw[self.index]; + const s = startup.posix_argv_raw[self.index]; self.index += 1; return cstr.toSlice(s); } @@ -1960,10 +1955,6 @@ pub const ArgIteratorPosix = struct { self.index += 1; return true; } - - /// This is marked as public but actually it's only meant to be used - /// internally by zig's startup code. - pub var raw: [][*]u8 = undefined; }; pub const ArgIteratorWindows = struct { @@ -2908,14 +2899,15 @@ pub const Thread = struct { pub const Data = if (use_pthreads) struct { handle: Thread.Handle, - stack_addr: usize, - stack_len: usize, + mmap_addr: usize, + mmap_len: usize, } else switch (builtin.os) { builtin.Os.linux => struct { handle: Thread.Handle, - stack_addr: usize, - stack_len: usize, + mmap_addr: usize, + mmap_len: usize, + tls_end_addr: usize, }, builtin.Os.windows => struct { handle: Thread.Handle, @@ -2955,7 +2947,7 @@ pub const Thread = struct { posix.EDEADLK => unreachable, else => unreachable, } - assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0); + assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0); } else switch (builtin.os) { builtin.Os.linux => { while (true) { @@ -2969,7 +2961,7 @@ pub const Thread = struct { else => unreachable, } } - assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0); + assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0); }, builtin.Os.windows => { assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0); @@ -3097,42 +3089,56 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0; - const mmap_len = default_stack_size; - const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); - if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory; - errdefer assert(posix.munmap(stack_addr, mmap_len) == 0); + var stack_end_offset: usize = undefined; + var thread_start_offset: usize = undefined; + var context_start_offset: usize = undefined; + var tls_start_offset: usize = undefined; + const mmap_len = blk: { + // First in memory will be the stack, which grows downwards. + var l: usize = mem.alignForward(default_stack_size, os.page_size); + stack_end_offset = l; + // Above the stack, so that it can be in the same mmap call, put the Thread object. + l = mem.alignForward(l, @alignOf(Thread)); + thread_start_offset = l; + l += @sizeOf(Thread); + // Next, the Context object. + if (@sizeOf(Context) != 0) { + l = mem.alignForward(l, @alignOf(Context)); + context_start_offset = l; + l += @sizeOf(Context); + } + // Finally, the Thread Local Storage, if any. + if (!Thread.use_pthreads) { + if (startup.linux_tls_phdr) |tls_phdr| { + l = mem.alignForward(l, tls_phdr.p_align); + tls_start_offset = l; + l += tls_phdr.p_memsz; + } + } + break :blk l; + }; + const mmap_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); + if (mmap_addr == posix.MAP_FAILED) return error.OutOfMemory; + errdefer assert(posix.munmap(mmap_addr, mmap_len) == 0); + + const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset)); + thread_ptr.data.mmap_addr = mmap_addr; + thread_ptr.data.mmap_len = mmap_len; - var stack_end: usize = stack_addr + mmap_len; var arg: usize = undefined; if (@sizeOf(Context) != 0) { - stack_end -= @sizeOf(Context); - stack_end -= stack_end % @alignOf(Context); - assert(stack_end >= stack_addr); - const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, stack_end)); + arg = mmap_addr + context_start_offset; + const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg)); context_ptr.* = context; - arg = stack_end; } - stack_end -= @sizeOf(Thread); - stack_end -= stack_end % @alignOf(Thread); - assert(stack_end >= stack_addr); - const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, stack_end)); - - thread_ptr.data.stack_addr = stack_addr; - thread_ptr.data.stack_len = mmap_len; - - if (builtin.os == builtin.Os.windows) { - // use windows API directly - @compileError("TODO support spawnThread for Windows"); - } else if (Thread.use_pthreads) { + if (Thread.use_pthreads) { // use pthreads var attr: c.pthread_attr_t = undefined; if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; defer assert(c.pthread_attr_destroy(&attr) == 0); - // align to page - stack_end -= stack_end % os.page_size; - assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, stack_addr), stack_end - stack_addr) == 0); + assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0); const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg)); switch (err) { @@ -3143,10 +3149,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread else => return unexpectedErrorPosix(@intCast(usize, err)), } } else if (builtin.os == builtin.Os.linux) { - // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly - const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; - const newtls: usize = 0; - const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle); + var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | + posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | + posix.CLONE_DETACHED; + var newtls: usize = undefined; + if (startup.linux_tls_phdr) |tls_phdr| { + @memcpy(@intToPtr([*]u8, mmap_addr + tls_start_offset), startup.linux_tls_img_src, tls_phdr.p_filesz); + thread_ptr.data.tls_end_addr = mmap_addr + mmap_len; + newtls = @ptrToInt(&thread_ptr.data.tls_end_addr); + flags |= posix.CLONE_SETTLS; + } + const rc = posix.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle); const err = posix.getErrno(rc); switch (err) { 0 => return thread_ptr, diff --git a/std/os/startup.zig b/std/os/startup.zig new file mode 100644 index 0000000000..c54d274c5d --- /dev/null +++ b/std/os/startup.zig @@ -0,0 +1,26 @@ +// This file contains global variables that are initialized on startup from +// std/special/bootstrap.zig. There are a few things to be aware of here. +// +// First, when building an object or library, and no entry point is defined +// (such as pub fn main), std/special/bootstrap.zig is not included in the +// compilation. And so these global variables will remain set to the values +// you see here. +// +// Second, when using `zig test` to test the standard library, note that +// `zig test` is self-hosted. This means that it uses std/special/bootstrap.zig +// and an @import("std") from the install directory, which is distinct from +// the standard library files that we are directly testing with `zig test`. +// This means that these global variables would not get set. So the workaround +// here is that references to these globals from the standard library must +// use `@import("std").startup` rather than +// `@import("path/to/std/index.zig").startup` (and rather than the file path of +// this file directly). We also put "std" as a reference to itself in the +// standard library package so that this can work. + +const std = @import("../index.zig"); + +pub var linux_tls_phdr: ?*std.elf.Phdr = null; +pub var linux_tls_img_src: [*]const u8 = undefined; // defined when linux_tls_phdr is non-null +pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null; +pub var posix_environ_raw: [][*]u8 = undefined; +pub var posix_argv_raw: [][*]u8 = undefined; diff --git a/std/os/test.zig b/std/os/test.zig index f14cf47786..bd9148d1b1 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -105,3 +105,19 @@ test "AtomicFile" { try os.deleteFile(test_out_file); } + +test "thread local storage" { + if (builtin.single_threaded) return error.SkipZigTest; + const thread1 = try std.os.spawnThread({}, testTls); + const thread2 = try std.os.spawnThread({}, testTls); + testTls({}); + thread1.wait(); + thread2.wait(); +} + +threadlocal var x: i32 = 1234; +fn testTls(context: void) void { + if (x != 1234) @panic("bad start value"); + x += 1; + if (x != 1235) @panic("bad end value"); +} diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 129bde913f..0e84f67481 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -4,6 +4,7 @@ const root = @import("@root"); const std = @import("std"); const builtin = @import("builtin"); +const assert = std.debug.assert; var argc_ptr: [*]usize = undefined; @@ -61,9 +62,23 @@ fn posixCallMainAndExit() noreturn { while (envp_optional[envp_count]) |_| : (envp_count += 1) {} const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count]; if (builtin.os == builtin.Os.linux) { - const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1); - std.os.linux_elf_aux_maybe = @ptrCast([*]std.elf.Auxv, auxv); - std.debug.assert(std.os.linuxGetAuxVal(std.elf.AT_PAGESZ) == std.os.page_size); + // Scan auxiliary vector. + const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1); + std.startup.linux_elf_aux_maybe = auxv; + var i: usize = 0; + var at_phdr: usize = 0; + var at_phnum: usize = 0; + var at_phent: usize = 0; + while (auxv[i].a_un.a_val != 0) : (i += 1) { + switch (auxv[i].a_type) { + std.elf.AT_PAGESZ => assert(auxv[i].a_un.a_val == std.os.page_size), + std.elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val, + std.elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val, + std.elf.AT_PHENT => at_phent = auxv[i].a_un.a_val, + else => {}, + } + } + if (!builtin.single_threaded) linuxInitializeThreadLocalStorage(at_phdr, at_phnum, at_phent); } std.os.posix.exit(callMainWithArgs(argc, argv, envp)); @@ -72,8 +87,8 @@ fn posixCallMainAndExit() noreturn { // This is marked inline because for some reason LLVM in release mode fails to inline it, // and we want fewer call frames in stack traces. inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 { - std.os.ArgIteratorPosix.raw = argv[0..argc]; - std.os.posix_environ_raw = envp; + std.startup.posix_argv_raw = argv[0..argc]; + std.startup.posix_environ_raw = envp; return callMain(); } @@ -116,3 +131,41 @@ inline fn callMain() u8 { else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"), } } + +var tls_end_addr: usize = undefined; +const main_thread_tls_align = 32; +var main_thread_tls_bytes: [64]u8 align(main_thread_tls_align) = [1]u8{0} ** 64; + +fn linuxInitializeThreadLocalStorage(at_phdr: usize, at_phnum: usize, at_phent: usize) void { + var phdr_addr = at_phdr; + var n = at_phnum; + var base: usize = 0; + while (n != 0) : ({n -= 1; phdr_addr += at_phent;}) { + const phdr = @intToPtr(*std.elf.Phdr, phdr_addr); + // TODO look for PT_DYNAMIC when we have https://github.com/ziglang/zig/issues/1917 + switch (phdr.p_type) { + std.elf.PT_PHDR => base = at_phdr - phdr.p_vaddr, + std.elf.PT_TLS => std.startup.linux_tls_phdr = phdr, + else => continue, + } + } + const tls_phdr = std.startup.linux_tls_phdr orelse return; + std.startup.linux_tls_img_src = @intToPtr([*]const u8, base + tls_phdr.p_vaddr); + assert(main_thread_tls_bytes.len >= tls_phdr.p_memsz); // not enough preallocated Thread Local Storage + assert(main_thread_tls_align >= tls_phdr.p_align); // preallocated Thread Local Storage not aligned enough + @memcpy(&main_thread_tls_bytes, std.startup.linux_tls_img_src, tls_phdr.p_filesz); + tls_end_addr = @ptrToInt(&main_thread_tls_bytes) + tls_phdr.p_memsz; + linuxSetThreadArea(@ptrToInt(&tls_end_addr)); +} + +fn linuxSetThreadArea(addr: usize) void { + switch (builtin.arch) { + builtin.Arch.x86_64 => { + const ARCH_SET_FS = 0x1002; + const rc = std.os.linux.syscall2(std.os.linux.SYS_arch_prctl, ARCH_SET_FS, addr); + // acrh_prctl is documented to never fail + assert(rc == 0); + }, + else => @compileError("Unsupported architecture"), + } +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 30d9ca5d70..acd1eada06 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,25 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "threadlocal qualifier on const", + \\threadlocal const x: i32 = 1234; + \\export fn entry() i32 { + \\ return x; + \\} + , + ".tmp_source.zig:1:13: error: threadlocal variable cannot be constant", + ); + + cases.add( + "threadlocal qualifier on local variable", + \\export fn entry() void { + \\ threadlocal var x: i32 = 1234; + \\} + , + ".tmp_source.zig:2:5: error: function-local variable 'x' cannot be threadlocal", + ); + cases.add( "@bitCast same size but bit count mismatch", \\export fn entry(byte: u8) void { diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index 8d2555dddd..3cc8e5f31e 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -685,3 +685,11 @@ test "fn call returning scalar optional in equality expression" { fn getNull() ?*i32 { return null; } + +test "thread local variable" { + const S = struct { + threadlocal var t: i32 = 1234; + }; + S.t += 1; + assertOrPanic(S.t == 1235); +} -- cgit v1.2.3 From b8cbe3872e702ab8ec388e75cb711330a45825b0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 10 Feb 2019 00:14:30 -0500 Subject: added C pointer type and implicit int-to-ptr for this type See #1059 --- src/all_types.hpp | 1 + src/analyze.cpp | 14 ++- src/ir.cpp | 236 ++++++++++++++++++++++++++------------ src/parser.cpp | 8 ++ src/tokenizer.cpp | 19 ++- src/tokenizer.hpp | 1 + test/stage1/behavior/pointers.zig | 6 + 7 files changed, 210 insertions(+), 75 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index 908c0e327c..fd66b77ad2 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1038,6 +1038,7 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b); enum PtrLen { PtrLenUnknown, PtrLenSingle, + PtrLenC, }; struct ZigTypePointer { diff --git a/src/analyze.cpp b/src/analyze.cpp index 970d1cc382..e561050e0d 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -417,6 +417,18 @@ ZigType *get_promise_type(CodeGen *g, ZigType *result_type) { return entry; } +static const char *ptr_len_to_star_str(PtrLen ptr_len) { + switch (ptr_len) { + case PtrLenSingle: + return "*"; + case PtrLenUnknown: + return "[*]"; + case PtrLenC: + return "[*c]"; + } + zig_unreachable(); +} + ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const, bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset_in_host, uint32_t host_int_bytes) @@ -466,7 +478,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons ZigType *entry = new_type_table_entry(ZigTypeIdPointer); - const char *star_str = ptr_len == PtrLenSingle ? "*" : "[*]"; + const char *star_str = ptr_len_to_star_str(ptr_len); const char *const_str = is_const ? "const " : ""; const char *volatile_str = is_volatile ? "volatile " : ""; buf_resize(&entry->name, 0); diff --git a/src/ir.cpp b/src/ir.cpp index 5d4013b4b9..76277f541b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -169,6 +169,10 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs); static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align); static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry); +static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, + ZigType *ptr_type); +static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, + ZigType *dest_type); static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); @@ -5019,10 +5023,23 @@ static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction * return ir_build_ref(irb, scope, value->source_node, value, false, false); } +static PtrLen star_token_to_ptr_len(TokenId token_id) { + switch (token_id) { + case TokenIdStar: + case TokenIdStarStar: + return PtrLenSingle; + case TokenIdBracketStarBracket: + return PtrLenUnknown; + case TokenIdBracketStarCBracket: + return PtrLenC; + default: + zig_unreachable(); + } +} + static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypePointerType); - PtrLen ptr_len = (node->data.pointer_type.star_token->id == TokenIdStar || - node->data.pointer_type.star_token->id == TokenIdStarStar) ? PtrLenSingle : PtrLenUnknown; + PtrLen ptr_len = star_token_to_ptr_len(node->data.pointer_type.star_token->id); bool is_const = node->data.pointer_type.is_const; bool is_volatile = node->data.pointer_type.is_volatile; AstNode *expr_node = node->data.pointer_type.op_expr; @@ -8538,6 +8555,20 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc } } } + if (other_type->id == ZigTypeIdPointer && other_type->data.pointer.ptr_len == PtrLenC && const_val_is_int) { + if (!bigint_fits_in_bits(&const_val->data.x_bigint, ira->codegen->pointer_size_bytes * 8, true) && + !bigint_fits_in_bits(&const_val->data.x_bigint, ira->codegen->pointer_size_bytes * 8, false)) + { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &const_val->data.x_bigint, 10); + + ir_add_error(ira, instruction, + buf_sprintf("integer value %s outside of pointer address range", + buf_ptr(val_buf))); + return false; + } + return true; + } const char *num_lit_str; Buf *val_buf = buf_alloc(); @@ -10811,6 +10842,37 @@ static IrInstruction *ir_analyze_vector_to_array(IrAnalyze *ira, IrInstruction * return ir_build_vector_to_array(ira, source_instr, vector, array_type); } +static IrInstruction *ir_analyze_int_to_c_ptr(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *integer, ZigType *dest_type) +{ + IrInstruction *unsigned_integer; + if (instr_is_comptime(integer)) { + unsigned_integer = integer; + } else { + assert(integer->value.type->id == ZigTypeIdInt); + + if (integer->value.type->data.integral.bit_count > + ira->codegen->builtin_types.entry_usize->data.integral.bit_count) + { + ir_add_error(ira, source_instr, + buf_sprintf("integer type too big for implicit @intToPtr to type '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_instruction; + } + + if (integer->value.type->data.integral.is_signed) { + ZigType *unsigned_int_type = get_int_type(ira->codegen, false, + integer->value.type->data.integral.bit_count); + unsigned_integer = ir_analyze_bit_cast(ira, source_instr, integer, unsigned_int_type); + if (type_is_invalid(unsigned_integer->value.type)) + return ira->codegen->invalid_instruction; + } else { + unsigned_integer = integer; + } + } + + return ir_analyze_int_to_ptr(ira, source_instr, unsigned_integer, dest_type); +} + static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, ZigType *wanted_type, IrInstruction *value) { @@ -11217,6 +11279,14 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_array_to_vector(ira, source_instr, value, wanted_type); } + // casting to C pointers + if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC) { + // cast from integer to C pointer + if (actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt) { + return ir_analyze_int_to_c_ptr(ira, source_instr, value, wanted_type); + } + } + // cast from undefined to anything if (actual_type->id == ZigTypeIdUndefined) { return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); @@ -20674,32 +20744,10 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou zig_unreachable(); } -static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) { - Error err; - IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_instruction; - - IrInstruction *value = instruction->value->child; - ZigType *src_type = value->value.type; - if (type_is_invalid(src_type)) - return ira->codegen->invalid_instruction; - - if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_instruction; - - if ((err = type_resolve(ira->codegen, src_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_instruction; - - if (get_codegen_ptr_type(src_type) != nullptr) { - ir_add_error(ira, value, - buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&src_type->name))); - return ira->codegen->invalid_instruction; - } - - switch (src_type->id) { +static bool type_can_bit_cast(ZigType *t) { + switch (t->id) { case ZigTypeIdInvalid: + zig_unreachable(); case ZigTypeIdMetaType: case ZigTypeIdOpaque: case ZigTypeIdBoundFn: @@ -20710,42 +20758,36 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct case ZigTypeIdComptimeInt: case ZigTypeIdUndefined: case ZigTypeIdNull: - ir_add_error(ira, dest_type_value, - buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name))); - return ira->codegen->invalid_instruction; + case ZigTypeIdPointer: + return false; default: - break; + // TODO list these types out explicitly, there are probably some other invalid ones here + return true; } +} - if (get_codegen_ptr_type(dest_type) != nullptr) { - ir_add_error(ira, dest_type_value, - buf_sprintf("unable to @bitCast to pointer type '%s'", buf_ptr(&dest_type->name))); +static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, + ZigType *dest_type) +{ + Error err; + + ZigType *src_type = value->value.type; + assert(get_codegen_ptr_type(src_type) == nullptr); + assert(type_can_bit_cast(src_type)); + assert(get_codegen_ptr_type(dest_type) == nullptr); + assert(type_can_bit_cast(dest_type)); + + if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_instruction; + + if ((err = type_resolve(ira->codegen, src_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; - } - switch (dest_type->id) { - case ZigTypeIdInvalid: - case ZigTypeIdMetaType: - case ZigTypeIdOpaque: - case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: - case ZigTypeIdNamespace: - case ZigTypeIdUnreachable: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - ir_add_error(ira, dest_type_value, - buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_instruction; - default: - break; - } uint64_t dest_size_bytes = type_size(ira->codegen, dest_type); uint64_t src_size_bytes = type_size(ira->codegen, src_type); if (dest_size_bytes != src_size_bytes) { - ir_add_error(ira, &instruction->base, + ir_add_error(ira, source_instr, buf_sprintf("destination type '%s' has size %" ZIG_PRI_u64 " but source type '%s' has size %" ZIG_PRI_u64, buf_ptr(&dest_type->name), dest_size_bytes, buf_ptr(&src_type->name), src_size_bytes)); @@ -20755,7 +20797,7 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct uint64_t dest_size_bits = type_size_bits(ira->codegen, dest_type); uint64_t src_size_bits = type_size_bits(ira->codegen, src_type); if (dest_size_bits != src_size_bits) { - ir_add_error(ira, &instruction->base, + ir_add_error(ira, source_instr, buf_sprintf("destination type '%s' has %" ZIG_PRI_u64 " bits but source type '%s' has %" ZIG_PRI_u64 " bits", buf_ptr(&dest_type->name), dest_size_bits, buf_ptr(&src_type->name), src_size_bits)); @@ -20767,44 +20809,63 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_const(ira, &instruction->base, dest_type); + IrInstruction *result = ir_const(ira, source_instr, dest_type); uint8_t *buf = allocate_nonzero(src_size_bytes); buf_write_value_bytes(ira->codegen, buf, val); - if ((err = buf_read_value_bytes(ira, ira->codegen, instruction->base.source_node, buf, &result->value))) + if ((err = buf_read_value_bytes(ira, ira->codegen, source_instr->source_node, buf, &result->value))) return ira->codegen->invalid_instruction; return result; } - IrInstruction *result = ir_build_bit_cast(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, nullptr, value); + IrInstruction *result = ir_build_bit_cast(&ira->new_irb, source_instr->scope, + source_instr->source_node, nullptr, value); result->value.type = dest_type; return result; } -static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) { - Error err; +static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) { IrInstruction *dest_type_value = instruction->dest_type->child; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; - // We explicitly check for the size, so we can use get_src_ptr_type - if (get_src_ptr_type(dest_type) == nullptr) { - ir_add_error(ira, dest_type_value, buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); + IrInstruction *value = instruction->value->child; + ZigType *src_type = value->value.type; + if (type_is_invalid(src_type)) + return ira->codegen->invalid_instruction; + + if (get_codegen_ptr_type(src_type) != nullptr) { + ir_add_error(ira, value, + buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&src_type->name))); return ira->codegen->invalid_instruction; } - if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) + if (!type_can_bit_cast(src_type)) { + ir_add_error(ira, dest_type_value, + buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name))); return ira->codegen->invalid_instruction; - if (!type_has_bits(dest_type)) { + } + + if (get_codegen_ptr_type(dest_type) != nullptr) { ir_add_error(ira, dest_type_value, - buf_sprintf("type '%s' has 0 bits and cannot store information", buf_ptr(&dest_type->name))); + buf_sprintf("unable to @bitCast to pointer type '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_instruction; } - IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + if (!type_can_bit_cast(dest_type)) { + ir_add_error(ira, dest_type_value, + buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_instruction; + } + + return ir_analyze_bit_cast(ira, &instruction->base, value, dest_type); +} + +static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, + ZigType *ptr_type) +{ + assert(get_src_ptr_type(ptr_type) != nullptr); + assert(type_has_bits(ptr_type)); IrInstruction *casted_int = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_usize); if (type_is_invalid(casted_int->value.type)) @@ -20815,19 +20876,48 @@ static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstru if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_const(ira, &instruction->base, dest_type); + IrInstruction *result = ir_const(ira, source_instr, ptr_type); result->value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr; result->value.data.x_ptr.mut = ConstPtrMutRuntimeVar; result->value.data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&val->data.x_bigint); return result; } - IrInstruction *result = ir_build_int_to_ptr(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, nullptr, casted_int); - result->value.type = dest_type; + IrInstruction *result = ir_build_int_to_ptr(&ira->new_irb, source_instr->scope, + source_instr->source_node, nullptr, casted_int); + result->value.type = ptr_type; return result; } +static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) { + Error err; + IrInstruction *dest_type_value = instruction->dest_type->child; + ZigType *dest_type = ir_resolve_type(ira, dest_type_value); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_instruction; + + // We explicitly check for the size, so we can use get_src_ptr_type + if (get_src_ptr_type(dest_type) == nullptr) { + ir_add_error(ira, dest_type_value, buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_instruction; + } + + if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_instruction; + if (!type_has_bits(dest_type)) { + ir_add_error(ira, dest_type_value, + buf_sprintf("type '%s' has 0 bits and cannot store information", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_instruction; + } + + + IrInstruction *target = instruction->target->child; + if (type_is_invalid(target->value.type)) + return ira->codegen->invalid_instruction; + + return ir_analyze_int_to_ptr(ira, &instruction->base, target, dest_type); +} + static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, IrInstructionDeclRef *instruction) { diff --git a/src/parser.cpp b/src/parser.cpp index 1c1af87c51..160a7268b0 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2779,6 +2779,7 @@ static AstNode *ast_parse_array_type_start(ParseContext *pc) { // <- ASTERISK // / ASTERISK2 // / LBRACKET ASTERISK RBRACKET +// / LBRACKET ASTERISK C RBRACKET static AstNode *ast_parse_ptr_type_start(ParseContext *pc) { Token *asterisk = eat_token_if(pc, TokenIdStar); if (asterisk != nullptr) { @@ -2804,6 +2805,13 @@ static AstNode *ast_parse_ptr_type_start(ParseContext *pc) { return res; } + Token *cptr = eat_token_if(pc, TokenIdBracketStarCBracket); + if (cptr != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypePointerType, cptr); + res->data.pointer_type.star_token = cptr; + return res; + } + return nullptr; } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 3acd605748..9ff6ed3bbe 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -221,6 +221,7 @@ enum TokenizeState { TokenizeStateError, TokenizeStateLBracket, TokenizeStateLBracketStar, + TokenizeStateLBracketStarC, }; @@ -846,7 +847,6 @@ void tokenize(Buf *buf, Tokenization *out) { switch (c) { case '*': t.state = TokenizeStateLBracketStar; - set_token_id(&t, t.cur_tok, TokenIdBracketStarBracket); break; default: // reinterpret as just an lbracket @@ -857,6 +857,21 @@ void tokenize(Buf *buf, Tokenization *out) { } break; case TokenizeStateLBracketStar: + switch (c) { + case 'c': + t.state = TokenizeStateLBracketStarC; + set_token_id(&t, t.cur_tok, TokenIdBracketStarCBracket); + break; + case ']': + set_token_id(&t, t.cur_tok, TokenIdBracketStarBracket); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + invalid_char_error(&t, c); + } + break; + case TokenizeStateLBracketStarC: switch (c) { case ']': end_token(&t); @@ -1491,6 +1506,7 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateLineStringContinue: case TokenizeStateLineStringContinueC: case TokenizeStateLBracketStar: + case TokenizeStateLBracketStarC: tokenize_error(&t, "unexpected EOF"); break; case TokenizeStateLineComment: @@ -1528,6 +1544,7 @@ const char * token_name(TokenId id) { case TokenIdBitShiftRightEq: return ">>="; case TokenIdBitXorEq: return "^="; case TokenIdBracketStarBracket: return "[*]"; + case TokenIdBracketStarCBracket: return "[*c]"; case TokenIdCharLiteral: return "CharLiteral"; case TokenIdCmpEq: return "=="; case TokenIdCmpGreaterOrEq: return ">="; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 17f36699b3..62117b5779 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -29,6 +29,7 @@ enum TokenId { TokenIdBitShiftRightEq, TokenIdBitXorEq, TokenIdBracketStarBracket, + TokenIdBracketStarCBracket, TokenIdCharLiteral, TokenIdCmpEq, TokenIdCmpGreaterOrEq, diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig index 47b19700ee..6969e151df 100644 --- a/test/stage1/behavior/pointers.zig +++ b/test/stage1/behavior/pointers.zig @@ -42,3 +42,9 @@ test "double pointer parsing" { fn PtrOf(comptime T: type) type { return *T; } + +test "assigning integer to C pointer" { + var x: i32 = 0; + var ptr: [*c]u8 = 0; + var ptr2: [*c]u8 = x; +} -- cgit v1.2.3 From 661fc79fba242f2ad72ede06d4e23e635a1b1452 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 10 Feb 2019 12:02:38 -0500 Subject: langref: update grammar with c pointers See #1059 --- doc/langref.html.in | 7 +++++-- src/parser.cpp | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'src/parser.cpp') diff --git a/doc/langref.html.in b/doc/langref.html.in index 779eb6a31b..82ec13c9bb 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -8164,7 +8164,8 @@ ArrayTypeStart <- LBRACKET Expr? RBRACKET PtrTypeStart <- ASTERISK / ASTERISK2 - / LBRACKET ASTERISK RBRACKET + / PTRUNKNOWN + / PTRC # ContainerDecl specific ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE @@ -8262,7 +8263,7 @@ LARROW2 <- '<<' ![=] skip LARROW2EQUAL <- '<<=' skip LARROWEQUAL <- '<=' skip LBRACE <- '{' skip -LBRACKET <- '[' skip +LBRACKET <- '[' ![*] skip LPAREN <- '(' skip MINUS <- '-' ![%=>] skip MINUSEQUAL <- '-=' skip @@ -8279,6 +8280,8 @@ PLUS2 <- '++' skip PLUSEQUAL <- '+=' skip PLUSPERCENT <- '+%' ![=] skip PLUSPERCENTEQUAL <- '+%=' skip +PTRC <- '[*c]' skip +PTRUNKNOWN <- '[*]' skip QUESTIONMARK <- '?' skip RARROW <- '>' ![>=] skip RARROW2 <- '>>' ![=] skip diff --git a/src/parser.cpp b/src/parser.cpp index 160a7268b0..3a6ce04647 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2778,8 +2778,8 @@ static AstNode *ast_parse_array_type_start(ParseContext *pc) { // PtrTypeStart // <- ASTERISK // / ASTERISK2 -// / LBRACKET ASTERISK RBRACKET -// / LBRACKET ASTERISK C RBRACKET +// / PTRUNKNOWN +// / PTRC static AstNode *ast_parse_ptr_type_start(ParseContext *pc) { Token *asterisk = eat_token_if(pc, TokenIdStar); if (asterisk != nullptr) { -- cgit v1.2.3